1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <hintids.hxx>
21 #include <vcl/font.hxx>
22 #include <editeng/fontitem.hxx>
23 #include <editeng/lrspitem.hxx>
24 #include <editeng/langitem.hxx>
25 #include <doc.hxx>
26 #include <docary.hxx>
27 #include <numrule.hxx>
28 #include <paratr.hxx>
29 #include <charfmt.hxx>
30 #include <ndtxt.hxx>
31 #include <unotools/fontcfg.hxx>
32 #include <com/sun/star/i18n/ScriptType.hpp>
33 
34 #include "sprmids.hxx"
35 
36 #include "ww8attributeoutput.hxx"
37 #include "writerhelper.hxx"
38 #include "writerwordglue.hxx"
39 #include "wrtww8.hxx"
40 #include "ww8par.hxx"
41 
42 #include <mutex>
43 
44 using namespace ::com::sun::star;
45 using namespace sw::types;
46 using namespace sw::util;
47 
DuplicateNumRuleImpl(const SwNumRule * pRule)48 SwNumRule* MSWordExportBase::DuplicateNumRuleImpl(const SwNumRule *pRule)
49 {
50     const OUString sPrefix("WW8TempExport" + OUString::number( m_nUniqueList++ ));
51     SwNumRule* pMyNumRule =
52             new SwNumRule( m_pDoc->GetUniqueNumRuleName( &sPrefix ),
53                            SvxNumberFormat::LABEL_WIDTH_AND_POSITION );
54     m_pUsedNumTable->push_back( pMyNumRule );
55 
56     for ( sal_uInt16 i = 0; i < MAXLEVEL; i++ )
57     {
58         const SwNumFormat& rSubRule = pRule->Get(i);
59         pMyNumRule->Set( i, rSubRule );
60     }
61     return pMyNumRule;
62 }
63 
64 // multiple SwList can be based on the same SwNumRule; ensure one w:abstractNum
65 // per SwList
DuplicateAbsNum(OUString const & rListId,SwNumRule const & rAbstractRule)66 sal_uInt16 MSWordExportBase::DuplicateAbsNum(OUString const& rListId,
67         SwNumRule const& rAbstractRule)
68 {
69     auto const it(m_Lists.find(rListId));
70     if (it != m_Lists.end())
71     {
72         return it->second;
73     }
74     else
75     {
76         auto const pNewAbstractRule = DuplicateNumRuleImpl(&rAbstractRule);
77         assert(GetNumberingId(*pNewAbstractRule) == m_pUsedNumTable->size() - 1);
78         (void) pNewAbstractRule;
79         m_Lists.insert(std::make_pair(rListId, m_pUsedNumTable->size() - 1));
80         return m_pUsedNumTable->size() - 1;
81     }
82 }
83 
84 // Ideally we want to map SwList to w:abstractNum and SwNumRule to w:num
85 // The current approach is to keep exporting every SwNumRule to
86 // 1 w:abstractNum and 1 w:num, and then add extra w:num via this function
87 // that reference an existing w:abstractNum and may override its formatting;
88 // of course this will end up exporting some w:num that aren't actually used.
OverrideNumRule(SwNumRule const & rExistingRule,OUString const & rListId,SwNumRule const & rAbstractRule)89 sal_uInt16 MSWordExportBase::OverrideNumRule(
90         SwNumRule const& rExistingRule,
91         OUString const& rListId,
92         SwNumRule const& rAbstractRule)
93 {
94     const sal_uInt16 numdef = GetNumberingId(rExistingRule);
95 
96     const sal_uInt16 absnumdef = rListId == rAbstractRule.GetDefaultListId()
97         ? GetNumberingId(rAbstractRule)
98         : DuplicateAbsNum(rListId, rAbstractRule);
99     auto const mapping = std::make_pair(numdef, absnumdef);
100 
101     auto it = m_OverridingNums.insert(std::make_pair(m_pUsedNumTable->size(), mapping));
102 
103     m_pUsedNumTable->push_back(nullptr); // dummy, it's unique_ptr...
104     ++m_nUniqueList; // counter for DuplicateNumRule...
105 
106     return it.first->first;
107 }
108 
AddListLevelOverride(sal_uInt16 nListId,sal_uInt16 nLevelNum,sal_uInt16 nStartAt)109 void MSWordExportBase::AddListLevelOverride(sal_uInt16 nListId,
110     sal_uInt16 nLevelNum,
111     sal_uInt16 nStartAt)
112 {
113     m_ListLevelOverrides[nListId][nLevelNum] = nStartAt;
114 }
115 
GetNumberingId(const SwNumRule & rNumRule)116 sal_uInt16 MSWordExportBase::GetNumberingId( const SwNumRule& rNumRule )
117 {
118     if ( !m_pUsedNumTable )
119     {
120         m_pUsedNumTable.reset(new SwNumRuleTable);
121         m_pUsedNumTable->insert( m_pUsedNumTable->begin(), m_pDoc->GetNumRuleTable().begin(), m_pDoc->GetNumRuleTable().end() );
122         // Check, if the outline rule is already inserted into <pUsedNumTable>.
123         // If yes, do not insert it again.
124         bool bOutlineRuleAdded( false );
125         for ( sal_uInt16 n = m_pUsedNumTable->size(); n; )
126         {
127             const SwNumRule& rRule = *(*m_pUsedNumTable)[ --n ];
128             if ( !SwDoc::IsUsed( rRule ) )
129             {
130                 m_pUsedNumTable->erase( m_pUsedNumTable->begin() + n );
131             }
132             else if ( &rRule == m_pDoc->GetOutlineNumRule() )
133             {
134                 bOutlineRuleAdded = true;
135             }
136         }
137 
138         if ( !bOutlineRuleAdded )
139         {
140             // still need to paste the OutlineRule
141             SwNumRule* pR = m_pDoc->GetOutlineNumRule();
142             m_pUsedNumTable->push_back( pR );
143         }
144     }
145     SwNumRule* p = const_cast<SwNumRule*>(&rNumRule);
146     sal_uInt16 nRet = static_cast<sal_uInt16>(m_pUsedNumTable->GetPos(p));
147 
148     return nRet;
149 }
150 
151 // GetFirstLineOffset should problem never appear unadorned apart from
152 // here in the ww export filter
GetWordFirstLineOffset(const SwNumFormat & rFormat)153 sal_Int16 GetWordFirstLineOffset(const SwNumFormat &rFormat)
154 {
155     OSL_ENSURE( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION,
156             "<GetWordFirstLineOffset> - misusage: position-and-space-mode does not equal LABEL_WIDTH_AND_POSITION" );
157 
158     short nFirstLineOffset;
159     if (rFormat.GetNumAdjust() == SvxAdjust::Right)
160         nFirstLineOffset = -rFormat.GetCharTextDistance();
161     else
162         nFirstLineOffset = rFormat.GetFirstLineOffset(); //TODO: overflow
163     return nFirstLineOffset;
164 }
165 
WriteNumbering()166 void WW8Export::WriteNumbering()
167 {
168     if ( !m_pUsedNumTable )
169         return; // no numbering is used
170 
171     // list formats - LSTF
172     pFib->m_fcPlcfLst = pTableStrm->Tell();
173     SwWW8Writer::WriteShort( *pTableStrm, m_pUsedNumTable->size() );
174     NumberingDefinitions();
175     // set len to FIB
176     pFib->m_lcbPlcfLst = pTableStrm->Tell() - pFib->m_fcPlcfLst;
177 
178     // list formats - LVLF
179     AbstractNumberingDefinitions();
180 
181     // list formats - LFO
182     OutOverrideListTab();
183 
184     // list formats - ListNames
185     OutListNamesTab();
186 }
187 
NumberingDefinition(sal_uInt16 nId,const SwNumRule & rRule)188 void WW8AttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule )
189 {
190     SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nId );
191     SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nId );
192 
193     // not associated with a Style
194     for ( int i = 0; i < WW8ListManager::nMaxLevel; ++i )
195         SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, 0xFFF );
196 
197     sal_uInt8 nFlags = 0;
198     if ( rRule.IsContinusNum() )
199         nFlags |= 0x1;
200 
201     m_rWW8Export.pTableStrm->WriteUChar( nFlags ).WriteUChar( 0/*nDummy*/ );
202 }
203 
NumberingDefinitions()204 void MSWordExportBase::NumberingDefinitions()
205 {
206     if ( !m_pUsedNumTable )
207         return; // no numbering is used
208 
209     sal_uInt16 nCount = m_pUsedNumTable->size();
210 
211     // Write static data of SwNumRule - LSTF
212     for ( sal_uInt16 n = 0; n < nCount; ++n )
213     {
214         const SwNumRule * pRule = (*m_pUsedNumTable)[ n ];
215         if (pRule)
216         {
217             AttrOutput().NumberingDefinition(n + 1, *pRule);
218         }
219         else
220         {
221             auto it = m_OverridingNums.find(n);
222             assert(it != m_OverridingNums.end());
223             pRule = (*m_pUsedNumTable)[it->second.first];
224             assert(pRule);
225             AttrOutput().OverrideNumberingDefinition(*pRule, n + 1, it->second.second + 1, m_ListLevelOverrides[n]);
226         }
227     }
228 }
229 
GetLevelNFC(sal_uInt16 eNumType,const SfxItemSet * pOutSet)230 static sal_uInt8 GetLevelNFC(  sal_uInt16 eNumType, const SfxItemSet *pOutSet)
231 {
232     sal_uInt8 nRet = 0;
233     switch( eNumType )
234     {
235     case SVX_NUM_CHARS_UPPER_LETTER:
236     case SVX_NUM_CHARS_UPPER_LETTER_N:  nRet = 3;       break;
237     case SVX_NUM_CHARS_LOWER_LETTER:
238     case SVX_NUM_CHARS_LOWER_LETTER_N:  nRet = 4;       break;
239     case SVX_NUM_ROMAN_UPPER:           nRet = 1;       break;
240     case SVX_NUM_ROMAN_LOWER:           nRet = 2;       break;
241 
242     case SVX_NUM_BITMAP:
243     case SVX_NUM_CHAR_SPECIAL:         nRet = 23;      break;
244     case SVX_NUM_FULL_WIDTH_ARABIC: nRet = 14; break;
245     case SVX_NUM_CIRCLE_NUMBER: nRet = 18;break;
246     case SVX_NUM_NUMBER_LOWER_ZH:
247         nRet = 35;
248         if ( pOutSet ) {
249             const SvxLanguageItem& rLang = pOutSet->Get( RES_CHRATR_CJK_LANGUAGE);
250             const LanguageType eLang = rLang.GetLanguage();
251             if (LANGUAGE_CHINESE_SIMPLIFIED ==eLang) {
252                 nRet = 39;
253             }
254         }
255         break;
256     case SVX_NUM_NUMBER_UPPER_ZH: nRet = 38; break;
257     case SVX_NUM_NUMBER_UPPER_ZH_TW: nRet = 34;break;
258     case SVX_NUM_TIAN_GAN_ZH: nRet = 30; break;
259     case SVX_NUM_DI_ZI_ZH: nRet = 31; break;
260     case SVX_NUM_NUMBER_TRADITIONAL_JA: nRet = 16; break;
261     case SVX_NUM_AIU_FULLWIDTH_JA: nRet = 20; break;
262     case SVX_NUM_AIU_HALFWIDTH_JA: nRet = 12; break;
263     case SVX_NUM_IROHA_FULLWIDTH_JA: nRet = 21; break;
264     case SVX_NUM_IROHA_HALFWIDTH_JA: nRet = 13; break;
265     case style::NumberingType::HANGUL_SYLLABLE_KO: nRet = 24; break;// ganada
266     case style::NumberingType::HANGUL_JAMO_KO: nRet = 25; break;// chosung
267     case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: nRet = 24; break;
268     case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: nRet = 25; break;
269     case style::NumberingType::NUMBER_HANGUL_KO: nRet = 41; break;
270     case style::NumberingType::NUMBER_UPPER_KO: nRet = 44; break;
271     case SVX_NUM_NUMBER_NONE:           nRet = 0xff;    break;
272     }
273     return nRet;
274 }
275 
276 
NumberingLevel(sal_uInt8,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)277 void WW8AttributeOutput::NumberingLevel( sal_uInt8 /*nLevel*/,
278         sal_uInt16 nStart,
279         sal_uInt16 nNumberingType,
280         SvxAdjust eAdjust,
281         const sal_uInt8 *pNumLvlPos,
282         sal_uInt8 nFollow,
283         const wwFont *pFont,
284         const SfxItemSet *pOutSet,
285         sal_Int16 nIndentAt,
286         sal_Int16 nFirstLineIndex,
287         sal_Int16 nListTabPos,
288         const OUString &rNumberingString,
289         const SvxBrushItem* pBrush //For i120928,to transfer graphic of bullet
290     )
291 {
292     // Start value
293     SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, nStart );
294 
295     // Type
296     m_rWW8Export.pTableStrm->WriteUChar( GetLevelNFC( nNumberingType ,pOutSet) );
297 
298     // Justification
299     sal_uInt8 nAlign;
300     switch ( eAdjust )
301     {
302     case SvxAdjust::Center:
303         nAlign = 1;
304         break;
305     case SvxAdjust::Right:
306         nAlign = 2;
307         break;
308     default:
309         nAlign = 0;
310         break;
311     }
312     m_rWW8Export.pTableStrm->WriteUChar( nAlign );
313 
314     // Write the rgbxchNums[9], positions of placeholders for paragraph
315     // numbers in the text
316     m_rWW8Export.pTableStrm->WriteBytes(pNumLvlPos, WW8ListManager::nMaxLevel);
317 
318     // Type of the character between the bullet and the text
319     m_rWW8Export.pTableStrm->WriteUChar( nFollow );
320 
321     // dxaSoace/dxaIndent (Word 6 compatibility)
322     SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, 0 );
323     SwWW8Writer::WriteLong( *m_rWW8Export.pTableStrm, 0 );
324 
325     // cbGrpprlChpx
326     std::unique_ptr<ww::bytes> pCharAtrs;
327     if ( pOutSet )
328     {
329         std::unique_ptr<ww::bytes> pOldpO = std::move(m_rWW8Export.pO);
330         m_rWW8Export.pO.reset(new ww::bytes);
331         if ( pFont )
332         {
333             sal_uInt16 nFontID = m_rWW8Export.m_aFontHelper.GetId( *pFont );
334 
335             m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc0 );
336             m_rWW8Export.InsUInt16( nFontID );
337             m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc2 );
338             m_rWW8Export.InsUInt16( nFontID );
339         }
340 
341         m_rWW8Export.OutputItemSet( *pOutSet, false, true, i18n::ScriptType::LATIN, m_rWW8Export.m_bExportModeRTF );
342         //For i120928,achieve graphic's index of bullet from the bullet bookmark
343         if (SVX_NUM_BITMAP == nNumberingType && pBrush)
344         {
345             int nIndex = m_rWW8Export.GetGrfIndex(*pBrush);
346             if ( nIndex != -1 )
347             {
348                 m_rWW8Export.InsUInt16(NS_sprm::sprmCPbiIBullet);
349                 m_rWW8Export.InsUInt32(nIndex);
350                 m_rWW8Export.InsUInt16(NS_sprm::sprmCPbiGrf);
351                 m_rWW8Export.InsUInt16(1);
352             }
353         }
354 
355         pCharAtrs = std::move(m_rWW8Export.pO);
356         m_rWW8Export.pO = std::move(pOldpO);
357     }
358     m_rWW8Export.pTableStrm->WriteUChar(sal_uInt8(pCharAtrs ? pCharAtrs->size() : 0));
359 
360     // cbGrpprlPapx
361     sal_uInt8 aPapSprms [] = {
362         0x5e, 0x84, 0, 0,               // sprmPDxaLeft
363         0x60, 0x84, 0, 0,               // sprmPDxaLeft1
364         0x15, 0xc6, 0x05, 0x00, 0x01, 0, 0, 0x06
365     };
366     m_rWW8Export.pTableStrm->WriteUChar( sal_uInt8( sizeof( aPapSprms ) ) );
367 
368     // reserved
369     SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, 0 );
370 
371     // pap sprms
372     sal_uInt8* pData = aPapSprms + 2;
373     Set_UInt16( pData, nIndentAt );
374     pData += 2;
375     Set_UInt16( pData, nFirstLineIndex );
376     pData += 5;
377     Set_UInt16( pData, nListTabPos );
378 
379     m_rWW8Export.pTableStrm->WriteBytes(aPapSprms, sizeof(aPapSprms));
380 
381     // write Chpx
382     if (pCharAtrs && !pCharAtrs->empty())
383         m_rWW8Export.pTableStrm->WriteBytes(pCharAtrs->data(), pCharAtrs->size());
384 
385     // write the num string
386     SwWW8Writer::WriteShort( *m_rWW8Export.pTableStrm, rNumberingString.getLength() );
387     SwWW8Writer::WriteString16( *m_rWW8Export.pTableStrm, rNumberingString, false );
388 }
389 
AbstractNumberingDefinitions()390 void MSWordExportBase::AbstractNumberingDefinitions()
391 {
392     sal_uInt16 nCount = m_pUsedNumTable->size();
393     sal_uInt16 n;
394 
395     for( n = 0; n < nCount; ++n )
396     {
397         if (nullptr == (*m_pUsedNumTable)[ n ])
398         {
399             continue;
400         }
401 
402         AttrOutput().StartAbstractNumbering( n + 1 );
403 
404         const SwNumRule& rRule = *(*m_pUsedNumTable)[ n ];
405         sal_uInt8 nLvl;
406         sal_uInt8 nLevels = static_cast< sal_uInt8 >(rRule.IsContinusNum() ?
407             WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel);
408         for( nLvl = 0; nLvl < nLevels; ++nLvl )
409         {
410             NumberingLevel(rRule, nLvl);
411         }
412 
413         AttrOutput().EndAbstractNumbering();
414     }
415 }
416 
NumberingLevel(SwNumRule const & rRule,sal_uInt8 const nLvl)417 void MSWordExportBase::NumberingLevel(
418         SwNumRule const& rRule, sal_uInt8 const nLvl)
419 {
420     // prepare the NodeNum to generate the NumString
421     static SwNumberTree::tNumberVector aNumVector;
422     static std::once_flag aInitOnce;
423     std::call_once(aInitOnce, []
424         {
425             for (int n = 0; n < WW8ListManager::nMaxLevel; ++n)
426             {
427                 aNumVector.push_back( n );
428             }
429         });
430 
431     // write the static data of the SwNumFormat of this level
432     sal_uInt8 aNumLvlPos[WW8ListManager::nMaxLevel] = { 0,0,0,0,0,0,0,0,0 };
433 
434     const SwNumFormat& rFormat = rRule.Get( nLvl );
435 
436     sal_uInt8 nFollow = 0;
437     // #i86652#
438     if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION)
439     {
440         // <nFollow = 2>, if minimum label width equals 0 and
441         // minimum distance between label and text equals 0
442         nFollow = (rFormat.GetFirstLineOffset() == 0 &&
443             rFormat.GetCharTextDistance() == 0)
444             ? 2 : 0;     // ixchFollow: 0 - tab, 1 - blank, 2 - nothing
445     }
446     else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT)
447     {
448         switch (rFormat.GetLabelFollowedBy())
449         {
450             case SvxNumberFormat::LISTTAB:
451             {
452                 // 0 (tab) unless there would be no content before the tab, in which case 2 (nothing)
453                 nFollow = (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) ? 0 : 2;
454             }
455             break;
456             case SvxNumberFormat::SPACE:
457             {
458                 // 1 (space) unless there would be no content before the space in which case 2 (nothing)
459                 nFollow = (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType()) ? 1 : 2;
460             }
461             break;
462             case SvxNumberFormat::NOTHING:
463             {
464                 nFollow = 2;
465             }
466             break;
467             default:
468             {
469                 nFollow = 0;
470                 OSL_FAIL( "unknown GetLabelFollowedBy() return value" );
471             }
472         }
473     }
474 
475     // Build the NumString for this Level
476     OUString sNumStr;
477     OUString sFontName;
478     bool bWriteBullet = false;
479     const vcl::Font* pBulletFont=nullptr;
480     rtl_TextEncoding eChrSet=0;
481     FontFamily eFamily=FAMILY_DECORATIVE;
482     if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() ||
483         SVX_NUM_BITMAP == rFormat.GetNumberingType())
484     {
485         // Use bullet
486         sNumStr = OUString(rFormat.GetBulletChar());
487     }
488     else
489     {
490         // Create level string
491         // For docx it is not the best way: we can just take it from rRule.Get(nLvl).GetListFormat()
492         // But for compatibility with doc we follow same routine
493         if (SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType())
494         {
495             sal_uInt8* pLvlPos = aNumLvlPos;
496             // the numbering string has to be restrict
497             // to the level currently working on.
498             sNumStr = rRule.MakeNumString(aNumVector, false, true, nLvl);
499 
500             // now search the nums in the string
501             for (sal_uInt8 i = 0; i <= nLvl; ++i)
502             {
503                 OUString sSrch(OUString::number(i));
504                 sal_Int32 nFnd = sNumStr.indexOf(sSrch);
505                 if (-1 != nFnd)
506                 {
507                     *pLvlPos = static_cast<sal_uInt8>(nFnd + rFormat.GetPrefix().getLength() + 1);
508                     ++pLvlPos;
509                     sNumStr = sNumStr.replaceAt(nFnd, 1, OUString(static_cast<char>(i)));
510                 }
511             }
512         }
513 
514         if (!rRule.Get(nLvl).HasListFormat())
515         {
516             if (!rFormat.GetPrefix().isEmpty())
517                 sNumStr = rFormat.GetPrefix() + sNumStr;
518             sNumStr += rFormat.GetSuffix();
519         }
520     }
521 
522     if (SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType() ||
523         SVX_NUM_BITMAP == rFormat.GetNumberingType())
524     {
525         bWriteBullet = true;
526 
527         pBulletFont = rFormat.GetBulletFont();
528         if (!pBulletFont)
529         {
530             pBulletFont = &numfunc::GetDefBulletFont();
531         }
532 
533         eChrSet = pBulletFont->GetCharSet();
534         sFontName = pBulletFont->GetFamilyName();
535         eFamily = pBulletFont->GetFamilyType();
536 
537         if (IsStarSymbol(sFontName))
538             SubstituteBullet(sNumStr, eChrSet, sFontName);
539     }
540 
541     // Attributes of the numbering
542     std::unique_ptr<wwFont> pPseudoFont;
543     const SfxItemSet* pOutSet = nullptr;
544 
545     // cbGrpprlChpx
546     SfxItemSet aSet( m_pDoc->GetAttrPool(), svl::Items<RES_CHRATR_BEGIN,
547                                           RES_CHRATR_END>{} );
548     if (rFormat.GetCharFormat() || bWriteBullet)
549     {
550         if (bWriteBullet)
551         {
552             pOutSet = &aSet;
553 
554             if (rFormat.GetCharFormat())
555                 aSet.Put( rFormat.GetCharFormat()->GetAttrSet() );
556             aSet.ClearItem( RES_CHRATR_CJK_FONT );
557             aSet.ClearItem( RES_CHRATR_FONT );
558 
559             if (sFontName.isEmpty())
560                 sFontName = pBulletFont->GetFamilyName();
561 
562             pPseudoFont.reset(new wwFont( sFontName, pBulletFont->GetPitch(),
563                 eFamily, eChrSet));
564         }
565         else
566             pOutSet = &rFormat.GetCharFormat()->GetAttrSet();
567     }
568 
569     sal_Int16 nIndentAt = 0;
570     sal_Int16 nFirstLineIndex = 0;
571     sal_Int16 nListTabPos = -1;
572 
573     // #i86652#
574     if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION)
575     {
576         nIndentAt = nListTabPos = rFormat.GetAbsLSpace(); //TODO: overflow
577         nFirstLineIndex = GetWordFirstLineOffset(rFormat);
578     }
579     else if (rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT)
580     {
581         nIndentAt = static_cast<sal_Int16>(rFormat.GetIndentAt());
582         nFirstLineIndex = static_cast<sal_Int16>(rFormat.GetFirstLineIndent());
583         nListTabPos = rFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB?
584                       static_cast<sal_Int16>( rFormat.GetListtabPos() ) : 0;
585     }
586 
587     AttrOutput().NumberingLevel( nLvl,
588             rFormat.GetStart(),
589             rFormat.GetNumberingType(),
590             rFormat.GetNumAdjust(),
591             aNumLvlPos,
592             nFollow,
593             pPseudoFont.get(), pOutSet,
594             nIndentAt, nFirstLineIndex, nListTabPos,
595             sNumStr,
596             rFormat.GetNumberingType()==SVX_NUM_BITMAP ? rFormat.GetBrush() : nullptr);
597 }
598 
OutOverrideListTab()599 void WW8Export::OutOverrideListTab()
600 {
601     if( !m_pUsedNumTable )
602         return ;            // no numbering is used
603 
604     // write the "list format override" - LFO
605     sal_uInt16 nCount = m_pUsedNumTable->size();
606     sal_uInt16 n;
607 
608     pFib->m_fcPlfLfo = pTableStrm->Tell();
609     SwWW8Writer::WriteLong( *pTableStrm, nCount );
610 
611     for( n = 0; n < nCount; ++n )
612     {
613         SwWW8Writer::WriteLong( *pTableStrm, n + 1 );
614         SwWW8Writer::FillCount( *pTableStrm, 12 );
615     }
616     for( n = 0; n < nCount; ++n )
617         SwWW8Writer::WriteLong( *pTableStrm, -1 );  // no overwrite
618 
619     // set len to FIB
620     pFib->m_lcbPlfLfo = pTableStrm->Tell() - pFib->m_fcPlfLfo;
621 }
622 
OutListNamesTab()623 void WW8Export::OutListNamesTab()
624 {
625     if( !m_pUsedNumTable )
626         return ;            // no numbering is used
627 
628     // write the "list format override" - LFO
629     sal_uInt16 nNms = 0, nCount = m_pUsedNumTable->size();
630 
631     pFib->m_fcSttbListNames = pTableStrm->Tell();
632     SwWW8Writer::WriteShort( *pTableStrm, -1 );
633     SwWW8Writer::WriteLong( *pTableStrm, nCount );
634 
635     for( ; nNms < nCount; ++nNms )
636     {
637         const SwNumRule& rRule = *(*m_pUsedNumTable)[ nNms ];
638         OUString sNm;
639         if( !rRule.IsAutoRule() )
640             sNm = rRule.GetName();
641 
642         SwWW8Writer::WriteShort( *pTableStrm, sNm.getLength() );
643         if (!sNm.isEmpty())
644             SwWW8Writer::WriteString16(*pTableStrm, sNm, false);
645     }
646 
647     SwWW8Writer::WriteLong( *pTableStrm, pFib->m_fcSttbListNames + 2, nNms );
648     // set len to FIB
649     pFib->m_lcbSttbListNames = pTableStrm->Tell() - pFib->m_fcSttbListNames;
650 }
651 
SubstituteBullet(OUString & rNumStr,rtl_TextEncoding & rChrSet,OUString & rFontName) const652 void MSWordExportBase::SubstituteBullet( OUString& rNumStr,
653     rtl_TextEncoding& rChrSet, OUString& rFontName ) const
654 {
655     if (!m_bSubstituteBullets)
656         return;
657     OUString sFontName = rFontName;
658 
659     // If Bullet char is "", don't change
660     if (rNumStr[0] != u'\0')
661     {
662         rNumStr = rNumStr.replaceAt(0, 1, OUString(
663             msfilter::util::bestFitOpenSymbolToMSFont(rNumStr[0], rChrSet, sFontName)));
664     }
665 
666     rFontName = sFontName;
667 }
668 
669 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
670