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