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 /*
21 * This file contains methods for the WW8 output
22 * (nodes, attributes, formats and chars).
23 */
24
25 #include <hintids.hxx>
26
27 #include <vcl/svapp.hxx>
28 #include <vcl/settings.hxx>
29 #include <sal/log.hxx>
30
31 #include <svl/zformat.hxx>
32 #include <svl/itemiter.hxx>
33 #include <svl/whiter.hxx>
34 #include <svl/grabbagitem.hxx>
35 #include <editeng/fontitem.hxx>
36 #include <editeng/tstpitem.hxx>
37 #include <editeng/adjustitem.hxx>
38 #include <editeng/spltitem.hxx>
39 #include <editeng/widwitem.hxx>
40 #include <editeng/lspcitem.hxx>
41 #include <editeng/keepitem.hxx>
42 #include <editeng/shaditem.hxx>
43 #include <editeng/brushitem.hxx>
44 #include <editeng/postitem.hxx>
45 #include <editeng/wghtitem.hxx>
46 #include <editeng/kernitem.hxx>
47 #include <editeng/crossedoutitem.hxx>
48 #include <editeng/cmapitem.hxx>
49 #include <editeng/wrlmitem.hxx>
50 #include <editeng/udlnitem.hxx>
51 #include <editeng/langitem.hxx>
52 #include <editeng/escapementitem.hxx>
53 #include <editeng/fhgtitem.hxx>
54 #include <editeng/colritem.hxx>
55 #include <editeng/hyphenzoneitem.hxx>
56 #include <editeng/formatbreakitem.hxx>
57 #include <editeng/lrspitem.hxx>
58 #include <editeng/ulspitem.hxx>
59 #include <editeng/boxitem.hxx>
60 #include <editeng/contouritem.hxx>
61 #include <editeng/shdditem.hxx>
62 #include <editeng/autokernitem.hxx>
63 #include <editeng/pbinitem.hxx>
64 #include <editeng/emphasismarkitem.hxx>
65 #include <editeng/twolinesitem.hxx>
66 #include <editeng/charscaleitem.hxx>
67 #include <editeng/charrotateitem.hxx>
68 #include <editeng/charreliefitem.hxx>
69 #include <editeng/paravertalignitem.hxx>
70 #include <editeng/pgrditem.hxx>
71 #include <editeng/frmdiritem.hxx>
72 #include <editeng/blinkitem.hxx>
73 #include <editeng/charhiddenitem.hxx>
74 #include <editeng/paperinf.hxx>
75 #include <svx/xfillit0.hxx>
76 #include <svx/xflgrit.hxx>
77 #include <fmtfld.hxx>
78 #include <fchrfmt.hxx>
79 #include <fmtfsize.hxx>
80 #include <fmtpdsc.hxx>
81 #include <fmtornt.hxx>
82 #include <fmtanchr.hxx>
83 #include <fmtclds.hxx>
84 #include <fmtsrnd.hxx>
85 #include <fmtftn.hxx>
86 #include <fmtflcnt.hxx>
87 #include <frmatr.hxx>
88 #include <swtable.hxx>
89 #include <fmtinfmt.hxx>
90 #include <txtfld.hxx>
91 #include <txtftn.hxx>
92 #include <poolfmt.hxx>
93 #include <doc.hxx>
94 #include <IDocumentSettingAccess.hxx>
95 #include <IDocumentFieldsAccess.hxx>
96 #include <IDocumentStylePoolAccess.hxx>
97 #include <IDocumentListsAccess.hxx>
98 #include <list.hxx>
99 #include <docary.hxx>
100 #include <pam.hxx>
101 #include <paratr.hxx>
102 #include <fldbas.hxx>
103 #include <docufld.hxx>
104 #include <expfld.hxx>
105 #include <pagedesc.hxx>
106 #include <flddat.hxx>
107 #include <ndtxt.hxx>
108 #include <swrect.hxx>
109 #include <redline.hxx>
110 #include <reffld.hxx>
111 #include <ftninfo.hxx>
112 #include <charfmt.hxx>
113 #include <section.hxx>
114 #include <lineinfo.hxx>
115 #include <fmtline.hxx>
116 #include <tox.hxx>
117 #include <fmtftntx.hxx>
118 #include <breakit.hxx>
119 #include <com/sun/star/i18n/ScriptType.hpp>
120 #include <com/sun/star/i18n/XBreakIterator.hpp>
121 #include <unotools/localedatawrapper.hxx>
122 #include <svx/unobrushitemhelper.hxx>
123 #include <svx/xenum.hxx>
124 #include <tgrditem.hxx>
125 #include <flddropdown.hxx>
126 #include <chpfld.hxx>
127 #include <fmthdft.hxx>
128 #include <authfld.hxx>
129 #include <dbfld.hxx>
130
131 #include "sprmids.hxx"
132
133 #include <fmtcntnt.hxx>
134 #include "writerhelper.hxx"
135 #include "writerwordglue.hxx"
136 #include "wrtww8.hxx"
137 #include "ww8par.hxx"
138 #include "ww8attributeoutput.hxx"
139 #include "fields.hxx"
140 #include <vcl/outdev.hxx>
141 #include <i18nlangtag/languagetag.hxx>
142 #include <unotools/fltrcfg.hxx>
143 #include <o3tl/enumrange.hxx>
144
145
146 using ::editeng::SvxBorderLine;
147 using namespace ::com::sun::star;
148 using namespace nsSwDocInfoSubType;
149 using namespace sw::util;
150 using namespace sw::types;
151
CollapseScriptsforWordOk(sal_uInt16 nScript,sal_uInt16 nWhich)152 bool WW8Export::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich )
153 {
154 bool bRet = true;
155 if ( nScript == i18n::ScriptType::ASIAN )
156 {
157 //for asian in ww8, there is only one fontsize
158 //and one fontstyle (posture/weight) for ww6
159 //there is the additional problem that there
160 //is only one font setting for all three scripts
161 switch ( nWhich )
162 {
163 case RES_CHRATR_FONTSIZE:
164 case RES_CHRATR_POSTURE:
165 case RES_CHRATR_WEIGHT:
166 bRet = false;
167 break;
168 case RES_CHRATR_LANGUAGE:
169 case RES_CHRATR_CTL_FONT:
170 case RES_CHRATR_CTL_FONTSIZE:
171 case RES_CHRATR_CTL_LANGUAGE:
172 case RES_CHRATR_CTL_POSTURE:
173 case RES_CHRATR_CTL_WEIGHT:
174 default:
175 break;
176 }
177 }
178 else if ( nScript == i18n::ScriptType::COMPLEX )
179 {
180 //Complex is ok in ww8, but for ww6 there is only
181 //one font, one fontsize, one fontsize (weight/posture)
182 //and only one language
183 }
184 else
185 {
186 //for western in ww8, there is only one fontsize
187 //and one fontstyle (posture/weight) for ww6
188 //there is the additional problem that there
189 //is only one font setting for all three scripts
190 switch ( nWhich )
191 {
192 case RES_CHRATR_CJK_FONTSIZE:
193 case RES_CHRATR_CJK_POSTURE:
194 case RES_CHRATR_CJK_WEIGHT:
195 bRet = false;
196 break;
197 case RES_CHRATR_CJK_LANGUAGE:
198 case RES_CHRATR_CTL_FONT:
199 case RES_CHRATR_CTL_FONTSIZE:
200 case RES_CHRATR_CTL_LANGUAGE:
201 case RES_CHRATR_CTL_POSTURE:
202 case RES_CHRATR_CTL_WEIGHT:
203 default:
204 break;
205 }
206 }
207 return bRet;
208 }
209
210
ExportPoolItemsToCHP(ww8::PoolItems & rItems,sal_uInt16 nScript,const SvxFontItem * pFont,bool bWriteCombChars)211 void MSWordExportBase::ExportPoolItemsToCHP( ww8::PoolItems &rItems, sal_uInt16 nScript, const SvxFontItem *pFont, bool bWriteCombChars )
212 {
213 for ( const auto& rItem : rItems )
214 {
215 const SfxPoolItem *pItem = rItem.second;
216 sal_uInt16 nWhich = pItem->Which();
217 if ( ( isCHRATR( nWhich ) || isTXTATR( nWhich ) ) && CollapseScriptsforWordOk( nScript, nWhich ) )
218 {
219 //In the id definition, RES_TXTATR_INETFMT must precede RES_TXTATR_CHARFMT, so that link style can overwrite char style.
220 //and in #i24291# it describes "All we want to do is ensure for now is that if a charfmt exist in the character
221 //properties that it rises to the top and is exported first."
222 //In bug 119649, it is in such situation, so we need to ignore the link style when doing ms word filter exports and
223 //add the second judgement for #i24291# definition.
224 if (nWhich == RES_TXTATR_CHARFMT)
225 {
226 const SfxPoolItem* pINetItem = SearchPoolItems(rItems, RES_TXTATR_INETFMT);
227 if (pINetItem)
228 {
229 const SwCharFormat* pFormat = static_cast<const SwFormatCharFormat&>(*pItem).GetCharFormat();
230 const SwCharFormat* pINetFormat = m_pDoc->FindCharFormatByName(
231 static_cast<const SwFormatINetFormat&>(*pINetItem).GetINetFormat());
232 ww8::PoolItems aCharItems, aINetItems;
233 GetPoolItems(pFormat->GetAttrSet(), aCharItems, false);
234 GetPoolItems(pINetFormat->GetAttrSet(), aINetItems, false);
235 for (const auto& rCharItem : aCharItems)
236 {
237 const SfxPoolItem* pCharItem = rCharItem.second;
238 sal_uInt16 nCharWhich = pCharItem->Which();
239 if (!SearchPoolItems(aINetItems, nCharWhich) && !SearchPoolItems(rItems, nCharWhich))
240 AttrOutput().OutputItem(*pCharItem);
241 }
242 continue;
243 }
244 }
245
246 // tdf#38778 Fix output of the font in DOC run for fields
247 if (pFont &&
248 nWhich == RES_TXTATR_FIELD)
249 {
250 AttrOutput().OutputItem( *pFont );
251 }
252
253 // tdf#66401 For Combined Characters in docx, MS Word uses half the normal font-size for the field's
254 // font-size, but only for <w:sz>. Therefore, we check if we are currently writing a field of type
255 // Combined Characters and if so, we half the font size.
256 if (bWriteCombChars &&
257 nWhich == RES_CHRATR_FONTSIZE)
258 {
259 SvxFontHeightItem fontHeight(item_cast<SvxFontHeightItem>( *pItem ));
260 fontHeight.SetHeight( fontHeight.GetHeight() / 2 );
261
262 AttrOutput().OutputItem( fontHeight );
263 }
264 else if (nWhich == RES_CHRATR_COLOR)
265 {
266 const SvxColorItem& rColor = static_cast<const SvxColorItem&>(*pItem);
267 const SfxPoolItem* pBackgroundItem = SearchPoolItems(rItems, RES_CHRATR_BACKGROUND);
268 if (rColor.GetValue() == COL_AUTO && pBackgroundItem)
269 {
270 const SvxBrushItem& rBrushBackground = static_cast<const SvxBrushItem&>(*pBackgroundItem);
271 SvxColorItem aForeground(rBrushBackground.GetColor().IsDark() ? COL_WHITE : COL_BLACK, RES_CHRATR_COLOR);
272 AttrOutput().OutputItem(aForeground);
273 }
274 else
275 {
276 // default
277 AttrOutput().OutputItem( *pItem );
278 }
279 }
280 else
281 {
282 AttrOutput().OutputItem( *pItem );
283 }
284 }
285 }
286 }
287
288 /*
289 * Output format as follows:
290 * - output the attributes; without parents!
291 */
292
OutputItemSet(const SfxItemSet & rSet,bool bPapFormat,bool bChpFormat,sal_uInt16 nScript,bool bExportParentItemSet)293 void MSWordExportBase::OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript,
294 bool bExportParentItemSet )
295 {
296 if( bExportParentItemSet || rSet.Count() )
297 {
298 const SfxPoolItem* pItem;
299 m_pISet = &rSet; // for double attributes
300
301 // If frame dir is set, but not adjust, then force adjust as well
302 if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_FRAMEDIR, bExportParentItemSet ) )
303 {
304 // No explicit adjust set ?
305 if ( SfxItemState::SET != rSet.GetItemState( RES_PARATR_ADJUST, bExportParentItemSet ) )
306 {
307 if ( nullptr != ( pItem = rSet.GetItem( RES_PARATR_ADJUST, bExportParentItemSet ) ) )
308 {
309 // then set the adjust used by the parent format
310 AttrOutput().OutputItem( *pItem );
311 }
312 }
313 }
314
315 if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_PARATR_NUMRULE, bExportParentItemSet, &pItem ) )
316 {
317 AttrOutput().OutputItem( *pItem );
318
319 // switch off the numbering?
320 if ( static_cast<const SwNumRuleItem*>(pItem)->GetValue().isEmpty() &&
321 SfxItemState::SET != rSet.GetItemState( RES_LR_SPACE, false) &&
322 SfxItemState::SET == rSet.GetItemState( RES_LR_SPACE, true, &pItem ) )
323 {
324 // the set the LR-Space of the parentformat!
325 AttrOutput().OutputItem( *pItem );
326 }
327 }
328
329 ww8::PoolItems aItems;
330 GetPoolItems( rSet, aItems, bExportParentItemSet );
331 if ( bChpFormat )
332 ExportPoolItemsToCHP(aItems, nScript, nullptr);
333 if ( bPapFormat )
334 {
335 for ( const auto& rItem : aItems )
336 {
337 pItem = rItem.second;
338 sal_uInt16 nWhich = pItem->Which();
339 // Handle fill attributes just like frame attributes for now.
340 if ( (nWhich >= RES_PARATR_BEGIN && nWhich < RES_FRMATR_END && nWhich != RES_PARATR_NUMRULE ) ||
341 (nWhich >= XATTR_FILL_FIRST && nWhich < XATTR_FILL_LAST))
342 AttrOutput().OutputItem( *pItem );
343 }
344
345 // Has to be called after RES_PARATR_GRABBAG is processed.
346 const XFillStyleItem* pXFillStyleItem(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
347 if (pXFillStyleItem && pXFillStyleItem->GetValue() == drawing::FillStyle_SOLID && !rSet.HasItem(RES_BACKGROUND))
348 {
349 // Construct an SvxBrushItem, as expected by the exporters.
350 std::shared_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
351 AttrOutput().OutputItem(*aBrush);
352 }
353 }
354 m_pISet = nullptr; // for double attributes
355 }
356 }
357
GatherChapterFields()358 void MSWordExportBase::GatherChapterFields()
359 {
360 //If the header/footer contains a chapter field
361 SwFieldType* pType = m_pDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter );
362 SwIterator<SwFormatField,SwFieldType> aFormatFields( *pType );
363 for ( SwFormatField* pField = aFormatFields.First(); pField; pField = aFormatFields.Next() )
364 {
365 if (const SwTextField *pTextField = pField->GetTextField())
366 {
367 const SwTextNode &rTextNode = pTextField->GetTextNode();
368 m_aChapterFieldLocs.push_back(rTextNode.GetIndex());
369 }
370 }
371 }
372
ContentContainsChapterField(const SwFormatContent & rContent) const373 bool MSWordExportBase::ContentContainsChapterField(const SwFormatContent &rContent) const
374 {
375 bool bRet = false;
376 if ( const SwNodeIndex* pSttIdx = rContent.GetContentIdx() )
377 {
378 SwNodeIndex aIdx( *pSttIdx, 1 );
379 SwNodeIndex aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
380 sal_uLong nStart = aIdx.GetIndex();
381 sal_uLong nEnd = aEnd.GetIndex();
382 //If the header/footer contains a chapter field
383 bRet = std::any_of(m_aChapterFieldLocs.cbegin(), m_aChapterFieldLocs.cend(),
384 [nStart, nEnd](sal_uLong i) { return ( nStart <= i ) && ( i <= nEnd ); });
385 }
386 return bRet;
387 }
388
FormatHdFtContainsChapterField(const SwFrameFormat & rFormat) const389 bool MSWordExportBase::FormatHdFtContainsChapterField(const SwFrameFormat &rFormat) const
390 {
391 if ( m_aChapterFieldLocs.empty() )
392 return false;
393
394 const SwFrameFormat *pFormat = nullptr;
395
396 pFormat = rFormat.GetHeader().GetHeaderFormat();
397 if ( pFormat && ContentContainsChapterField( pFormat->GetContent() ) )
398 return true;
399
400 pFormat = rFormat.GetFooter().GetFooterFormat();
401 return pFormat && ContentContainsChapterField( pFormat->GetContent() );
402 }
403
SetCurrentPageDescFromNode(const SwNode & rNd)404 bool MSWordExportBase::SetCurrentPageDescFromNode(const SwNode &rNd)
405 {
406 bool bNewPageDesc = false;
407 const SwPageDesc* pCurrent = SwPageDesc::GetPageDescOfNode(rNd);
408 OSL_ENSURE(pCurrent && m_pCurrentPageDesc, "Not possible surely");
409 if (m_pCurrentPageDesc && pCurrent)
410 {
411 if (pCurrent != m_pCurrentPageDesc)
412 {
413 if (m_pCurrentPageDesc->GetFollow() != pCurrent)
414 bNewPageDesc = true;
415 else
416 {
417 const SwFrameFormat& rTitleFormat = m_pCurrentPageDesc->GetFirstMaster();
418 const SwFrameFormat& rFollowFormat = pCurrent->GetMaster();
419
420 bNewPageDesc = !IsPlausableSingleWordSection(rTitleFormat,
421 rFollowFormat);
422 }
423 m_pCurrentPageDesc = pCurrent;
424 }
425 else
426 {
427 const SwFrameFormat &rFormat = pCurrent->GetMaster();
428 bNewPageDesc = FormatHdFtContainsChapterField(rFormat);
429 }
430 }
431 return bNewPageDesc;
432 }
433
434 /**
435 * WW only knows Break-After (page break and section breaks),
436 * whereas in SW page breaks exist both "before" and "after" and PageDesc exists
437 * only "before". Therefore the breaks are iterated two times, namely before
438 * and after every line.
439 * Depending on the break type they're set before or after the line.
440 * Only functions can be called, which do not write in output area pO,
441 * because that one only exits once for CHP and PAP and therefore end up in
442 * the wrong one.
443 */
OutputSectionBreaks(const SfxItemSet * pSet,const SwNode & rNd,bool isCellOpen,bool isTextNodeEmpty)444 void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen, bool isTextNodeEmpty)
445 {
446 if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs )
447 return;
448
449 m_bBreakBefore = true;
450 bool bNewPageDesc = false;
451 const SfxPoolItem* pItem=nullptr;
452 const SwFormatPageDesc *pPgDesc=nullptr;
453
454 //Output a sectionbreak if there's a new pagedescriptor. Otherwise output a
455 //pagebreak if there is a pagebreak here, unless the new page (follow
456 //style) is different to the current one, in which case plump for a
457 //section.
458 bool bBreakSet = false;
459
460 const SwPageDesc * pPageDesc = rNd.FindPageDesc();
461
462 // Even if pAktPageDesc != pPageDesc ,it might be because of the different header & footer types.
463 if (m_pCurrentPageDesc != pPageDesc)
464 {
465 if ( ( isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() )) ||
466 ( isTextNodeEmpty || m_bPrevTextNodeIsEmpty ))
467 {
468 /* Do not output a section break in the following scenarios.
469 1) Table cell is open and page header types are different
470 2) PageBreak is present but text node has no string - it is an empty node.
471 3) If the previous node was an empty text node and current node is a non empty text node or vice versa.
472 4) If previous node and current node both are empty text nodes.
473 Converting a page break to section break would cause serious issues while importing
474 the RT files with different first page being set.
475 */
476
477 /*
478 * If Table cell is open and page header types are different
479 * set pSet to NULL as we don't want to add any section breaks.
480 */
481 if ( isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() ) )
482 pSet = nullptr;
483
484 // tdf#118393: FILESAVE: DOCX Export loses header/footer
485 {
486 bool bPlausableSingleWordSection = sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster());
487
488 {
489 const SwFrameFormat& rTitleFormat = m_pCurrentPageDesc->GetFirstMaster();
490 const SwFrameFormat& rFollowFormat = pPageDesc->GetMaster();
491
492 auto pHeaderFormat1 = rTitleFormat.GetHeader().GetHeaderFormat();
493 auto pHeaderFormat2 = rFollowFormat.GetHeader().GetHeaderFormat();
494
495 if (pHeaderFormat1 != pHeaderFormat2)
496 bPlausableSingleWordSection = false;
497
498 auto pFooterFormat1 = rTitleFormat.GetFooter().GetFooterFormat();
499 auto pFooterFormat2 = rFollowFormat.GetFooter().GetFooterFormat();
500
501 if (pFooterFormat1 != pFooterFormat2)
502 bPlausableSingleWordSection = false;
503 }
504
505 if ( !bPlausableSingleWordSection && m_bFirstTOCNodeWithSection )
506 {
507 bBreakSet = false;
508 bNewPageDesc = true;
509 m_pCurrentPageDesc = pPageDesc;
510 }
511 }
512 }
513 else if (!sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster()))
514 {
515 bBreakSet = true;
516 bNewPageDesc = true;
517 m_pCurrentPageDesc = pPageDesc;
518 }
519 }
520
521 if ( pSet && pSet->Count() )
522 {
523 if ( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem ) &&
524 static_cast<const SwFormatPageDesc*>(pItem)->GetRegisteredIn() != nullptr)
525 {
526 bBreakSet = true;
527 bNewPageDesc = true;
528 pPgDesc = static_cast<const SwFormatPageDesc*>(pItem);
529 m_pCurrentPageDesc = pPgDesc->GetPageDesc();
530 }
531 else if ( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem ) )
532 {
533 // Word does not like hard break attributes in some table cells
534 bool bRemoveHardBreakInsideTable = false;
535 if ( m_bOutTable )
536 {
537 const SwTableNode* pTableNode = rNd.FindTableNode();
538 if ( pTableNode )
539 {
540 const SwTableBox* pBox = rNd.GetTableBox();
541 const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr;
542 // but only for non-complex tables
543 if ( pLine && !pLine->GetUpper() )
544 {
545 // check if box is not first in that line:
546 if ( 0 < pLine->GetBoxPos( pBox ) && pBox->GetSttNd() )
547 {
548 bRemoveHardBreakInsideTable = true;
549 }
550 }
551 }
552 }
553 bBreakSet = true;
554
555 if ( !bRemoveHardBreakInsideTable )
556 {
557 OSL_ENSURE(m_pCurrentPageDesc, "should not be possible");
558 /*
559 If because of this pagebreak the page desc following the page
560 break is the follow style of the current page desc then output a
561 section break using that style instead. At least in those cases
562 we end up with the same style in word and writer, nothing can be
563 done when it happens when we get a new pagedesc because we
564 overflow from the first page style.
565 */
566 if ( m_pCurrentPageDesc )
567 {
568 // #i76301# - assure that there is a page break before set at the node.
569 const SvxFormatBreakItem* pBreak = dynamic_cast<const SvxFormatBreakItem*>(pItem);
570 if ( pBreak &&
571 pBreak->GetBreak() == SvxBreak::PageBefore )
572 {
573 bNewPageDesc |= SetCurrentPageDescFromNode( rNd );
574 }
575 if( isTextNodeEmpty )
576 bNewPageDesc = false;
577 }
578 if ( !bNewPageDesc )
579 AttrOutput().OutputItem( *pItem );
580 }
581 }
582 }
583
584 /*
585 #i9301#
586 No explicit page break, lets see if the style had one and we've moved to a
587 new page style because of it, if we have to then we take the opportunity to
588 set the equivalent word section here. We *could* do it for every paragraph
589 that moves onto a new page because of layout, but that would be insane.
590 */
591 bool bHackInBreak = false;
592 if ( !bBreakSet )
593 {
594 if ( const SwContentNode *pNd = rNd.GetContentNode() )
595 {
596 const SvxFormatBreakItem &rBreak =
597 ItemGet<SvxFormatBreakItem>( *pNd, RES_BREAK );
598 if ( rBreak.GetBreak() == SvxBreak::PageBefore )
599 bHackInBreak = true;
600 else
601 { // Even a pagedesc item is set, the break item can be set 'NONE',
602 // but a pagedesc item is an implicit page break before...
603 const SwFormatPageDesc &rPageDesc =
604 ItemGet<SwFormatPageDesc>( *pNd, RES_PAGEDESC );
605 if ( rPageDesc.KnowsPageDesc() )
606 bHackInBreak = true;
607 }
608 }
609 }
610
611 if ( bHackInBreak )
612 {
613 OSL_ENSURE( m_pCurrentPageDesc, "should not be possible" );
614 if ( m_pCurrentPageDesc )
615 bNewPageDesc = SetCurrentPageDescFromNode( rNd );
616 }
617
618 if ( bNewPageDesc && m_pCurrentPageDesc )
619 {
620 PrepareNewPageDesc( pSet, rNd, pPgDesc, m_pCurrentPageDesc );
621 }
622 m_bBreakBefore = false;
623 m_bPrevTextNodeIsEmpty = isTextNodeEmpty ;
624 }
625
626 // #i76300#
OutputFollowPageDesc(const SfxItemSet * pSet,const SwTextNode * pNd)627 bool MSWordExportBase::OutputFollowPageDesc( const SfxItemSet* pSet, const SwTextNode* pNd )
628 {
629 bool bRet = false;
630
631 if ( pNd &&
632 m_pCurrentPageDesc &&
633 m_pCurrentPageDesc != m_pCurrentPageDesc->GetFollow() )
634 {
635 PrepareNewPageDesc( pSet, *pNd, nullptr, m_pCurrentPageDesc->GetFollow() );
636 bRet = true;
637 }
638
639 return bRet;
640 }
641
GetSectionFormat(const SwNode & rNd)642 const SwSectionFormat* MSWordExportBase::GetSectionFormat( const SwNode& rNd )
643 {
644 const SwSectionFormat* pFormat = nullptr;
645 const SwSectionNode* pSect = rNd.FindSectionNode();
646 if ( pSect &&
647 CONTENT_SECTION == pSect->GetSection().GetType() )
648 {
649 pFormat = pSect->GetSection().GetFormat();
650 }
651
652 return pFormat;
653 }
654
GetSectionLineNo(const SfxItemSet * pSet,const SwNode & rNd)655 sal_uLong MSWordExportBase::GetSectionLineNo( const SfxItemSet* pSet, const SwNode& rNd )
656 {
657 const SwFormatLineNumber* pNItem = nullptr;
658 if ( pSet )
659 {
660 pNItem = &( ItemGet<SwFormatLineNumber>( *pSet, RES_LINENUMBER ) );
661 }
662 else if ( const SwContentNode *pNd = rNd.GetContentNode() )
663 {
664 pNItem = &( ItemGet<SwFormatLineNumber>( *pNd, RES_LINENUMBER ) );
665 }
666
667 return pNItem? pNItem->GetStartValue() : 0;
668 }
669
PrepareNewPageDesc(const SfxItemSet * pSet,const SwNode & rNd,const SwFormatPageDesc * pNewPgDescFormat,const SwPageDesc * pNewPgDesc)670 void WW8Export::PrepareNewPageDesc( const SfxItemSet*pSet,
671 const SwNode& rNd,
672 const SwFormatPageDesc* pNewPgDescFormat,
673 const SwPageDesc* pNewPgDesc )
674 {
675 // The PageDescs will only be inserted in WW8Writer::pSepx with the corresponding
676 // position by the occurrences of PageDesc attributes. The construction and
677 // output of the attributes and header/footer of the PageDesc are done
678 // after the main text and its attributes.
679
680 sal_uLong nFcPos = ReplaceCr( msword::PageBreak ); // Page/Section-Break
681
682 // actually nothing is outputted here, rather the arrays aCps, aSects
683 // accordingly completed
684 if ( !nFcPos )
685 return;
686
687 const SwSectionFormat* pFormat = GetSectionFormat( rNd );
688 const sal_uLong nLnNm = GetSectionLineNo( pSet, rNd );
689
690 OSL_ENSURE( pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided." );
691
692 if ( pNewPgDescFormat )
693 {
694 pSepx->AppendSep( Fc2Cp( nFcPos ), *pNewPgDescFormat, rNd, pFormat, nLnNm );
695 }
696 else if ( pNewPgDesc )
697 {
698 pSepx->AppendSep( Fc2Cp( nFcPos ), pNewPgDesc, rNd, pFormat, nLnNm );
699 }
700 }
701
CorrectTabStopInSet(SfxItemSet & rSet,sal_Int32 nAbsLeft)702 void MSWordExportBase::CorrectTabStopInSet( SfxItemSet& rSet, sal_Int32 nAbsLeft )
703 {
704 if (const SvxTabStopItem *pItem = rSet.GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP))
705 {
706 // then it must be corrected for the output
707 SvxTabStopItem aTStop(*pItem);
708 for ( sal_uInt16 nCnt = 0; nCnt < aTStop.Count(); ++nCnt )
709 {
710 SvxTabStop& rTab = const_cast<SvxTabStop&>(aTStop[ nCnt ]);
711 if ( SvxTabAdjust::Default != rTab.GetAdjustment() &&
712 rTab.GetTabPos() >= nAbsLeft )
713 {
714 rTab.GetTabPos() -= nAbsLeft;
715 }
716 else
717 {
718 aTStop.Remove( nCnt );
719 --nCnt;
720 }
721 }
722 rSet.Put( aTStop );
723 }
724 }
725
GetNumId(sal_uInt16 eNumType)726 sal_uInt8 WW8Export::GetNumId( sal_uInt16 eNumType )
727 {
728 sal_uInt8 nRet = 0;
729 switch( eNumType )
730 {
731 case SVX_NUM_CHARS_UPPER_LETTER:
732 case SVX_NUM_CHARS_UPPER_LETTER_N: nRet = 3; break;
733 case SVX_NUM_CHARS_LOWER_LETTER:
734 case SVX_NUM_CHARS_LOWER_LETTER_N: nRet = 4; break;
735 case SVX_NUM_ROMAN_UPPER: nRet = 1; break;
736 case SVX_NUM_ROMAN_LOWER: nRet = 2; break;
737
738 case SVX_NUM_BITMAP:
739 case SVX_NUM_CHAR_SPECIAL: nRet = 23; break;
740
741 // nothing, WW does the same (undocumented)
742 case SVX_NUM_NUMBER_NONE: nRet = 0xff; break;
743 }
744 return nRet;
745 }
746
OutlineNumbering(sal_uInt8 nLvl)747 void WW8AttributeOutput::OutlineNumbering(sal_uInt8 nLvl)
748 {
749 if ( nLvl >= WW8ListManager::nMaxLevel )
750 nLvl = WW8ListManager::nMaxLevel-1;
751
752 // write sprmPOutLvl sprmPIlvl and sprmPIlfo
753 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPOutLvl );
754 m_rWW8Export.pO->push_back( nLvl );
755 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPIlvl );
756 m_rWW8Export.pO->push_back( nLvl );
757 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPIlfo );
758 SwWW8Writer::InsUInt16( *m_rWW8Export.pO,
759 1 + m_rWW8Export.GetNumberingId(*m_rWW8Export.m_pDoc->GetOutlineNumRule()) );
760 }
761
762 // #i77805#
DisallowInheritingOutlineNumbering(const SwFormat & rFormat)763 bool WW8Export::DisallowInheritingOutlineNumbering(const SwFormat &rFormat)
764 {
765 bool bRet( false );
766
767 //If there is no numbering on this fmt, but its parent was outline
768 //numbered, then in writer this is no inheritied, but in word it would
769 //be, so we must export "no numbering" and "body level" to make word
770 //behave like writer (see #i25755)
771 if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
772 {
773 if (const SwFormat *pParent = rFormat.DerivedFrom())
774 {
775 if (static_cast<const SwTextFormatColl*>(pParent)->IsAssignedToListLevelOfOutlineStyle())
776 {
777 SwWW8Writer::InsUInt16(*pO, NS_sprm::sprmPOutLvl);
778 pO->push_back(sal_uInt8(9));
779 SwWW8Writer::InsUInt16(*pO, NS_sprm::sprmPIlfo);
780 SwWW8Writer::InsUInt16(*pO, 0);
781
782 bRet = true;
783 }
784 }
785 }
786
787 return bRet;
788 }
789
OutputFormat(const SwFormat & rFormat,bool bPapFormat,bool bChpFormat,bool bFlyFormat)790 void MSWordExportBase::OutputFormat( const SwFormat& rFormat, bool bPapFormat, bool bChpFormat, bool bFlyFormat )
791 {
792 bool bCallOutSet = true;
793 const SwModify* pOldMod = m_pOutFormatNode;
794 m_pOutFormatNode = &rFormat;
795
796 switch( rFormat.Which() )
797 {
798 case RES_CONDTXTFMTCOLL:
799 case RES_TXTFMTCOLL:
800 if( bPapFormat )
801 {
802 int nLvl = MAXLEVEL;
803
804 if (static_cast<const SwTextFormatColl&>(rFormat).IsAssignedToListLevelOfOutlineStyle())
805 nLvl = static_cast<const SwTextFormatColl&>(rFormat).GetAssignedOutlineStyleLevel();
806
807 if (nLvl >= 0 && nLvl < MAXLEVEL)
808 {
809 //if outline numbered
810 // if Write StyleDefinition then write the OutlineRule
811 const SwNumFormat& rNFormat = m_pDoc->GetOutlineNumRule()->Get( static_cast<sal_uInt16>( nLvl ) );
812 if ( m_bStyDef )
813 AttrOutput().OutlineNumbering(static_cast<sal_uInt8>(nLvl));
814
815 if ( rNFormat.GetPositionAndSpaceMode() ==
816 SvxNumberFormat::LABEL_WIDTH_AND_POSITION &&
817 rNFormat.GetAbsLSpace() )
818 {
819 SfxItemSet aSet( rFormat.GetAttrSet() );
820 SvxLRSpaceItem aLR(
821 ItemGet<SvxLRSpaceItem>(aSet, RES_LR_SPACE));
822
823 aLR.SetTextLeft( aLR.GetTextLeft() + rNFormat.GetAbsLSpace() );
824 aLR.SetTextFirstLineOfst( GetWordFirstLineOffset(rNFormat));
825
826 aSet.Put( aLR );
827 CorrectTabStopInSet( aSet, rNFormat.GetAbsLSpace() );
828 OutputItemSet( aSet, bPapFormat, bChpFormat,
829 i18n::ScriptType::LATIN, m_bExportModeRTF);
830 bCallOutSet = false;
831 }
832 }
833 else
834 {
835 //otherwise we might have to remove outline numbering from
836 //what gets exported if the parent style was outline numbered
837 // #i77805#
838 // If inherited outline numbering is suppress, the left/right
839 // margins has to be exported explicitly.
840 if ( m_bStyDef && DisallowInheritingOutlineNumbering(rFormat) )
841 {
842 SfxItemSet aSet( rFormat.GetAttrSet() );
843 const SvxLRSpaceItem& aLR(
844 ItemGet<SvxLRSpaceItem>(aSet, RES_LR_SPACE));
845 aSet.Put( aLR );
846 OutputItemSet( aSet, bPapFormat, bChpFormat,
847 css::i18n::ScriptType::LATIN, m_bExportModeRTF);
848 bCallOutSet = false;
849 }
850 }
851 }
852 break;
853
854 case RES_CHRFMT:
855 break;
856 case RES_FLYFRMFMT:
857 if (bFlyFormat)
858 {
859 OSL_ENSURE(m_pParentFrame, "No parent frame, all broken");
860
861 if (m_pParentFrame)
862 {
863 const SwFrameFormat &rFrameFormat = m_pParentFrame->GetFrameFormat();
864
865 SfxItemSet aSet(m_pDoc->GetAttrPool(), svl::Items<RES_FRMATR_BEGIN,
866 RES_FRMATR_END-1,
867 XATTR_FILL_FIRST, XATTR_FILL_LAST>{});
868 aSet.Set(rFrameFormat.GetAttrSet());
869
870 // Fly as character becomes a paragraph bound
871 // now set the distance to paragraph margin
872 if (m_pFlyOffset)
873 {
874 aSet.Put(SwFormatHoriOrient(m_pFlyOffset->X()));
875 aSet.Put(SwFormatVertOrient(m_pFlyOffset->Y()));
876 SwFormatAnchor aAnchor(rFrameFormat.GetAnchor());
877 aAnchor.SetType(m_eNewAnchorType);
878 aSet.Put(aAnchor);
879 }
880
881 if (SfxItemState::SET != aSet.GetItemState(RES_SURROUND))
882 aSet.Put(SwFormatSurround(css::text::WrapTextMode_NONE));
883
884 const XFillStyleItem* pXFillStyleItem(rFrameFormat.GetAttrSet().GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
885 if (pXFillStyleItem)
886 {
887 switch (pXFillStyleItem->GetValue())
888 {
889 case drawing::FillStyle_NONE:
890 break;
891 case drawing::FillStyle_SOLID:
892 {
893 // Construct an SvxBrushItem, as expected by the exporters.
894 std::shared_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rFrameFormat.GetAttrSet(), RES_BACKGROUND));
895 aSet.Put(*aBrush);
896 break;
897 }
898 default:
899 break;
900 }
901 }
902
903 m_bOutFlyFrameAttrs = true;
904 //script doesn't matter if not exporting chp
905 OutputItemSet(aSet, true, false,
906 i18n::ScriptType::LATIN, m_bExportModeRTF);
907 m_bOutFlyFrameAttrs = false;
908
909 bCallOutSet = false;
910 }
911 }
912 break;
913 case RES_FRMFMT:
914 break;
915 default:
916 OSL_ENSURE( false, "Which format is exported here?" );
917 break;
918 }
919
920 if( bCallOutSet )
921 OutputItemSet( rFormat.GetAttrSet(), bPapFormat, bChpFormat,
922 i18n::ScriptType::LATIN, m_bExportModeRTF);
923 m_pOutFormatNode = pOldMod;
924 }
925
HasRefToObject(sal_uInt16 nTyp,const OUString * pName,sal_uInt16 nSeqNo)926 bool MSWordExportBase::HasRefToObject( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo )
927 {
928
929 SwFieldType* pType = m_pDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef );
930 SwIterator<SwFormatField, SwFieldType> aFormatFields( *pType );
931 for ( SwFormatField* pFormatField = aFormatFields.First(); pFormatField; pFormatField = aFormatFields.Next() )
932 {
933 const SwTextNode* pNd;
934 if ( pFormatField->GetTextField() && nTyp == pFormatField->GetField()->GetSubType() &&
935 nullptr != ( pNd = pFormatField->GetTextField()->GetpTextNode() ) &&
936 pNd->GetNodes().IsDocNodes() )
937 {
938 const SwGetRefField& rRField = *static_cast< SwGetRefField* >( pFormatField->GetField() );
939 switch ( nTyp )
940 {
941 case REF_BOOKMARK:
942 case REF_SETREFATTR:
943 if ( pName && *pName == rRField.GetSetRefName() )
944 return true;
945 break;
946 case REF_FOOTNOTE:
947 case REF_ENDNOTE:
948 if ( nSeqNo == rRField.GetSeqNo() )
949 return true;
950 break;
951 case REF_SEQUENCEFLD:
952 break; // ???
953 case REF_OUTLINE:
954 break; // ???
955 }
956 }
957 }
958
959 return false;
960 }
961
GetBookmarkName(sal_uInt16 nTyp,const OUString * pName,sal_uInt16 nSeqNo)962 OUString MSWordExportBase::GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo )
963 {
964 OUString sRet;
965 switch ( nTyp )
966 {
967 case REF_SETREFATTR:
968 if ( pName )
969 {
970 sRet = "Ref_" + *pName;
971 }
972 break;
973 case REF_SEQUENCEFLD:
974 {
975 assert(pName);
976 sRet = "Ref_" + *pName;
977 break;
978 }
979 case REF_BOOKMARK:
980 if ( pName )
981 sRet = *pName;
982 break;
983 case REF_OUTLINE:
984 break; // ???
985 case REF_FOOTNOTE:
986 sRet = "_RefF" + OUString::number( nSeqNo );
987 break;
988 case REF_ENDNOTE:
989 sRet = "_RefE" + OUString::number( nSeqNo );
990 break;
991 }
992 return BookmarkToWord( sRet ); // #i43956# - encode bookmark accordingly
993 }
994
995 /* File CHRATR.HXX: */
RTLAndCJKState(bool bIsRTL,sal_uInt16 nScript)996 void WW8AttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript )
997 {
998 if (bIsRTL)
999 {
1000 if( m_rWW8Export.m_pDoc->GetDocumentType() != SwDoc::DOCTYPE_MSWORD )
1001 {
1002 m_rWW8Export.InsUInt16( NS_sprm::sprmCFBiDi );
1003 m_rWW8Export.pO->push_back( sal_uInt8(1) );
1004 }
1005 }
1006
1007 // #i46087# patch from james_clark; complex texts needs the undocumented SPRM CComplexScript with param 0x81.
1008 if (nScript == i18n::ScriptType::COMPLEX && !bIsRTL)
1009 {
1010 m_rWW8Export.InsUInt16( NS_sprm::sprmCFComplexScripts );
1011 m_rWW8Export.pO->push_back( sal_uInt8(0x81) );
1012 m_rWW8Export.pDop->bUseThaiLineBreakingRules = true;
1013 }
1014 }
1015
EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner)1016 void WW8AttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
1017 {
1018 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell() - (mbOnTOXEnding?2:0), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
1019 mbOnTOXEnding = false;
1020 m_rWW8Export.pO->clear();
1021
1022 if ( pTextNodeInfoInner.get() != nullptr )
1023 {
1024 if ( pTextNodeInfoInner->isEndOfLine() )
1025 {
1026 TableRowEnd( pTextNodeInfoInner->getDepth() );
1027
1028 SVBT16 nSty;
1029 ShortToSVBT16( 0, nSty );
1030 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nSty, nSty+2 ); // Style #
1031 TableInfoRow( pTextNodeInfoInner );
1032 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data());
1033 m_rWW8Export.pO->clear();
1034 }
1035 }
1036
1037 // Clear bookmarks of the current paragraph
1038 m_aBookmarksOfParagraphStart.clear();
1039 m_aBookmarksOfParagraphEnd.clear();
1040 }
1041
StartRunProperties()1042 void WW8AttributeOutput::StartRunProperties()
1043 {
1044 WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
1045 m_nFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
1046 }
1047
StartRun(const SwRedlineData * pRedlineData,sal_Int32 nPos,bool)1048 void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool /*bSingleEmptyRun*/ )
1049 {
1050 if (pRedlineData)
1051 {
1052 const OUString &rComment = pRedlineData->GetComment();
1053 //Only possible to export to main text
1054 if (!rComment.isEmpty() && (m_rWW8Export.m_nTextTyp == TXT_MAINTEXT))
1055 {
1056 if (m_rWW8Export.m_pAtn->IsNewRedlineComment(pRedlineData))
1057 {
1058 m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pRedlineData );
1059 m_rWW8Export.WritePostItBegin( m_rWW8Export.pO.get() );
1060 }
1061 }
1062 }
1063
1064 /// Insert bookmarks started at this run
1065 auto aRange = m_aBookmarksOfParagraphStart.equal_range(nPos);
1066 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1067 {
1068 GetExport().AppendBookmark(BookmarkToWord(aIter->second));
1069 }
1070 }
1071
OnTOXEnding()1072 void WW8AttributeOutput::OnTOXEnding()
1073 {
1074 mbOnTOXEnding = true;
1075 }
1076
EndRun(const SwTextNode *,sal_Int32 nPos,bool bLastRun)1077 void WW8AttributeOutput::EndRun( const SwTextNode* /*pNode*/, sal_Int32 nPos, bool bLastRun )
1078 {
1079 /// Insert bookmarks ended after this run
1080 auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nPos);
1081 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1082 {
1083 if(bLastRun)
1084 GetExport().AppendBookmarkEndWithCorrection(BookmarkToWord(aIter->second));
1085 else
1086 GetExport().AppendBookmark(BookmarkToWord(aIter->second));
1087 }
1088 }
1089
EndRunProperties(const SwRedlineData * pRedlineData)1090 void WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData )
1091 {
1092 Redline( pRedlineData );
1093
1094 WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
1095 sal_uInt16 nNewFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
1096
1097 bool bExportedFieldResult = ( m_nFieldResults != nNewFieldResults );
1098
1099 // If we have exported a field result, then we will have been forced to
1100 // split up the text into a 0x13, 0x14, <result> 0x15 sequence with the
1101 // properties forced out at the end of the result, so the 0x15 itself
1102 // should remain clean of all other attributes to avoid #iXXXXX#
1103 if ( !bExportedFieldResult )
1104 {
1105 m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
1106 m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
1107 }
1108 m_rWW8Export.pO->clear();
1109 }
1110
RunText(const OUString & rText,rtl_TextEncoding eCharSet)1111 void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet )
1112 {
1113 RawText(rText, eCharSet);
1114 }
1115
RawText(const OUString & rText,rtl_TextEncoding)1116 void WW8AttributeOutput::RawText(const OUString& rText, rtl_TextEncoding)
1117 {
1118 m_rWW8Export.OutSwString(rText, 0, rText.getLength());
1119 }
1120
OutputFKP(bool bForce)1121 void WW8AttributeOutput::OutputFKP(bool bForce)
1122 {
1123 if (!m_rWW8Export.pO->empty() || bForce)
1124 {
1125 m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
1126 m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
1127 m_rWW8Export.pO->clear();
1128 }
1129 }
1130
ParagraphStyle(sal_uInt16 nStyle)1131 void WW8AttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
1132 {
1133 OSL_ENSURE( m_rWW8Export.pO->empty(), " pO is not empty at line end" );
1134
1135 SVBT16 nSty;
1136 ShortToSVBT16( nStyle, nSty );
1137 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nSty, nSty+2 ); // style #
1138 }
1139
OutputWW8Attribute(sal_uInt8 nId,bool bVal)1140 void WW8AttributeOutput::OutputWW8Attribute( sal_uInt8 nId, bool bVal )
1141 {
1142 m_rWW8Export.InsUInt16( 8 == nId ? NS_sprm::sprmCFDStrike : NS_sprm::sprmCFBold + nId );
1143
1144 m_rWW8Export.pO->push_back( bVal ? 1 : 0 );
1145 }
1146
OutputWW8AttributeCTL(sal_uInt8 nId,bool bVal)1147 void WW8AttributeOutput::OutputWW8AttributeCTL( sal_uInt8 nId, bool bVal )
1148 {
1149 OSL_ENSURE( nId <= 1, "out of range" );
1150 if (nId > 1)
1151 return;
1152
1153 m_rWW8Export.InsUInt16( NS_sprm::sprmCFBoldBi + nId );
1154 m_rWW8Export.pO->push_back( bVal ? 1 : 0 );
1155 }
1156
CharFont(const SvxFontItem & rFont)1157 void WW8AttributeOutput::CharFont( const SvxFontItem& rFont )
1158 {
1159 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1160
1161 m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc0 );
1162 m_rWW8Export.InsUInt16( nFontID );
1163 m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc2 );
1164
1165 m_rWW8Export.InsUInt16( nFontID );
1166 }
1167
CharFontCTL(const SvxFontItem & rFont)1168 void WW8AttributeOutput::CharFontCTL( const SvxFontItem& rFont )
1169 {
1170 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1171 m_rWW8Export.InsUInt16( NS_sprm::sprmCFtcBi );
1172 m_rWW8Export.InsUInt16( nFontID );
1173 }
1174
CharFontCJK(const SvxFontItem & rFont)1175 void WW8AttributeOutput::CharFontCJK( const SvxFontItem& rFont )
1176 {
1177 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1178 m_rWW8Export.InsUInt16( NS_sprm::sprmCRgFtc1 );
1179 m_rWW8Export.InsUInt16( nFontID );
1180 }
1181
CharWeightCTL(const SvxWeightItem & rWeight)1182 void WW8AttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight )
1183 {
1184 OutputWW8AttributeCTL( 0, WEIGHT_BOLD == rWeight.GetWeight());
1185 }
1186
CharPostureCTL(const SvxPostureItem & rPosture)1187 void WW8AttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture )
1188 {
1189 OutputWW8AttributeCTL( 1, ITALIC_NONE != rPosture.GetPosture() );
1190 }
1191
CharPosture(const SvxPostureItem & rPosture)1192 void WW8AttributeOutput::CharPosture( const SvxPostureItem& rPosture )
1193 {
1194 OutputWW8Attribute( 1, ITALIC_NONE != rPosture.GetPosture() );
1195 }
1196
CharWeight(const SvxWeightItem & rWeight)1197 void WW8AttributeOutput::CharWeight( const SvxWeightItem& rWeight )
1198 {
1199 OutputWW8Attribute( 0, WEIGHT_BOLD == rWeight.GetWeight() );
1200 }
1201
1202 // Shadowed and Contour are not in WW-UI. JP: ??
CharContour(const SvxContourItem & rContour)1203 void WW8AttributeOutput::CharContour( const SvxContourItem& rContour )
1204 {
1205 OutputWW8Attribute( 3, rContour.GetValue() );
1206 }
1207
CharShadow(const SvxShadowedItem & rShadow)1208 void WW8AttributeOutput::CharShadow( const SvxShadowedItem& rShadow )
1209 {
1210 OutputWW8Attribute( 4, rShadow.GetValue() );
1211 }
1212
CharKerning(const SvxKerningItem & rKerning)1213 void WW8AttributeOutput::CharKerning( const SvxKerningItem& rKerning )
1214 {
1215 m_rWW8Export.InsUInt16( NS_sprm::sprmCDxaSpace );
1216
1217 m_rWW8Export.InsUInt16( rKerning.GetValue() );
1218 }
1219
CharAutoKern(const SvxAutoKernItem & rAutoKern)1220 void WW8AttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern )
1221 {
1222 m_rWW8Export.InsUInt16( NS_sprm::sprmCHpsKern );
1223
1224 m_rWW8Export.InsUInt16( rAutoKern.GetValue() ? 2 : 0 );
1225 }
1226
CharAnimatedText(const SvxBlinkItem & rBlink)1227 void WW8AttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
1228 {
1229 m_rWW8Export.InsUInt16( NS_sprm::sprmCSfxText );
1230 // At the moment the only animated text effect we support is blinking
1231 m_rWW8Export.pO->push_back( rBlink.GetValue() ? 2 : 0 );
1232 }
1233
CharCrossedOut(const SvxCrossedOutItem & rCrossed)1234 void WW8AttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossed )
1235 {
1236 FontStrikeout eSt = rCrossed.GetStrikeout();
1237 if ( STRIKEOUT_DOUBLE == eSt )
1238 {
1239 OutputWW8Attribute( 8, true );
1240 return;
1241 }
1242 if ( STRIKEOUT_NONE != eSt )
1243 {
1244 OutputWW8Attribute( 2, true );
1245 return;
1246 }
1247
1248 // otherwise both off
1249 OutputWW8Attribute( 8, false );
1250 OutputWW8Attribute( 2, false );
1251 }
1252
CharCaseMap(const SvxCaseMapItem & rCaseMap)1253 void WW8AttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap )
1254 {
1255 SvxCaseMap eSt = rCaseMap.GetValue();
1256 switch ( eSt )
1257 {
1258 case SvxCaseMap::SmallCaps:
1259 OutputWW8Attribute( 5, true );
1260 return;
1261 case SvxCaseMap::Uppercase:
1262 OutputWW8Attribute( 6, true );
1263 return;
1264 case SvxCaseMap::Capitalize:
1265 // no such feature in word
1266 break;
1267 default:
1268 // otherwise both off
1269 OutputWW8Attribute( 5, false );
1270 OutputWW8Attribute( 6, false );
1271 return;
1272 }
1273 }
1274
CharHidden(const SvxCharHiddenItem & rHidden)1275 void WW8AttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden )
1276 {
1277 OutputWW8Attribute( 7, rHidden.GetValue() );
1278 }
1279
CharBorder(const SvxBorderLine * pAllBorder,const sal_uInt16,const bool bShadow)1280 void WW8AttributeOutput::CharBorder( const SvxBorderLine* pAllBorder, const sal_uInt16 /*nDist*/, const bool bShadow )
1281 {
1282 WW8Export::Out_BorderLine( *m_rWW8Export.pO, pAllBorder, 0, NS_sprm::sprmCBrc80, NS_sprm::sprmCBrc, bShadow );
1283 }
1284
CharHighlight(const SvxBrushItem & rBrush)1285 void WW8AttributeOutput::CharHighlight( const SvxBrushItem& rBrush )
1286 {
1287 if (rBrush.GetColor() != COL_TRANSPARENT)
1288 {
1289 sal_uInt8 nColor = msfilter::util::TransColToIco( rBrush.GetColor() );
1290 // sprmCHighlight
1291 m_rWW8Export.InsUInt16( NS_sprm::sprmCHighlight );
1292 m_rWW8Export.pO->push_back( nColor );
1293 }
1294 }
1295
CharUnderline(const SvxUnderlineItem & rUnderline)1296 void WW8AttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline )
1297 {
1298 m_rWW8Export.InsUInt16( NS_sprm::sprmCKul );
1299
1300 const SfxPoolItem* pItem = m_rWW8Export.HasItem( RES_CHRATR_WORDLINEMODE );
1301 bool bWord = false;
1302 if (pItem)
1303 bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue();
1304
1305 // WW95 - parameters: 0 = none, 1 = single, 2 = by Word,
1306 // 3 = double, 4 = dotted, 5 = hidden
1307 // WW97 - additional parameters:
1308 // 6 = thick, 7 = dash, 8 = dot(not used)
1309 // 9 = dotdash 10 = dotdotdash, 11 = wave
1310 sal_uInt8 b = 0;
1311 switch ( rUnderline.GetLineStyle() )
1312 {
1313 case LINESTYLE_SINGLE:
1314 b = bWord ? 2 : 1;
1315 break;
1316 case LINESTYLE_BOLD:
1317 b = 6;
1318 break;
1319 case LINESTYLE_DOUBLE:
1320 b = 3;
1321 break;
1322 case LINESTYLE_DOTTED:
1323 b = 4;
1324 break;
1325 case LINESTYLE_DASH:
1326 b = 7;
1327 break;
1328 case LINESTYLE_DASHDOT:
1329 b = 9;
1330 break;
1331 case LINESTYLE_DASHDOTDOT:
1332 b = 10;
1333 break;
1334 case LINESTYLE_WAVE:
1335 b = 11;
1336 break;
1337 // new in WW2000
1338 case LINESTYLE_BOLDDOTTED:
1339 b = 20;
1340 break;
1341 case LINESTYLE_BOLDDASH:
1342 b = 23;
1343 break;
1344 case LINESTYLE_LONGDASH:
1345 b = 39;
1346 break;
1347 case LINESTYLE_BOLDLONGDASH:
1348 b = 55;
1349 break;
1350 case LINESTYLE_BOLDDASHDOT:
1351 b = 25;
1352 break;
1353 case LINESTYLE_BOLDDASHDOTDOT:
1354 b = 26;
1355 break;
1356 case LINESTYLE_BOLDWAVE:
1357 b = 27;
1358 break;
1359 case LINESTYLE_DOUBLEWAVE:
1360 b = 43;
1361 break;
1362 case LINESTYLE_NONE:
1363 b = 0;
1364 break;
1365 default:
1366 OSL_ENSURE( rUnderline.GetLineStyle() == LINESTYLE_NONE, "Unhandled underline type" );
1367 break;
1368 }
1369
1370 m_rWW8Export.pO->push_back( b );
1371 Color aColor = rUnderline.GetColor();
1372 if( aColor != COL_TRANSPARENT )
1373 {
1374 m_rWW8Export.InsUInt16( NS_sprm::sprmCCvUl );
1375
1376 m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( aColor ) );
1377 }
1378 }
1379
CharLanguage(const SvxLanguageItem & rLanguage)1380 void WW8AttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage )
1381 {
1382 sal_uInt16 nId = 0;
1383 switch ( rLanguage.Which() )
1384 {
1385 case RES_CHRATR_LANGUAGE:
1386 nId = NS_sprm::sprmCRgLid0_80;
1387 break;
1388 case RES_CHRATR_CJK_LANGUAGE:
1389 nId = NS_sprm::sprmCRgLid1_80;
1390 break;
1391 case RES_CHRATR_CTL_LANGUAGE:
1392 nId = NS_sprm::sprmCLidBi;
1393 break;
1394 }
1395
1396 if ( nId )
1397 {
1398 // use sprmCRgLid0_80 rather than sprmCLid
1399 m_rWW8Export.InsUInt16( nId );
1400 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1401
1402 // Word 2000 and above apparently require both old and new versions of
1403 // these sprms to be set, without it spellchecking doesn't work
1404 if ( nId == NS_sprm::sprmCRgLid0_80 )
1405 {
1406 m_rWW8Export.InsUInt16( NS_sprm::sprmCRgLid0 );
1407 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1408 }
1409 else if ( nId == NS_sprm::sprmCRgLid1_80 )
1410 {
1411 m_rWW8Export.InsUInt16( NS_sprm::sprmCRgLid1 );
1412 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1413 }
1414 }
1415 }
1416
CharEscapement(const SvxEscapementItem & rEscapement)1417 void WW8AttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement )
1418 {
1419 sal_uInt8 b = 0xFF;
1420 short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight();
1421 if ( !nEsc )
1422 {
1423 b = 0;
1424 nEsc = 0;
1425 nProp = 100;
1426 }
1427 else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 )
1428 {
1429 if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
1430 b = 2;
1431 else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
1432 b = 1;
1433 }
1434 else if ( DFLT_ESC_AUTO_SUPER == nEsc )
1435 {
1436 // Raised by the differences between the ascenders (ascent = baseline to top of highest letter).
1437 // The ascent is generally about 80% of the total font height.
1438 // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER)
1439 nEsc = .8 * (100 - nProp);
1440 }
1441 else if ( DFLT_ESC_AUTO_SUB == nEsc )
1442 {
1443 // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter).
1444 // The descent is generally about 20% of the total font height.
1445 // That is why DFLT_ESC_PROP (58) _originally_ lead to 8% (DFLT_ESC_SUB)
1446 nEsc = .2 * -(100 - nProp);
1447 }
1448
1449 if ( 0xFF != b )
1450 {
1451 m_rWW8Export.InsUInt16( NS_sprm::sprmCIss );
1452
1453 m_rWW8Export.pO->push_back( b );
1454 }
1455
1456 if ( 0 == b || 0xFF == b )
1457 {
1458 double fHeight = m_rWW8Export.GetItem( RES_CHRATR_FONTSIZE ).GetHeight();
1459 m_rWW8Export.InsUInt16( NS_sprm::sprmCHpsPos );
1460
1461 m_rWW8Export.InsUInt16(static_cast<short>( round(fHeight * nEsc / 1000) ));
1462
1463 if( 100 != nProp || !b )
1464 {
1465 m_rWW8Export.InsUInt16( NS_sprm::sprmCHps );
1466 m_rWW8Export.InsUInt16(msword_cast<sal_uInt16>( round(fHeight * nProp / 1000) ));
1467 }
1468 }
1469 }
1470
CharFontSize(const SvxFontHeightItem & rHeight)1471 void WW8AttributeOutput::CharFontSize( const SvxFontHeightItem& rHeight )
1472 {
1473 sal_uInt16 nId = 0;
1474 switch ( rHeight.Which() )
1475 {
1476 case RES_CHRATR_FONTSIZE:
1477 case RES_CHRATR_CJK_FONTSIZE:
1478 nId = NS_sprm::sprmCHps;
1479 break;
1480 case RES_CHRATR_CTL_FONTSIZE:
1481 nId = NS_sprm::sprmCHpsBi;
1482 break;
1483 }
1484
1485 if ( nId )
1486 {
1487 m_rWW8Export.InsUInt16( nId );
1488
1489 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(( rHeight.GetHeight() + 5 ) / 10 ) );
1490 }
1491 }
1492
CharScaleWidth(const SvxCharScaleWidthItem & rScaleWidth)1493 void WW8AttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth )
1494 {
1495 m_rWW8Export.InsUInt16( NS_sprm::sprmCCharScale );
1496 m_rWW8Export.InsUInt16( rScaleWidth.GetValue() );
1497 }
1498
CharRelief(const SvxCharReliefItem & rRelief)1499 void WW8AttributeOutput::CharRelief( const SvxCharReliefItem& rRelief )
1500 {
1501 sal_uInt16 nId;
1502 switch ( rRelief.GetValue() )
1503 {
1504 case FontRelief::Embossed: nId = NS_sprm::sprmCFEmboss; break;
1505 case FontRelief::Engraved: nId = NS_sprm::sprmCFImprint; break;
1506 default: nId = 0; break;
1507 }
1508
1509 if( nId )
1510 {
1511 m_rWW8Export.InsUInt16( nId );
1512 m_rWW8Export.pO->push_back( sal_uInt8(0x81) );
1513 }
1514 else
1515 {
1516 // switch both flags off
1517 m_rWW8Export.InsUInt16( NS_sprm::sprmCFEmboss );
1518 m_rWW8Export.pO->push_back( sal_uInt8(0x0) );
1519 m_rWW8Export.InsUInt16( NS_sprm::sprmCFImprint );
1520 m_rWW8Export.pO->push_back( sal_uInt8(0x0) );
1521 }
1522 }
1523
CharBidiRTL(const SfxPoolItem & rHt)1524 void WW8AttributeOutput::CharBidiRTL( const SfxPoolItem& rHt )
1525 {
1526 const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
1527 if( rAttr.GetValue() == 1 )
1528 {
1529 m_rWW8Export.InsUInt16(0x85a);
1530 m_rWW8Export.pO->push_back(sal_uInt8(1));
1531 }
1532 }
1533
CharIdctHint(const SfxPoolItem & rHt)1534 void WW8AttributeOutput::CharIdctHint( const SfxPoolItem& rHt )
1535 {
1536 const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
1537 m_rWW8Export.InsUInt16(0x286F);
1538 m_rWW8Export.pO->push_back(static_cast<sal_uInt8>(rAttr.GetValue()));
1539 }
1540
CharRotate(const SvxCharRotateItem & rRotate)1541 void WW8AttributeOutput::CharRotate( const SvxCharRotateItem& rRotate )
1542 {
1543 // #i28331# - check that a Value is set
1544 if ( !rRotate.GetValue() )
1545 return;
1546
1547 if (!m_rWW8Export.IsInTable())
1548 {
1549 // #i36867 In word the text in a table is rotated via the TC or NS_sprm::sprmTTextFlow
1550 // This means you can only rotate all or none of the text adding NS_sprm::sprmCFELayout
1551 // here corrupts the table, hence !m_rWW8Export.bIsInTable
1552
1553 m_rWW8Export.InsUInt16( NS_sprm::sprmCFELayout );
1554 m_rWW8Export.pO->push_back( sal_uInt8(0x06) ); //len 6
1555 m_rWW8Export.pO->push_back( sal_uInt8(0x01) );
1556
1557 m_rWW8Export.InsUInt16( rRotate.IsFitToLine() ? 1 : 0 );
1558 static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
1559 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aZeroArr, aZeroArr+3);
1560 }
1561 }
1562
CharEmphasisMark(const SvxEmphasisMarkItem & rEmphasisMark)1563 void WW8AttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark )
1564 {
1565 sal_uInt8 nVal;
1566 const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
1567 if (v == FontEmphasisMark::NONE)
1568 nVal = 0;
1569 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
1570 nVal = 2;
1571 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
1572 nVal = 3;
1573 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
1574 nVal = 4;
1575 else
1576 // case 1:
1577 nVal = 1;
1578
1579 m_rWW8Export.InsUInt16( NS_sprm::sprmCKcd );
1580 m_rWW8Export.pO->push_back( nVal );
1581 }
1582
1583 /**
1584 * TransBrush converts SW-Brushes to WW. The result is WW8_SHD.
1585 * Non-standard colours of SW won't be converted now to the mixed values
1586 * ( 0 .. 95% ) of WW.
1587 * Also if transparent, e.g. for tables a transparent brush is returned
1588 *
1589 * @return real brush ( not transparent )
1590 */
TransBrush(const Color & rCol,WW8_SHD & rShd)1591 bool WW8Export::TransBrush(const Color& rCol, WW8_SHD& rShd)
1592 {
1593 if( rCol.GetTransparency() )
1594 rShd = WW8_SHD(); // all zeros: transparent
1595 else
1596 {
1597 rShd.SetFore( 0);
1598 rShd.SetBack( msfilter::util::TransColToIco( rCol ) );
1599 rShd.SetStyle( 0 );
1600 }
1601 return !rCol.GetTransparency();
1602 }
1603
SuitableBGColor(Color nIn)1604 static sal_uInt32 SuitableBGColor(Color nIn)
1605 {
1606 if (nIn == COL_AUTO)
1607 return 0xFF000000;
1608 return wwUtility::RGBToBGR(nIn);
1609 }
1610
CharColor(const SvxColorItem & rColor)1611 void WW8AttributeOutput::CharColor( const SvxColorItem& rColor )
1612 {
1613 m_rWW8Export.InsUInt16( NS_sprm::sprmCIco );
1614
1615 sal_uInt8 nColor = msfilter::util::TransColToIco( rColor.GetValue() );
1616 m_rWW8Export.pO->push_back( nColor );
1617
1618 if (nColor)
1619 {
1620 m_rWW8Export.InsUInt16( NS_sprm::sprmCCv );
1621 m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( rColor.GetValue() ) );
1622 }
1623 }
1624
CharBackground(const SvxBrushItem & rBrush)1625 void WW8AttributeOutput::CharBackground( const SvxBrushItem& rBrush )
1626 {
1627 WW8_SHD aSHD;
1628
1629 WW8Export::TransBrush( rBrush.GetColor(), aSHD );
1630 // sprmCShd80
1631 m_rWW8Export.InsUInt16( NS_sprm::sprmCShd80 );
1632 m_rWW8Export.InsUInt16( aSHD.GetValue() );
1633
1634 //Quite a few unknowns, some might be transparency or something
1635 //of that nature...
1636 m_rWW8Export.InsUInt16( NS_sprm::sprmCShd );
1637 m_rWW8Export.pO->push_back( 10 );
1638 m_rWW8Export.InsUInt32( 0xFF000000 );
1639 m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) );
1640 m_rWW8Export.InsUInt16( 0x0000);
1641 }
1642
TextINetFormat(const SwFormatINetFormat & rINet)1643 void WW8AttributeOutput::TextINetFormat( const SwFormatINetFormat& rINet )
1644 {
1645 if ( !rINet.GetValue().isEmpty() )
1646 {
1647 const sal_uInt16 nId = rINet.GetINetFormatId();
1648 const OUString& rStr = rINet.GetINetFormat();
1649 if (rStr.isEmpty())
1650 {
1651 OSL_ENSURE( false, "WW8AttributeOutput::TextINetFormat(..) - missing unvisited character format at hyperlink attribute" );
1652 }
1653
1654 const SwCharFormat* pFormat = IsPoolUserFormat( nId )
1655 ? m_rWW8Export.m_pDoc->FindCharFormatByName( rStr )
1656 : m_rWW8Export.m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId );
1657
1658 m_rWW8Export.InsUInt16( NS_sprm::sprmCIstd );
1659
1660 m_rWW8Export.InsUInt16( m_rWW8Export.GetId( pFormat ) );
1661 }
1662 }
1663
1664 // #i43956# - add optional parameter <pLinkStr>
1665 // It's needed to write the hyperlink data for a certain cross-reference
1666 // - it contains the name of the link target, which is a bookmark.
1667 // add optional parameter <bIncludeEmptyPicLocation>
1668 // It is needed to write an empty picture location for page number field separators
InsertSpecialChar(WW8Export & rWrt,sal_uInt8 c,OUString const * pLinkStr,bool bIncludeEmptyPicLocation=false)1669 static void InsertSpecialChar( WW8Export& rWrt, sal_uInt8 c,
1670 OUString const * pLinkStr,
1671 bool bIncludeEmptyPicLocation = false )
1672 {
1673 ww::bytes aItems;
1674 rWrt.GetCurrentItems(aItems);
1675
1676 if (c == 0x13)
1677 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell());
1678 else
1679 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
1680
1681 rWrt.WriteChar(c);
1682
1683 // store empty sprmCPicLocation for field separator
1684 if ( bIncludeEmptyPicLocation &&
1685 ( c == 0x13 || c == 0x14 || c == 0x15 ) )
1686 {
1687 SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCPicLocation );
1688 SwWW8Writer::InsUInt32( aItems, 0x00000000 );
1689 }
1690
1691 // #i43956# - write hyperlink data and attributes
1692 if ( c == 0x01 && pLinkStr)
1693 {
1694 // write hyperlink data to data stream
1695 SvStream& rStrm = *rWrt.pDataStrm;
1696 // position of hyperlink data
1697 const sal_uInt32 nLinkPosInDataStrm = rStrm.Tell();
1698 // write empty header
1699 const sal_uInt16 nEmptyHdrLen = 0x44;
1700 sal_uInt8 aEmptyHeader[ nEmptyHdrLen ] = { 0 };
1701 aEmptyHeader[ 4 ] = 0x44;
1702 rStrm.WriteBytes( aEmptyHeader, nEmptyHdrLen );
1703 // writer fixed header
1704 const sal_uInt16 nFixHdrLen = 0x19;
1705 sal_uInt8 const aFixHeader[ nFixHdrLen ] =
1706 {
1707 0x08, 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE,
1708 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9,
1709 0x0B, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
1710 0x00,
1711 };
1712 rStrm.WriteBytes( aFixHeader, nFixHdrLen );
1713 // write reference string including length+1
1714 sal_uInt32 nStrLen( pLinkStr->getLength() + 1 );
1715 SwWW8Writer::WriteLong( rStrm, nStrLen );
1716 SwWW8Writer::WriteString16( rStrm, *pLinkStr, false );
1717 // write additional two NULL Bytes
1718 SwWW8Writer::WriteLong( rStrm, 0 );
1719 // write length of hyperlink data
1720 const sal_uInt32 nCurrPos = rStrm.Tell();
1721 rStrm.Seek( nLinkPosInDataStrm );
1722 rStrm.WriteUInt32(nCurrPos - nLinkPosInDataStrm);
1723 rStrm.Seek( nCurrPos );
1724
1725 // write attributes of hyperlink character 0x01
1726 SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCFFldVanish );
1727 aItems.push_back( sal_uInt8(0x81) );
1728 SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCPicLocation );
1729 SwWW8Writer::InsUInt32( aItems, nLinkPosInDataStrm );
1730 SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCFData );
1731 aItems.push_back( sal_uInt8(0x01) );
1732 }
1733
1734 //Technically we should probably Remove all attribs
1735 //here for the 0x13, 0x14, 0x15, but our import
1736 //is slightly lacking
1737 //aItems.Remove(0, aItems.Count());
1738 // fSpec-Attribute true
1739 SwWW8Writer::InsUInt16( aItems, NS_sprm::sprmCFSpec );
1740 aItems.push_back( 1 );
1741
1742 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
1743 }
1744
lcl_GetExpandedField(const SwField & rField)1745 static OUString lcl_GetExpandedField(const SwField &rField)
1746 {
1747 //replace LF 0x0A with VT 0x0B
1748 return rField.ExpandField(true, nullptr).replace(0x0A, 0x0B);
1749 }
1750
CurrentFieldPlc() const1751 WW8_WrPlcField* WW8Export::CurrentFieldPlc() const
1752 {
1753 WW8_WrPlcField* pFieldP = nullptr;
1754 switch (m_nTextTyp)
1755 {
1756 case TXT_MAINTEXT:
1757 pFieldP = m_pFieldMain.get();
1758 break;
1759 case TXT_HDFT:
1760 pFieldP = m_pFieldHdFt.get();
1761 break;
1762 case TXT_FTN:
1763 pFieldP = m_pFieldFootnote.get();
1764 break;
1765 case TXT_EDN:
1766 pFieldP = m_pFieldEdn.get();
1767 break;
1768 case TXT_ATN:
1769 pFieldP = m_pFieldAtn.get();
1770 break;
1771 case TXT_TXTBOX:
1772 pFieldP = m_pFieldTextBxs.get();
1773 break;
1774 case TXT_HFTXTBOX:
1775 pFieldP = m_pFieldHFTextBxs.get();
1776 break;
1777 default:
1778 OSL_ENSURE( false, "what type of SubDoc is that?" );
1779 }
1780 return pFieldP;
1781 }
1782
OutputField(const SwField * pField,ww::eField eFieldType,const OUString & rFieldCmd,FieldFlags nMode)1783 void WW8Export::OutputField( const SwField* pField, ww::eField eFieldType,
1784 const OUString& rFieldCmd, FieldFlags nMode )
1785 {
1786 OUString sFieldCmd(rFieldCmd);
1787 switch (eFieldType)
1788 {
1789 // map fields that are not supported in WW8 as of Word 2003
1790 case ww::eBIBLIOGRAPHY:
1791 eFieldType = ww::eQUOTE;
1792 assert(rFieldCmd == FieldString(ww::eBIBLIOGRAPHY));
1793 sFieldCmd = FieldString(ww::eQUOTE);
1794 break;
1795 case ww::eCITATION:
1796 eFieldType = ww::eQUOTE;
1797 assert(rFieldCmd.trim().startsWith("CITATION"));
1798 sFieldCmd = rFieldCmd.replaceFirst(FieldString(ww::eCITATION),
1799 FieldString(ww::eQUOTE));
1800 break;
1801 default:
1802 break;
1803 }
1804
1805 assert(eFieldType <= 0x5F); // 95 is the highest documented one
1806
1807 WW8_WrPlcField* pFieldP = CurrentFieldPlc();
1808
1809 const bool bIncludeEmptyPicLocation = ( eFieldType == ww::ePAGE );
1810 if (FieldFlags::Start & nMode)
1811 {
1812 sal_uInt8 aField13[2] = { 0x13, 0x00 }; // will change
1813 //#i3958#, Needed to make this field work correctly in Word 2000
1814 if (eFieldType == ww::eSHAPE)
1815 aField13[0] |= 0x80;
1816 aField13[1] = static_cast< sal_uInt8 >(eFieldType); // add type
1817 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField13 );
1818 InsertSpecialChar( *this, 0x13, nullptr, bIncludeEmptyPicLocation );
1819 }
1820 if (FieldFlags::CmdStart & nMode)
1821 {
1822 SwWW8Writer::WriteString16(Strm(), sFieldCmd, false);
1823 // #i43956# - write hyperlink character including
1824 // attributes and corresponding binary data for certain reference fields.
1825 bool bHandleBookmark = false;
1826
1827 if (pField)
1828 {
1829 if (pField->GetTyp()->Which() == SwFieldIds::GetRef &&
1830 ( eFieldType == ww::ePAGEREF || eFieldType == ww::eREF ||
1831 eFieldType == ww::eNOTEREF || eFieldType == ww::eFOOTREF ))
1832 bHandleBookmark = true;
1833 }
1834
1835 if ( bHandleBookmark )
1836 {
1837 // retrieve reference destination - the name of the bookmark
1838 OUString aLinkStr;
1839 const sal_uInt16 nSubType = pField->GetSubType();
1840 const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
1841 if ( nSubType == REF_SETREFATTR ||
1842 nSubType == REF_BOOKMARK )
1843 {
1844 const OUString& aRefName(rRField.GetSetRefName());
1845 aLinkStr = GetBookmarkName( nSubType, &aRefName, 0 );
1846 }
1847 else if ( nSubType == REF_FOOTNOTE ||
1848 nSubType == REF_ENDNOTE )
1849 {
1850 aLinkStr = GetBookmarkName( nSubType, nullptr, rRField.GetSeqNo() );
1851 }
1852 else if ( nSubType == REF_SEQUENCEFLD )
1853 {
1854 aLinkStr = pField->GetPar2();
1855 }
1856 // insert hyperlink character including attributes and data.
1857 InsertSpecialChar( *this, 0x01, &aLinkStr );
1858 }
1859 }
1860 if (FieldFlags::CmdEnd & nMode)
1861 {
1862 static const sal_uInt8 aField14[2] = { 0x14, 0xff };
1863 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField14 );
1864 pFieldP->ResultAdded();
1865 InsertSpecialChar( *this, 0x14, nullptr, bIncludeEmptyPicLocation );
1866 }
1867 if (FieldFlags::End & nMode)
1868 {
1869 OUString sOut;
1870 if( pField )
1871 sOut = lcl_GetExpandedField(*pField);
1872 else
1873 sOut = sFieldCmd;
1874 if( !sOut.isEmpty() )
1875 {
1876 SwWW8Writer::WriteString16(Strm(), sOut, false);
1877
1878 if (pField)
1879 {
1880 if (pField->GetTyp()->Which() == SwFieldIds::Input &&
1881 eFieldType == ww::eFORMTEXT)
1882 {
1883 sal_uInt8 aArr[12];
1884 sal_uInt8 *pArr = aArr;
1885
1886 Set_UInt16( pArr, NS_sprm::sprmCPicLocation );
1887 Set_UInt32( pArr, 0x0 );
1888
1889 Set_UInt16( pArr, NS_sprm::sprmCFSpec );
1890 Set_UInt8( pArr, 1 );
1891
1892 Set_UInt16( pArr, NS_sprm::sprmCFNoProof );
1893 Set_UInt8( pArr, 1 );
1894
1895 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
1896 }
1897 }
1898 }
1899 }
1900 if (FieldFlags::Close & nMode)
1901 {
1902 sal_uInt8 aField15[2] = { 0x15, 0x80 };
1903
1904 if (pField)
1905 {
1906 if (pField->GetTyp()->Which() == SwFieldIds::Input &&
1907 eFieldType == ww::eFORMTEXT)
1908 {
1909 sal_uInt16 nSubType = pField->GetSubType();
1910
1911 if (nSubType == REF_SEQUENCEFLD)
1912 aField15[0] |= (0x4 << 5);
1913 }
1914 }
1915
1916 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField15 );
1917 InsertSpecialChar( *this, 0x15, nullptr, bIncludeEmptyPicLocation );
1918 }
1919 }
1920
StartCommentOutput(const OUString & rName)1921 void WW8Export::StartCommentOutput(const OUString& rName)
1922 {
1923 const OUString sStr{ FieldString(ww::eQUOTE) + "[" + rName + "] " };
1924 OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::Start | FieldFlags::CmdStart);
1925 }
1926
EndCommentOutput(const OUString & rName)1927 void WW8Export::EndCommentOutput(const OUString& rName)
1928 {
1929 const OUString sStr{ " [" + rName + "] " };
1930 OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::CmdEnd | FieldFlags::End |
1931 FieldFlags::Close);
1932 }
1933
GetId(const SwTOXType & rTOXType)1934 sal_uInt16 MSWordExportBase::GetId( const SwTOXType& rTOXType )
1935 {
1936 std::vector<const SwTOXType*>::iterator it
1937 = std::find( m_aTOXArr.begin(), m_aTOXArr.end(), &rTOXType );
1938 if ( it != m_aTOXArr.end() )
1939 {
1940 return it - m_aTOXArr.begin();
1941 }
1942 m_aTOXArr.push_back( &rTOXType );
1943 return m_aTOXArr.size() - 1;
1944 }
1945
1946 // return values: 1 - no PageNum,
1947 // 2 - TabStop before PageNum,
1948 // 3 - Text before PageNum - rText hold the text
1949 // 4 - no Text and no TabStop before PageNum
lcl_CheckForm(const SwForm & rForm,sal_uInt8 nLvl,OUString & rText)1950 static int lcl_CheckForm( const SwForm& rForm, sal_uInt8 nLvl, OUString& rText )
1951 {
1952 int nRet = 4;
1953 rText.clear();
1954
1955 // #i21237#
1956 SwFormTokens aPattern = rForm.GetPattern(nLvl);
1957 SwFormTokens::iterator aIt = aPattern.begin();
1958 FormTokenType eTType;
1959
1960 // #i61362#
1961 if (! aPattern.empty())
1962 {
1963 bool bPgNumFnd = false;
1964
1965 // #i21237#
1966 while( ++aIt != aPattern.end() && !bPgNumFnd )
1967 {
1968 eTType = aIt->eTokenType;
1969
1970 switch( eTType )
1971 {
1972 case TOKEN_PAGE_NUMS:
1973 bPgNumFnd = true;
1974 break;
1975
1976 case TOKEN_TAB_STOP:
1977 nRet = 2;
1978 break;
1979 case TOKEN_TEXT:
1980 {
1981 nRet = 3;
1982 sal_Int32 nCount = std::min<sal_Int32>(5, aIt->sText.getLength());
1983 rText = aIt->sText.copy(0, nCount); // #i21237#
1984 break;
1985 }
1986 case TOKEN_LINK_START:
1987 case TOKEN_LINK_END:
1988 break;
1989
1990 default:
1991 nRet = 4;
1992 break;
1993 }
1994 }
1995
1996 if( !bPgNumFnd )
1997 nRet = 1;
1998 }
1999
2000 return nRet;
2001 }
2002
lcl_IsHyperlinked(const SwForm & rForm,sal_uInt16 nTOXLvl)2003 static bool lcl_IsHyperlinked(const SwForm& rForm, sal_uInt16 nTOXLvl)
2004 {
2005 bool bRes = false;
2006 for (sal_uInt16 nI = 1; nI < nTOXLvl; ++nI)
2007 {
2008 // #i21237#
2009 SwFormTokens aPattern = rForm.GetPattern(nI);
2010
2011 if ( !aPattern.empty() )
2012 {
2013 SwFormTokens::iterator aIt = aPattern.begin();
2014
2015 FormTokenType eTType;
2016
2017 // #i21237#
2018 while ( ++aIt != aPattern.end() )
2019 {
2020 eTType = aIt->eTokenType;
2021 switch (eTType)
2022 {
2023 case TOKEN_LINK_START:
2024 case TOKEN_LINK_END:
2025 bRes = true;
2026 break;
2027 default:
2028 ;
2029 }
2030 }
2031 }
2032 }
2033 return bRes;
2034 }
2035
GenerateBookmarksForSequenceField(const SwTextNode & rNode,SwWW8AttrIter & rAttrIter)2036 void AttributeOutputBase::GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter)
2037 {
2038 if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) // Not implemented for RTF
2039 return;
2040
2041 if (const SwpHints* pTextAttrs = rNode.GetpSwpHints())
2042 {
2043 for( size_t i = 0; i < pTextAttrs->Count(); ++i )
2044 {
2045 const SwTextAttr* pHt = pTextAttrs->Get(i);
2046 if (pHt->GetAttr().Which() == RES_TXTATR_FIELD)
2047 {
2048 const SwFormatField& rField = static_cast<const SwFormatField&>(pHt->GetAttr());
2049 const SwField* pField = rField.GetField();
2050 // Need to have bookmarks only for sequence fields
2051 if (pField && pField->GetTyp()->Which() == SwFieldIds::SetExp && pField->GetSubType() == nsSwGetSetExpType::GSE_SEQ)
2052 {
2053 const sal_uInt16 nSeqFieldNumber = static_cast<const SwSetExpField*>(pField)->GetSeqNumber();
2054 const OUString sObjectName = static_cast<const SwSetExpFieldType*>(pField->GetTyp())->GetName();
2055 const SwFieldTypes* pFieldTypes = GetExport().m_pDoc->getIDocumentFieldsAccess().GetFieldTypes();
2056 bool bHaveFullBkm = false;
2057 bool bHaveLabelAndNumberBkm = false;
2058 bool bHaveCaptionOnlyBkm = false;
2059 bool bHaveNumberOnlyBkm = false;
2060 bool bRunSplittedAtSep = false;
2061 for( auto const & pFieldType : *pFieldTypes )
2062 {
2063 if( SwFieldIds::GetRef == pFieldType->Which() )
2064 {
2065 SwIterator<SwFormatField,SwFieldType> aIter( *pFieldType );
2066 for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() )
2067 {
2068 SwGetRefField* pRefField = static_cast<SwGetRefField*>(pFormatField->GetField());
2069 // If we have a reference to the current sequence field
2070 if(pRefField->GetSeqNo() == nSeqFieldNumber && pRefField->GetSetRefName() == sObjectName)
2071 {
2072 // Need to create a separate run for separator character
2073 SwWW8AttrIter aLocalAttrIter( GetExport(), rNode ); // We need a local iterator having the right number of runs
2074 const OUString& aText = rNode.GetText();
2075 const sal_Int32 nCategoryStart = aText.indexOf(pRefField->GetSetRefName());
2076 const sal_Int32 nPosBeforeSeparator = std::max(nCategoryStart, pHt->GetStart());
2077 bool bCategoryFirst = nCategoryStart < pHt->GetStart();
2078 sal_Int32 nSeparatorPos = 0;
2079 if (bCategoryFirst)
2080 {
2081 nSeparatorPos = aLocalAttrIter.WhereNext();
2082 while (nSeparatorPos <= nPosBeforeSeparator)
2083 {
2084 aLocalAttrIter.NextPos();
2085 nSeparatorPos = aLocalAttrIter.WhereNext();
2086 }
2087 }
2088 else
2089 {
2090 nSeparatorPos = nCategoryStart + pRefField->GetSetRefName().getLength();
2091 }
2092 sal_Int32 nRefTextPos = 0;
2093 if(nSeparatorPos < aText.getLength())
2094 {
2095 nRefTextPos = SwGetExpField::GetReferenceTextPos(pHt->GetFormatField(), *GetExport().m_pDoc, nSeparatorPos);
2096 if(nRefTextPos != nSeparatorPos)
2097 {
2098 if(!bRunSplittedAtSep)
2099 {
2100 if(!bCategoryFirst)
2101 rAttrIter.SplitRun(nSeparatorPos);
2102 rAttrIter.SplitRun(nRefTextPos);
2103 bRunSplittedAtSep = true;
2104 }
2105 if(!bCategoryFirst)
2106 aLocalAttrIter.SplitRun(nSeparatorPos);
2107 aLocalAttrIter.SplitRun(nRefTextPos);
2108 }
2109 else if (bCategoryFirst)
2110 {
2111 if(!bRunSplittedAtSep)
2112 {
2113 rAttrIter.SplitRun(nSeparatorPos);
2114 bRunSplittedAtSep = true;
2115 }
2116 aLocalAttrIter.SplitRun(nSeparatorPos);
2117 }
2118 }
2119 // Generate bookmarks on the right position
2120 OUString sName("Ref_" + pRefField->GetSetRefName() + OUString::number(pRefField->GetSeqNo()));
2121 switch (pRefField->GetFormat())
2122 {
2123 case REF_PAGE:
2124 case REF_PAGE_PGDESC:
2125 case REF_CONTENT:
2126 case REF_UPDOWN:
2127 if(!bHaveFullBkm)
2128 {
2129 sal_Int32 nLastAttrStart = 0;
2130 sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
2131 while (nActAttr < rNode.GetText().getLength())
2132 {
2133 nLastAttrStart = nActAttr;
2134 aLocalAttrIter.NextPos();
2135 nActAttr = aLocalAttrIter.WhereNext();
2136 }
2137 WriteBookmarkInActParagraph( sName + "_full", std::min(nCategoryStart, pHt->GetStart()), nLastAttrStart );
2138 bHaveFullBkm = true;
2139 }
2140 break;
2141 case REF_ONLYNUMBER:
2142 {
2143 if(!bHaveLabelAndNumberBkm)
2144 {
2145 sName += "_label_and_number";
2146 if(bCategoryFirst)
2147 WriteBookmarkInActParagraph( sName, std::min(nCategoryStart, pHt->GetStart()), std::max(nCategoryStart, pHt->GetStart()) );
2148 else
2149 {
2150 // Find the last run which contains category text
2151 SwWW8AttrIter aLocalAttrIter2( GetExport(), rNode );
2152 sal_Int32 nCatLastRun = 0;
2153 sal_Int32 nNextAttr = aLocalAttrIter2.WhereNext();
2154 while (nNextAttr < nSeparatorPos)
2155 {
2156 nCatLastRun = nNextAttr;
2157 aLocalAttrIter2.NextPos();
2158 nNextAttr = aLocalAttrIter2.WhereNext();
2159 }
2160 WriteBookmarkInActParagraph( sName, pHt->GetStart(), nCatLastRun );
2161 }
2162 bHaveLabelAndNumberBkm = true;
2163 }
2164 break;
2165 }
2166 case REF_ONLYCAPTION:
2167 {
2168 if(!bHaveCaptionOnlyBkm)
2169 {
2170 // Find last run
2171 sal_Int32 nLastAttrStart = 0;
2172 sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
2173 while (nActAttr < rNode.GetText().getLength())
2174 {
2175 nLastAttrStart = nActAttr;
2176 aLocalAttrIter.NextPos();
2177 nActAttr = aLocalAttrIter.WhereNext();
2178 }
2179 WriteBookmarkInActParagraph( sName + "_caption_only", nRefTextPos, nLastAttrStart );
2180 bHaveCaptionOnlyBkm = true;
2181 }
2182 break;
2183 }
2184 case REF_ONLYSEQNO:
2185 {
2186 if(!bHaveNumberOnlyBkm)
2187 {
2188 WriteBookmarkInActParagraph( sName + "_number_only", pHt->GetStart(), pHt->GetStart() );
2189 bHaveNumberOnlyBkm = true;
2190 }
2191 break;
2192 }
2193 }
2194 }
2195 }
2196 }
2197 }
2198 return;
2199 }
2200 }
2201 }
2202 }
2203 }
2204
StartTOX(const SwSection & rSect)2205 void AttributeOutputBase::StartTOX( const SwSection& rSect )
2206 {
2207 if ( const SwTOXBase* pTOX = rSect.GetTOXBase() )
2208 {
2209 static const sal_Char sEntryEnd[] = "\" ";
2210
2211 ww::eField eCode = ww::eTOC;
2212 OUString sStr = pTOX ->GetMSTOCExpression();
2213 if ( sStr.isEmpty() )
2214 {
2215 switch (pTOX->GetType())
2216 {
2217 case TOX_INDEX:
2218 eCode = ww::eINDEX;
2219 sStr = FieldString(eCode);
2220
2221 {
2222 const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
2223 const SwColumns& rColumns = rCol.GetColumns();
2224 sal_Int32 nCol = rColumns.size();
2225
2226 if ( 0 < nCol )
2227 {
2228 // Add a continuous section break
2229 if( GetExport().AddSectionBreaksForTOX() )
2230 {
2231 SwSection *pParent = rSect.GetParent();
2232 WW8_SepInfo rInfo(&GetExport( ).m_pDoc->GetPageDesc(0),
2233 pParent ? pParent->GetFormat() : nullptr, 0/*nRstLnNum*/);
2234 GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
2235 }
2236
2237 sStr += "\\c \"" + OUString::number( nCol ) + "\"";
2238 }
2239 }
2240
2241 if (pTOX->GetTOXForm().IsCommaSeparated())
2242 sStr += "\\r ";
2243
2244 if (SwTOIOptions::AlphaDelimiter & pTOX->GetOptions())
2245 sStr += "\\h \"A\" ";
2246
2247 if(SwTOXElement::IndexEntryType & pTOX->GetCreateType())
2248 {
2249 sStr += "\\f ";
2250 const OUString& sName = pTOX->GetEntryTypeName();
2251 if(!sName.isEmpty())
2252 {
2253 sStr += sName + sEntryEnd;
2254 }
2255 }
2256
2257 if (!pTOX->GetTOXForm().IsCommaSeparated())
2258 {
2259 // In case of Run-in style no separators are added.
2260 OUString aFillText;
2261 for (sal_uInt8 n = 1; n <= 3; ++n)
2262 {
2263 OUString aText;
2264 int nRet = ::lcl_CheckForm(pTOX->GetTOXForm(), n, aText);
2265
2266 if( 3 == nRet )
2267 aFillText = aText;
2268 else if ((4 == nRet) || (2 == nRet))
2269 aFillText = "\t";
2270 else
2271 aFillText.clear();
2272 }
2273 sStr += "\\e \"" + aFillText + sEntryEnd;
2274 }
2275 break;
2276
2277 case TOX_ILLUSTRATIONS:
2278 case TOX_OBJECTS:
2279 case TOX_TABLES:
2280 if (!pTOX->IsFromObjectNames())
2281 {
2282 sStr = FieldString(eCode) + "\\c ";
2283 const OUString& seqName = pTOX->GetSequenceName();
2284 if(!seqName.isEmpty())
2285 {
2286 sStr += "\"" + seqName + sEntryEnd;
2287 }
2288 OUString aText;
2289 int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(), 1, aText );
2290 if (1 == nRet)
2291 sStr += "\\n ";
2292 else if( 3 == nRet || 4 == nRet )
2293 {
2294 sStr += "\\p \"" + aText + sEntryEnd;
2295 }
2296 }
2297 break;
2298
2299 case TOX_AUTHORITIES:
2300 eCode = ww::eBIBLIOGRAPHY;
2301 sStr = FieldString(eCode);
2302 break;
2303 // case TOX_USER:
2304 // case TOX_CONTENT:
2305 default:
2306 {
2307 sStr = FieldString(eCode);
2308
2309 OUString sTOption;
2310 sal_uInt16 n, nTOXLvl = pTOX->GetLevel();
2311 if( !nTOXLvl )
2312 ++nTOXLvl;
2313
2314 if(SwTOXElement::TableLeader & pTOX->GetCreateType())
2315 {
2316 sStr +="\\z " ;
2317 GetExport( ).m_bHideTabLeaderAndPageNumbers = true ;
2318 }
2319 if(SwTOXElement::TableInToc & pTOX->GetCreateType())
2320 {
2321 sStr +="\\w " ;
2322 GetExport( ).m_bTabInTOC = true ;
2323 }
2324 if(SwTOXElement::Newline & pTOX->GetCreateType())
2325 {
2326 sStr +="\\x " ;
2327 }
2328 if( SwTOXElement::Mark & pTOX->GetCreateType() )
2329 {
2330 sStr += "\\f ";
2331
2332 if( TOX_USER == pTOX->GetType() )
2333 {
2334 sStr += "\""
2335 + OUStringChar(static_cast<sal_Char>( 'A' + GetExport( ).GetId( *pTOX->GetTOXType() ) ))
2336 + sEntryEnd;
2337 }
2338 }
2339 if(SwTOXElement::Bookmark & pTOX->GetCreateType())
2340 {
2341 sStr += "\\b \"" + pTOX->GetBookmarkName() + sEntryEnd;
2342 }
2343
2344 if( SwTOXElement::OutlineLevel & pTOX->GetCreateType() )
2345 {
2346 // Take the TOC value of the max level to evaluate to as
2347 // the starting point for the \o flag, but reduce it to the
2348 // value of the highest outline level filled by a *standard*
2349 // Heading 1 - 9 style because \o "Builds a table of
2350 // contents from paragraphs formatted with built-in heading
2351 // styles". And afterward fill in any outline styles left
2352 // uncovered by that range to the \t flag
2353
2354 // i.e. for
2355 // Heading 1
2356 // Heading 2
2357 // custom-style
2358 // Heading 4
2359 // output
2360 // \o 1-2 \tcustom-style,3,Heading 3,4
2361
2362 // Search over all the outline styles used and figure out
2363 // what is the minimum outline level (if any) filled by a
2364 // non-standard style for that level, i.e. ignore headline
2365 // styles 1-9 and find the lowest valid outline level
2366 sal_uInt8 nPosOfLowestNonStandardLvl = MAXLEVEL;
2367 const SwTextFormatColls& rColls = *GetExport().m_pDoc->GetTextFormatColls();
2368 for( n = rColls.size(); n; )
2369 {
2370 const SwTextFormatColl* pColl = rColls[ --n ];
2371 sal_uInt16 nPoolId = pColl->GetPoolFormatId();
2372 if (
2373 //Is a Non-Standard Outline Style
2374 (RES_POOLCOLL_HEADLINE1 > nPoolId || RES_POOLCOLL_HEADLINE9 < nPoolId) &&
2375 //Has a valid outline level
2376 (pColl->IsAssignedToListLevelOfOutlineStyle()) &&
2377 // Is less than the lowest known non-standard level
2378 (pColl->GetAssignedOutlineStyleLevel() < nPosOfLowestNonStandardLvl)
2379 )
2380 {
2381 nPosOfLowestNonStandardLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
2382 }
2383 }
2384
2385 sal_uInt8 nMaxMSAutoEvaluate = nPosOfLowestNonStandardLvl < nTOXLvl ? nPosOfLowestNonStandardLvl : static_cast<sal_uInt8>(nTOXLvl);
2386
2387 //output \o 1-X where X is the highest normal outline style to be included in the toc
2388 if ( nMaxMSAutoEvaluate )
2389 {
2390 if (nMaxMSAutoEvaluate > WW8ListManager::nMaxLevel)
2391 nMaxMSAutoEvaluate = WW8ListManager::nMaxLevel;
2392
2393 sStr += "\\o \"1-" + OUString::number(nMaxMSAutoEvaluate) + sEntryEnd;
2394 }
2395
2396 //collect up any other styles in the writer TOC which will
2397 //not already appear in the MS TOC and place then into the
2398 //\t option
2399 if( nMaxMSAutoEvaluate < nTOXLvl )
2400 {
2401 // collect this templates into the \t option
2402 for( n = rColls.size(); n;)
2403 {
2404 const SwTextFormatColl* pColl = rColls[ --n ];
2405 if (!pColl->IsAssignedToListLevelOfOutlineStyle())
2406 continue;
2407 sal_uInt8 nTestLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
2408 if (nTestLvl < nTOXLvl && nTestLvl >= nMaxMSAutoEvaluate)
2409 {
2410 if (!sTOption.isEmpty())
2411 sTOption += ",";
2412 sTOption += pColl->GetName() + "," + OUString::number( nTestLvl + 1 );
2413 }
2414 }
2415 }
2416 }
2417
2418 if( SwTOXElement::ParagraphOutlineLevel & pTOX->GetCreateType() )
2419 {
2420 sStr +="\\u " ;
2421 }
2422
2423 if( SwTOXElement::Template & pTOX->GetCreateType() )
2424 {
2425 // #i99641# - Consider additional styles regardless of TOX-outlinelevel
2426 for( n = 0; n < MAXLEVEL; ++n )
2427 {
2428 const OUString& rStyles = pTOX->GetStyleNames( n );
2429 if( !rStyles.isEmpty() )
2430 {
2431 sal_Int32 nPos = 0;
2432 const OUString sLvl{ "," + OUString::number( n + 1 ) };
2433 do {
2434 const OUString sStyle( rStyles.getToken( 0, TOX_STYLE_DELIMITER, nPos ));
2435 if( !sStyle.isEmpty() )
2436 {
2437 SwTextFormatColl* pColl = GetExport().m_pDoc->FindTextFormatCollByName(sStyle);
2438 if (pColl)
2439 {
2440 if (!pColl->IsAssignedToListLevelOfOutlineStyle() || pColl->GetAssignedOutlineStyleLevel() < nTOXLvl)
2441 {
2442 if( !sTOption.isEmpty() )
2443 sTOption += ",";
2444 sTOption += sStyle + sLvl;
2445 }
2446 }
2447 }
2448 } while( -1 != nPos );
2449 }
2450 }
2451 }
2452
2453 // No 'else' branch; why the below snippet is a block I have no idea.
2454 {
2455 OUString aFillText;
2456 sal_uInt8 nNoPgStt = MAXLEVEL, nNoPgEnd = MAXLEVEL;
2457 bool bFirstFillText = true, bOnlyText = true;
2458 for( n = 0; n < nTOXLvl; ++n )
2459 {
2460 OUString aText;
2461 int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(),
2462 static_cast< sal_uInt8 >(n+1), aText );
2463 if( 1 == nRet )
2464 {
2465 bOnlyText = false;
2466 if( MAXLEVEL == nNoPgStt )
2467 nNoPgStt = static_cast< sal_uInt8 >(n+1);
2468 }
2469 else
2470 {
2471 if( MAXLEVEL != nNoPgStt &&
2472 MAXLEVEL == nNoPgEnd )
2473 nNoPgEnd = sal_uInt8(n);
2474
2475 bOnlyText = bOnlyText && 3 == nRet;
2476 if( 3 == nRet || 4 == nRet )
2477 {
2478 if( bFirstFillText )
2479 aFillText = aText;
2480 else if( aFillText != aText )
2481 aFillText.clear();
2482 bFirstFillText = false;
2483 }
2484 }
2485 }
2486 if( MAXLEVEL != nNoPgStt )
2487 {
2488 if (WW8ListManager::nMaxLevel < nNoPgEnd)
2489 nNoPgEnd = WW8ListManager::nMaxLevel;
2490 sStr += "\\n "
2491 + OUString::number( nNoPgStt )
2492 + "-"
2493 + OUString::number( nNoPgEnd )
2494 + " ";
2495 }
2496 if( bOnlyText )
2497 {
2498 sStr += "\\p \"" + aFillText + sEntryEnd;
2499 }
2500 }
2501
2502 if( !sTOption.isEmpty() )
2503 {
2504 sStr += "\\t \"" + sTOption + sEntryEnd;
2505 }
2506
2507 if (lcl_IsHyperlinked(pTOX->GetTOXForm(), nTOXLvl))
2508 sStr += "\\h";
2509 break;
2510 }
2511 }
2512 }
2513
2514 if (!sStr.isEmpty())
2515 {
2516 GetExport( ).m_bInWriteTOX = true;
2517 if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
2518 { // tdf#129574: required for RTF; doesn't work with DOCX
2519 StartRun(nullptr, -42, true);
2520 }
2521 GetExport( ).OutputField( nullptr, eCode, sStr, FieldFlags::Start | FieldFlags::CmdStart |
2522 FieldFlags::CmdEnd );
2523 if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
2524 {
2525 EndRun(nullptr, -42, true);
2526 }
2527 }
2528 }
2529
2530 GetExport( ).m_bStartTOX = false;
2531 }
2532
EndTOX(const SwSection & rSect,bool bCareEnd)2533 void AttributeOutputBase::EndTOX( const SwSection& rSect,bool bCareEnd )
2534 {
2535 const SwTOXBase* pTOX = rSect.GetTOXBase();
2536 if ( pTOX )
2537 {
2538 ww::eField eCode = TOX_INDEX == pTOX->GetType() ? ww::eINDEX : ww::eTOC;
2539 GetExport( ).OutputField( nullptr, eCode, OUString(), FieldFlags::Close );
2540
2541 if ( pTOX->GetType() == TOX_INDEX && GetExport().AddSectionBreaksForTOX() )
2542 {
2543 const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
2544 const SwColumns& rColumns = rCol.GetColumns();
2545 sal_Int32 nCol = rColumns.size();
2546
2547 if ( 0 < nCol )
2548 {
2549 WW8_SepInfo rInfo( &GetExport( ).m_pDoc->GetPageDesc( 0 ), rSect.GetFormat(), 0/*nRstLnNum*/ );
2550 GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
2551 }
2552 }
2553 }
2554 GetExport( ).m_bInWriteTOX = false;
2555 GetExport( ).m_bHideTabLeaderAndPageNumbers = false;
2556 if (bCareEnd)
2557 OnTOXEnding();
2558 }
2559
GetNumberFormat(const SwField & rField,OUString & rStr)2560 bool MSWordExportBase::GetNumberFormat(const SwField& rField, OUString& rStr)
2561 {
2562 // Returns a date or time format string by using the US NfKeywordTable
2563 bool bHasFormat = false;
2564 SvNumberFormatter* pNFormatr = m_pDoc->GetNumberFormatter();
2565 sal_uInt32 nFormatIdx = rField.GetFormat();
2566 const SvNumberformat* pNumFormat = pNFormatr->GetEntry( nFormatIdx );
2567 if( pNumFormat )
2568 {
2569 LanguageType nLng = rField.GetLanguage();
2570 LocaleDataWrapper aLocDat(pNFormatr->GetComponentContext(),
2571 LanguageTag(nLng));
2572
2573 OUString sFormat(pNumFormat->GetMappedFormatstring(GetNfKeywordTable(),
2574 aLocDat));
2575
2576 if (!sFormat.isEmpty())
2577 {
2578 sw::ms::SwapQuotesInField(sFormat);
2579
2580 rStr = "\\@\"" + sFormat + "\" " ;
2581 bHasFormat = true;
2582 }
2583 }
2584 return bHasFormat;
2585 }
2586
GetNumberPara(OUString & rStr,const SwField & rField)2587 void AttributeOutputBase::GetNumberPara( OUString& rStr, const SwField& rField )
2588 {
2589 switch(rField.GetFormat())
2590 {
2591 case SVX_NUM_CHARS_UPPER_LETTER:
2592 case SVX_NUM_CHARS_UPPER_LETTER_N:
2593 rStr += "\\* ALPHABETIC ";
2594 break;
2595 case SVX_NUM_CHARS_LOWER_LETTER:
2596 case SVX_NUM_CHARS_LOWER_LETTER_N:
2597 rStr += "\\* alphabetic ";
2598 break;
2599 case SVX_NUM_ROMAN_UPPER:
2600 rStr += "\\* ROMAN ";
2601 break;
2602 case SVX_NUM_ROMAN_LOWER:
2603 rStr += "\\* roman ";
2604 break;
2605 default:
2606 OSL_ENSURE(rField.GetFormat() == SVX_NUM_ARABIC,
2607 "Unknown numbering type exported as default of Arabic");
2608 [[fallthrough]];
2609 case SVX_NUM_ARABIC:
2610 rStr += "\\* ARABIC ";
2611 break;
2612 case SVX_NUM_PAGEDESC:
2613 //Nothing, use word's default
2614 break;
2615 }
2616 }
2617
WritePostItBegin(ww::bytes * pOut)2618 void WW8Export::WritePostItBegin( ww::bytes* pOut )
2619 {
2620 sal_uInt8 aArr[ 3 ];
2621 sal_uInt8* pArr = aArr;
2622
2623 // sprmCFSpec true
2624 Set_UInt16( pArr, NS_sprm::sprmCFSpec );
2625 Set_UInt8( pArr, 1 );
2626
2627 m_pChpPlc->AppendFkpEntry( Strm().Tell() );
2628 WriteChar( 0x05 ); // Annotation reference
2629
2630 if( pOut )
2631 pOut->insert( pOut->end(), aArr, pArr );
2632 else
2633 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
2634 }
2635
FieldString(ww::eField eIndex)2636 OUString FieldString(ww::eField eIndex)
2637 {
2638 if (const char *pField = ww::GetEnglishFieldName(eIndex))
2639 return " " + OUString::createFromAscii(pField) + " ";
2640 return " ";
2641 }
2642
HiddenField(const SwField & rField)2643 void WW8AttributeOutput::HiddenField( const SwField& rField )
2644 {
2645 //replace LF 0x0A with VT 0x0B
2646 const OUString sExpand(rField.GetPar2().replace(0x0A, 0x0B));
2647
2648 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell());
2649 SwWW8Writer::WriteString16(m_rWW8Export.Strm(), sExpand, false);
2650 static sal_uInt8 aArr[] =
2651 {
2652 0x3C, 0x08, 0x1
2653 };
2654 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), sizeof(aArr), aArr);
2655 }
2656
SetField(const SwField & rField,ww::eField eType,const OUString & rCmd)2657 void WW8AttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd )
2658 {
2659 const SwSetExpField* pSet = static_cast<const SwSetExpField*>(&rField);
2660 const OUString &rVar = pSet->GetPar2();
2661
2662 sal_uLong nFrom = m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell());
2663
2664 GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Start |
2665 FieldFlags::CmdStart | FieldFlags::CmdEnd);
2666
2667 /*
2668 Is there a bookmark at the start position of this field, if so
2669 move it to the 0x14 of the result of the field. This is what word
2670 does. MoveFieldMarks moves any bookmarks at this position to
2671 the beginning of the field result, and marks the bookmark as a
2672 fieldbookmark which is to be ended before the field end mark
2673 instead of after it like a normal bookmark.
2674 */
2675 m_rWW8Export.MoveFieldMarks(nFrom,m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell()));
2676
2677 if (!rVar.isEmpty())
2678 {
2679 SwWW8Writer::WriteString16(m_rWW8Export.Strm(), rVar, false);
2680 }
2681 GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Close);
2682 }
2683
PostitField(const SwField * pField)2684 void WW8AttributeOutput::PostitField( const SwField* pField )
2685 {
2686 const SwPostItField *pPField = static_cast<const SwPostItField*>(pField);
2687 m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pPField );
2688 m_rWW8Export.WritePostItBegin( m_rWW8Export.pO.get() );
2689 }
2690
DropdownField(const SwField * pField)2691 bool WW8AttributeOutput::DropdownField( const SwField* pField )
2692 {
2693 const SwDropDownField& rField2 = *static_cast<const SwDropDownField*>(pField);
2694 uno::Sequence<OUString> aItems =
2695 rField2.GetItemSequence();
2696 GetExport().DoComboBox(rField2.GetName(),
2697 rField2.GetHelp(),
2698 rField2.GetToolTip(),
2699 rField2.GetSelectedItem(), aItems);
2700 return false;
2701 }
2702
PlaceholderField(const SwField *)2703 bool WW8AttributeOutput::PlaceholderField( const SwField* )
2704 {
2705 return true; // expand to text?
2706 }
2707
RefField(const SwField & rField,const OUString & rRef)2708 void WW8AttributeOutput::RefField( const SwField &rField, const OUString &rRef)
2709 {
2710 const OUString sStr{ FieldString( ww::eREF ) + "\"" + rRef + "\" " };
2711 m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Start |
2712 FieldFlags::CmdStart | FieldFlags::CmdEnd );
2713 const OUString sVar = lcl_GetExpandedField( rField );
2714 if ( !sVar.isEmpty() )
2715 {
2716 SwWW8Writer::WriteString16( m_rWW8Export.Strm(), sVar, false );
2717 }
2718 m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Close );
2719 }
2720
WriteExpand(const SwField * pField)2721 void WW8AttributeOutput::WriteExpand( const SwField* pField )
2722 {
2723 SwWW8Writer::WriteString16( m_rWW8Export.Strm(), lcl_GetExpandedField( *pField ), false );
2724 }
2725
2726 namespace
2727 {
2728 // Escapes a token string for storing in Word formats. Its import counterpart
2729 // is lcl_ExtractToken in writerfilter/source/dmapper/DomainMapper_Impl.cxx
EscapeToken(const OUString & rCommand)2730 OUString EscapeToken(const OUString& rCommand)
2731 {
2732 bool bWasEscaped = false;
2733
2734 const int nBufferLen = rCommand.getLength()*1.5;
2735 OUStringBuffer sResult(nBufferLen);
2736 sResult.append('"'); // opening quote
2737 for (sal_Int32 i = 0; i < rCommand.getLength(); ++i)
2738 {
2739 sal_Unicode ch = rCommand[i];
2740 switch (ch)
2741 {
2742 case '\\':
2743 case '"':
2744 // Backslashes and doublequotes must be escaped
2745 bWasEscaped = true;
2746 sResult.append('\\');
2747 break;
2748 case ' ':
2749 // Spaces require quotation
2750 bWasEscaped = true;
2751 break;
2752 }
2753 sResult.append(ch);
2754 }
2755
2756 if (bWasEscaped)
2757 {
2758 sResult.append('"'); // closing quote
2759 return sResult.makeStringAndClear();
2760 }
2761 // No escapement/quotation was required
2762 return rCommand;
2763 }
2764 }
2765
TextField(const SwFormatField & rField)2766 void AttributeOutputBase::TextField( const SwFormatField& rField )
2767 {
2768 const SwField* pField = rField.GetField();
2769 bool bWriteExpand = false;
2770 const sal_uInt16 nSubType = pField->GetSubType();
2771
2772 switch (pField->GetTyp()->Which())
2773 {
2774 case SwFieldIds::GetExp:
2775 if (nSubType == nsSwGetSetExpType::GSE_STRING)
2776 {
2777 const SwGetExpField *pGet = static_cast<const SwGetExpField*>(pField);
2778 RefField( *pGet, pGet->GetFormula() );
2779 }
2780 else
2781 bWriteExpand = true;
2782 break;
2783 case SwFieldIds::SetExp:
2784 if (nsSwGetSetExpType::GSE_SEQ == nSubType)
2785 {
2786 OUString sStr;
2787 if (GetExport().FieldsQuoted())
2788 sStr = FieldString(ww::eSEQ) + pField->GetTyp()->GetName() + " ";
2789 else
2790 sStr = FieldString(ww::eSEQ) + "\"" + pField->GetTyp()->GetName() +"\" ";
2791 GetNumberPara( sStr, *pField );
2792 GetExport().OutputField(pField, ww::eSEQ, sStr);
2793 }
2794 else if (nSubType & nsSwGetSetExpType::GSE_STRING)
2795 {
2796 bool bShowAsWell = false;
2797 ww::eField eFieldNo;
2798 const SwSetExpField *pSet = static_cast<const SwSetExpField*>(pField);
2799 const OUString sVar = pSet->GetPar2();
2800 OUString sStr;
2801 if (pSet->GetInputFlag())
2802 {
2803 sStr = FieldString(ww::eASK) + "\""
2804 + pSet->GetPar1() + "\" "
2805 + pSet->GetPromptText() + " \\d "
2806 + sVar;
2807 eFieldNo = ww::eASK;
2808 }
2809 else
2810 {
2811 sStr = FieldString(ww::eSET)
2812 + pSet->GetPar1() + " \""
2813 + sVar + "\" ";
2814 eFieldNo = ww::eSET;
2815 bShowAsWell = (nSubType & nsSwExtendedSubType::SUB_INVISIBLE) == 0;
2816 }
2817
2818 SetField( *pField, eFieldNo, sStr );
2819
2820 if (bShowAsWell)
2821 RefField( *pSet, pSet->GetPar1() );
2822 }
2823 else
2824 bWriteExpand = true;
2825 break;
2826 case SwFieldIds::PageNumber:
2827 {
2828 OUString sStr = FieldString(ww::ePAGE);
2829 GetNumberPara(sStr, *pField);
2830 GetExport().OutputField(pField, ww::ePAGE, sStr);
2831 }
2832 break;
2833 case SwFieldIds::Filename:
2834 {
2835 OUString sStr = FieldString(ww::eFILENAME);
2836 if (pField->GetFormat() == FF_PATHNAME)
2837 sStr += "\\p ";
2838 GetExport().OutputField(pField, ww::eFILENAME, sStr);
2839 }
2840 break;
2841 case SwFieldIds::Database:
2842 {
2843 OUString sStr = FieldString(ww::eMERGEFIELD)
2844 + EscapeToken(static_cast<SwDBFieldType *>(pField->GetTyp())->GetColumnName()) + " ";
2845 GetExport().OutputField(pField, ww::eMERGEFIELD, sStr);
2846 }
2847 break;
2848 case SwFieldIds::DatabaseName:
2849 {
2850 SwDBData aData = GetExport().m_pDoc->GetDBData();
2851 const OUString sStr = FieldString(ww::eDATABASE)
2852 + aData.sDataSource
2853 + OUStringChar(DB_DELIM)
2854 + aData.sCommand;
2855 GetExport().OutputField(pField, ww::eDATABASE, sStr);
2856 }
2857 break;
2858 case SwFieldIds::Author:
2859 {
2860 ww::eField eField =
2861 ((AF_SHORTCUT & pField->GetFormat()) ? ww::eUSERINITIALS : ww::eUSERNAME);
2862 GetExport().OutputField(pField, eField, FieldString(eField));
2863 }
2864 break;
2865 case SwFieldIds::TemplateName:
2866 GetExport().OutputField(pField, ww::eTEMPLATE, FieldString(ww::eTEMPLATE));
2867 break;
2868 case SwFieldIds::DocInfo: // Last printed, last edited,...
2869 if( DI_SUB_FIXED & nSubType )
2870 bWriteExpand = true;
2871 else
2872 {
2873 OUString sStr;
2874 ww::eField eField(ww::eNONE);
2875 switch (0xff & nSubType)
2876 {
2877 case DI_TITLE:
2878 eField = ww::eTITLE;
2879 break;
2880 case DI_THEMA:
2881 eField = ww::eSUBJECT;
2882 break;
2883 case DI_KEYS:
2884 eField = ww::eKEYWORDS;
2885 break;
2886 case DI_COMMENT:
2887 eField = ww::eCOMMENTS;
2888 break;
2889 case DI_DOCNO:
2890 eField = ww::eREVNUM;
2891 break;
2892 case DI_CREATE:
2893 if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
2894 eField = ww::eAUTHOR;
2895 else if (GetExport().GetNumberFormat(*pField, sStr))
2896 eField = ww::eCREATEDATE;
2897 break;
2898
2899 case DI_CHANGE:
2900 if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
2901 eField = ww::eLASTSAVEDBY;
2902 else if (GetExport().GetNumberFormat(*pField, sStr))
2903 eField = ww::eSAVEDATE;
2904 break;
2905
2906 case DI_PRINT:
2907 if (DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK) &&
2908 GetExport().GetNumberFormat(*pField, sStr))
2909 eField = ww::ePRINTDATE;
2910 break;
2911 case DI_EDIT:
2912 if( DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK ) &&
2913 GetExport().GetNumberFormat( *pField, sStr ))
2914 eField = ww::eSAVEDATE;
2915 else
2916 eField = ww::eEDITTIME;
2917 break;
2918 case DI_CUSTOM:
2919 eField = ww::eDOCPROPERTY;
2920 {
2921 const SwDocInfoField * pDocInfoField =
2922 dynamic_cast<const SwDocInfoField *> (pField);
2923
2924 if (pDocInfoField != nullptr)
2925 {
2926 OUString sFieldname = pDocInfoField->GetFieldName();
2927
2928 const sal_Int32 nIndex = sFieldname.indexOf(':');
2929 if (nIndex >= 0)
2930 sFieldname = sFieldname.copy(nIndex + 1);
2931
2932 sStr = "\"" + sFieldname + "\"";
2933 }
2934 }
2935 break;
2936 default:
2937 break;
2938 }
2939
2940 if (eField != ww::eNONE)
2941 {
2942 GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
2943 }
2944 else
2945 bWriteExpand = true;
2946 }
2947 break;
2948 case SwFieldIds::DateTime:
2949 {
2950 OUString sStr;
2951 if (!GetExport().GetNumberFormat(*pField, sStr))
2952 bWriteExpand = true;
2953 else
2954 {
2955 ww::eField eField = (DATEFLD & nSubType) ? ww::eDATE : ww::eTIME;
2956 GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
2957 }
2958 }
2959 break;
2960 case SwFieldIds::DocStat:
2961 {
2962 ww::eField eField = ww::eNONE;
2963
2964 switch (nSubType)
2965 {
2966 case DS_PAGE:
2967 eField = ww::eNUMPAGES;
2968 break;
2969 case DS_WORD:
2970 eField = ww::eNUMWORDS;
2971 break;
2972 case DS_CHAR:
2973 eField = ww::eNUMCHARS;
2974 break;
2975 }
2976
2977 if (eField != ww::eNONE)
2978 {
2979 OUString sStr = FieldString(eField);
2980 GetNumberPara(sStr, *pField);
2981 GetExport().OutputField(pField, eField, sStr);
2982 }
2983 else
2984 bWriteExpand = true;
2985 }
2986 break;
2987 case SwFieldIds::ExtUser:
2988 {
2989 ww::eField eField = ww::eNONE;
2990 switch (0xFF & nSubType)
2991 {
2992 case EU_FIRSTNAME:
2993 case EU_NAME:
2994 eField = ww::eUSERNAME;
2995 break;
2996 case EU_SHORTCUT:
2997 eField = ww::eUSERINITIALS;
2998 break;
2999 case EU_STREET:
3000 case EU_COUNTRY:
3001 case EU_ZIP:
3002 case EU_CITY:
3003 eField = ww::eUSERADDRESS;
3004 break;
3005 }
3006
3007 if (eField != ww::eNONE)
3008 {
3009 GetExport().OutputField(pField, eField, FieldString(eField));
3010 }
3011 else
3012 bWriteExpand = true;
3013 }
3014 break;
3015 case SwFieldIds::TableOfAuthorities:
3016 {
3017 OUString sRet(static_cast<SwAuthorityField const*>(pField)
3018 ->ExpandCitation(AUTH_FIELD_IDENTIFIER, nullptr));
3019 // FIXME: DomainMapper_Impl::CloseFieldCommand() stuffs fully formed
3020 // field instructions in here, but if the field doesn't originate
3021 // from those filters it won't have that
3022 if (!sRet.trim().startsWith("CITATION"))
3023 {
3024 sRet = FieldString(ww::eCITATION) + " \"" + sRet + "\"";
3025 }
3026 GetExport().OutputField( pField, ww::eCITATION, sRet );
3027 }
3028 break;
3029 case SwFieldIds::Postit:
3030 //Sadly only possible for word in main document text
3031 if (GetExport().m_nTextTyp == TXT_MAINTEXT)
3032 {
3033 PostitField( pField );
3034 }
3035 break;
3036 case SwFieldIds::Input:
3037 {
3038 const SwInputField * pInputField = dynamic_cast<const SwInputField *>(pField);
3039
3040 if (pInputField && pInputField->isFormField())
3041 GetExport().DoFormText(pInputField);
3042 else
3043 {
3044 const OUString sStr = FieldString(ww::eFILLIN) + "\""
3045 + pField->GetPar2() + "\"";
3046
3047 GetExport().OutputField(pField, ww::eFILLIN, sStr);
3048 }
3049 }
3050 break;
3051 case SwFieldIds::GetRef:
3052 {
3053 ww::eField eField = ww::eNONE;
3054 OUString sStr;
3055 const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
3056 switch (nSubType)
3057 {
3058 case REF_SETREFATTR:
3059 case REF_BOOKMARK:
3060 switch (pField->GetFormat())
3061 {
3062 case REF_PAGE_PGDESC:
3063 case REF_PAGE:
3064 eField = ww::ePAGEREF;
3065 break;
3066 default:
3067 eField = ww::eREF;
3068 break;
3069 }
3070 {
3071 const OUString& aRefName(rRField.GetSetRefName());
3072 sStr = FieldString(eField)
3073 + MSWordExportBase::GetBookmarkName(nSubType, &aRefName, 0);
3074 }
3075 switch (pField->GetFormat())
3076 {
3077 case REF_NUMBER:
3078 sStr += " \\r";
3079 break;
3080 case REF_NUMBER_NO_CONTEXT:
3081 sStr += " \\n";
3082 break;
3083 case REF_NUMBER_FULL_CONTEXT:
3084 sStr += " \\w";
3085 break;
3086 }
3087 break;
3088 case REF_SEQUENCEFLD:
3089 {
3090 // Not implemented for RTF
3091 if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
3092 break;
3093
3094 switch (pField->GetFormat())
3095 {
3096 case REF_PAGE:
3097 case REF_PAGE_PGDESC:
3098 eField = ww::ePAGEREF;
3099 break;
3100 default:
3101 eField = ww::eREF;
3102 break;
3103 }
3104 // Generate a unique bookmark name
3105 {
3106 OUString sName{rRField.GetSetRefName() + OUString::number(rRField.GetSeqNo())};
3107 switch (pField->GetFormat())
3108 {
3109 case REF_PAGE:
3110 case REF_PAGE_PGDESC:
3111 case REF_CONTENT:
3112 case REF_UPDOWN:
3113 sName += "_full";
3114 break;
3115 case REF_ONLYNUMBER:
3116 sName += "_label_and_number";
3117 break;
3118 case REF_ONLYCAPTION:
3119 sName += "_caption_only";
3120 break;
3121 case REF_ONLYSEQNO:
3122 sName += "_number_only";
3123 break;
3124 default: // Ignore other types of reference fields
3125 eField = ww::eNONE;
3126 break;
3127 }
3128 sStr = FieldString(eField) + MSWordExportBase::GetBookmarkName(nSubType, &sName, 0);
3129 }
3130 switch (pField->GetFormat())
3131 {
3132 case REF_NUMBER:
3133 sStr += " \\r";
3134 break;
3135 case REF_NUMBER_NO_CONTEXT:
3136 sStr += " \\n";
3137 break;
3138 case REF_NUMBER_FULL_CONTEXT:
3139 sStr += " \\w";
3140 break;
3141 }
3142 break;
3143 }
3144 case REF_FOOTNOTE:
3145 case REF_ENDNOTE:
3146 switch (pField->GetFormat())
3147 {
3148 case REF_PAGE_PGDESC:
3149 case REF_PAGE:
3150 eField = ww::ePAGEREF;
3151 break;
3152 case REF_UPDOWN:
3153 eField = ww::eREF;
3154 break;
3155 default:
3156 eField =
3157 REF_ENDNOTE == nSubType ? ww::eNOTEREF : ww::eFOOTREF;
3158 break;
3159 }
3160 sStr = FieldString(eField)
3161 + MSWordExportBase::GetBookmarkName(nSubType, nullptr, rRField.GetSeqNo());
3162 break;
3163 }
3164
3165 if (eField != ww::eNONE)
3166 {
3167 switch (pField->GetFormat())
3168 {
3169 case REF_UPDOWN:
3170 sStr += " \\p \\h "; // with hyperlink
3171 break;
3172 case REF_CHAPTER:
3173 sStr += " \\n \\h "; // with hyperlink
3174 break;
3175 default:
3176 sStr += " \\h "; // insert hyperlink
3177 break;
3178 }
3179 GetExport().OutputField(pField, eField, sStr);
3180 }
3181 else
3182 bWriteExpand = true;
3183 }
3184 break;
3185 case SwFieldIds::CombinedChars:
3186 {
3187 /*
3188 We need a font size to fill in the defaults, if these are overridden
3189 (as they generally are) by character properties then those properties
3190 win.
3191
3192 The fontsize that is used in MS for determining the defaults is always
3193 the CJK fontsize even if the text is not in that language, in OOo the
3194 largest fontsize used in the field is the one we should take, but
3195 whatever we do, word will actually render using the fontsize set for
3196 CJK text. Nevertheless we attempt to guess whether the script is in
3197 asian or western text based up on the first character and use the
3198 font size of that script as our default.
3199 */
3200 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
3201 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( pField->GetPar1(), 0);
3202
3203 long nHeight = static_cast<const SvxFontHeightItem&>((GetExport().GetItem(
3204 GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript)))).GetHeight();
3205
3206 nHeight = (nHeight + 10) / 20; //Font Size in points;
3207
3208 /*
3209 Divide the combined char string into its up and down part. Get the
3210 font size and fill in the defaults as up == half the font size and
3211 down == a fifth the font size
3212 */
3213 const sal_Int32 nAbove = (pField->GetPar1().getLength()+1)/2;
3214 const OUString sStr = FieldString(ww::eEQ)
3215 + "\\o (\\s\\up "
3216 + OUString::number(nHeight/2)
3217 + "("
3218 + pField->GetPar1().copy(0, nAbove)
3219 + "), \\s\\do "
3220 + OUString::number(nHeight/5)
3221 + "("
3222 + pField->GetPar1().copy(nAbove)
3223 + "))";
3224 GetExport().OutputField(pField, ww::eEQ, sStr);
3225 }
3226 break;
3227 case SwFieldIds::Dropdown:
3228 bWriteExpand = DropdownField( pField );
3229 break;
3230 case SwFieldIds::Chapter:
3231 bWriteExpand = true;
3232 if (GetExport().m_bOutKF && rField.GetTextField())
3233 {
3234 const SwTextNode *pTextNd = GetExport().GetHdFtPageRoot();
3235 if (!pTextNd)
3236 {
3237 pTextNd = GetExport().m_pCurPam->GetNode().GetTextNode();
3238 }
3239
3240 if (pTextNd)
3241 {
3242 SwChapterField aCopy(*static_cast<const SwChapterField*>(pField));
3243 aCopy.ChangeExpansion(*pTextNd, false);
3244 const OUString sStr = FieldString(ww::eSTYLEREF)
3245 + " "
3246 + OUString::number(aCopy.GetLevel() + 1)
3247 + " \\* MERGEFORMAT ";
3248 GetExport().OutputField(pField, ww::eSTYLEREF, sStr);
3249 bWriteExpand = false;
3250 }
3251 }
3252 break;
3253 case SwFieldIds::HiddenText:
3254 {
3255 OUString sExpand(pField->GetPar2());
3256 if (!sExpand.isEmpty())
3257 {
3258 HiddenField( *pField );
3259 }
3260 }
3261 break;
3262 case SwFieldIds::JumpEdit:
3263 bWriteExpand = PlaceholderField( pField );
3264 break;
3265 case SwFieldIds::Macro:
3266 {
3267 const OUString sStr = " MACROBUTTON"
3268 + pField->GetPar1().replaceFirst("StarOffice.Standard.Modul1.", " ")
3269 + " "
3270 + lcl_GetExpandedField(*pField);
3271 GetExport().OutputField( pField, ww::eMACROBUTTON, sStr );
3272 }
3273 break;
3274 default:
3275 bWriteExpand = true;
3276 break;
3277 }
3278
3279 if (bWriteExpand)
3280 WriteExpand( pField );
3281 }
3282
TextFlyContent(const SwFormatFlyCnt & rFlyContent)3283 void AttributeOutputBase::TextFlyContent( const SwFormatFlyCnt& rFlyContent )
3284 {
3285 if ( auto pTextNd = dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) )
3286 {
3287 Point const origin;
3288 Point aLayPos = pTextNd->FindLayoutRect( false, &origin ).Pos();
3289
3290 SwPosition aPos( *pTextNd );
3291 ww8::Frame aFrame( *rFlyContent.GetFrameFormat(), aPos );
3292
3293 OutputFlyFrame_Impl( aFrame, aLayPos );
3294 }
3295 }
3296
3297 // TOXMarks are still missing
3298
3299 // WW allows detailed settings for hyphenation only for the whole document.
3300 // One could implement following mimic: The values of the style "Standard" will
3301 // be set in the Document Properties ( DOP ) if they exist.
3302
3303 // ACK. This suggestion fits exactly to our implementation of the import,
3304 // therefore I'll implement that right now. (KHZ, 07/15/2000)
ParaHyphenZone(const SvxHyphenZoneItem & rHyphenZone)3305 void WW8AttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone )
3306 {
3307 // sprmPFNoAutoHyph
3308 m_rWW8Export.InsUInt16( NS_sprm::sprmPFNoAutoHyph );
3309
3310 m_rWW8Export.pO->push_back( rHyphenZone.IsHyphen() ? 0 : 1 );
3311 }
3312
ParaScriptSpace(const SfxBoolItem & rScriptSpace)3313 void WW8AttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace )
3314 {
3315 m_rWW8Export.InsUInt16( NS_sprm::sprmPFAutoSpaceDE );
3316 m_rWW8Export.pO->push_back( rScriptSpace.GetValue() ? 1 : 0 );
3317 }
3318
ParaHangingPunctuation(const SfxBoolItem & rItem)3319 void WW8AttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem )
3320 {
3321 m_rWW8Export.InsUInt16( NS_sprm::sprmPFOverflowPunct );
3322 m_rWW8Export.pO->push_back( rItem.GetValue() ? 1 : 0 );
3323 }
3324
ParaForbiddenRules(const SfxBoolItem & rItem)3325 void WW8AttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem )
3326 {
3327 m_rWW8Export.InsUInt16( NS_sprm::sprmPFKinsoku );
3328 m_rWW8Export.pO->push_back( rItem.GetValue() ? 1 : 0 );
3329 }
3330
ParaSnapToGrid(const SvxParaGridItem & rGrid)3331 void WW8AttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid )
3332 {
3333 // sprmPFUsePgsuSettings
3334
3335 m_rWW8Export.InsUInt16( NS_sprm::sprmPFUsePgsuSettings );
3336 m_rWW8Export.pO->push_back( rGrid.GetValue() ? 1 : 0 );
3337 }
3338
ParaVerticalAlign(const SvxParaVertAlignItem & rAlign)3339 void WW8AttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign )
3340 {
3341 // sprmPWAlignFont
3342
3343 m_rWW8Export.InsUInt16( NS_sprm::sprmPWAlignFont );
3344
3345 SvxParaVertAlignItem::Align nAlign = rAlign.GetValue();
3346 sal_uInt16 nVal;
3347 switch ( nAlign )
3348 {
3349 case SvxParaVertAlignItem::Align::Baseline:
3350 nVal = 2;
3351 break;
3352 case SvxParaVertAlignItem::Align::Top:
3353 nVal = 0;
3354 break;
3355 case SvxParaVertAlignItem::Align::Center:
3356 nVal = 1;
3357 break;
3358 case SvxParaVertAlignItem::Align::Bottom:
3359 nVal = 3;
3360 break;
3361 case SvxParaVertAlignItem::Align::Automatic:
3362 nVal = 4;
3363 break;
3364 default:
3365 nVal = 4;
3366 OSL_FAIL( "Unknown vert alignment" );
3367 break;
3368 }
3369 m_rWW8Export.InsUInt16( nVal );
3370 }
3371
3372 // NoHyphen: I didn't find an equal in the SW UI and WW UI
3373
3374 // RefMark, NoLineBreakHere are still missing
3375
WriteFootnoteBegin(const SwFormatFootnote & rFootnote,ww::bytes * pOutArr)3376 void WW8Export::WriteFootnoteBegin( const SwFormatFootnote& rFootnote, ww::bytes* pOutArr )
3377 {
3378 ww::bytes aAttrArr;
3379 const bool bAutoNum = rFootnote.GetNumStr().isEmpty();
3380 if( bAutoNum )
3381 {
3382 static const sal_uInt8 aSpec[] =
3383 {
3384 0x03, 0x6a, 0, 0, 0, 0, // sprmCObjLocation
3385 0x55, 0x08, 1 // sprmCFSpec
3386 };
3387
3388 aAttrArr.insert(aAttrArr.end(), aSpec, aSpec+sizeof(aSpec));
3389 }
3390
3391 // sprmCIstd
3392 const SwEndNoteInfo* pInfo;
3393 if( rFootnote.IsEndNote() )
3394 pInfo = &m_pDoc->GetEndNoteInfo();
3395 else
3396 pInfo = &m_pDoc->GetFootnoteInfo();
3397 const SwCharFormat* pCFormat = pOutArr
3398 ? pInfo->GetAnchorCharFormat( *m_pDoc )
3399 : pInfo->GetCharFormat( *m_pDoc );
3400 SwWW8Writer::InsUInt16( aAttrArr, NS_sprm::sprmCIstd );
3401 SwWW8Writer::InsUInt16( aAttrArr, GetId( pCFormat ) );
3402
3403 // fSpec-Attribut true
3404 // For Auto-Number a special character must go
3405 // into the text and therefore a fSpec attribute
3406 m_pChpPlc->AppendFkpEntry( Strm().Tell() );
3407 if( bAutoNum )
3408 WriteChar( 0x02 ); // auto number character
3409 else
3410 // user numbering
3411 OutSwString(rFootnote.GetNumStr(), 0, rFootnote.GetNumStr().getLength());
3412
3413 if( pOutArr )
3414 {
3415 // insert at start of array, so the "hard" attribute overrule the
3416 // attributes of the character template
3417 pOutArr->insert( pOutArr->begin(), aAttrArr.begin(), aAttrArr.end() );
3418 }
3419 else
3420 {
3421 std::unique_ptr<ww::bytes> pOwnOutArr(new ww::bytes);
3422
3423 // insert at start of array, so the "hard" attribute overrule the
3424 // attributes of the character template
3425 pOwnOutArr->insert(pOwnOutArr->begin(), aAttrArr.begin(), aAttrArr.end());
3426
3427 // write for the ftn number in the content, the font of the anchor
3428 const SwTextFootnote* pTextFootnote = rFootnote.GetTextFootnote();
3429 if( pTextFootnote )
3430 {
3431 std::unique_ptr<ww::bytes> pOld = std::move(pO);
3432 pO = std::move(pOwnOutArr);
3433 SfxItemSet aSet( m_pDoc->GetAttrPool(), svl::Items<RES_CHRATR_FONT,
3434 RES_CHRATR_FONT>{} );
3435
3436 pCFormat = pInfo->GetCharFormat( *m_pDoc );
3437
3438 pTextFootnote->GetTextNode().GetParaAttr(aSet,
3439 pTextFootnote->GetStart(), pTextFootnote->GetStart() + 1, true);
3440 if (aSet.Count())
3441 {
3442 m_pAttrOutput->OutputItem( aSet.Get( RES_CHRATR_FONT ) );
3443 }
3444 else
3445 {
3446 m_pAttrOutput->OutputItem( pCFormat->GetAttrSet().Get(RES_CHRATR_FONT) );
3447 }
3448 pOwnOutArr = std::move(pO);
3449 pO = std::move(pOld);
3450 }
3451 m_pChpPlc->AppendFkpEntry( Strm().Tell(), pOwnOutArr->size(),
3452 pOwnOutArr->data() );
3453 }
3454 }
3455
lcl_IsAtTextEnd(const SwFormatFootnote & rFootnote)3456 static bool lcl_IsAtTextEnd(const SwFormatFootnote& rFootnote)
3457 {
3458 bool bRet = true;
3459 if( rFootnote.GetTextFootnote() )
3460 {
3461 sal_uInt16 nWh = rFootnote.IsEndNote() ? sal_uInt16(RES_END_AT_TXTEND)
3462 : sal_uInt16(RES_FTN_AT_TXTEND);
3463 const SwSectionNode* pSectNd = rFootnote.GetTextFootnote()->GetTextNode().
3464 FindSectionNode();
3465 while( pSectNd && FTNEND_ATPGORDOCEND ==
3466 static_cast<const SwFormatFootnoteEndAtTextEnd&>(pSectNd->GetSection().GetFormat()->
3467 GetFormatAttr( nWh)).GetValue() )
3468 pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
3469
3470 if (!pSectNd)
3471 bRet = false; // the is ftn/end collected at Page- or Doc-End
3472 }
3473 return bRet;
3474 }
3475
TextFootnote(const SwFormatFootnote & rFootnote)3476 void AttributeOutputBase::TextFootnote( const SwFormatFootnote& rFootnote )
3477 {
3478 sal_uInt16 nTyp;
3479 if ( rFootnote.IsEndNote() )
3480 {
3481 nTyp = REF_ENDNOTE;
3482 if ( GetExport().m_bEndAtTextEnd )
3483 GetExport().m_bEndAtTextEnd = lcl_IsAtTextEnd( rFootnote );
3484 }
3485 else
3486 {
3487 nTyp = REF_FOOTNOTE;
3488 if ( GetExport().m_bFootnoteAtTextEnd )
3489 GetExport().m_bFootnoteAtTextEnd = lcl_IsAtTextEnd( rFootnote );
3490 }
3491
3492 // if any reference to this footnote/endnote then insert an internal
3493 // Bookmark.
3494 OUString sBkmkNm;
3495 if ( GetExport().HasRefToObject( nTyp, nullptr, rFootnote.GetTextFootnote()->GetSeqRefNo() ))
3496 {
3497 sBkmkNm = MSWordExportBase::GetBookmarkName( nTyp, nullptr,
3498 rFootnote.GetTextFootnote()->GetSeqRefNo() );
3499 GetExport().AppendBookmark( sBkmkNm );
3500 }
3501
3502 TextFootnote_Impl( rFootnote );
3503
3504 if ( !sBkmkNm.isEmpty() )
3505 GetExport().AppendBookmark( sBkmkNm ); // FIXME: Why is it added twice? Shouldn't this one go to WW8AttributeOutput::TextFootnote_Impl()?
3506 }
3507
TextFootnote_Impl(const SwFormatFootnote & rFootnote)3508 void WW8AttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote )
3509 {
3510 WW8_WrPlcFootnoteEdn* pFootnoteEnd;
3511 if ( rFootnote.IsEndNote() || GetExport().m_pDoc->GetFootnoteInfo().ePos == FTNPOS_CHAPTER )
3512 pFootnoteEnd = m_rWW8Export.pEdn.get();
3513 else
3514 pFootnoteEnd = m_rWW8Export.pFootnote.get();
3515
3516 pFootnoteEnd->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), rFootnote );
3517 m_rWW8Export.WriteFootnoteBegin( rFootnote, m_rWW8Export.pO.get() );
3518 }
3519
TextCharFormat(const SwFormatCharFormat & rCharFormat)3520 void WW8AttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat )
3521 {
3522 if( rCharFormat.GetCharFormat() )
3523 {
3524 m_rWW8Export.InsUInt16( NS_sprm::sprmCIstd );
3525
3526 m_rWW8Export.InsUInt16( m_rWW8Export.GetId( rCharFormat.GetCharFormat() ) );
3527 }
3528 }
3529
3530 /*
3531 See ww8par6.cxx Read_DoubleLine for some more info
3532 */
CharTwoLines(const SvxTwoLinesItem & rTwoLines)3533 void WW8AttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
3534 {
3535 // #i28331# - check that bOn is set
3536 if ( rTwoLines.GetValue() )
3537 {
3538 m_rWW8Export.InsUInt16( NS_sprm::sprmCFELayout );
3539 m_rWW8Export.pO->push_back( sal_uInt8(0x06) ); //len 6
3540 m_rWW8Export.pO->push_back( sal_uInt8(0x02) );
3541
3542 sal_Unicode cStart = rTwoLines.GetStartBracket();
3543 sal_Unicode cEnd = rTwoLines.GetEndBracket();
3544
3545 /*
3546 As per usual we have problems. We can have separate left and right brackets
3547 in OOo, it doesn't appear that you can in word. Also in word there appear
3548 to only be a limited number of possibilities, we can use pretty much
3549 anything.
3550
3551 So if we have none, we export none, if either bracket is set to a known
3552 word type we export both as that type (with the bracket winning out in
3553 the case of a conflict simply being the order of test here.
3554
3555 Upshot being a documented created in word will be reexported with no
3556 ill effects.
3557 */
3558
3559 sal_uInt16 nType;
3560 if (!cStart && !cEnd)
3561 nType = 0;
3562 else if ((cStart == '{') || (cEnd == '}'))
3563 nType = 4;
3564 else if ((cStart == '<') || (cEnd == '>'))
3565 nType = 3;
3566 else if ((cStart == '[') || (cEnd == ']'))
3567 nType = 2;
3568 else
3569 nType = 1;
3570 m_rWW8Export.InsUInt16( nType );
3571 static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
3572 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aZeroArr, aZeroArr+3);
3573 }
3574 }
3575
ParaNumRule(const SwNumRuleItem & rNumRule)3576 void AttributeOutputBase::ParaNumRule( const SwNumRuleItem& rNumRule )
3577 {
3578 const SwTextNode* pTextNd = nullptr;
3579 if (rNumRule.GetValue().isEmpty())
3580 {
3581 ParaNumRule_Impl(pTextNd, 0, 0);
3582 return;
3583 }
3584 const SwNumRule* pRule = GetExport().m_pDoc->FindNumRulePtr(
3585 rNumRule.GetValue() );
3586 if (!pRule)
3587 return;
3588
3589 sal_uInt16 nNumId = GetExport().GetNumberingId(*pRule) + 1;
3590 sal_uInt8 nLvl = 0;
3591
3592 if (!GetExport().m_pOutFormatNode)
3593 {
3594 ParaNumRule_Impl(pTextNd, nLvl, nNumId);
3595 return;
3596 }
3597
3598 if ( dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) != nullptr )
3599 {
3600 pTextNd = static_cast<const SwTextNode*>(GetExport().m_pOutFormatNode);
3601
3602 if( pTextNd->IsCountedInList())
3603 {
3604 int nLevel = pTextNd->GetActualListLevel();
3605
3606 if (nLevel < 0)
3607 nLevel = 0;
3608
3609 if (nLevel >= MAXLEVEL)
3610 nLevel = MAXLEVEL - 1;
3611
3612 nLvl = static_cast< sal_uInt8 >(nLevel);
3613
3614 if (GetExport().GetExportFormat() == MSWordExportBase::DOCX) // FIXME
3615 {
3616 // tdf#95848 find the abstract list definition
3617 OUString const listId(pTextNd->GetListId());
3618 if (!listId.isEmpty()
3619 && (listId != pRule->GetDefaultListId() // default list id uses the 1:1 mapping
3620 || pTextNd->IsListRestart()) // or restarting previous list
3621 )
3622 {
3623 SwList const*const pList(
3624 GetExport().m_pDoc->getIDocumentListsAccess().getListByName(listId));
3625 if (pList)
3626 {
3627 SwNumRule const*const pAbstractRule(
3628 GetExport().m_pDoc->FindNumRulePtr(
3629 pList->GetDefaultListStyleName()));
3630 assert(pAbstractRule);
3631 if (pAbstractRule == pRule && !pTextNd->IsListRestart())
3632 {
3633 // different list, but no override
3634 nNumId = GetExport().DuplicateAbsNum(listId, *pAbstractRule) + 1;
3635 }
3636 else
3637 {
3638 nNumId = GetExport().OverrideNumRule(
3639 *pRule, listId, *pAbstractRule) + 1;
3640
3641 if (pTextNd->IsListRestart())
3642 {
3643 // For restarted lists we should also keep value for
3644 // future w:lvlOverride / w:startOverride
3645 GetExport().AddListLevelOverride(nNumId-1, pTextNd->GetActualListLevel(),
3646 pTextNd->GetActualListStartValue());
3647 }
3648 }
3649 }
3650 }
3651 }
3652 }
3653 else
3654 {
3655 // #i44815# adjust numbering for numbered paragraphs
3656 // without number (NO_NUMLEVEL). These paragraphs
3657 // will receive a list id 0, which WW interprets as
3658 // 'no number'.
3659 nNumId = 0;
3660 }
3661 }
3662 else if ( dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) != nullptr )
3663 {
3664 const SwTextFormatColl* pC = static_cast<const SwTextFormatColl*>(GetExport().m_pOutFormatNode);
3665 if ( pC && pC->IsAssignedToListLevelOfOutlineStyle() )
3666 nLvl = static_cast< sal_uInt8 >( pC->GetAssignedOutlineStyleLevel() );
3667 }
3668
3669 if ( nLvl >= WW8ListManager::nMaxLevel )
3670 nLvl = WW8ListManager::nMaxLevel - 1;
3671
3672 ParaNumRule_Impl( pTextNd, nLvl, nNumId);
3673 }
3674
ParaNumRule_Impl(const SwTextNode *,sal_Int32 const nLvl,sal_Int32 const nNumId)3675 void WW8AttributeOutput::ParaNumRule_Impl(const SwTextNode* /*pTextNd*/,
3676 sal_Int32 const nLvl, sal_Int32 const nNumId)
3677 {
3678 if (USHRT_MAX == nNumId)
3679 return;
3680
3681 // write sprmPIlvl and sprmPIlfo
3682 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPIlvl );
3683 m_rWW8Export.pO->push_back( ::sal::static_int_cast<sal_uInt8>(nLvl) );
3684 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::sprmPIlfo );
3685 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, ::sal::static_int_cast<sal_uInt16>(nNumId) );
3686 }
3687
3688 /* File FRMATR.HXX */
3689
FormatFrameSize(const SwFormatFrameSize & rSize)3690 void WW8AttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize )
3691 {
3692 if( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
3693 {
3694 if( m_rWW8Export.m_bOutGrf )
3695 return; // Fly around graphic -> Auto-size
3696
3697 //???? What about percentages ???
3698 if ( rSize.GetWidth() && rSize.GetWidthSizeType() == ATT_FIX_SIZE)
3699 {
3700 //"sprmPDxaWidth"
3701 m_rWW8Export.InsUInt16( NS_sprm::sprmPDxaWidth );
3702 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rSize.GetWidth()) );
3703 }
3704
3705 if ( rSize.GetHeight() )
3706 {
3707 // sprmPWHeightAbs
3708 m_rWW8Export.InsUInt16( NS_sprm::sprmPWHeightAbs );
3709
3710 sal_uInt16 nH = 0;
3711 switch ( rSize.GetHeightSizeType() )
3712 {
3713 case ATT_VAR_SIZE: break;
3714 case ATT_FIX_SIZE: nH = static_cast<sal_uInt16>(rSize.GetHeight()) & 0x7fff; break;
3715 default: nH = static_cast<sal_uInt16>(rSize.GetHeight()) | 0x8000; break;
3716 }
3717 m_rWW8Export.InsUInt16( nH );
3718 }
3719 }
3720 else if( m_rWW8Export.m_bOutPageDescs ) // PageDesc : width + height
3721 {
3722 if( m_rWW8Export.m_pCurrentPageDesc->GetLandscape() )
3723 {
3724 /*sprmSBOrientation*/
3725 m_rWW8Export.InsUInt16( NS_sprm::sprmSBOrientation );
3726 m_rWW8Export.pO->push_back( 2 );
3727 }
3728
3729 /*sprmSXaPage*/
3730 m_rWW8Export.InsUInt16( NS_sprm::sprmSXaPage );
3731 m_rWW8Export.InsUInt16(
3732 msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetWidth())));
3733
3734 /*sprmSYaPage*/
3735 m_rWW8Export.InsUInt16( NS_sprm::sprmSYaPage );
3736 m_rWW8Export.InsUInt16(
3737 msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetHeight())));
3738 }
3739 }
3740
3741 // FillOrder is still missing
3742
3743 /**
3744 * ReplaceCr() is used for Pagebreaks and Pagedescs. An already written CR
3745 * will be replaced by a break character. Replace must be called right after
3746 * the writing of CR.
3747 *
3748 * @return FilePos + 1 of the replaced CR or 0 if nothing was replaced.
3749 */
ReplaceCr(sal_uInt8 nChar)3750 sal_uLong WW8Export::ReplaceCr( sal_uInt8 nChar )
3751 {
3752 OSL_ENSURE( nChar, "replaced with 0 crashes WW97/95" );
3753
3754 bool bReplaced = false;
3755 SvStream& rStrm = Strm();
3756 sal_uLong nRetPos = 0, nPos = rStrm.Tell();
3757 //If there is at least two characters already output
3758 if (nPos - 2 >= sal_uLong(pFib->m_fcMin))
3759 {
3760 sal_uInt16 nUCode=0;
3761
3762 rStrm.SeekRel(-2);
3763 rStrm.ReadUInt16( nUCode );
3764 //If the last char was a cr
3765 if (nUCode == 0x0d) // CR ?
3766 {
3767 if ((nChar == 0x0c) &&
3768 (nPos - 4 >= sal_uLong(pFib->m_fcMin)))
3769 {
3770 rStrm.SeekRel(-4);
3771 rStrm.ReadUInt16( nUCode );
3772 }
3773 else
3774 {
3775 rStrm.SeekRel(-2);
3776 nUCode = 0x0;
3777 }
3778 //And the para is not of len 0, then replace this cr with the mark
3779 //#120140# If there is a cr before a column break, need replace the cr. So remove the "nChar==0x0e" check.
3780 if( nUCode == 0x0d )
3781 bReplaced = false;
3782 else
3783 {
3784 bReplaced = true;
3785 WriteChar(nChar);
3786 nRetPos = nPos;
3787 }
3788 }
3789 else if ((nUCode == 0x0c) && (nChar == 0x0e))
3790 {
3791 // a column break after a section has no effect in writer
3792 bReplaced = true;
3793 }
3794 rStrm.Seek( nPos );
3795 }
3796 else
3797 bReplaced = true;
3798
3799 if (!bReplaced)
3800 {
3801 // then write as normal char
3802 WriteChar(nChar);
3803 m_pPiece->SetParaBreak();
3804 m_pPapPlc->AppendFkpEntry(rStrm.Tell());
3805 m_pChpPlc->AppendFkpEntry(rStrm.Tell());
3806 nRetPos = rStrm.Tell();
3807 }
3808 return nRetPos;
3809 }
3810
TableRowEnd(sal_uInt32 nDepth)3811 void WW8AttributeOutput::TableRowEnd(sal_uInt32 nDepth)
3812 {
3813 if ( nDepth == 1 )
3814 m_rWW8Export.WriteChar( 0x07 );
3815 else if ( nDepth > 1 )
3816 m_rWW8Export.WriteChar( 0x0d );
3817
3818 //Technically in a word document this is a different value for a row ends
3819 //that are not row ends directly after a cell with a graphic. But it
3820 //doesn't seem to make a difference
3821 //pMagicTable->Append(Fc2Cp(Strm().Tell()),0x1B6);
3822 }
3823
FormatPageDescription(const SwFormatPageDesc & rPageDesc)3824 void AttributeOutputBase::FormatPageDescription( const SwFormatPageDesc& rPageDesc )
3825 {
3826 if ( GetExport().m_bStyDef && dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
3827 {
3828 const SwTextFormatColl* pC = static_cast<const SwTextFormatColl*>(GetExport().m_pOutFormatNode);
3829 if ( (SfxItemState::SET != pC->GetItemState( RES_BREAK, false ) ) && rPageDesc.KnowsPageDesc() )
3830 FormatBreak( SvxFormatBreakItem( SvxBreak::PageBefore, RES_BREAK ) );
3831 }
3832 }
3833
PageBreakBefore(bool bBreak)3834 void WW8AttributeOutput::PageBreakBefore( bool bBreak )
3835 {
3836 // sprmPPageBreakBefore/sprmPFPageBreakBefore
3837 m_rWW8Export.InsUInt16( NS_sprm::sprmPFPageBreakBefore );
3838
3839 m_rWW8Export.pO->push_back( bBreak ? 1 : 0 );
3840 }
3841
3842 /**
3843 * breaks write nothing in the output field rWrt.pO,
3844 * but only in the text stream (requirement so they can
3845 * be called from Out_Break...)
3846 */
FormatBreak(const SvxFormatBreakItem & rBreak)3847 void AttributeOutputBase::FormatBreak( const SvxFormatBreakItem& rBreak )
3848 {
3849 if ( GetExport().m_bStyDef )
3850 {
3851 switch ( rBreak.GetBreak() )
3852 {
3853 case SvxBreak::NONE:
3854 case SvxBreak::PageBefore:
3855 case SvxBreak::PageBoth:
3856 PageBreakBefore( rBreak.GetValue() != SvxBreak::NONE );
3857 break;
3858 default:
3859 break;
3860 }
3861 }
3862 else if ( !GetExport().m_pParentFrame )
3863 {
3864 sal_uInt8 nC = 0;
3865 bool bBefore = false;
3866 // #i76300# - Note: Can only be <true>, if <bBefore> equals <false>.
3867 bool bCheckForFollowPageDesc = false;
3868
3869 switch ( rBreak.GetBreak() )
3870 {
3871 case SvxBreak::NONE: // disabled
3872 if ( !GetExport().m_bBreakBefore )
3873 PageBreakBefore( false );
3874 return;
3875
3876 case SvxBreak::ColumnBefore: // ColumnBreak
3877 bBefore = true;
3878 [[fallthrough]];
3879 case SvxBreak::ColumnAfter:
3880 case SvxBreak::ColumnBoth:
3881 if ( GetExport().Sections().CurrentNumberOfColumns( *GetExport().m_pDoc ) > 1 || GetExport().SupportsOneColumnBreak() )
3882 {
3883 nC = msword::ColumnBreak;
3884 }
3885 break;
3886
3887 case SvxBreak::PageBefore: // PageBreak
3888 // From now on(fix for #i77900#) we prefer to save a page break
3889 // as paragraph attribute (if the exporter is OK with that),
3890 // this has to be done after the export of the paragraph ( =>
3891 // !GetExport().bBreakBefore )
3892 if (GetExport().PreferPageBreakBefore())
3893 {
3894 if (!GetExport().m_bBreakBefore)
3895 PageBreakBefore(true);
3896 }
3897 else
3898 {
3899 bBefore = true;
3900 nC = msword::PageBreak;
3901 }
3902 break;
3903 case SvxBreak::PageAfter:
3904 case SvxBreak::PageBoth:
3905 nC = msword::PageBreak;
3906 // #i76300# - check for follow page description,
3907 // if current writing attributes of a paragraph.
3908 if ( dynamic_cast< const SwTextNode* >( GetExport().m_pOutFormatNode ) &&
3909 GetExport().GetCurItemSet() )
3910 {
3911 bCheckForFollowPageDesc = true;
3912 }
3913 break;
3914
3915 default:
3916 break;
3917 }
3918
3919 if ( ( bBefore == GetExport().m_bBreakBefore ) && nC )
3920 {
3921 // #i76300#
3922 bool bFollowPageDescWritten = false;
3923 if ( bCheckForFollowPageDesc )
3924 {
3925 bFollowPageDescWritten =
3926 GetExport().OutputFollowPageDesc( GetExport().GetCurItemSet(),
3927 dynamic_cast<const SwTextNode*>( GetExport().m_pOutFormatNode ) );
3928 }
3929 if ( !bFollowPageDescWritten )
3930 {
3931 SectionBreak(nC, !bBefore);
3932 }
3933 }
3934 }
3935 }
3936
SectionBreak(sal_uInt8 nC,bool,const WW8_SepInfo *)3937 void WW8AttributeOutput::SectionBreak( sal_uInt8 nC, bool /*bBreakAfter*/, const WW8_SepInfo* /*pSectionInfo*/ )
3938 {
3939 m_rWW8Export.ReplaceCr( nC );
3940 }
3941
GridCharacterPitch(const SwTextGridItem & rGrid) const3942 sal_uInt32 AttributeOutputBase::GridCharacterPitch( const SwTextGridItem& rGrid ) const
3943 {
3944 MSWordStyles * pStyles = GetExport().m_pStyles.get();
3945 const SwFormat * pSwFormat = pStyles->GetSwFormat(0);
3946
3947 sal_uInt32 nPageCharSize = 0;
3948
3949 if (pSwFormat != nullptr)
3950 {
3951 nPageCharSize = ItemGet<SvxFontHeightItem>
3952 (*pSwFormat, RES_CHRATR_FONTSIZE).GetHeight();
3953 }
3954 sal_uInt16 nPitch = rGrid.IsSquaredMode() ? rGrid.GetBaseHeight() :
3955 rGrid.GetBaseWidth( );
3956
3957 sal_Int32 nCharWidth = nPitch - nPageCharSize;
3958 sal_Int32 nFraction = nCharWidth % 20;
3959 if ( nCharWidth < 0 )
3960 nFraction = 20 + nFraction;
3961 nFraction = ( nFraction * 0xFFF ) / 20;
3962 nFraction = ( nFraction & 0x00000FFF );
3963
3964 sal_Int32 nMain = nCharWidth / 20;
3965 if ( nCharWidth < 0 )
3966 nMain -= 1;
3967 nMain = nMain * 0x1000;
3968 nMain = ( nMain & 0xFFFFF000 );
3969
3970 return sal_uInt32( nFraction + nMain );
3971 }
3972
FormatTextGrid(const SwTextGridItem & rGrid)3973 void WW8AttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid )
3974 {
3975 if (m_rWW8Export.m_bOutPageDescs)
3976 {
3977 sal_uInt16 nGridType = 0;
3978 switch ( rGrid.GetGridType() )
3979 {
3980 default:
3981 OSL_FAIL("Unknown grid type");
3982 [[fallthrough]];
3983 case GRID_NONE:
3984 nGridType = 0;
3985 break;
3986 case GRID_LINES_ONLY:
3987 nGridType = 2;
3988 break;
3989 case GRID_LINES_CHARS:
3990 if ( rGrid.IsSnapToChars() )
3991 nGridType = 3;
3992 else
3993 nGridType = 1;
3994 break;
3995 }
3996 m_rWW8Export.InsUInt16( NS_sprm::sprmSClm );
3997 m_rWW8Export.InsUInt16( nGridType );
3998
3999 sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
4000 m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaLinePitch );
4001 m_rWW8Export.InsUInt16( nHeight );
4002
4003 m_rWW8Export.InsUInt16( NS_sprm::sprmSDxtCharSpace );
4004 m_rWW8Export.InsUInt32( GridCharacterPitch( rGrid ) );
4005 }
4006 }
4007
FormatPaperBin(const SvxPaperBinItem & rPaperBin)4008 void WW8AttributeOutput::FormatPaperBin( const SvxPaperBinItem& rPaperBin )
4009 {
4010 if ( m_rWW8Export.m_bOutPageDescs )
4011 {
4012 sal_uInt16 nVal;
4013 switch ( rPaperBin.GetValue() )
4014 {
4015 case 0: nVal = 15; break; // Automatically select
4016 case 1: nVal = 1; break; // Upper paper tray
4017 case 2: nVal = 4; break; // Manual paper feed
4018 default: nVal = 0; break;
4019 }
4020
4021 if ( nVal )
4022 {
4023 m_rWW8Export.InsUInt16( m_rWW8Export.m_bOutFirstPage
4024 ? NS_sprm::sprmSDmBinFirst : NS_sprm::sprmSDmBinOther );
4025
4026 m_rWW8Export.InsUInt16( nVal );
4027 }
4028 }
4029 }
4030
FormatLRSpace(const SvxLRSpaceItem & rLR)4031 void WW8AttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLR )
4032 {
4033 // Flys are still missing ( see RTF )
4034
4035 if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
4036 {
4037 // sprmPDxaFromText10
4038 m_rWW8Export.InsUInt16( NS_sprm::LN_PDxaFromText10 );
4039 // use average, since WW only knows one value
4040 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>( ( rLR.GetLeft() + rLR.GetRight() ) / 2 ) );
4041 }
4042 else if ( m_rWW8Export.m_bOutPageDescs ) // PageDescs
4043 {
4044 m_pageMargins.nLeft = 0;
4045 m_pageMargins.nRight = 0;
4046
4047 if ( auto pBoxItem = static_cast<const SvxBoxItem*>(m_rWW8Export.HasItem( RES_BOX )) )
4048 {
4049 m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
4050 m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
4051 }
4052
4053 m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLR.GetLeft());
4054 m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLR.GetRight());
4055
4056 // sprmSDxaLeft
4057 m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaLeft );
4058 m_rWW8Export.InsUInt16( m_pageMargins.nLeft );
4059
4060 // sprmSDxaRight
4061 m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaRight );
4062 m_rWW8Export.InsUInt16( m_pageMargins.nRight );
4063 }
4064 else
4065 { // normal paragraphs
4066 // sprmPDxaLeft
4067 m_rWW8Export.InsUInt16( 0x845E ); //asian version ?
4068 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLR.GetTextLeft()) );
4069
4070 // sprmPDxaRight
4071 m_rWW8Export.InsUInt16( 0x845D ); //asian version ?
4072 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLR.GetRight()) );
4073
4074 // sprmPDxaLeft1
4075 m_rWW8Export.InsUInt16( 0x8460 ); //asian version ?
4076 m_rWW8Export.InsUInt16( rLR.GetTextFirstLineOfst() );
4077 }
4078 }
4079
FormatULSpace(const SvxULSpaceItem & rUL)4080 void WW8AttributeOutput::FormatULSpace( const SvxULSpaceItem& rUL )
4081 {
4082 // Flys are still missing ( see RTF )
4083
4084 if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
4085 {
4086 // sprmPDyaFromText
4087 m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaFromText );
4088 // use average, since WW only knows one value
4089 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>( ( rUL.GetUpper() + rUL.GetLower() ) / 2 ) );
4090 }
4091 else if ( m_rWW8Export.m_bOutPageDescs ) // Page-UL
4092 {
4093 OSL_ENSURE( m_rWW8Export.GetCurItemSet(), "Impossible" );
4094 if ( !m_rWW8Export.GetCurItemSet() )
4095 return;
4096
4097 HdFtDistanceGlue aDistances( *m_rWW8Export.GetCurItemSet() );
4098
4099 if ( aDistances.HasHeader() )
4100 {
4101 //sprmSDyaHdrTop
4102 m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaHdrTop );
4103 m_rWW8Export.InsUInt16( aDistances.dyaHdrTop );
4104 }
4105
4106 // sprmSDyaTop
4107 m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaTop );
4108 m_rWW8Export.InsUInt16( aDistances.dyaTop );
4109 m_pageMargins.nTop = aDistances.dyaTop;
4110
4111 if ( aDistances.HasFooter() )
4112 {
4113 //sprmSDyaHdrBottom
4114 m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaHdrBottom );
4115 m_rWW8Export.InsUInt16( aDistances.dyaHdrBottom );
4116 }
4117
4118 //sprmSDyaBottom
4119 m_rWW8Export.InsUInt16( NS_sprm::sprmSDyaBottom );
4120 m_rWW8Export.InsUInt16( aDistances.dyaBottom );
4121 m_pageMargins.nBottom = aDistances.dyaBottom;
4122 }
4123 else
4124 {
4125 // sprmPDyaBefore
4126 m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaBefore );
4127 m_rWW8Export.InsUInt16( rUL.GetUpper() );
4128 // sprmPDyaAfter
4129 m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaAfter );
4130 m_rWW8Export.InsUInt16( rUL.GetLower() );
4131 // sprmPFContextualSpacing
4132 if (rUL.GetContext())
4133 {
4134 m_rWW8Export.InsUInt16(NS_sprm::sprmPFContextualSpacing);
4135 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(rUL.GetContext()) );
4136 }
4137 }
4138 }
4139
4140 // print, opaque, protect are still missing
4141
FormatSurround(const SwFormatSurround & rSurround)4142 void WW8AttributeOutput::FormatSurround( const SwFormatSurround& rSurround )
4143 {
4144 if ( m_rWW8Export.m_bOutFlyFrameAttrs )
4145 {
4146 m_rWW8Export.InsUInt16( NS_sprm::sprmPWr );
4147
4148 m_rWW8Export.pO->push_back(
4149 ( css::text::WrapTextMode_NONE != rSurround.GetSurround() ) ? 2 : 1 );
4150 }
4151 }
4152
FormatVertOrientation(const SwFormatVertOrient & rFlyVert)4153 void WW8AttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert )
4154 {
4155
4156 //!!!! anchor type and corresponding borders are still missing
4157 if ( m_rWW8Export.m_bOutFlyFrameAttrs )
4158 {
4159 short nPos;
4160 switch( rFlyVert.GetVertOrient() )
4161 {
4162 case text::VertOrientation::NONE:
4163 nPos = static_cast<short>(rFlyVert.GetPos());
4164 break;
4165 case text::VertOrientation::CENTER:
4166 case text::VertOrientation::LINE_CENTER:
4167 nPos = -8;
4168 break;
4169 case text::VertOrientation::BOTTOM:
4170 case text::VertOrientation::LINE_BOTTOM:
4171 nPos = -12;
4172 break;
4173 case text::VertOrientation::TOP:
4174 case text::VertOrientation::LINE_TOP:
4175 default:
4176 nPos = -4;
4177 break;
4178 }
4179
4180 // sprmPDyaAbs
4181 m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaAbs );
4182 m_rWW8Export.InsUInt16( nPos );
4183 }
4184 }
4185
FormatHorizOrientation(const SwFormatHoriOrient & rFlyHori)4186 void WW8AttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori )
4187 {
4188 if ( !m_rWW8Export.m_pParentFrame )
4189 {
4190 OSL_ENSURE( m_rWW8Export.m_pParentFrame, "HoriOrient without mpParentFrame !!" );
4191 return;
4192 }
4193
4194 //!!!! anchor type and corresponding borders are still missing
4195 if ( m_rWW8Export.m_bOutFlyFrameAttrs )
4196 {
4197 short nPos;
4198 switch( rFlyHori.GetHoriOrient() )
4199 {
4200 case text::HoriOrientation::NONE:
4201 nPos = static_cast<short>(rFlyHori.GetPos());
4202 if( !nPos )
4203 nPos = 1; // WW: 0 is reserved
4204 break;
4205 case text::HoriOrientation::LEFT:
4206 nPos = rFlyHori.IsPosToggle() ? -12 : 0;
4207 break;
4208 case text::HoriOrientation::RIGHT:
4209 nPos = rFlyHori.IsPosToggle() ? -16 : -8;
4210 break;
4211 case text::HoriOrientation::CENTER:
4212 case text::HoriOrientation::FULL: // FULL only for tables
4213 default:
4214 nPos = -4;
4215 break;
4216 }
4217
4218 // sprmPDxaAbs
4219 m_rWW8Export.InsUInt16( NS_sprm::sprmPDxaAbs );
4220 m_rWW8Export.InsUInt16( nPos );
4221 }
4222 }
4223
FormatAnchor(const SwFormatAnchor & rAnchor)4224 void WW8AttributeOutput::FormatAnchor( const SwFormatAnchor& rAnchor )
4225 {
4226 OSL_ENSURE( m_rWW8Export.m_pParentFrame, "Anchor without mpParentFrame !!" );
4227
4228 if ( m_rWW8Export.m_bOutFlyFrameAttrs )
4229 {
4230 sal_uInt8 nP = 0;
4231 switch ( rAnchor.GetAnchorId() )
4232 {
4233 case RndStdIds::FLY_AT_PAGE:
4234 // vertical: page | horizontal: page
4235 nP |= (1 << 4) | (2 << 6);
4236 break;
4237 // in case of Fly as characters: set paragraph-bound!!!
4238 case RndStdIds::FLY_AT_FLY:
4239 case RndStdIds::FLY_AT_CHAR:
4240 case RndStdIds::FLY_AT_PARA:
4241 case RndStdIds::FLY_AS_CHAR:
4242 // vertical: page | horizontal: page
4243 nP |= (2 << 4) | (0 << 6);
4244 break;
4245 default:
4246 break;
4247 }
4248
4249 // sprmPPc
4250 m_rWW8Export.InsUInt16( NS_sprm::sprmPPc );
4251 m_rWW8Export.pO->push_back( nP );
4252 }
4253 }
4254
FormatBackground(const SvxBrushItem & rBrush)4255 void WW8AttributeOutput::FormatBackground( const SvxBrushItem& rBrush )
4256 {
4257 // WW cannot have background in a section
4258 if ( !m_rWW8Export.m_bOutPageDescs )
4259 {
4260 WW8_SHD aSHD;
4261 WW8Export::TransBrush( rBrush.GetColor(), aSHD );
4262
4263 m_rWW8Export.InsUInt16( NS_sprm::sprmPShd80 );
4264 m_rWW8Export.InsUInt16( aSHD.GetValue() );
4265
4266 m_rWW8Export.InsUInt16( NS_sprm::sprmPShd );
4267 m_rWW8Export.pO->push_back( 10 ); //size of operand: MUST be 10
4268 m_rWW8Export.InsUInt32( 0xFF000000 ); //cvFore: Foreground BGR = cvAuto
4269 m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) ); //cvBack
4270 m_rWW8Export.InsUInt16( 0x0000 ); //iPat: specifies the pattern used for shading = clear/100% background
4271 }
4272 }
4273
FormatFillStyle(const XFillStyleItem & rFillStyle)4274 void WW8AttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle )
4275 {
4276 // WW cannot have background in a section
4277 if ( !m_rWW8Export.m_bOutPageDescs )
4278 {
4279 // see MSWordExportBase::OutputItemSet for how _SOLID is handled
4280 if ( rFillStyle.GetValue() == drawing::FillStyle_NONE )
4281 {
4282 //Shd80Nil
4283 m_rWW8Export.InsUInt16( NS_sprm::sprmPShd80 );
4284 m_rWW8Export.InsUInt16( 0xffff );
4285
4286 //cvAuto
4287 m_rWW8Export.InsUInt16( NS_sprm::sprmPShd );
4288 m_rWW8Export.pO->push_back( 10 );
4289 m_rWW8Export.InsUInt32( 0xFF000000 );
4290 m_rWW8Export.InsUInt32( 0xFF000000 );
4291 m_rWW8Export.InsUInt16( 0x0000 );
4292 }
4293 }
4294 }
4295
FormatFillGradient(const XFillGradientItem &)4296 void WW8AttributeOutput::FormatFillGradient( const XFillGradientItem& /*rFillGradient*/ )
4297 {
4298 }
4299
TranslateBorderLine(const SvxBorderLine & rLine,sal_uInt16 nDist,bool bShadow)4300 WW8_BRCVer9 WW8Export::TranslateBorderLine(const SvxBorderLine& rLine,
4301 sal_uInt16 nDist, bool bShadow)
4302 {
4303 sal_uInt32 nColBGR = 0;
4304 sal_uInt16 nWidth = ::editeng::ConvertBorderWidthToWord(
4305 rLine.GetBorderLineStyle(), rLine.GetWidth());
4306 sal_uInt8 brcType = 0;
4307
4308 if( nWidth ) // line ?
4309 {
4310 // BRC.brcType
4311 brcType = 0;
4312 // All the border types values are available on
4313 // http://msdn.microsoft.com/en-us/library/dd908142%28v=office.12%29.aspx
4314 switch (rLine.GetBorderLineStyle())
4315 {
4316 case SvxBorderLineStyle::SOLID:
4317 {
4318 if ( rLine.GetWidth( ) == DEF_LINE_WIDTH_0 )
4319 brcType = 5;
4320 else
4321 brcType = 1;
4322 }
4323 break;
4324 case SvxBorderLineStyle::DOTTED:
4325 brcType = 6;
4326 break;
4327 case SvxBorderLineStyle::DASHED:
4328 brcType = 7;
4329 break;
4330 case SvxBorderLineStyle::DOUBLE:
4331 case SvxBorderLineStyle::DOUBLE_THIN:
4332 brcType = 3;
4333 break;
4334 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
4335 brcType = 11;
4336 break;
4337 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
4338 brcType = 14;
4339 break;
4340 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
4341 brcType = 17;
4342 break;
4343 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
4344 brcType = 12;
4345 break;
4346 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
4347 brcType = 15;
4348 break;
4349 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
4350 brcType = 18;
4351 break;
4352 case SvxBorderLineStyle::EMBOSSED:
4353 brcType = 24;
4354 break;
4355 case SvxBorderLineStyle::ENGRAVED:
4356 brcType = 25;
4357 break;
4358 case SvxBorderLineStyle::OUTSET:
4359 brcType = 26;
4360 break;
4361 case SvxBorderLineStyle::INSET:
4362 brcType = 27;
4363 break;
4364 case SvxBorderLineStyle::FINE_DASHED:
4365 brcType = 22;
4366 break;
4367 case SvxBorderLineStyle::DASH_DOT:
4368 brcType = 8;
4369 break;
4370 case SvxBorderLineStyle::DASH_DOT_DOT:
4371 brcType = 9;
4372 break;
4373 default:
4374 break;
4375 }
4376
4377 // convert width from twips (1/20 pt) to eighths of a point
4378 nWidth = (( nWidth * 8 ) + 10 ) / 20;
4379 if( 0xff < nWidth )
4380 nWidth = 0xff;
4381
4382 if( 0 == nWidth ) // really thin line
4383 nWidth = 1; // don't omit
4384
4385 // BRC.cv
4386 nColBGR = wwUtility::RGBToBGR(rLine.GetColor().GetRGBColor());
4387 }
4388
4389 // BRC.dptSpace
4390 sal_uInt16 nLDist = nDist;
4391 nLDist /= 20; // unit of measurement: pt
4392 if( nLDist > 0x1f )
4393 nLDist = 0x1f;
4394
4395 return WW8_BRCVer9(nColBGR, sal_uInt8(nWidth), brcType, sal_uInt8(nLDist),
4396 bShadow, false);
4397 }
4398
4399 /**
4400 * Gets passed a WW8Bytes*, so the function also can be used for the table border.
4401 *
4402 * @param nSprmNo If nSprmNo == 0, then the opcode isn't outputted.
4403 * @param bShadow SHOULDN'T be set for table cells !
4404 */
Out_BorderLine(ww::bytes & rO,const SvxBorderLine * pLine,sal_uInt16 nDist,sal_uInt16 nSprmNo,sal_uInt16 nSprmNoVer9,bool bShadow)4405 void WW8Export::Out_BorderLine(ww::bytes& rO, const SvxBorderLine* pLine,
4406 sal_uInt16 nDist, sal_uInt16 nSprmNo, sal_uInt16 nSprmNoVer9, bool bShadow)
4407 {
4408 OSL_ENSURE( ( nSprmNo == 0 ) ||
4409 ( nSprmNo >= 38 && nSprmNo <= 41 ) ||
4410 ( nSprmNo >= NS_sprm::sprmPBrcTop80
4411 && nSprmNo <= NS_sprm::sprmPBrcRight80 ) ||
4412 ( nSprmNo >= NS_sprm::sprmSBrcTop80
4413 && nSprmNo <= NS_sprm::sprmSBrcRight80 ),
4414 "Sprm for border out is of range" );
4415
4416 WW8_BRCVer9 aBrcVer9;
4417 WW8_BRC aBrcVer8;
4418
4419 if( pLine && pLine->GetBorderLineStyle() != SvxBorderLineStyle::NONE )
4420 {
4421 aBrcVer9 = TranslateBorderLine( *pLine, nDist, bShadow );
4422 sal_uInt8 ico = msfilter::util::TransColToIco( msfilter::util::BGRToRGB(aBrcVer9.cv()) );
4423 aBrcVer8 = WW8_BRC( aBrcVer9.dptLineWidth(), aBrcVer9.brcType(), ico,
4424 aBrcVer9.dptSpace(), aBrcVer9.fShadow(), aBrcVer9.fFrame() );
4425 }
4426
4427 // WW97-SprmIds
4428 if ( nSprmNo != 0 )
4429 SwWW8Writer::InsUInt16( rO, nSprmNo );
4430
4431 rO.insert( rO.end(), aBrcVer8.aBits1, aBrcVer8.aBits2+2 );
4432
4433 if ( nSprmNoVer9 != 0 )
4434 {
4435 SwWW8Writer::InsUInt16( rO, nSprmNoVer9 );
4436 rO.push_back(sizeof(WW8_BRCVer9));
4437 rO.insert( rO.end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4);
4438 }
4439 }
4440
4441 /**
4442 * is for all boxes except in tables. pO of the WW8Writer is used
4443 *
4444 * @param rBox
4445 * @param bShadow
4446 */
Out_SwFormatBox(const SvxBoxItem & rBox,bool bShadow)4447 void WW8Export::Out_SwFormatBox(const SvxBoxItem& rBox, bool bShadow)
4448 {
4449 static const SvxBoxItemLine aBorders[] =
4450 {
4451 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4452 };
4453 static const sal_uInt16 aPBrc[] =
4454 {
4455 // WW8 SPRMs
4456 NS_sprm::sprmPBrcTop80, NS_sprm::sprmPBrcLeft80,
4457 NS_sprm::sprmPBrcBottom80, NS_sprm::sprmPBrcRight80,
4458 // WW9 SPRMs
4459 NS_sprm::sprmPBrcTop, NS_sprm::sprmPBrcLeft,
4460 NS_sprm::sprmPBrcBottom, NS_sprm::sprmPBrcRight
4461 };
4462 static const sal_uInt16 aSBrc[] =
4463 {
4464 // WW8 SPRMs
4465 NS_sprm::sprmSBrcTop80, NS_sprm::sprmSBrcLeft80,
4466 NS_sprm::sprmSBrcBottom80, NS_sprm::sprmSBrcRight80,
4467 // WW9 SPRMs
4468 NS_sprm::sprmSBrcTop, NS_sprm::sprmSBrcLeft,
4469 NS_sprm::sprmSBrcBottom, NS_sprm::sprmSBrcRight
4470 };
4471
4472 const SvxBoxItemLine* pBrd = aBorders;
4473 for( sal_uInt16 i = 0; i < 4; ++i, ++pBrd )
4474 {
4475 const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
4476
4477 sal_uInt16 nSprmNo, nSprmNoVer9 = 0;
4478 if (m_bOutPageDescs)
4479 {
4480 nSprmNo = aSBrc[i];
4481 nSprmNoVer9 = aSBrc[i+4];
4482 }
4483 else
4484 {
4485 nSprmNo = aPBrc[i];
4486 nSprmNoVer9 = aPBrc[i+4];
4487 }
4488
4489 Out_BorderLine( *pO, pLn, rBox.GetDistance( *pBrd ), nSprmNo,
4490 nSprmNoVer9, bShadow );
4491 }
4492 }
4493
4494 /**
4495 * FormatBox2() is for TC structures in tables. The Sprm opcode isn't written
4496 * because it is packed into the TC structure without opcode.
4497 * dxpSpace always becomes 0, because WW requires that in tables
4498 * ( table borders otherwise will fray )
4499 *
4500 * @param rO A WW8Bytes pointer is passed in as output parameter
4501 */
Out_SwFormatTableBox(ww::bytes & rO,const SvxBoxItem * pBox)4502 void WW8Export::Out_SwFormatTableBox( ww::bytes& rO, const SvxBoxItem * pBox )
4503 {
4504 // possible and maybe better would be 0xffff
4505 static const SvxBoxItemLine aBorders[] =
4506 {
4507 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4508 };
4509 static const SvxBorderLine aBorderLine;
4510
4511 for(const SvxBoxItemLine & rBorder : aBorders)
4512 {
4513 const SvxBorderLine* pLn;
4514 if (pBox != nullptr)
4515 pLn = pBox->GetLine( rBorder );
4516 else
4517 pLn = & aBorderLine;
4518
4519 Out_BorderLine(rO, pLn, 0, 0, 0, false);
4520 }
4521 }
4522
Out_CellRangeBorders(const SvxBoxItem * pBox,sal_uInt8 nStart,sal_uInt8 nLimit)4523 void WW8Export::Out_CellRangeBorders( const SvxBoxItem * pBox, sal_uInt8 nStart,
4524 sal_uInt8 nLimit )
4525 {
4526 static const SvxBoxItemLine aBorders[] =
4527 {
4528 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4529 };
4530
4531 for( int i = 0; i < 4; ++i )
4532 {
4533 const SvxBorderLine* pLn = nullptr;
4534 if (pBox != nullptr)
4535 pLn = pBox->GetLine( aBorders[i] );
4536 if (!pLn)
4537 continue;
4538
4539 InsUInt16( NS_sprm::sprmTSetBrc );
4540 pO->push_back( 11 );
4541 pO->push_back( nStart );
4542 pO->push_back( nLimit );
4543 pO->push_back( 1<<i );
4544 WW8_BRCVer9 aBrcVer9 = TranslateBorderLine( *pLn, 0, false );
4545 pO->insert( pO->end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4 );
4546 }
4547 }
4548
FormatBox(const SvxBoxItem & rBox)4549 void WW8AttributeOutput::FormatBox( const SvxBoxItem& rBox )
4550 {
4551 // Fly around graphic -> here no border, because the
4552 // graphics header already has the border
4553 if ( !m_rWW8Export.m_bOutGrf )
4554 {
4555 bool bShadow = false;
4556 const SfxPoolItem* pItem = m_rWW8Export.HasItem( RES_SHADOW );
4557 if ( pItem )
4558 {
4559 const SvxShadowItem* p = static_cast<const SvxShadowItem*>(pItem);
4560 bShadow = ( p->GetLocation() != SvxShadowLocation::NONE )
4561 && ( p->GetWidth() != 0 );
4562 }
4563
4564 SvxBoxItem aBox(rBox);
4565 if (m_rWW8Export.m_bOutPageDescs)
4566 {
4567 editeng::WordBorderDistances aDistances;
4568 editeng::BorderDistancesToWord(aBox, m_pageMargins, aDistances);
4569
4570 aBox.SetDistance(aDistances.nTop, SvxBoxItemLine::TOP);
4571 aBox.SetDistance(aDistances.nLeft, SvxBoxItemLine::LEFT);
4572 aBox.SetDistance(aDistances.nBottom, SvxBoxItemLine::BOTTOM);
4573 aBox.SetDistance(aDistances.nRight, SvxBoxItemLine::RIGHT);
4574
4575 m_bFromEdge = aDistances.bFromEdge;
4576 }
4577
4578 m_rWW8Export.Out_SwFormatBox( aBox, bShadow );
4579 }
4580 }
4581
CurrentPageWidth(SwTwips & rLeft,SwTwips & rRight) const4582 SwTwips WW8Export::CurrentPageWidth(SwTwips &rLeft, SwTwips &rRight) const
4583 {
4584 const SwFrameFormat* pFormat = m_pCurrentPageDesc ? &m_pCurrentPageDesc->GetMaster()
4585 : &m_pDoc->GetPageDesc(0).GetMaster();
4586
4587 const SvxLRSpaceItem& rLR = pFormat->GetLRSpace();
4588 SwTwips nPageSize = pFormat->GetFrameSize().GetWidth();
4589 rLeft = rLR.GetLeft();
4590 rRight = rLR.GetRight();
4591 return nPageSize;
4592 }
4593
FormatColumns_Impl(sal_uInt16 nCols,const SwFormatCol & rCol,bool bEven,SwTwips nPageSize)4594 void WW8AttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize )
4595 {
4596 // CColumns
4597 m_rWW8Export.InsUInt16( NS_sprm::sprmSCcolumns );
4598 m_rWW8Export.InsUInt16( nCols - 1 );
4599
4600 // DxaColumns
4601 m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaColumns );
4602 m_rWW8Export.InsUInt16( rCol.GetGutterWidth( true ) );
4603
4604 // LBetween
4605 m_rWW8Export.InsUInt16( NS_sprm::sprmSLBetween );
4606 m_rWW8Export.pO->push_back( COLADJ_NONE == rCol.GetLineAdj( )? 0 : 1 );
4607
4608 const SwColumns & rColumns = rCol.GetColumns( );
4609
4610 // FEvenlySpaced
4611 m_rWW8Export.InsUInt16( NS_sprm::sprmSFEvenlySpaced );
4612 m_rWW8Export.pO->push_back( bEven ? 1 : 0 );
4613
4614 if ( !bEven )
4615 {
4616 for ( sal_uInt16 n = 0; n < nCols; ++n )
4617 {
4618 //sprmSDxaColWidth
4619 m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaColWidth );
4620 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(n) );
4621 m_rWW8Export.InsUInt16( rCol.
4622 CalcPrtColWidth( n,
4623 static_cast<sal_uInt16>(nPageSize) ) );
4624
4625 if ( n + 1 != nCols )
4626 {
4627 //sprmSDxaColSpacing
4628 m_rWW8Export.InsUInt16( NS_sprm::sprmSDxaColSpacing );
4629 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(n) );
4630 m_rWW8Export.InsUInt16( rColumns[n].GetRight( ) +
4631 rColumns[n + 1].GetLeft( ) );
4632 }
4633 }
4634 }
4635 }
4636
FormatColumns(const SwFormatCol & rCol)4637 void AttributeOutputBase::FormatColumns( const SwFormatCol& rCol )
4638 {
4639 const SwColumns& rColumns = rCol.GetColumns();
4640
4641 sal_uInt16 nCols = rColumns.size();
4642 if ( 1 < nCols && !GetExport( ).m_bOutFlyFrameAttrs )
4643 {
4644 // get the page width without borders !!
4645
4646 const SwFrameFormat* pFormat = GetExport( ).m_pCurrentPageDesc ? &GetExport( ).m_pCurrentPageDesc->GetMaster() : &const_cast<const SwDoc *>(GetExport( ).m_pDoc)->GetPageDesc(0).GetMaster();
4647 const SvxFrameDirectionItem &frameDirection = pFormat->GetFrameDir();
4648 SwTwips nPageSize;
4649 if ( frameDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB || frameDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB )
4650 {
4651 const SvxULSpaceItem &rUL = pFormat->GetULSpace();
4652 nPageSize = pFormat->GetFrameSize().GetHeight();
4653 nPageSize -= rUL.GetUpper() + rUL.GetLower();
4654
4655 const SwFormatHeader *header = pFormat->GetAttrSet().GetItem(RES_HEADER);
4656 if ( header )
4657 {
4658 const SwFrameFormat *headerFormat = header->GetHeaderFormat();
4659 if (headerFormat)
4660 {
4661 nPageSize -= headerFormat->GetFrameSize().GetHeight();
4662 }
4663 }
4664 const SwFormatFooter *footer = pFormat->GetAttrSet().GetItem(RES_FOOTER);
4665 if ( footer )
4666 {
4667 const SwFrameFormat *footerFormat = footer->GetFooterFormat();
4668 if ( footerFormat )
4669 {
4670 nPageSize -= footerFormat->GetFrameSize().GetHeight();
4671 }
4672 }
4673 }
4674 else
4675 {
4676 const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
4677 nPageSize = pFormat->GetFrameSize().GetWidth();
4678 nPageSize -= rLR.GetLeft() + rLR.GetRight();
4679 //i120133: The Section width should consider page indent value.
4680 nPageSize -= rCol.GetAdjustValue();
4681
4682 }
4683
4684 // look if all columns are equal
4685 bool bEven = true;
4686 sal_uInt16 n;
4687 sal_uInt16 nColWidth = rCol.CalcPrtColWidth( 0, static_cast<sal_uInt16>(nPageSize) );
4688 for ( n = 1; n < nCols; n++ )
4689 {
4690 short nDiff = nColWidth -
4691 rCol.CalcPrtColWidth( n, static_cast<sal_uInt16>(nPageSize) );
4692
4693 if ( nDiff > 10 || nDiff < -10 ) // Tolerance: 10 tw
4694 {
4695 bEven = false;
4696 break;
4697 }
4698 }
4699
4700 FormatColumns_Impl( nCols, rCol, bEven, nPageSize );
4701 }
4702 }
4703
4704 // "Paragraphs together"
FormatKeep(const SvxFormatKeepItem & rKeep)4705 void WW8AttributeOutput::FormatKeep( const SvxFormatKeepItem& rKeep )
4706 {
4707 // sprmFKeepFollow
4708 m_rWW8Export.InsUInt16( NS_sprm::sprmPFKeepFollow );
4709
4710 m_rWW8Export.pO->push_back( rKeep.GetValue() ? 1 : 0 );
4711 }
4712
4713 // exclude a paragraph from Line Numbering
FormatLineNumbering(const SwFormatLineNumber & rNumbering)4714 void WW8AttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering )
4715 {
4716 // sprmPFNoLineNumb
4717 m_rWW8Export.InsUInt16( NS_sprm::sprmPFNoLineNumb );
4718
4719 m_rWW8Export.pO->push_back( rNumbering.IsCount() ? 0 : 1 );
4720 }
4721
4722 /* File PARATR.HXX */
4723
ParaLineSpacing_Impl(short nSpace,short nMulti)4724 void WW8AttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti )
4725 {
4726 // sprmPDyaLine
4727 m_rWW8Export.InsUInt16( NS_sprm::sprmPDyaLine );
4728
4729 m_rWW8Export.InsUInt16( nSpace );
4730 m_rWW8Export.InsUInt16( nMulti );
4731 }
4732
ParaLineSpacing(const SvxLineSpacingItem & rSpacing)4733 void AttributeOutputBase::ParaLineSpacing( const SvxLineSpacingItem& rSpacing )
4734 {
4735 short nSpace = 240, nMulti = 0;
4736
4737 switch ( rSpacing.GetLineSpaceRule() )
4738 {
4739 default:
4740 break;
4741 case SvxLineSpaceRule::Fix: // Fix
4742 nSpace = -static_cast<short>(rSpacing.GetLineHeight());
4743 break;
4744 case SvxLineSpaceRule::Min: // At least
4745 nSpace = static_cast<short>(rSpacing.GetLineHeight());
4746 break;
4747 case SvxLineSpaceRule::Auto:
4748 {
4749 if( rSpacing.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) // Leading
4750 {
4751 // doesn't exist in WW - how do you get the MaxLineHeight?
4752 nSpace = rSpacing.GetInterLineSpace();
4753 sal_uInt16 nScript =
4754 i18n::ScriptType::LATIN;
4755 const SwAttrSet *pSet = nullptr;
4756 if ( auto pFormat = dynamic_cast< const SwFormat *>( GetExport().m_pOutFormatNode ) )
4757 {
4758 pSet = &pFormat->GetAttrSet();
4759 }
4760 else if ( auto pNd = dynamic_cast< const SwTextNode *>( GetExport().m_pOutFormatNode ) )
4761 {
4762 pSet = &pNd->GetSwAttrSet();
4763 nScript = g_pBreakIt->GetBreakIter()->getScriptType(pNd->GetText(), 0);
4764 }
4765 OSL_ENSURE( pSet, "No attrset for lineheight :-(" );
4766 if ( pSet )
4767 {
4768 nSpace = nSpace + static_cast<short>( AttrSetToLineHeight( GetExport().m_pDoc->getIDocumentSettingAccess(),
4769 *pSet, *Application::GetDefaultDevice(), nScript ) );
4770 }
4771 }
4772 else // Proportional
4773 {
4774 if ( rSpacing.GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off )
4775 nSpace = static_cast<short>( ( 240L * rSpacing.GetPropLineSpace() ) / 100L );
4776 nMulti = 1;
4777 }
4778 }
4779 break;
4780 }
4781 // if nSpace is negative, it is a fixed size in 1/20 of a point
4782 // if nSpace is positive and nMulti is 1, it is 1/240 of a single line height
4783 // otherwise, it is a minimum size in 1/20 of a point
4784 ParaLineSpacing_Impl( nSpace, nMulti );
4785 }
4786
ParaAdjust(const SvxAdjustItem & rAdjust)4787 void WW8AttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust )
4788 {
4789 // sprmPJc
4790 sal_uInt8 nAdj;
4791 sal_uInt8 nAdjBiDi;
4792 switch ( rAdjust.GetAdjust() )
4793 {
4794 case SvxAdjust::Left:
4795 nAdj = 0;
4796 nAdjBiDi = 2;
4797 break;
4798 case SvxAdjust::Right:
4799 nAdj = 2;
4800 nAdjBiDi = 0;
4801 break;
4802 case SvxAdjust::BlockLine:
4803 case SvxAdjust::Block:
4804 nAdj = nAdjBiDi = 3;
4805 break;
4806 case SvxAdjust::Center:
4807 nAdj = nAdjBiDi = 1;
4808 break;
4809 default:
4810 return; // not a supported Attribute
4811 }
4812
4813 m_rWW8Export.InsUInt16(NS_sprm::sprmPJc80);
4814 m_rWW8Export.pO->push_back(nAdj);
4815
4816 /*
4817 Sadly for left to right paragraphs both these values are the same,
4818 for right to left paragraphs the bidi one is the reverse of the
4819 normal one.
4820 */
4821 m_rWW8Export.InsUInt16(NS_sprm::sprmPJc); //bidi version ?
4822 bool bBiDiSwap = false;
4823 if (m_rWW8Export.m_pOutFormatNode)
4824 {
4825 SvxFrameDirection nDirection = SvxFrameDirection::Horizontal_LR_TB;
4826 if (dynamic_cast<const SwTextNode*>(m_rWW8Export.m_pOutFormatNode) != nullptr)
4827 {
4828 SwPosition aPos(*static_cast<const SwContentNode*>(m_rWW8Export.m_pOutFormatNode));
4829 nDirection = m_rWW8Export.m_pDoc->GetTextDirection(aPos);
4830 }
4831 else if (dynamic_cast<const SwTextFormatColl*>(m_rWW8Export.m_pOutFormatNode) != nullptr)
4832 {
4833 const SwTextFormatColl* pC =
4834 static_cast<const SwTextFormatColl*>(m_rWW8Export.m_pOutFormatNode);
4835 const SvxFrameDirectionItem &rItem =
4836 ItemGet<SvxFrameDirectionItem>(*pC, RES_FRAMEDIR);
4837 nDirection = rItem.GetValue();
4838 }
4839 if ( ( nDirection == SvxFrameDirection::Horizontal_RL_TB ) ||
4840 ( nDirection == SvxFrameDirection::Environment && AllSettings::GetLayoutRTL() ) )
4841 {
4842 bBiDiSwap = true;
4843 }
4844 }
4845
4846 if (bBiDiSwap)
4847 m_rWW8Export.pO->push_back(nAdjBiDi);
4848 else
4849 m_rWW8Export.pO->push_back(nAdj);
4850 }
4851
FormatFrameDirection(const SvxFrameDirectionItem & rDirection)4852 void WW8AttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection )
4853 {
4854 sal_uInt16 nTextFlow=0;
4855 bool bBiDi = false;
4856 SvxFrameDirection nDir = rDirection.GetValue();
4857
4858 if ( nDir == SvxFrameDirection::Environment )
4859 nDir = GetExport( ).GetDefaultFrameDirection( );
4860
4861
4862 switch ( nDir )
4863 {
4864 default:
4865 //Can't get an unknown type here
4866 OSL_FAIL("Unknown frame direction");
4867 [[fallthrough]];
4868 case SvxFrameDirection::Horizontal_LR_TB:
4869 nTextFlow = 0;
4870 break;
4871 case SvxFrameDirection::Horizontal_RL_TB:
4872 nTextFlow = 0;
4873 bBiDi = true;
4874 break;
4875 case SvxFrameDirection::Vertical_LR_TB: //word doesn't have this
4876 case SvxFrameDirection::Vertical_RL_TB:
4877 nTextFlow = 1;
4878 break;
4879 }
4880
4881 if ( m_rWW8Export.m_bOutPageDescs )
4882 {
4883 m_rWW8Export.InsUInt16( NS_sprm::sprmSTextFlow );
4884 m_rWW8Export.InsUInt16( nTextFlow );
4885 m_rWW8Export.InsUInt16( NS_sprm::sprmSFBiDi );
4886 m_rWW8Export.pO->push_back( bBiDi ? 1 : 0 );
4887 }
4888 else if ( !m_rWW8Export.m_bOutFlyFrameAttrs ) //paragraph/style
4889 {
4890 m_rWW8Export.InsUInt16( NS_sprm::sprmPFBiDi );
4891 m_rWW8Export.pO->push_back( bBiDi ? 1 : 0 );
4892 }
4893 }
4894
ParaGrabBag(const SfxGrabBagItem &)4895 void WW8AttributeOutput::ParaGrabBag(const SfxGrabBagItem& /*rItem*/)
4896 {
4897 }
4898
CharGrabBag(const SfxGrabBagItem &)4899 void WW8AttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/)
4900 {
4901 }
4902
ParaOutlineLevel(const SfxUInt16Item &)4903 void WW8AttributeOutput::ParaOutlineLevel(const SfxUInt16Item& /*rItem*/)
4904 {
4905 }
4906
4907 // "Separate paragraphs"
ParaSplit(const SvxFormatSplitItem & rSplit)4908 void WW8AttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit )
4909 {
4910 // sprmPFKeep
4911 m_rWW8Export.InsUInt16( NS_sprm::sprmPFKeep );
4912 m_rWW8Export.pO->push_back( rSplit.GetValue() ? 0 : 1 );
4913 }
4914
4915 /**
4916 * Only convert the item "SvxWidowItem" and not the orphans, because
4917 * in WW only one attribute "paragraph control" exists for both and
4918 * in SW probably both or none is set by the user.
4919 */
ParaWidows(const SvxWidowsItem & rWidows)4920 void WW8AttributeOutput::ParaWidows( const SvxWidowsItem& rWidows )
4921 {
4922 // sprmPFWidowControl
4923 m_rWW8Export.InsUInt16( NS_sprm::sprmPFWidowControl );
4924 m_rWW8Export.pO->push_back( rWidows.GetValue() ? 1 : 0 );
4925 }
4926
4927 class SwWW8WrTabu
4928 {
4929 std::unique_ptr<sal_uInt8[]> pDel; // DelArray
4930 std::unique_ptr<sal_uInt8[]> pAddPos; // AddPos-Array
4931 std::unique_ptr<sal_uInt8[]> pAddTyp; // AddTyp-Array
4932 sal_uInt16 nAdd; // number of tabs to be added
4933 sal_uInt16 nDel; // number of tabs to be deleted
4934
4935 SwWW8WrTabu(const SwWW8WrTabu&) = delete;
4936 SwWW8WrTabu& operator=(const SwWW8WrTabu&) = delete;
4937
4938 public:
4939 SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax);
4940
4941 void Add(const SvxTabStop &rTS, long nAdjustment);
4942 void Del(const SvxTabStop &rTS, long nAdjustment);
4943 void PutAll(WW8Export& rWW8Wrt);
4944 };
4945
SwWW8WrTabu(sal_uInt16 nDelMax,sal_uInt16 nAddMax)4946 SwWW8WrTabu::SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax)
4947 : nAdd(0), nDel(0)
4948 {
4949 if (nDelMax)
4950 pDel.reset( new sal_uInt8[nDelMax * 2] );
4951 pAddPos.reset( new sal_uInt8[nAddMax * 2] );
4952 pAddTyp.reset( new sal_uInt8[nAddMax] );
4953 }
4954
4955 /**
4956 * insert a tab in the WW structure
4957 */
Add(const SvxTabStop & rTS,long nAdjustment)4958 void SwWW8WrTabu::Add(const SvxTabStop & rTS, long nAdjustment)
4959 {
4960 // insert tab position
4961 ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment),
4962 pAddPos.get() + (nAdd * 2));
4963
4964 // insert tab type
4965 sal_uInt8 nPara = 0;
4966 switch (rTS.GetAdjustment())
4967 {
4968 case SvxTabAdjust::Right:
4969 nPara = 2;
4970 break;
4971 case SvxTabAdjust::Center:
4972 nPara = 1;
4973 break;
4974 case SvxTabAdjust::Decimal:
4975 /*
4976 There is nothing we can do btw the decimal separator has been
4977 customized, but if you think different remember that different
4978 locales have different separators, i.e. german is a , while english
4979 is a .
4980 */
4981 nPara = 3;
4982 break;
4983 default:
4984 break;
4985 }
4986
4987 switch( rTS.GetFill() )
4988 {
4989 case '.': // dotted leader
4990 nPara |= 1 << 3;
4991 break;
4992 case '_': // Single line leader
4993 nPara |= 3 << 3;
4994 break;
4995 case '-': // hyphenated leader
4996 nPara |= 2 << 3;
4997 break;
4998 case '=': // heavy line leader
4999 nPara |= 4 << 3;
5000 break;
5001 }
5002
5003 pAddTyp[nAdd] = nPara;
5004 ++nAdd;
5005 }
5006
5007 /**
5008 * Insert a to be deleted tab in the WW structure
5009 */
Del(const SvxTabStop & rTS,long nAdjustment)5010 void SwWW8WrTabu::Del(const SvxTabStop &rTS, long nAdjustment)
5011 {
5012 // insert tab position
5013 ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment),
5014 pDel.get() + (nDel * 2));
5015 ++nDel;
5016 }
5017
5018 /**
5019 * Writes the attribute to rWrt.pO
5020 */
PutAll(WW8Export & rWrt)5021 void SwWW8WrTabu::PutAll(WW8Export& rWrt)
5022 {
5023 if (!nAdd && !nDel) //If it's a no-op
5024 return;
5025 OSL_ENSURE(nAdd <= 255, "more than 255 added tabstops?");
5026 OSL_ENSURE(nDel <= 255, "more than 244 removed tabstops?");
5027 if (nAdd > 255)
5028 nAdd = 255;
5029 if (nDel > 255)
5030 nDel = 255;
5031
5032 sal_uInt16 nSiz = 2 * nDel + 3 * nAdd + 2;
5033 if (nSiz > 255)
5034 nSiz = 255;
5035
5036 rWrt.InsUInt16(NS_sprm::sprmPChgTabsPapx);
5037 // insert cch
5038 rWrt.pO->push_back(msword_cast<sal_uInt8>(nSiz));
5039 // write DelArr
5040 rWrt.pO->push_back(msword_cast<sal_uInt8>(nDel));
5041 rWrt.OutSprmBytes(pDel.get(), nDel * 2);
5042 // write InsArr
5043 rWrt.pO->push_back(msword_cast<sal_uInt8>(nAdd));
5044 rWrt.OutSprmBytes(pAddPos.get(), 2 * nAdd); // AddPosArray
5045 rWrt.OutSprmBytes(pAddTyp.get(), nAdd); // AddTypArray
5046 }
5047
ParaTabStopAdd(WW8Export & rWrt,const SvxTabStopItem & rTStops,const long nLParaMgn)5048 static void ParaTabStopAdd( WW8Export& rWrt,
5049 const SvxTabStopItem& rTStops,
5050 const long nLParaMgn )
5051 {
5052 SwWW8WrTabu aTab( 0, rTStops.Count());
5053
5054 for( sal_uInt16 n = 0; n < rTStops.Count(); n++ )
5055 {
5056 const SvxTabStop& rTS = rTStops[n];
5057 // ignore default tabs
5058 if (SvxTabAdjust::Default != rTS.GetAdjustment())
5059 aTab.Add(rTS, nLParaMgn);
5060 }
5061 aTab.PutAll( rWrt );
5062 }
5063
lcl_IsEqual(long nOneLeft,const SvxTabStop & rOne,long nTwoLeft,const SvxTabStop & rTwo)5064 static bool lcl_IsEqual(long nOneLeft, const SvxTabStop &rOne,
5065 long nTwoLeft, const SvxTabStop &rTwo)
5066 {
5067 return(
5068 nOneLeft == nTwoLeft &&
5069 rOne.GetAdjustment() == rTwo.GetAdjustment() &&
5070 rOne.GetDecimal() == rTwo.GetDecimal() &&
5071 rOne.GetFill() == rTwo.GetFill()
5072 );
5073 }
5074
ParaTabStopDelAdd(WW8Export & rWrt,const SvxTabStopItem & rTStyle,const long nLStypeMgn,const SvxTabStopItem & rTNew,const long nLParaMgn)5075 static void ParaTabStopDelAdd( WW8Export& rWrt,
5076 const SvxTabStopItem& rTStyle,
5077 const long nLStypeMgn,
5078 const SvxTabStopItem& rTNew,
5079 const long nLParaMgn )
5080 {
5081 SwWW8WrTabu aTab(rTStyle.Count(), rTNew.Count());
5082
5083 sal_uInt16 nO = 0; // rTStyle Index
5084 sal_uInt16 nN = 0; // rTNew Index
5085
5086 do {
5087 const SvxTabStop* pTO;
5088 long nOP;
5089 if( nO < rTStyle.Count() ) // old not yet at the end?
5090 {
5091 pTO = &rTStyle[ nO ];
5092 nOP = pTO->GetTabPos() + nLStypeMgn;
5093 if( SvxTabAdjust::Default == pTO->GetAdjustment() )
5094 {
5095 nO++; // ignore default tab
5096 continue;
5097 }
5098 }
5099 else
5100 {
5101 pTO = nullptr;
5102 nOP = LONG_MAX;
5103 }
5104
5105 const SvxTabStop* pTN;
5106 long nNP;
5107 if( nN < rTNew.Count() ) // new not yet at the end
5108 {
5109 pTN = &rTNew[ nN ];
5110 nNP = pTN->GetTabPos() + nLParaMgn;
5111 if( SvxTabAdjust::Default == pTN->GetAdjustment() )
5112 {
5113 nN++; // ignore default tab
5114 continue;
5115 }
5116 }
5117 else
5118 {
5119 pTN = nullptr;
5120 nNP = LONG_MAX;
5121 }
5122
5123 if( nOP == LONG_MAX && nNP == LONG_MAX )
5124 break; // everything done
5125
5126 if( nOP < nNP ) // next tab is old
5127 {
5128 assert(pTO);
5129 aTab.Del(*pTO, nLStypeMgn); // must be deleted
5130 nO++;
5131 }
5132 else if( nNP < nOP ) // next tab is new
5133 {
5134 assert(pTN);
5135 aTab.Add(*pTN, nLParaMgn); // must be inserted
5136 nN++;
5137 }
5138 else if (lcl_IsEqual(nOP, *pTO, nNP, *pTN)) // tabs are equal
5139 {
5140 nO++; // nothing to do
5141 nN++;
5142 }
5143 else // tabs same position, different type
5144 {
5145 aTab.Del(*pTO, nLStypeMgn); // delete old one
5146 aTab.Add(*pTN, nLParaMgn); // insert new one
5147 nO++;
5148 nN++;
5149 }
5150 } while( true );
5151
5152 aTab.PutAll( rWrt );
5153 }
5154
ParaTabStop(const SvxTabStopItem & rTabStops)5155 void WW8AttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStops )
5156 {
5157 const bool bTabsRelativeToIndex = m_rWW8Export.m_pCurPam->GetDoc()->getIDocumentSettingAccess().get( DocumentSettingId::TABS_RELATIVE_TO_INDENT );
5158
5159 long nCurrentLeft = 0;
5160 if ( bTabsRelativeToIndex )
5161 {
5162 const SfxPoolItem* pLR = m_rWW8Export.HasItem( RES_LR_SPACE );
5163
5164 if ( pLR != nullptr )
5165 nCurrentLeft = static_cast<const SvxLRSpaceItem*>(pLR)->GetTextLeft();
5166 }
5167
5168 // #i100264#
5169 if ( m_rWW8Export.m_bStyDef &&
5170 m_rWW8Export.m_pCurrentStyle != nullptr &&
5171 m_rWW8Export.m_pCurrentStyle->DerivedFrom() != nullptr )
5172 {
5173 SvxTabStopItem aParentTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
5174 const SwFormat *pParentStyle = m_rWW8Export.m_pCurrentStyle->DerivedFrom();
5175 {
5176 if (const SvxTabStopItem* pParentTabs = pParentStyle->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP))
5177 {
5178 aParentTabs.Insert( pParentTabs );
5179 }
5180 }
5181
5182 // #i120938# - consider left indentation of style and its parent style
5183 long nParentLeft = 0;
5184 if ( bTabsRelativeToIndex )
5185 {
5186 const SvxLRSpaceItem &rStyleLR = ItemGet<SvxLRSpaceItem>( pParentStyle->GetAttrSet(), RES_LR_SPACE );
5187 nParentLeft = rStyleLR.GetTextLeft();
5188 }
5189
5190 ParaTabStopDelAdd( m_rWW8Export, aParentTabs, nParentLeft, rTabStops, nCurrentLeft );
5191 return;
5192 }
5193
5194 const SvxTabStopItem* pStyleTabs = nullptr;
5195 if ( !m_rWW8Export.m_bStyDef && m_rWW8Export.m_pStyAttr )
5196 {
5197 pStyleTabs = m_rWW8Export.m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
5198 }
5199
5200 if ( !pStyleTabs )
5201 {
5202 ParaTabStopAdd(m_rWW8Export, rTabStops, nCurrentLeft);
5203 }
5204 else
5205 {
5206 long nStyleLeft = 0;
5207 if ( bTabsRelativeToIndex )
5208 {
5209 const SvxLRSpaceItem &rStyleLR = ItemGet<SvxLRSpaceItem>(*m_rWW8Export.m_pStyAttr, RES_LR_SPACE);
5210 nStyleLeft = rStyleLR.GetTextLeft();
5211 }
5212
5213 ParaTabStopDelAdd( m_rWW8Export,
5214 *pStyleTabs, nStyleLeft,
5215 rTabStops, nCurrentLeft);
5216 }
5217 }
5218
OutputItem(const SfxPoolItem & rHt)5219 void AttributeOutputBase::OutputItem( const SfxPoolItem& rHt )
5220 {
5221 // FIXME maybe use 'item_cast', like 'item_cast<SvxCharHiddenItem>( rHt )'?
5222 switch ( rHt.Which() )
5223 {
5224 case RES_CHRATR_CASEMAP:
5225 CharCaseMap( static_cast< const SvxCaseMapItem& >( rHt ) );
5226 break;
5227 case RES_CHRATR_COLOR:
5228 CharColor( static_cast< const SvxColorItem& >( rHt ) );
5229 break;
5230 case RES_CHRATR_CONTOUR:
5231 CharContour( static_cast< const SvxContourItem& >( rHt ) );
5232 break;
5233 case RES_CHRATR_CROSSEDOUT:
5234 CharCrossedOut( static_cast< const SvxCrossedOutItem& >( rHt ) );
5235 break;
5236 case RES_CHRATR_ESCAPEMENT:
5237 CharEscapement( static_cast< const SvxEscapementItem& >( rHt ) );
5238 break;
5239 case RES_CHRATR_FONT:
5240 CharFont( static_cast< const SvxFontItem& >( rHt ) );
5241 break;
5242 case RES_CHRATR_FONTSIZE:
5243 CharFontSize( static_cast< const SvxFontHeightItem& >( rHt ) );
5244 break;
5245 case RES_CHRATR_KERNING:
5246 CharKerning( static_cast< const SvxKerningItem& >( rHt ) );
5247 break;
5248 case RES_CHRATR_LANGUAGE:
5249 CharLanguage( static_cast< const SvxLanguageItem& >( rHt ) );
5250 break;
5251 case RES_CHRATR_POSTURE:
5252 CharPosture( static_cast< const SvxPostureItem& >( rHt ) );
5253 break;
5254 case RES_CHRATR_SHADOWED:
5255 CharShadow( static_cast< const SvxShadowedItem& >( rHt ) );
5256 break;
5257 case RES_CHRATR_UNDERLINE:
5258 CharUnderline( static_cast< const SvxUnderlineItem& >( rHt ) );
5259 break;
5260 case RES_CHRATR_WEIGHT:
5261 CharWeight( static_cast< const SvxWeightItem& >( rHt ) );
5262 break;
5263 case RES_CHRATR_AUTOKERN:
5264 CharAutoKern( static_cast< const SvxAutoKernItem& >( rHt ) );
5265 break;
5266 case RES_CHRATR_BLINK:
5267 CharAnimatedText( static_cast< const SvxBlinkItem& >( rHt ) );
5268 break;
5269 case RES_CHRATR_BACKGROUND:
5270 CharBackgroundBase( static_cast< const SvxBrushItem& >( rHt ) );
5271 break;
5272
5273 case RES_CHRATR_CJK_FONT:
5274 CharFontCJK( static_cast< const SvxFontItem& >( rHt ) );
5275 break;
5276 case RES_CHRATR_CJK_FONTSIZE:
5277 CharFontSizeCJK( static_cast< const SvxFontHeightItem& >( rHt ) );
5278 break;
5279 case RES_CHRATR_CJK_LANGUAGE:
5280 CharLanguageCJK( static_cast< const SvxLanguageItem& >( rHt ) );
5281 break;
5282 case RES_CHRATR_CJK_POSTURE:
5283 CharPostureCJK( static_cast< const SvxPostureItem& >( rHt ) );
5284 break;
5285 case RES_CHRATR_CJK_WEIGHT:
5286 CharWeightCJK( static_cast< const SvxWeightItem& >( rHt ) );
5287 break;
5288
5289 case RES_CHRATR_CTL_FONT:
5290 CharFontCTL( static_cast< const SvxFontItem& >( rHt ) );
5291 break;
5292 case RES_CHRATR_CTL_FONTSIZE:
5293 CharFontSizeCTL( static_cast< const SvxFontHeightItem& >( rHt ) );
5294 break;
5295 case RES_CHRATR_CTL_LANGUAGE:
5296 CharLanguageCTL( static_cast< const SvxLanguageItem& >( rHt ) );
5297 break;
5298 case RES_CHRATR_CTL_POSTURE:
5299 CharPostureCTL( static_cast< const SvxPostureItem& >( rHt ) );
5300 break;
5301 case RES_CHRATR_CTL_WEIGHT:
5302 CharWeightCTL( static_cast< const SvxWeightItem& >( rHt ) );
5303 break;
5304
5305 case RES_CHRATR_ROTATE:
5306 CharRotate( static_cast< const SvxCharRotateItem& >( rHt ) );
5307 break;
5308 case RES_CHRATR_EMPHASIS_MARK:
5309 CharEmphasisMark( static_cast< const SvxEmphasisMarkItem& >( rHt ) );
5310 break;
5311 case RES_CHRATR_TWO_LINES:
5312 CharTwoLines( static_cast< const SvxTwoLinesItem& >( rHt ) );
5313 break;
5314 case RES_CHRATR_SCALEW:
5315 CharScaleWidth( static_cast< const SvxCharScaleWidthItem& >( rHt ) );
5316 break;
5317 case RES_CHRATR_RELIEF:
5318 CharRelief( static_cast< const SvxCharReliefItem& >( rHt ) );
5319 break;
5320 case RES_CHRATR_HIDDEN:
5321 CharHidden( static_cast< const SvxCharHiddenItem& >( rHt ) );
5322 break;
5323 case RES_CHRATR_BOX:
5324 FormatCharBorder( static_cast< const SvxBoxItem& >( rHt ) );
5325 break;
5326 case RES_CHRATR_HIGHLIGHT:
5327 CharHighlight( static_cast< const SvxBrushItem& >( rHt ) );
5328 break;
5329 case RES_CHRATR_BIDIRTL:
5330 CharBidiRTL( rHt );
5331 break;
5332 case RES_CHRATR_IDCTHINT:
5333 CharIdctHint( rHt );
5334 break;
5335 case RES_TXTATR_INETFMT:
5336 TextINetFormat( static_cast< const SwFormatINetFormat& >( rHt ) );
5337 break;
5338 case RES_TXTATR_CHARFMT:
5339 TextCharFormat( static_cast< const SwFormatCharFormat& >( rHt ) );
5340 break;
5341
5342 case RES_TXTATR_FIELD:
5343 case RES_TXTATR_ANNOTATION:
5344 case RES_TXTATR_INPUTFIELD:
5345 TextField( static_cast< const SwFormatField& >( rHt ) );
5346 break;
5347
5348 case RES_TXTATR_FLYCNT:
5349 TextFlyContent( static_cast< const SwFormatFlyCnt& >( rHt ) );
5350 break;
5351 case RES_TXTATR_FTN:
5352 TextFootnote( static_cast< const SwFormatFootnote& >( rHt ) );
5353 break;
5354
5355 case RES_PARATR_LINESPACING:
5356 ParaLineSpacing( static_cast< const SvxLineSpacingItem& >( rHt ) );
5357 break;
5358 case RES_PARATR_ADJUST:
5359 ParaAdjust( static_cast< const SvxAdjustItem& >( rHt ) );
5360 break;
5361 case RES_PARATR_SPLIT:
5362 ParaSplit( static_cast< const SvxFormatSplitItem& >( rHt ) );
5363 break;
5364 case RES_PARATR_WIDOWS:
5365 ParaWidows( static_cast< const SvxWidowsItem& >( rHt ) );
5366 break;
5367 case RES_PARATR_TABSTOP:
5368 ParaTabStop( static_cast< const SvxTabStopItem& >( rHt ) );
5369 break;
5370 case RES_PARATR_HYPHENZONE:
5371 ParaHyphenZone( static_cast< const SvxHyphenZoneItem& >( rHt ) );
5372 break;
5373 case RES_PARATR_NUMRULE:
5374 ParaNumRule( static_cast< const SwNumRuleItem& >( rHt ) );
5375 break;
5376 case RES_PARATR_SCRIPTSPACE:
5377 ParaScriptSpace( static_cast< const SfxBoolItem& >( rHt ) );
5378 break;
5379 case RES_PARATR_HANGINGPUNCTUATION:
5380 ParaHangingPunctuation( static_cast< const SfxBoolItem& >( rHt ) );
5381 break;
5382 case RES_PARATR_FORBIDDEN_RULES:
5383 ParaForbiddenRules( static_cast< const SfxBoolItem& >( rHt ) );
5384 break;
5385 case RES_PARATR_VERTALIGN:
5386 ParaVerticalAlign( static_cast< const SvxParaVertAlignItem& >( rHt ) );
5387 break;
5388 case RES_PARATR_SNAPTOGRID:
5389 ParaSnapToGrid( static_cast< const SvxParaGridItem& >( rHt ) );
5390 break;
5391
5392 case RES_FRM_SIZE:
5393 FormatFrameSize( static_cast< const SwFormatFrameSize& >( rHt ) );
5394 break;
5395 case RES_PAPER_BIN:
5396 FormatPaperBin( static_cast< const SvxPaperBinItem& >( rHt ) );
5397 break;
5398 case RES_LR_SPACE:
5399 FormatLRSpace( static_cast< const SvxLRSpaceItem& >( rHt ) );
5400 break;
5401 case RES_UL_SPACE:
5402 FormatULSpace( static_cast< const SvxULSpaceItem& >( rHt ) );
5403 break;
5404 case RES_PAGEDESC:
5405 FormatPageDescription( static_cast< const SwFormatPageDesc& >( rHt ) );
5406 break;
5407 case RES_BREAK:
5408 FormatBreak( static_cast< const SvxFormatBreakItem& >( rHt ) );
5409 break;
5410 case RES_SURROUND:
5411 FormatSurround( static_cast< const SwFormatSurround& >( rHt ) );
5412 break;
5413 case RES_VERT_ORIENT:
5414 FormatVertOrientation( static_cast< const SwFormatVertOrient& >( rHt ) );
5415 break;
5416 case RES_HORI_ORIENT:
5417 FormatHorizOrientation( static_cast< const SwFormatHoriOrient& >( rHt ) );
5418 break;
5419 case RES_ANCHOR:
5420 FormatAnchor( static_cast< const SwFormatAnchor& >( rHt ) );
5421 break;
5422 case RES_BACKGROUND:
5423 FormatBackground( static_cast< const SvxBrushItem& >( rHt ) );
5424 break;
5425 case XATTR_FILLSTYLE:
5426 FormatFillStyle( static_cast< const XFillStyleItem& >( rHt ) );
5427 break;
5428 case XATTR_FILLGRADIENT:
5429 FormatFillGradient( static_cast< const XFillGradientItem& >( rHt ) );
5430 break;
5431 case RES_BOX:
5432 FormatBox( static_cast< const SvxBoxItem& >( rHt ) );
5433 break;
5434 case RES_COL:
5435 FormatColumns( static_cast< const SwFormatCol& >( rHt ) );
5436 break;
5437 case RES_KEEP:
5438 FormatKeep( static_cast< const SvxFormatKeepItem& >( rHt ) );
5439 break;
5440 case RES_TEXTGRID:
5441 FormatTextGrid( static_cast< const SwTextGridItem& >( rHt ) );
5442 break;
5443 case RES_LINENUMBER:
5444 FormatLineNumbering( static_cast< const SwFormatLineNumber& >( rHt ) );
5445 break;
5446 case RES_FRAMEDIR:
5447 FormatFrameDirection( static_cast< const SvxFrameDirectionItem& >( rHt ) );
5448 break;
5449 case RES_PARATR_GRABBAG:
5450 ParaGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
5451 break;
5452 case RES_PARATR_OUTLINELEVEL:
5453 ParaOutlineLevel(static_cast<const SfxUInt16Item&>(rHt));
5454 break;
5455 case RES_CHRATR_GRABBAG:
5456 CharGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
5457 break;
5458
5459 default:
5460 SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() );
5461 break;
5462 }
5463 }
5464
OutputStyleItemSet(const SfxItemSet & rSet,bool bTestForDefault)5465 void AttributeOutputBase::OutputStyleItemSet( const SfxItemSet& rSet, bool bTestForDefault )
5466 {
5467 // based on OutputItemSet() from wrt_fn.cxx
5468
5469 const SfxItemPool& rPool = *rSet.GetPool();
5470 const SfxItemSet* pSet = &rSet;
5471 if ( !pSet->Count() )
5472 {
5473 while ( nullptr != ( pSet = pSet->GetParent() ) && !pSet->Count() )
5474 ;
5475
5476 if ( !pSet )
5477 return;
5478 }
5479
5480 const SfxPoolItem* pItem;
5481 if ( !pSet->GetParent() )
5482 {
5483 assert(rSet.Count() && "Was already handled or?");
5484 SfxItemIter aIter( *pSet );
5485 pItem = aIter.GetCurItem();
5486 do {
5487 OutputItem( *pItem );
5488 } while ((pItem = aIter.NextItem()));
5489 }
5490 else
5491 {
5492 SfxWhichIter aIter( *pSet );
5493 sal_uInt16 nWhich = aIter.FirstWhich();
5494 while ( nWhich )
5495 {
5496 if ( SfxItemState::SET == pSet->GetItemState( nWhich, true/*bDeep*/, &pItem ) &&
5497 ( !bTestForDefault ||
5498 nWhich == RES_UL_SPACE ||
5499 nWhich == RES_LR_SPACE ||
5500 *pItem != rPool.GetDefaultItem( nWhich ) ||
5501 ( pSet->GetParent() && *pItem != pSet->GetParent()->Get( nWhich ) ) ) )
5502 {
5503 OutputItem( *pItem );
5504 }
5505 nWhich = aIter.NextWhich();
5506 }
5507 }
5508 }
5509
FormatCharBorder(const SvxBoxItem & rBox)5510 void AttributeOutputBase::FormatCharBorder( const SvxBoxItem& rBox )
5511 {
5512 // Get one of the borders (if there is any border then in docx also will be)
5513 const SvxBorderLine* pBorderLine = nullptr;
5514 sal_uInt16 nDist = 0;
5515 if( rBox.GetTop() )
5516 {
5517 pBorderLine = rBox.GetTop();
5518 nDist = rBox.GetDistance( SvxBoxItemLine::TOP );
5519 }
5520 else if( rBox.GetLeft() )
5521 {
5522 pBorderLine = rBox.GetLeft();
5523 nDist = rBox.GetDistance( SvxBoxItemLine::LEFT );
5524 }
5525 else if( rBox.GetBottom() )
5526 {
5527 pBorderLine = rBox.GetBottom();
5528 nDist = rBox.GetDistance( SvxBoxItemLine::BOTTOM );
5529 }
5530 else if( rBox.GetRight() )
5531 {
5532 pBorderLine = rBox.GetRight();
5533 nDist = rBox.GetDistance( SvxBoxItemLine::RIGHT );
5534 }
5535
5536 if( pBorderLine )
5537 {
5538 const SfxPoolItem* pItem = GetExport().HasItem( RES_CHRATR_SHADOW );
5539 const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
5540 const bool bShadow =
5541 pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE &&
5542 pShadowItem->GetWidth() > 0;
5543
5544 CharBorder( pBorderLine, nDist, bShadow );
5545 }
5546 }
5547
5548 /*
5549 * This function is used to check if the current SwTextNode (paragraph) has a redline object
5550 * that is attached to the paragraph marker.
5551 * This is done by checking if the range (SwPaM) of the redline is :
5552 * - Start = the last character of the current paragraph
5553 * - End = the first character of the next paragraph
5554 */
GetParagraphMarkerRedline(const SwTextNode & rNode,RedlineType aRedlineType)5555 const SwRedlineData* AttributeOutputBase::GetParagraphMarkerRedline( const SwTextNode& rNode, RedlineType aRedlineType)
5556 {
5557 // ToDo : this is not the most ideal ... should start maybe from 'nCurRedlinePos'
5558 for(SwRangeRedline* pRedl : GetExport().m_pDoc->getIDocumentRedlineAccess().GetRedlineTable())
5559 {
5560 // Only check redlines that are of type 'Delete'
5561 if ( pRedl->GetRedlineData().GetType() != aRedlineType )
5562 continue;
5563
5564 sal_uLong uStartNodeIndex = pRedl->Start()->nNode.GetIndex();
5565 sal_uLong uEndNodeIndex = pRedl->End()->nNode.GetIndex();
5566 sal_uLong uNodeIndex = rNode.GetIndex();
5567
5568 if( uStartNodeIndex <= uNodeIndex && uNodeIndex < uEndNodeIndex )
5569 return &( pRedl->GetRedlineData() );
5570 }
5571 return nullptr;
5572 }
5573
CharBackgroundBase(const SvxBrushItem & rBrush)5574 void AttributeOutputBase::CharBackgroundBase( const SvxBrushItem& rBrush )
5575 {
5576 bool bConvertToShading = SvtFilterOptions::Get().IsCharBackground2Shading();
5577 bool bHasShadingMarker = false;
5578
5579 // Check shading marker
5580 const SfxPoolItem* pItem = GetExport().HasItem(RES_CHRATR_GRABBAG);
5581 if( pItem )
5582 {
5583 const SfxGrabBagItem aGrabBag = static_cast< const SfxGrabBagItem& >(*pItem);
5584 const std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
5585 auto aIterator = rMap.find("CharShadingMarker");
5586 if( aIterator != rMap.end() )
5587 {
5588 aIterator->second >>= bHasShadingMarker;
5589 }
5590 }
5591
5592 if( bConvertToShading || bHasShadingMarker )
5593 {
5594 CharBackground(rBrush);
5595 }
5596 else
5597 {
5598 CharHighlight(rBrush);
5599 }
5600 }
5601
5602 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
5603