/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ww8attributeoutput.hxx" #include "writerhelper.hxx" #include "writerwordglue.hxx" #include "wrtww8.hxx" #include "ww8par.hxx" using namespace ::com::sun::star; using namespace sw::types; using namespace sw::util; sal_uInt16 MSWordExportBase::DuplicateNumRule( const SwNumRule *pRule, sal_uInt8 nLevel, sal_uInt16 nVal ) { sal_uInt16 nNumId = USHRT_MAX; const OUString sPrefix("WW8TempExport" + OUString::number( m_nUniqueList++ )); SwNumRule* pMyNumRule = new SwNumRule( m_pDoc->GetUniqueNumRuleName( &sPrefix ), SvxNumberFormat::LABEL_WIDTH_AND_POSITION ); m_pUsedNumTable->push_back( pMyNumRule ); for ( sal_uInt16 i = 0; i < MAXLEVEL; i++ ) { const SwNumFormat& rSubRule = pRule->Get(i); pMyNumRule->Set( i, rSubRule ); } SwNumFormat aNumFormat( pMyNumRule->Get( nLevel ) ); aNumFormat.SetStart( nVal ); pMyNumRule->Set( nLevel, aNumFormat ); nNumId = GetId( *pMyNumRule ); // Map the old list to our new list m_aRuleDuplicates[GetId( *pRule )] = nNumId; return nNumId; } sal_uInt16 MSWordExportBase::GetId( const SwNumRule& rNumRule ) { if ( !m_pUsedNumTable ) { m_pUsedNumTable = new SwNumRuleTable; m_pUsedNumTable->insert( m_pUsedNumTable->begin(), m_pDoc->GetNumRuleTable().begin(), m_pDoc->GetNumRuleTable().end() ); // Check, if the outline rule is already inserted into . // If yes, do not insert it again. bool bOutlineRuleAdded( false ); for ( sal_uInt16 n = m_pUsedNumTable->size(); n; ) { const SwNumRule& rRule = *(*m_pUsedNumTable)[ --n ]; if ( !SwDoc::IsUsed( rRule ) ) { m_pUsedNumTable->erase( m_pUsedNumTable->begin() + n ); } else if ( &rRule == m_pDoc->GetOutlineNumRule() ) { bOutlineRuleAdded = true; } } if ( !bOutlineRuleAdded ) { // still need to paste the OutlineRule SwNumRule* pR = m_pDoc->GetOutlineNumRule(); m_pUsedNumTable->push_back( pR ); } } SwNumRule* p = const_cast(&rNumRule); sal_uInt16 nRet = static_cast(m_pUsedNumTable->GetPos(p)); // Is this list now duplicated into a new list which we should use // #i77812# - perform 'deep' search in duplication map std::map::const_iterator aResult = m_aRuleDuplicates.end(); do { aResult = m_aRuleDuplicates.find(nRet); if ( aResult != m_aRuleDuplicates.end() ) { nRet = (*aResult).second; } } while ( aResult != m_aRuleDuplicates.end() ); return nRet; } // GetFirstLineOffset should problem never appear unadorned apart from // here in the ww export filter sal_Int16 GetWordFirstLineOffset(const SwNumFormat &rFormat) { OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION, " - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" ); short nFirstLineOffset; if (rFormat.GetNumAdjust() == SvxAdjust::Right) nFirstLineOffset = -rFormat.GetCharTextDistance(); else nFirstLineOffset = rFormat.GetFirstLineOffset(); return nFirstLineOffset; } void WW8Export::WriteNumbering() { if ( !m_pUsedNumTable ) return; // no numbering is used // list formats - LSTF pFib->m_fcPlcfLst = pTableStrm->Tell(); SwWW8Writer::WriteShort( *pTableStrm, m_pUsedNumTable->size() ); NumberingDefinitions(); // set len to FIB pFib->m_lcbPlcfLst = pTableStrm->Tell() - pFib->m_fcPlcfLst; // list formats - LVLF AbstractNumberingDefinitions(); // list formats - LFO OutOverrideListTab(); // list formats - ListNames OutListNamesTab(); } void WW8AttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule ) { SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nId ); SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nId ); // not associated with a Style for ( int i = 0; i < WW8ListManager::nMaxLevel; ++i ) SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, 0xFFF ); sal_uInt8 nFlags = 0; if ( rRule.IsContinusNum() ) nFlags |= 0x1; m_rWW8Export.pTableStrm->WriteUChar( nFlags ).WriteUChar( 0/*nDummy*/ ); } void MSWordExportBase::NumberingDefinitions() { if ( !m_pUsedNumTable ) return; // no numbering is used sal_uInt16 nCount = m_pUsedNumTable->size(); // Write static data of SwNumRule - LSTF for ( sal_uInt16 n = 0; n < nCount; ++n ) { const SwNumRule& rRule = *(*m_pUsedNumTable)[ n ]; AttrOutput().NumberingDefinition( n + 1, rRule ); } } static sal_uInt8 GetLevelNFC( sal_uInt16 eNumType, const SfxItemSet *pOutSet) { sal_uInt8 nRet = 0; switch( eNumType ) { case SVX_NUM_CHARS_UPPER_LETTER: case SVX_NUM_CHARS_UPPER_LETTER_N: nRet = 3; break; case SVX_NUM_CHARS_LOWER_LETTER: case SVX_NUM_CHARS_LOWER_LETTER_N: nRet = 4; break; case SVX_NUM_ROMAN_UPPER: nRet = 1; break; case SVX_NUM_ROMAN_LOWER: nRet = 2; break; case SVX_NUM_BITMAP: case SVX_NUM_CHAR_SPECIAL: nRet = 23; break; case SVX_NUM_FULL_WIDTH_ARABIC: nRet = 14; break; case SVX_NUM_CIRCLE_NUMBER: nRet = 18;break; case SVX_NUM_NUMBER_LOWER_ZH: nRet = 35; if ( pOutSet ) { const SvxLanguageItem rLang = static_cast( pOutSet->Get( RES_CHRATR_CJK_LANGUAGE) ); const LanguageType eLang = rLang.GetLanguage(); if (LANGUAGE_CHINESE_SIMPLIFIED ==eLang) { nRet = 39; } } break; case SVX_NUM_NUMBER_UPPER_ZH: nRet = 38; break; case SVX_NUM_NUMBER_UPPER_ZH_TW: nRet = 34;break; case SVX_NUM_TIAN_GAN_ZH: nRet = 30; break; case SVX_NUM_DI_ZI_ZH: nRet = 31; break; case SVX_NUM_NUMBER_TRADITIONAL_JA: nRet = 16; break; case SVX_NUM_AIU_FULLWIDTH_JA: nRet = 20; break; case SVX_NUM_AIU_HALFWIDTH_JA: nRet = 12; break; case SVX_NUM_IROHA_FULLWIDTH_JA: nRet = 21; break; case SVX_NUM_IROHA_HALFWIDTH_JA: nRet = 13; break; case style::NumberingType::HANGUL_SYLLABLE_KO: nRet = 24; break;// ganada case style::NumberingType::HANGUL_JAMO_KO: nRet = 25; break;// chosung case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: nRet = 24; break; case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: nRet = 25; break; case style::NumberingType::NUMBER_HANGUL_KO: nRet = 41; break; case style::NumberingType::NUMBER_UPPER_KO: nRet = 44; break; case SVX_NUM_NUMBER_NONE: nRet = 0xff; break; } return nRet; } void WW8AttributeOutput::NumberingLevel( sal_uInt8 /*nLevel*/, sal_uInt16 nStart, sal_uInt16 nNumberingType, SvxAdjust eAdjust, const sal_uInt8 *pNumLvlPos, sal_uInt8 nFollow, const wwFont *pFont, const SfxItemSet *pOutSet, sal_Int16 nIndentAt, sal_Int16 nFirstLineIndex, sal_Int16 nListTabPos, const OUString &rNumberingString, const SvxBrushItem* pBrush //For i120928,to transfer graphic of bullet ) { // Start value SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nStart ); // Type m_rWW8Export.pTableStrm->WriteUChar( GetLevelNFC( nNumberingType ,pOutSet) ); // Justification sal_uInt8 nAlign; switch ( eAdjust ) { case SvxAdjust::Center: nAlign = 1; break; case SvxAdjust::Right: nAlign = 2; break; default: nAlign = 0; break; } m_rWW8Export.pTableStrm->WriteUChar( nAlign ); // Write the rgbxchNums[9], positions of placeholders for paragraph // numbers in the text m_rWW8Export.pTableStrm->WriteBytes(pNumLvlPos, WW8ListManager::nMaxLevel); // Type of the character between the bullet and the text m_rWW8Export.pTableStrm->WriteUChar( nFollow ); // dxaSoace/dxaIndent (Word 6 compatibility) SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, 0 ); SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, 0 ); // cbGrpprlChpx ww::bytes aCharAtrs; if ( pOutSet ) { ww::bytes* pOldpO = m_rWW8Export.pO; m_rWW8Export.pO = &aCharAtrs; if ( pFont ) { sal_uInt16 nFontID = m_rWW8Export.m_aFontHelper.GetId( *pFont ); m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc0 ); m_rWW8Export.InsUInt16( nFontID ); m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc2 ); m_rWW8Export.InsUInt16( nFontID ); } m_rWW8Export.OutputItemSet( *pOutSet, false, true, i18n::ScriptType::LATIN, m_rWW8Export.m_bExportModeRTF ); //For i120928,achieve graphic's index of bullet from the bullet bookmark if (SVX_NUM_BITMAP == nNumberingType && pBrush) { int nIndex = m_rWW8Export.GetGrfIndex(*pBrush); if ( nIndex != -1 ) { m_rWW8Export.InsUInt16(NS_sprm::sprmCPbiIBullet); m_rWW8Export.InsUInt32(nIndex); m_rWW8Export.InsUInt16(NS_sprm::sprmCPbiGrf); m_rWW8Export.InsUInt16(1); } } m_rWW8Export.pO = pOldpO; } m_rWW8Export.pTableStrm->WriteUChar( sal_uInt8( aCharAtrs.size() ) ); // cbGrpprlPapx sal_uInt8 aPapSprms [] = { 0x5e, 0x84, 0, 0, // sprmPDxaLeft 0x60, 0x84, 0, 0, // sprmPDxaLeft1 0x15, 0xc6, 0x05, 0x00, 0x01, 0, 0, 0x06 }; m_rWW8Export.pTableStrm->WriteUChar( sal_uInt8( sizeof( aPapSprms ) ) ); // reserved SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, 0 ); // pap sprms sal_uInt8* pData = aPapSprms + 2; Set_UInt16( pData, nIndentAt ); pData += 2; Set_UInt16( pData, nFirstLineIndex ); pData += 5; Set_UInt16( pData, nListTabPos ); m_rWW8Export.pTableStrm->WriteBytes(aPapSprms, sizeof(aPapSprms)); // write Chpx if( !aCharAtrs.empty() ) m_rWW8Export.pTableStrm->WriteBytes(aCharAtrs.data(), aCharAtrs.size()); // write the num string SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, rNumberingString.getLength() ); SwWW8Writer::WriteString16( *m_rWW8Export.pTableStrm, rNumberingString, false ); } void MSWordExportBase::AbstractNumberingDefinitions() { sal_uInt16 nCount = m_pUsedNumTable->size(); sal_uInt16 n; // prepare the NodeNum to generate the NumString SwNumberTree::tNumberVector aNumVector; for ( n = 0; n < WW8ListManager::nMaxLevel; ++n ) aNumVector.push_back( n ); for( n = 0; n < nCount; ++n ) { AttrOutput().StartAbstractNumbering( n + 1 ); const SwNumRule& rRule = *(*m_pUsedNumTable)[ n ]; sal_uInt8 nLvl; sal_uInt8 nLevels = static_cast< sal_uInt8 >(rRule.IsContinusNum() ? WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel); for( nLvl = 0; nLvl < nLevels; ++nLvl ) { // write the static data of the SwNumFormat of this level sal_uInt8 aNumLvlPos[WW8ListManager::nMaxLevel] = { 0,0,0,0,0,0,0,0,0 }; const SwNumFormat& rFormat = rRule.Get( nLvl ); sal_uInt8 nFollow = 0; // #i86652# if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { nFollow = 2; // ixchFollow: 0 - tab, 1 - blank, 2 - nothing } else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) { switch ( rFormat.GetLabelFollowedBy() ) { case SvxNumberFormat::LISTTAB: { // 0 (tab) unless there would be no content before the tab, in which case 2 (nothing) nFollow = (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) ? 0 : 2; } break; case SvxNumberFormat::SPACE: { // 1 (space) unless there would be no content before the space in which case 2 (nothing) nFollow = (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) ? 1 : 2; } break; case SvxNumberFormat::NOTHING: { nFollow = 2; } break; default: { nFollow = 0; OSL_FAIL( "unknown GetLabelFollowedBy() return value" ); } } } // Build the NumString for this Level OUString sNumStr; OUString sFontName; bool bWriteBullet = false; const vcl::Font* pBulletFont=nullptr; rtl_TextEncoding eChrSet=0; FontFamily eFamily=FAMILY_DECORATIVE; if( SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() || SVX_NUM_BITMAP == rFormat.GetNumberingType() ) { sNumStr = OUString(rFormat.GetBulletChar()); bWriteBullet = true; pBulletFont = rFormat.GetBulletFont(); if (!pBulletFont) { pBulletFont = &numfunc::GetDefBulletFont(); } eChrSet = pBulletFont->GetCharSet(); sFontName = pBulletFont->GetFamilyName(); eFamily = pBulletFont->GetFamilyType(); if ( IsStarSymbol(sFontName) ) SubstituteBullet( sNumStr, eChrSet, sFontName ); // #i86652# if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { // , if minimum label width equals 0 and // minimum distance between label and text equals 0 nFollow = ( rFormat.GetFirstLineOffset() == 0 && rFormat.GetCharTextDistance() == 0 ) ? 2 : 0; // ixchFollow: 0 - tab, 1 - blank, 2 - nothing } } else { if (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) { sal_uInt8* pLvlPos = aNumLvlPos; // the numbering string has to be restrict // to the level currently working on. sNumStr = rRule.MakeNumString(aNumVector, false, true, nLvl); // now search the nums in the string for( sal_uInt8 i = 0; i <= nLvl; ++i ) { OUString sSrch( OUString::number( i )); sal_Int32 nFnd = sNumStr.indexOf( sSrch ); if( -1 != nFnd ) { *pLvlPos = (sal_uInt8)(nFnd + rFormat.GetPrefix().getLength() + 1 ); ++pLvlPos; sNumStr = sNumStr.replaceAt( nFnd, 1, OUString((char)i) ); } } // #i86652# if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { // , if minimum label width equals 0 and // minimum distance between label and text equals 0 nFollow = ( rFormat.GetFirstLineOffset() == 0 && rFormat.GetCharTextDistance() == 0 ) ? 2 : 0; // ixchFollow: 0 - tab, 1 - blank, 2 - nothing } } if( !rFormat.GetPrefix().isEmpty() ) sNumStr = rFormat.GetPrefix() + sNumStr; sNumStr += rFormat.GetSuffix(); } // Attributes of the numbering wwFont *pPseudoFont = nullptr; const SfxItemSet* pOutSet = nullptr; // cbGrpprlChpx SfxItemSet aSet( m_pDoc->GetAttrPool(), svl::Items{} ); if ( rFormat.GetCharFormat() || bWriteBullet ) { if ( bWriteBullet ) { pOutSet = &aSet; if ( rFormat.GetCharFormat() ) aSet.Put( rFormat.GetCharFormat()->GetAttrSet() ); aSet.ClearItem( RES_CHRATR_CJK_FONT ); aSet.ClearItem( RES_CHRATR_FONT ); if ( sFontName.isEmpty() ) sFontName = pBulletFont->GetFamilyName(); pPseudoFont = new wwFont( sFontName, pBulletFont->GetPitch(), eFamily, eChrSet); } else pOutSet = &rFormat.GetCharFormat()->GetAttrSet(); } sal_Int16 nIndentAt = 0; sal_Int16 nFirstLineIndex = 0; sal_Int16 nListTabPos = 0; // #i86652# if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) { nIndentAt = nListTabPos = rFormat.GetAbsLSpace(); nFirstLineIndex = GetWordFirstLineOffset(rFormat); } else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ) { nIndentAt = static_cast(rFormat.GetIndentAt()); nFirstLineIndex = static_cast(rFormat.GetFirstLineIndent()); nListTabPos = rFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB? static_cast( rFormat.GetListtabPos() ) : 0; } AttrOutput().NumberingLevel( nLvl, rFormat.GetStart(), rFormat.GetNumberingType(), rFormat.GetNumAdjust(), aNumLvlPos, nFollow, pPseudoFont, pOutSet, nIndentAt, nFirstLineIndex, nListTabPos, sNumStr, rFormat.GetNumberingType()==SVX_NUM_BITMAP ? rFormat.GetBrush():nullptr); delete pPseudoFont; } AttrOutput().EndAbstractNumbering(); } } void WW8Export::OutOverrideListTab() { if( !m_pUsedNumTable ) return ; // no numbering is used // write the "list format override" - LFO sal_uInt16 nCount = m_pUsedNumTable->size(); sal_uInt16 n; pFib->m_fcPlfLfo = pTableStrm->Tell(); SwWW8Writer::WriteLong( *pTableStrm, nCount ); for( n = 0; n < nCount; ++n ) { SwWW8Writer::WriteLong( *pTableStrm, n + 1 ); SwWW8Writer::FillCount( *pTableStrm, 12 ); } for( n = 0; n < nCount; ++n ) SwWW8Writer::WriteLong( *pTableStrm, -1 ); // no overwrite // set len to FIB pFib->m_lcbPlfLfo = pTableStrm->Tell() - pFib->m_fcPlfLfo; } void WW8Export::OutListNamesTab() { if( !m_pUsedNumTable ) return ; // no numbering is used // write the "list format override" - LFO sal_uInt16 nNms = 0, nCount = m_pUsedNumTable->size(); pFib->m_fcSttbListNames = pTableStrm->Tell(); SwWW8Writer::WriteShort( *pTableStrm, -1 ); SwWW8Writer::WriteLong( *pTableStrm, nCount ); for( ; nNms < nCount; ++nNms ) { const SwNumRule& rRule = *(*m_pUsedNumTable)[ nNms ]; OUString sNm; if( !rRule.IsAutoRule() ) sNm = rRule.GetName(); SwWW8Writer::WriteShort( *pTableStrm, sNm.getLength() ); if (!sNm.isEmpty()) SwWW8Writer::WriteString16(*pTableStrm, sNm, false); } SwWW8Writer::WriteLong( *pTableStrm, pFib->m_fcSttbListNames + 2, nNms ); // set len to FIB pFib->m_lcbSttbListNames = pTableStrm->Tell() - pFib->m_fcSttbListNames; } void MSWordExportBase::SubstituteBullet( OUString& rNumStr, rtl_TextEncoding& rChrSet, OUString& rFontName ) const { if (!m_bSubstituteBullets) return; OUString sFontName = rFontName; // If Bullet char is "", don't change if (rNumStr[0] != u'\0') { rNumStr = rNumStr.replaceAt(0, 1, OUString( msfilter::util::bestFitOpenSymbolToMSFont(rNumStr[0], rChrSet, sFontName))); } rFontName = sFontName; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */