android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / ww8 / ww8atr.cxx
bloba794a5d05a59cad451897b32e1ed0deb2d620869
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 * This file contains methods for the WW8 output
22 * (nodes, attributes, formats and chars).
26 #include <algorithm>
27 #include <hintids.hxx>
29 #include <o3tl/safeint.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/settings.hxx>
32 #include <sal/log.hxx>
34 #include <svl/numformat.hxx>
35 #include <svl/zformat.hxx>
36 #include <svl/itemiter.hxx>
37 #include <svl/whiter.hxx>
38 #include <svl/grabbagitem.hxx>
39 #include <editeng/fontitem.hxx>
40 #include <editeng/tstpitem.hxx>
41 #include <editeng/adjustitem.hxx>
42 #include <editeng/spltitem.hxx>
43 #include <editeng/widwitem.hxx>
44 #include <editeng/lspcitem.hxx>
45 #include <editeng/keepitem.hxx>
46 #include <editeng/shaditem.hxx>
47 #include <editeng/brushitem.hxx>
48 #include <editeng/postitem.hxx>
49 #include <editeng/wghtitem.hxx>
50 #include <editeng/kernitem.hxx>
51 #include <editeng/crossedoutitem.hxx>
52 #include <editeng/cmapitem.hxx>
53 #include <editeng/wrlmitem.hxx>
54 #include <editeng/udlnitem.hxx>
55 #include <editeng/langitem.hxx>
56 #include <editeng/escapementitem.hxx>
57 #include <editeng/fhgtitem.hxx>
58 #include <editeng/colritem.hxx>
59 #include <editeng/hyphenzoneitem.hxx>
60 #include <editeng/formatbreakitem.hxx>
61 #include <editeng/lrspitem.hxx>
62 #include <editeng/ulspitem.hxx>
63 #include <editeng/boxitem.hxx>
64 #include <editeng/contouritem.hxx>
65 #include <editeng/shdditem.hxx>
66 #include <editeng/autokernitem.hxx>
67 #include <editeng/pbinitem.hxx>
68 #include <editeng/emphasismarkitem.hxx>
69 #include <editeng/twolinesitem.hxx>
70 #include <editeng/charscaleitem.hxx>
71 #include <editeng/charrotateitem.hxx>
72 #include <editeng/charreliefitem.hxx>
73 #include <editeng/paravertalignitem.hxx>
74 #include <editeng/pgrditem.hxx>
75 #include <editeng/frmdiritem.hxx>
76 #include <editeng/blinkitem.hxx>
77 #include <editeng/charhiddenitem.hxx>
78 #include <editeng/paperinf.hxx>
79 #include <svx/xfillit0.hxx>
80 #include <svx/xflgrit.hxx>
81 #include <o3tl/string_view.hxx>
82 #include <fmtfld.hxx>
83 #include <fchrfmt.hxx>
84 #include <fmtfsize.hxx>
85 #include <fmtpdsc.hxx>
86 #include <fmtornt.hxx>
87 #include <fmtanchr.hxx>
88 #include <fmtclds.hxx>
89 #include <fmtsrnd.hxx>
90 #include <fmtftn.hxx>
91 #include <fmtflcnt.hxx>
92 #include <frmatr.hxx>
93 #include <swtable.hxx>
94 #include <fmtinfmt.hxx>
95 #include <txtftn.hxx>
96 #include <poolfmt.hxx>
97 #include <doc.hxx>
98 #include <IDocumentSettingAccess.hxx>
99 #include <IDocumentFieldsAccess.hxx>
100 #include <IDocumentStylePoolAccess.hxx>
101 #include <IDocumentListsAccess.hxx>
102 #include <list.hxx>
103 #include <docary.hxx>
104 #include <pam.hxx>
105 #include <paratr.hxx>
106 #include <fldbas.hxx>
107 #include <docufld.hxx>
108 #include <expfld.hxx>
109 #include <pagedesc.hxx>
110 #include <ndtxt.hxx>
111 #include <swrect.hxx>
112 #include <redline.hxx>
113 #include <reffld.hxx>
114 #include <ftninfo.hxx>
115 #include <charfmt.hxx>
116 #include <section.hxx>
117 #include <fmtline.hxx>
118 #include <tox.hxx>
119 #include <fmtftntx.hxx>
120 #include <breakit.hxx>
121 #include <com/sun/star/i18n/ScriptType.hpp>
122 #include <com/sun/star/i18n/XBreakIterator.hpp>
123 #include <unotools/localedatawrapper.hxx>
124 #include <svx/unobrushitemhelper.hxx>
125 #include <tgrditem.hxx>
126 #include <flddropdown.hxx>
127 #include <chpfld.hxx>
128 #include <fmthdft.hxx>
129 #include <authfld.hxx>
130 #include <dbfld.hxx>
132 #include "sprmids.hxx"
134 #include <fmtcntnt.hxx>
135 #include "writerhelper.hxx"
136 #include "writerwordglue.hxx"
137 #include "wrtww8.hxx"
138 #include "ww8par.hxx"
139 #include "ww8attributeoutput.hxx"
140 #include "fields.hxx"
141 #include <i18nlangtag/mslangid.hxx>
142 #include <i18nlangtag/languagetag.hxx>
143 #include <unotools/fltrcfg.hxx>
146 using ::editeng::SvxBorderLine;
147 using namespace ::com::sun::star;
148 using namespace nsSwDocInfoSubType;
149 using namespace sw::util;
150 using namespace sw::types;
152 bool WW8Export::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich )
154 bool bRet = true;
155 if ( nScript == i18n::ScriptType::ASIAN )
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 )
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;
178 else if ( nScript == i18n::ScriptType::COMPLEX )
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
184 else
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 )
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;
207 return bRet;
211 void MSWordExportBase::ExportPoolItemsToCHP( ww8::PoolItems &rItems, sal_uInt16 nScript, const SvxFontItem *pFont, bool bWriteCombChars )
213 for ( const auto& rItem : rItems )
215 const SfxPoolItem *pItem = rItem.second;
216 sal_uInt16 nWhich = pItem->Which();
217 if ( ( isCHRATR( nWhich ) || isTXTATR( nWhich ) ) && CollapseScriptsforWordOk( nScript, nWhich ) )
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)
226 const SfxPoolItem* pINetItem = SearchPoolItems(rItems, RES_TXTATR_INETFMT);
228 if (pINetItem)
230 const SwFormatINetFormat& rINet = pINetItem->StaticWhichCast(RES_TXTATR_INETFMT);
231 const SwCharFormat* pINetFormat = GetSwCharFormat(rINet, m_rDoc);
232 if (!pINetFormat)
233 continue;
235 const SwCharFormat* pFormat = pItem->StaticWhichCast(RES_TXTATR_CHARFMT).GetCharFormat();
236 ww8::PoolItems aCharItems, aINetItems;
237 GetPoolItems(pFormat->GetAttrSet(), aCharItems, false);
238 GetPoolItems(pINetFormat->GetAttrSet(), aINetItems, false);
239 for (const auto& rCharItem : aCharItems)
241 const SfxPoolItem* pCharItem = rCharItem.second;
242 sal_uInt16 nCharWhich = pCharItem->Which();
243 if (!SearchPoolItems(aINetItems, nCharWhich) && !SearchPoolItems(rItems, nCharWhich))
244 AttrOutput().OutputItem(*pCharItem);
246 continue;
250 // tdf#38778 Fix output of the font in DOC run for fields
251 if (pFont &&
252 nWhich == RES_TXTATR_FIELD)
254 AttrOutput().OutputItem( *pFont );
257 // tdf#66401 For Combined Characters in docx, MS Word uses half the normal font-size for the field's
258 // font-size, but only for <w:sz>. Therefore, we check if we are currently writing a field of type
259 // Combined Characters and if so, we half the font size.
260 if (bWriteCombChars &&
261 nWhich == RES_CHRATR_FONTSIZE)
263 SvxFontHeightItem fontHeight(item_cast<SvxFontHeightItem>( *pItem ));
264 fontHeight.SetHeight( fontHeight.GetHeight() / 2 );
266 AttrOutput().OutputItem( fontHeight );
268 else if (nWhich == RES_CHRATR_COLOR)
270 const SvxColorItem& rColor = pItem->StaticWhichCast(RES_CHRATR_COLOR);
271 const SfxPoolItem* pBackgroundItem = SearchPoolItems(rItems, RES_CHRATR_BACKGROUND);
272 if (rColor.GetValue() == COL_AUTO && pBackgroundItem)
274 const SvxBrushItem& rBrushBackground = pBackgroundItem->StaticWhichCast(RES_CHRATR_BACKGROUND);
275 SvxColorItem aForeground(rBrushBackground.GetColor().IsDark() ? COL_WHITE : COL_BLACK, RES_CHRATR_COLOR);
276 AttrOutput().OutputItem(aForeground);
278 else
280 // default
281 AttrOutput().OutputItem( *pItem );
284 else if (nWhich == RES_CHRATR_HIGHLIGHT)
286 const SvxBrushItem& rBrush = pItem->StaticWhichCast(RES_CHRATR_HIGHLIGHT);
287 // The UI easily adds unnecessary highlights, so identify and avoid exporting those.
288 // Highlight is not valid in character styles, so must not check there.
289 // Check the (para) style hierarchy to find the nearest defined highlight.
290 const SfxPoolItem* pInherited = nullptr;
291 if ( auto pNd = dynamic_cast< const SwContentNode *>( m_pOutFormatNode ) ) //paragraph
292 pInherited = static_cast<SwTextFormatColl&>( pNd->GetAnyFormatColl() ).GetAttrSet().GetItem(nWhich);
293 else if ( m_bStyDef && m_pCurrentStyle && m_pCurrentStyle->DerivedFrom() ) //style
294 pInherited = &m_pCurrentStyle->DerivedFrom()->GetFormatAttr(nWhich);
296 // Ignore highlight if style already sets the same one.
297 // Also ignore a transparent highlight if there is no inherited highlight to cancel
298 if ( (pInherited && *pInherited != *pItem) || (!pInherited && rBrush.GetColor() != COL_TRANSPARENT) )
299 AttrOutput().OutputItem( *pItem );
301 else
303 AttrOutput().OutputItem( *pItem );
310 * Output format as follows:
311 * - output the attributes; without parents!
314 void MSWordExportBase::OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript,
315 bool bExportParentItemSet )
317 if( !(bExportParentItemSet || rSet.Count()) )
318 return;
320 m_pISet = &rSet; // for double attributes
322 // If frame dir is set, but not adjust, then force adjust as well
323 if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_FRAMEDIR, bExportParentItemSet ) )
325 // No explicit adjust set ?
326 if ( SfxItemState::SET != rSet.GetItemState( RES_PARATR_ADJUST, bExportParentItemSet ) )
328 const SvxAdjustItem* pItem = rSet.GetItem( RES_PARATR_ADJUST, bExportParentItemSet );
329 if ( nullptr != pItem )
331 // then set the adjust used by the parent format
332 AttrOutput().OutputItem( *pItem );
337 const SwNumRuleItem* pRuleItem;
338 if ( bPapFormat && (pRuleItem = rSet.GetItemIfSet( RES_PARATR_NUMRULE, bExportParentItemSet )) )
340 AttrOutput().OutputItem( *pRuleItem );
342 // switch off the numbering?
343 const SfxPoolItem* pLRItem;
344 if ( pRuleItem->GetValue().isEmpty() &&
345 SfxItemState::SET != rSet.GetItemState(RES_MARGIN_FIRSTLINE, false) &&
346 (pLRItem = rSet.GetItemIfSet(RES_MARGIN_FIRSTLINE)) )
348 // set the LR-Space of the parentformat!
349 AttrOutput().OutputItem( *pLRItem );
351 if ( pRuleItem->GetValue().isEmpty() &&
352 SfxItemState::SET != rSet.GetItemState(RES_MARGIN_TEXTLEFT, false) &&
353 (pLRItem = rSet.GetItemIfSet(RES_MARGIN_TEXTLEFT)) )
355 // set the LR-Space of the parentformat!
356 AttrOutput().OutputItem( *pLRItem );
360 ww8::PoolItems aItems;
361 GetPoolItems( rSet, aItems, bExportParentItemSet );
362 if ( bChpFormat )
363 ExportPoolItemsToCHP(aItems, nScript, nullptr);
364 if ( bPapFormat )
366 const bool bAlreadyOutputBrushItem = AttrOutput().MaybeOutputBrushItem(rSet);
368 for ( const auto& rItem : aItems )
370 const SfxPoolItem* pItem = rItem.second;
371 sal_uInt16 nWhich = pItem->Which();
372 // Handle fill attributes just like frame attributes for now.
373 if ( (nWhich >= RES_PARATR_BEGIN && nWhich < RES_FRMATR_END && nWhich != RES_PARATR_NUMRULE ) ||
374 (nWhich >= XATTR_FILL_FIRST && nWhich < XATTR_FILL_LAST))
375 AttrOutput().OutputItem( *pItem );
378 // Has to be called after RES_PARATR_GRABBAG is processed.
379 const XFillStyleItem* pFill(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, false));
380 if (!bAlreadyOutputBrushItem && pFill
381 && (pFill->GetValue() == drawing::FillStyle_SOLID || pFill->GetValue() == drawing::FillStyle_NONE)
382 && !rSet.GetItem(RES_BACKGROUND, false))
384 const bool bFillStyleNone = pFill->GetValue() == drawing::FillStyle_NONE;
385 // No need to write out a NONE background if it can't inherit something else, or if it already inherits a NONE.
386 std::unique_ptr<SvxBrushItem> pInherited;
387 if (bFillStyleNone)
389 if ( auto pNd = dynamic_cast<const SwContentNode*>(m_pOutFormatNode)) //paragraph
390 pInherited = getSvxBrushItemFromSourceSet(static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet(), RES_BACKGROUND);
391 else if (m_bStyDef && m_pCurrentStyle && m_pCurrentStyle->DerivedFrom()) //style
392 pInherited = getSvxBrushItemFromSourceSet(m_pCurrentStyle->DerivedFrom()->GetAttrSet(), RES_BACKGROUND);
394 // Construct an SvxBrushItem, as expected by the exporters.
395 std::unique_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
396 if (!bFillStyleNone || (pInherited && *pInherited != *aBrush))
397 AttrOutput().OutputItem(*aBrush);
399 #if 0
400 else
402 // note: *does not work* due to undocumented Word behavior: must be before a:ln element at least
403 AttrOutput().MaybeOutputBrushItem(rSet);
405 #endif
407 m_pISet = nullptr; // for double attributes
410 void MSWordExportBase::GatherChapterFields()
412 //If the header/footer contains a chapter field
413 SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter );
414 pType->GatherNodeIndex(m_aChapterFieldLocs);
417 bool MSWordExportBase::ContentContainsChapterField(const SwFormatContent &rContent) const
419 bool bRet = false;
420 if ( const SwNodeIndex* pSttIdx = rContent.GetContentIdx() )
422 SwNodeIndex aIdx( *pSttIdx, 1 );
423 SwNodeIndex aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
424 SwNodeOffset nStart = aIdx.GetIndex();
425 SwNodeOffset nEnd = aEnd.GetIndex();
426 //If the header/footer contains a chapter field
427 bRet = std::any_of(m_aChapterFieldLocs.cbegin(), m_aChapterFieldLocs.cend(),
428 [nStart, nEnd](SwNodeOffset i) { return ( nStart <= i ) && ( i <= nEnd ); });
430 return bRet;
433 bool MSWordExportBase::FormatHdFtContainsChapterField(const SwFrameFormat &rFormat) const
435 if ( m_aChapterFieldLocs.empty() )
436 return false;
438 const SwFrameFormat *pFormat = nullptr;
440 pFormat = rFormat.GetHeader().GetHeaderFormat();
441 if ( pFormat && ContentContainsChapterField( pFormat->GetContent() ) )
442 return true;
444 pFormat = rFormat.GetFooter().GetFooterFormat();
445 return pFormat && ContentContainsChapterField( pFormat->GetContent() );
448 bool MSWordExportBase::SetCurrentPageDescFromNode(const SwNode &rNd)
450 bool bNewPageDesc = false;
451 const SwPageDesc* pCurrent = SwPageDesc::GetPageDescOfNode(rNd);
452 OSL_ENSURE(pCurrent && m_pCurrentPageDesc, "Not possible surely");
453 if (m_pCurrentPageDesc && pCurrent)
455 if (pCurrent != m_pCurrentPageDesc)
457 if (m_pCurrentPageDesc->GetFollow() != pCurrent)
458 bNewPageDesc = true;
459 else
461 const SwFrameFormat& rTitleFormat = m_pCurrentPageDesc->GetFirstMaster();
462 const SwFrameFormat& rFollowFormat = pCurrent->GetMaster();
464 bNewPageDesc = !IsPlausableSingleWordSection(rTitleFormat,
465 rFollowFormat);
467 m_pCurrentPageDesc = pCurrent;
469 else
471 const SwFrameFormat &rFormat = pCurrent->GetMaster();
472 bNewPageDesc = FormatHdFtContainsChapterField(rFormat);
475 return bNewPageDesc;
479 * WW only knows Break-After (page break and section breaks),
480 * whereas in SW page breaks exist both "before" and "after" and PageDesc exists
481 * only "before". Therefore the breaks are iterated two times, namely before
482 * and after every line.
483 * Depending on the break type they're set before or after the line.
484 * Only functions can be called, which do not write in output area pO,
485 * because that one only exits once for CHP and PAP and therefore end up in
486 * the wrong one.
488 void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen )
490 if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs )
491 return;
493 m_bBreakBefore = true;
494 bool bNewPageDesc = false;
495 const SwFormatPageDesc *pPgDesc=nullptr;
496 bool bExtraPageBreakBeforeSectionBreak = false;
498 //Output a sectionbreak if there's a new pagedescriptor. Otherwise output a
499 //pagebreak if there is a pagebreak here, unless the new page (follow
500 //style) is different to the current one, in which case plump for a
501 //section.
502 bool bBreakSet = false;
504 const SwPageDesc * pPageDesc = rNd.FindPageDesc();
506 // Even if m_pCurrentPageDesc != pPageDesc ,it might be because of the different header & footer types.
507 if (m_pCurrentPageDesc != pPageDesc)
509 if (isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() ))
512 * If Table cell is open and page header types are different
513 * set pSet to NULL as we don't want to add any section breaks inside a table.
515 pSet = nullptr;
517 else if (!sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster()))
519 bBreakSet = true;
520 bNewPageDesc = true;
521 m_pCurrentPageDesc = pPageDesc;
525 if ( pSet && pSet->Count() )
527 const SwFormatPageDesc * pItem = pSet->GetItemIfSet( RES_PAGEDESC, false );
528 if ( pItem && pItem->GetRegisteredIn() != nullptr)
530 bBreakSet = true;
531 // Avoid unnecessary section breaks if possible. LO can't notice identical
532 // sections during import, so minimize unnecessary duplication
533 // by substituting a simple page break when the resulting section is identical,
534 // unless this is needed to re-number the page.
535 // DOCX only.
536 if (!bNewPageDesc && !pItem->GetNumOffset() && !PreferPageBreakBefore()
537 && m_pCurrentPageDesc && m_pCurrentPageDesc->GetFollow() == pItem->GetPageDesc())
539 // A section break on the very first paragraph is ignored by LO/Word
540 // and should NOT be turned into a page break.
541 SwNodeIndex aDocEnd(m_rDoc.GetNodes().GetEndOfContent());
542 SwNodeIndex aStart(*aDocEnd.GetNode().StartOfSectionNode());
543 // Set aStart to the first content node in the section
544 m_rDoc.GetNodes().GoNext(&aStart);
545 assert(aStart <= aDocEnd && "impossible: end section must have one content node");
546 if (rNd.GetIndex() > aStart.GetNode().GetIndex())
547 AttrOutput().OutputItem(SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
549 else
550 bNewPageDesc = true;
552 pPgDesc = pItem;
553 m_pCurrentPageDesc = pPgDesc->GetPageDesc();
555 // tdf#121666: nodes that have pagebreak + sectionbreak may need to export both breaks
556 // tested / implemented with docx format only.
557 // If other formats (rtf /doc) need similar fix, then that may can be done similar way.
558 if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false))
560 bExtraPageBreakBeforeSectionBreak = true;
563 else if ( const SvxFormatBreakItem* pBreak = pSet->GetItemIfSet( RES_BREAK, false ) )
565 // Word does not like hard break attributes in some table cells
566 bool bRemoveHardBreakInsideTable = false;
567 if ( m_bOutTable )
569 const SwTableNode* pTableNode = rNd.FindTableNode();
570 if ( pTableNode )
572 const SwTableBox* pBox = rNd.GetTableBox();
573 const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr;
574 // but only for non-complex tables
575 if ( pLine && !pLine->GetUpper() )
577 // check if box is not first in that line:
578 if ( 0 < pLine->GetBoxPos( pBox ) && pBox->GetSttNd() )
580 bRemoveHardBreakInsideTable = true;
585 bBreakSet = true;
587 if ( !bRemoveHardBreakInsideTable )
589 OSL_ENSURE(m_pCurrentPageDesc, "should not be possible");
591 If because of this pagebreak the page desc following the page
592 break is the follow style of the current page desc then output a
593 section break using that style instead. At least in those cases
594 we end up with the same style in word and writer, nothing can be
595 done when it happens when we get a new pagedesc because we
596 overflow from the first page style.
598 if ( m_pCurrentPageDesc )
600 // #i76301# - assure that there is a page break before set at the node.
601 if ( pBreak->GetBreak() == SvxBreak::PageBefore )
603 bNewPageDesc |= SetCurrentPageDescFromNode( rNd );
607 // If the columns are different in LO's adjacent sections, create a new MS section
608 if (!bNewPageDesc && pBreak->GetBreak() == SvxBreak::PageBefore
609 && Sections().CurrentSectionInfo())
611 const SwSectionFormat* pSectionFormat = MSWordExportBase::GetSectionFormat(rNd);
612 if (pSectionFormat)
614 const SwFormatCol& rNewSect = pSectionFormat->GetFormatAttr(RES_COL);
615 const SwFormatCol& rPrevSect
616 = MSWordSections::GetFormatCol(m_rDoc,
617 *Sections().CurrentSectionInfo());
618 if (rNewSect.GetNumCols() != rPrevSect.GetNumCols()
619 || !rNewSect.IsOrtho() || !rPrevSect.IsOrtho()
620 || rNewSect.GetLineStyle() != rPrevSect.GetLineStyle()
621 || rNewSect.GetLineWidth() != rPrevSect.GetLineWidth()
622 || rNewSect.GetLineColor() != rPrevSect.GetLineColor()
623 || rNewSect.GetLineHeight() != rPrevSect.GetLineHeight()
624 || rNewSect.GetLineAdj() != rPrevSect.GetLineAdj())
626 bNewPageDesc = true;
631 if ( !bNewPageDesc )
632 AttrOutput().OutputItem( *pBreak );
638 #i9301#
639 No explicit page break, lets see if the style had one and we've moved to a
640 new page style because of it, if we have to then we take the opportunity to
641 set the equivalent word section here. We *could* do it for every paragraph
642 that moves onto a new page because of layout, but that would be insane.
644 bool bHackInBreak = false;
645 if ( !bBreakSet )
647 if ( const SwContentNode *pNd = rNd.GetContentNode() )
649 const SvxFormatBreakItem &rBreak = pNd->GetAttr( RES_BREAK );
650 if ( rBreak.GetBreak() == SvxBreak::PageBefore )
651 bHackInBreak = true;
652 else
653 { // Even a pagedesc item is set, the break item can be set 'NONE',
654 // but a pagedesc item is an implicit page break before...
655 const SwFormatPageDesc &rPageDesc = pNd->GetAttr( RES_PAGEDESC );
656 if ( rPageDesc.KnowsPageDesc() )
657 bHackInBreak = true;
662 if ( bHackInBreak )
664 OSL_ENSURE( m_pCurrentPageDesc, "should not be possible" );
665 if ( m_pCurrentPageDesc )
666 bNewPageDesc = SetCurrentPageDescFromNode( rNd );
669 if ( bNewPageDesc && m_pCurrentPageDesc )
671 PrepareNewPageDesc( pSet, rNd, pPgDesc, m_pCurrentPageDesc, bExtraPageBreakBeforeSectionBreak );
673 m_bBreakBefore = false;
676 // #i76300#
677 bool MSWordExportBase::OutputFollowPageDesc( const SfxItemSet* pSet, const SwTextNode* pNd )
679 bool bRet = false;
681 if ( pNd &&
682 m_pCurrentPageDesc &&
683 m_pCurrentPageDesc != m_pCurrentPageDesc->GetFollow() )
685 PrepareNewPageDesc( pSet, *pNd, nullptr, m_pCurrentPageDesc->GetFollow() );
686 bRet = true;
689 return bRet;
692 const SwSectionFormat* MSWordExportBase::GetSectionFormat( const SwNode& rNd )
694 const SwSectionFormat* pFormat = nullptr;
695 const SwSectionNode* pSect = rNd.FindSectionNode();
696 if ( pSect &&
697 SectionType::Content == pSect->GetSection().GetType() )
699 pFormat = pSect->GetSection().GetFormat();
702 return pFormat;
705 sal_uLong MSWordExportBase::GetSectionLineNo( const SfxItemSet* pSet, const SwNode& rNd )
707 const SwFormatLineNumber* pNItem = nullptr;
708 if ( pSet )
710 pNItem = & pSet->Get( RES_LINENUMBER );
712 else if ( const SwContentNode *pNd = rNd.GetContentNode() )
714 pNItem = &pNd->GetAttr( RES_LINENUMBER );
717 return pNItem? pNItem->GetStartValue() : 0;
720 void WW8Export::PrepareNewPageDesc( const SfxItemSet*pSet,
721 const SwNode& rNd,
722 const SwFormatPageDesc* pNewPgDescFormat,
723 const SwPageDesc* pNewPgDesc,
724 bool /*bExtraPageBreak*/ )
726 // The PageDescs will only be inserted in WW8Writer::pSepx with the corresponding
727 // position by the occurrences of PageDesc attributes. The construction and
728 // output of the attributes and header/footer of the PageDesc are done
729 // after the main text and its attributes.
731 sal_uLong nFcPos = ReplaceCr( msword::PageBreak ); // Page/Section-Break
733 // actually nothing is outputted here, rather the arrays aCps, aSects
734 // accordingly completed
735 if ( !nFcPos )
736 return;
738 const SwSectionFormat* pFormat = GetSectionFormat( rNd );
739 const sal_uLong nLnNm = GetSectionLineNo( pSet, rNd );
741 OSL_ENSURE( pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided." );
743 if ( pNewPgDescFormat )
745 m_pSepx->AppendSep( Fc2Cp( nFcPos ), *pNewPgDescFormat, rNd, pFormat, nLnNm );
747 else if ( pNewPgDesc )
749 m_pSepx->AppendSep( Fc2Cp( nFcPos ), pNewPgDesc, rNd, pFormat, nLnNm );
753 void MSWordExportBase::CorrectTabStopInSet( SfxItemSet& rSet, sal_Int32 nAbsLeft )
755 const SvxTabStopItem *pItem = rSet.GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
756 if (!pItem)
757 return;
759 // then it must be corrected for the output
760 SvxTabStopItem aTStop(*pItem);
761 for ( sal_uInt16 nCnt = 0; nCnt < aTStop.Count(); ++nCnt )
763 SvxTabStop& rTab = const_cast<SvxTabStop&>(aTStop[ nCnt ]);
764 if ( SvxTabAdjust::Default != rTab.GetAdjustment() &&
765 rTab.GetTabPos() >= nAbsLeft )
767 rTab.GetTabPos() -= nAbsLeft;
769 else
771 aTStop.Remove( nCnt );
772 --nCnt;
775 rSet.Put( aTStop );
778 tools::Long MSWordExportBase::GetParaTabStopOffset() const
780 tools::Long nOffset = 0;
781 // Tabs are absolute by default.
782 if (m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT))
784 // don't do it for editengine text, it doesn't implement this anyway
785 if (!m_pISet || m_pISet->GetRanges()[0].first < RES_WHICHHINT_END)
787 nOffset = GetItem(RES_MARGIN_TEXTLEFT).GetTextLeft();
790 return nOffset;
793 sal_uInt8 WW8Export::GetNumId( sal_uInt16 eNumType )
795 sal_uInt8 nRet = 0;
796 switch( eNumType )
798 case SVX_NUM_CHARS_UPPER_LETTER:
799 case SVX_NUM_CHARS_UPPER_LETTER_N: nRet = 3; break;
800 case SVX_NUM_CHARS_LOWER_LETTER:
801 case SVX_NUM_CHARS_LOWER_LETTER_N: nRet = 4; break;
802 case SVX_NUM_ROMAN_UPPER: nRet = 1; break;
803 case SVX_NUM_ROMAN_LOWER: nRet = 2; break;
804 case style::NumberingType::TEXT_NUMBER: nRet = 5; break;
805 case style::NumberingType::TEXT_CARDINAL: nRet = 6; break;
806 case style::NumberingType::TEXT_ORDINAL: nRet = 7; break;
807 case style::NumberingType::AIU_HALFWIDTH_JA: nRet = 12; break;
808 case style::NumberingType::IROHA_HALFWIDTH_JA: nRet = 13; break;
809 case style::NumberingType::FULLWIDTH_ARABIC: nRet = 14; break;
810 case style::NumberingType::NUMBER_TRADITIONAL_JA: nRet = 16; break;
811 case style::NumberingType::CIRCLE_NUMBER: nRet = 18; break;
812 case style::NumberingType::AIU_FULLWIDTH_JA: nRet = 20; break;
813 case style::NumberingType::IROHA_FULLWIDTH_JA: nRet = 21; break;
815 case SVX_NUM_BITMAP:
816 case SVX_NUM_CHAR_SPECIAL: nRet = 23; break;
817 case style::NumberingType::HANGUL_SYLLABLE_KO: nRet = 24; break;// ganada
818 case style::NumberingType::HANGUL_JAMO_KO: nRet = 25; break;// chosung
819 case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO: nRet = 24; break;
820 case style::NumberingType::HANGUL_CIRCLED_JAMO_KO: nRet = 25; break;
821 case style::NumberingType::TIAN_GAN_ZH: nRet = 30; break;
822 case style::NumberingType::DI_ZI_ZH: nRet = 31; break;
823 case style::NumberingType::NUMBER_UPPER_ZH_TW: nRet = 34;break;
824 case style::NumberingType::NUMBER_UPPER_ZH: nRet = 38; break;
825 case style::NumberingType::NUMBER_DIGITAL_KO: nRet = 41; break;
826 case style::NumberingType::NUMBER_HANGUL_KO: nRet = 42; break;
827 case style::NumberingType::NUMBER_LEGAL_KO: nRet = 43; break;
828 case style::NumberingType::NUMBER_DIGITAL2_KO: nRet = 44; break;
829 case style::NumberingType::NUMBER_HEBREW: nRet = 45; break;
830 case style::NumberingType::CHARS_ARABIC: nRet = 46; break;
831 case style::NumberingType::CHARS_HEBREW: nRet = 47; break;
832 case style::NumberingType::CHARS_ARABIC_ABJAD: nRet = 48; break;
833 case style::NumberingType::CHARS_PERSIAN:
834 case style::NumberingType::CHARS_NEPALI: nRet = 49; break;
835 case style::NumberingType::CHARS_THAI: nRet = 53; break;
836 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU:
837 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU: nRet = 58; break;
838 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU:
839 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU: nRet = 59; break;
840 // nothing, WW does the same (undocumented)
841 case SVX_NUM_NUMBER_NONE: nRet = 0xff; break;
842 case SVX_NUM_SYMBOL_CHICAGO:
843 // 0x09, msonfcChiManSty
844 nRet = 9;
845 break;
846 case SVX_NUM_ARABIC_ZERO:
847 // 0x16, msonfcArabicLZ
848 nRet = 22;
849 break;
851 return nRet;
854 void WW8AttributeOutput::OutlineNumbering(sal_uInt8 /*nLvl*/)
856 // Handled by ParaOutlineLevel and ParaNumRule
859 // #i77805#
860 bool WW8Export::DisallowInheritingOutlineNumbering(const SwFormat &rFormat)
862 bool bRet( false );
864 //If there is no numbering on this fmt, but its parent was outline
865 //numbered, then in writer this is no inheritied, but in word it would
866 //be, so we must export "no numbering" and "body level" to make word
867 //behave like writer (see #i25755)
868 if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
870 if (const SwFormat *pParent = rFormat.DerivedFrom())
872 if (static_cast<const SwTextFormatColl*>(pParent)->IsAssignedToListLevelOfOutlineStyle())
874 SwWW8Writer::InsUInt16(*m_pO, NS_sprm::POutLvl::val);
875 m_pO->push_back(sal_uInt8(9));
876 SwWW8Writer::InsUInt16(*m_pO, NS_sprm::PIlfo::val);
877 SwWW8Writer::InsUInt16(*m_pO, 0);
879 bRet = true;
884 return bRet;
887 void MSWordExportBase::OutputFormat( const SwFormat& rFormat, bool bPapFormat, bool bChpFormat, bool bFlyFormat )
889 bool bCallOutSet = true;
890 const sw::BroadcastingModify* pOldMod = m_pOutFormatNode;
891 m_pOutFormatNode = &rFormat;
893 switch( rFormat.Which() )
895 case RES_CONDTXTFMTCOLL:
896 case RES_TXTFMTCOLL:
897 if( bPapFormat )
899 int nLvl = MAXLEVEL;
901 if (static_cast<const SwTextFormatColl&>(rFormat).IsAssignedToListLevelOfOutlineStyle())
902 nLvl = static_cast<const SwTextFormatColl&>(rFormat).GetAssignedOutlineStyleLevel();
904 if (nLvl >= 0 && nLvl < MAXLEVEL)
906 //if outline numbered
907 // if Write StyleDefinition then write the OutlineRule
908 const SwNumFormat& rNFormat = m_rDoc.GetOutlineNumRule()->Get( o3tl::narrowing<sal_uInt16>( nLvl ) );
909 if ( m_bStyDef )
910 AttrOutput().OutlineNumbering(static_cast<sal_uInt8>(nLvl));
912 if ( rNFormat.GetPositionAndSpaceMode() ==
913 SvxNumberFormat::LABEL_WIDTH_AND_POSITION &&
914 rNFormat.GetAbsLSpace() )
916 SfxItemSet aSet( rFormat.GetAttrSet() );
917 SvxFirstLineIndentItem firstLine(aSet.Get(RES_MARGIN_FIRSTLINE));
918 SvxTextLeftMarginItem leftMargin(aSet.Get(RES_MARGIN_TEXTLEFT));
920 leftMargin.SetTextLeft(leftMargin.GetTextLeft() + rNFormat.GetAbsLSpace());
921 firstLine.SetTextFirstLineOffset(GetWordFirstLineOffset(rNFormat));
923 aSet.Put(firstLine);
924 aSet.Put(leftMargin);
925 CorrectTabStopInSet( aSet, rNFormat.GetAbsLSpace() );
926 OutputItemSet( aSet, bPapFormat, bChpFormat,
927 i18n::ScriptType::LATIN, m_bExportModeRTF);
928 bCallOutSet = false;
931 else
933 //otherwise we might have to remove outline numbering from
934 //what gets exported if the parent style was outline numbered
935 // #i77805#
936 // If inherited outline numbering is suppress, the left/right
937 // margins has to be exported explicitly.
938 if ( m_bStyDef && DisallowInheritingOutlineNumbering(rFormat) )
940 SfxItemSet aSet( rFormat.GetAttrSet() );
941 SvxFirstLineIndentItem const& rFirstLine(aSet.Get(RES_MARGIN_FIRSTLINE));
942 SvxTextLeftMarginItem const& rLeftMargin(aSet.Get(RES_MARGIN_TEXTLEFT));
943 aSet.Put(rFirstLine);
944 aSet.Put(rLeftMargin);
945 OutputItemSet( aSet, bPapFormat, bChpFormat,
946 css::i18n::ScriptType::LATIN, m_bExportModeRTF);
947 bCallOutSet = false;
951 break;
953 case RES_CHRFMT:
954 break;
955 case RES_FLYFRMFMT:
956 if (bFlyFormat)
958 OSL_ENSURE(m_pParentFrame, "No parent frame, all broken");
960 if (m_pParentFrame)
962 const SwFrameFormat &rFrameFormat = m_pParentFrame->GetFrameFormat();
964 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1,
965 XATTR_FILL_FIRST, XATTR_FILL_LAST> aSet(m_rDoc.GetAttrPool());
966 aSet.Set(rFrameFormat.GetAttrSet());
968 // Fly as character becomes a paragraph bound
969 // now set the distance to paragraph margin
970 if (m_pFlyOffset)
972 aSet.Put(SwFormatHoriOrient(m_pFlyOffset->X()));
973 aSet.Put(SwFormatVertOrient(m_pFlyOffset->Y()));
974 SwFormatAnchor aAnchor(rFrameFormat.GetAnchor());
975 aAnchor.SetType(m_eNewAnchorType);
976 aSet.Put(aAnchor);
979 if (SfxItemState::SET != aSet.GetItemState(RES_SURROUND))
980 aSet.Put(SwFormatSurround(css::text::WrapTextMode_NONE));
982 const XFillStyleItem* pXFillStyleItem(rFrameFormat.GetAttrSet().GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
983 if (pXFillStyleItem)
985 switch (pXFillStyleItem->GetValue())
987 case drawing::FillStyle_NONE:
988 break;
989 case drawing::FillStyle_SOLID:
991 // Construct an SvxBrushItem, as expected by the exporters.
992 aSet.Put(getSvxBrushItemFromSourceSet(rFrameFormat.GetAttrSet(), RES_BACKGROUND));
993 break;
995 default:
996 break;
1000 m_bOutFlyFrameAttrs = true;
1001 //script doesn't matter if not exporting chp
1002 OutputItemSet(aSet, true, false,
1003 i18n::ScriptType::LATIN, m_bExportModeRTF);
1004 m_bOutFlyFrameAttrs = false;
1006 bCallOutSet = false;
1009 break;
1010 case RES_FRMFMT:
1011 break;
1012 default:
1013 OSL_ENSURE( false, "Which format is exported here?" );
1014 break;
1017 if( bCallOutSet )
1018 OutputItemSet( rFormat.GetAttrSet(), bPapFormat, bChpFormat,
1019 i18n::ScriptType::LATIN, m_bExportModeRTF);
1020 m_pOutFormatNode = pOldMod;
1023 bool MSWordExportBase::HasRefToAttr(const OUString& rName)
1025 SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef);
1026 std::vector<SwGetRefField*> vpRFields;
1027 pType->GatherRefFields(vpRFields, REF_SETREFATTR);
1028 return std::any_of(vpRFields.begin(), vpRFields.end(),
1029 [rName](SwGetRefField* pF) { return rName == pF->GetSetRefName(); });
1032 bool MSWordExportBase::HasRefToFootOrEndnote(const bool isEndNote, const sal_uInt16 nSeqNo)
1034 SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef);
1035 std::vector<SwGetRefField*> vpRFields;
1036 pType->GatherRefFields(vpRFields, isEndNote ? REF_ENDNOTE : REF_FOOTNOTE);
1037 return std::any_of(vpRFields.begin(), vpRFields.end(),
1038 [nSeqNo](SwGetRefField* pF) { return nSeqNo == pF->GetSeqNo(); });
1041 OUString MSWordExportBase::GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo )
1043 OUString sRet;
1044 switch ( nTyp )
1046 case REF_SETREFATTR:
1047 if ( pName )
1049 sRet = "Ref_" + *pName;
1051 break;
1052 case REF_SEQUENCEFLD:
1054 assert(pName);
1055 sRet = "Ref_" + *pName;
1056 break;
1058 case REF_BOOKMARK:
1059 if ( pName )
1060 sRet = *pName;
1061 break;
1062 case REF_OUTLINE:
1063 break; // ???
1064 case REF_FOOTNOTE:
1065 sRet = "_RefF" + OUString::number( nSeqNo );
1066 break;
1067 case REF_ENDNOTE:
1068 sRet = "_RefE" + OUString::number( nSeqNo );
1069 break;
1071 return BookmarkToWord( sRet ); // #i43956# - encode bookmark accordingly
1074 /* File CHRATR.HXX: */
1075 void WW8AttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript )
1077 if (bIsRTL)
1079 if( m_rWW8Export.m_rDoc.GetDocumentType() != SwDoc::DOCTYPE_MSWORD )
1081 m_rWW8Export.InsUInt16( NS_sprm::CFBiDi::val );
1082 m_rWW8Export.m_pO->push_back( sal_uInt8(1) );
1086 // #i46087# patch from james_clark; complex texts needs the undocumented SPRM CComplexScript with param 0x81.
1087 if (nScript == i18n::ScriptType::COMPLEX && !bIsRTL)
1089 m_rWW8Export.InsUInt16( NS_sprm::CFComplexScripts::val );
1090 m_rWW8Export.m_pO->push_back( sal_uInt8(0x81) );
1091 m_rWW8Export.m_pDop->bUseThaiLineBreakingRules = true;
1095 void WW8AttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
1097 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell() - (mbOnTOXEnding?2:0), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
1098 mbOnTOXEnding = false;
1099 m_rWW8Export.m_pO->clear();
1101 if ( pTextNodeInfoInner )
1103 if ( pTextNodeInfoInner->isEndOfLine() )
1105 TableRowEnd( pTextNodeInfoInner->getDepth() );
1107 SVBT16 nSty;
1108 ShortToSVBT16( 0, nSty );
1109 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nSty, nSty+2 ); // Style #
1110 TableInfoRow( pTextNodeInfoInner );
1111 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data());
1112 m_rWW8Export.m_pO->clear();
1113 //For Bug 119650, should break the properties of CHP PLC after a paragraph end.
1114 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data());
1118 // Clear bookmarks of the current paragraph
1119 m_aBookmarksOfParagraphStart.clear();
1120 m_aBookmarksOfParagraphEnd.clear();
1123 void WW8AttributeOutput::StartRunProperties()
1125 WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
1126 m_nFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
1129 void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool /*bSingleEmptyRun*/ )
1131 if (pRedlineData)
1133 const OUString &rComment = pRedlineData->GetComment();
1134 //Only possible to export to main text
1135 if (!rComment.isEmpty() && (m_rWW8Export.m_nTextTyp == TXT_MAINTEXT) &&
1136 // tdf#153016 don't export the new automatic comments added by tdf#148032
1137 rComment != SwResId(STR_REDLINE_COMMENT_DELETED) &&
1138 rComment != SwResId(STR_REDLINE_COMMENT_ADDED))
1140 if (m_rWW8Export.m_pAtn->IsNewRedlineComment(pRedlineData))
1142 m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pRedlineData );
1143 m_rWW8Export.WritePostItBegin( m_rWW8Export.m_pO.get() );
1148 /// Insert bookmarks started at this run
1149 auto aRange = m_aBookmarksOfParagraphStart.equal_range(nPos);
1150 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1152 GetExport().AppendBookmark(GetExport().BookmarkToWord(aIter->second));
1156 void WW8AttributeOutput::OnTOXEnding()
1158 mbOnTOXEnding = true;
1161 void WW8AttributeOutput::EndRun( const SwTextNode* /*pNode*/, sal_Int32 nPos, sal_Int32 /*nLen*/, bool bLastRun )
1163 /// Insert bookmarks ended after this run
1164 auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nPos);
1165 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1167 if(bLastRun)
1168 GetExport().AppendBookmarkEndWithCorrection(GetExport().BookmarkToWord(aIter->second));
1169 else
1170 GetExport().AppendBookmark(GetExport().BookmarkToWord(aIter->second));
1174 void WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData )
1176 Redline( pRedlineData );
1178 WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
1179 sal_uInt16 nNewFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
1181 bool bExportedFieldResult = ( m_nFieldResults != nNewFieldResults );
1183 // If we have exported a field result, then we will have been forced to
1184 // split up the text into a 0x13, 0x14, <result> 0x15 sequence with the
1185 // properties forced out at the end of the result, so the 0x15 itself
1186 // should remain clean of all other attributes to avoid #iXXXXX#
1187 if ( !bExportedFieldResult )
1189 m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
1190 m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
1192 m_rWW8Export.m_pO->clear();
1195 void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet, const OUString& /*rSymbolFont*/ )
1197 RawText(rText, eCharSet);
1200 void WW8AttributeOutput::RawText(const OUString& rText, rtl_TextEncoding)
1202 m_rWW8Export.OutSwString(rText, 0, rText.getLength());
1205 void WW8AttributeOutput::OutputFKP(bool bForce)
1207 if (!m_rWW8Export.m_pO->empty() || bForce)
1209 m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
1210 m_rWW8Export.m_pO->size(), m_rWW8Export.m_pO->data() );
1211 m_rWW8Export.m_pO->clear();
1215 void WW8AttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
1217 OSL_ENSURE( m_rWW8Export.m_pO->empty(), " pO is not empty at line end" );
1219 SVBT16 nSty;
1220 ShortToSVBT16( nStyle, nSty );
1221 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), nSty, nSty+2 ); // style #
1224 void WW8AttributeOutput::OutputWW8Attribute( sal_uInt8 nId, bool bVal )
1226 m_rWW8Export.InsUInt16( 8 == nId ? NS_sprm::CFDStrike::val : NS_sprm::CFBold::val + nId );
1228 m_rWW8Export.m_pO->push_back( bVal ? 1 : 0 );
1231 void WW8AttributeOutput::OutputWW8AttributeCTL( sal_uInt8 nId, bool bVal )
1233 OSL_ENSURE( nId <= 1, "out of range" );
1234 if (nId > 1)
1235 return;
1237 m_rWW8Export.InsUInt16( NS_sprm::CFBoldBi::val + nId );
1238 m_rWW8Export.m_pO->push_back( bVal ? 1 : 0 );
1241 void WW8AttributeOutput::CharFont( const SvxFontItem& rFont )
1243 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1245 m_rWW8Export.InsUInt16( NS_sprm::CRgFtc0::val );
1246 m_rWW8Export.InsUInt16( nFontID );
1247 m_rWW8Export.InsUInt16( NS_sprm::CRgFtc2::val );
1249 m_rWW8Export.InsUInt16( nFontID );
1252 void WW8AttributeOutput::CharFontCTL( const SvxFontItem& rFont )
1254 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1255 m_rWW8Export.InsUInt16( NS_sprm::CFtcBi::val );
1256 m_rWW8Export.InsUInt16( nFontID );
1259 void WW8AttributeOutput::CharFontCJK( const SvxFontItem& rFont )
1261 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1262 m_rWW8Export.InsUInt16( NS_sprm::CRgFtc1::val );
1263 m_rWW8Export.InsUInt16( nFontID );
1266 void WW8AttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight )
1268 OutputWW8AttributeCTL( 0, WEIGHT_BOLD == rWeight.GetWeight());
1271 void WW8AttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture )
1273 OutputWW8AttributeCTL( 1, ITALIC_NONE != rPosture.GetPosture() );
1276 void WW8AttributeOutput::CharPosture( const SvxPostureItem& rPosture )
1278 OutputWW8Attribute( 1, ITALIC_NONE != rPosture.GetPosture() );
1281 void WW8AttributeOutput::CharWeight( const SvxWeightItem& rWeight )
1283 OutputWW8Attribute( 0, WEIGHT_BOLD == rWeight.GetWeight() );
1286 // Shadowed and Contour are not in WW-UI. JP: ??
1287 void WW8AttributeOutput::CharContour( const SvxContourItem& rContour )
1289 OutputWW8Attribute( 3, rContour.GetValue() );
1292 void WW8AttributeOutput::CharShadow( const SvxShadowedItem& rShadow )
1294 OutputWW8Attribute( 4, rShadow.GetValue() );
1297 void WW8AttributeOutput::CharKerning( const SvxKerningItem& rKerning )
1299 m_rWW8Export.InsUInt16( NS_sprm::CDxaSpace::val );
1301 m_rWW8Export.InsUInt16( rKerning.GetValue() );
1304 void WW8AttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern )
1306 m_rWW8Export.InsUInt16( NS_sprm::CHpsKern::val );
1308 m_rWW8Export.InsUInt16( rAutoKern.GetValue() ? 2 : 0 );
1311 void WW8AttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
1313 m_rWW8Export.InsUInt16( NS_sprm::CSfxText::val );
1314 // At the moment the only animated text effect we support is blinking
1315 m_rWW8Export.m_pO->push_back( rBlink.GetValue() ? 2 : 0 );
1318 void WW8AttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossed )
1320 FontStrikeout eSt = rCrossed.GetStrikeout();
1321 if ( STRIKEOUT_DOUBLE == eSt )
1323 OutputWW8Attribute( 8, true );
1324 return;
1326 if ( STRIKEOUT_NONE != eSt )
1328 OutputWW8Attribute( 2, true );
1329 return;
1332 // otherwise both off
1333 OutputWW8Attribute( 8, false );
1334 OutputWW8Attribute( 2, false );
1337 void WW8AttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap )
1339 SvxCaseMap eSt = rCaseMap.GetValue();
1340 switch ( eSt )
1342 case SvxCaseMap::SmallCaps:
1343 OutputWW8Attribute( 5, true );
1344 return;
1345 case SvxCaseMap::Uppercase:
1346 OutputWW8Attribute( 6, true );
1347 return;
1348 case SvxCaseMap::Capitalize:
1349 // no such feature in word
1350 break;
1351 default:
1352 // otherwise both off
1353 OutputWW8Attribute( 5, false );
1354 OutputWW8Attribute( 6, false );
1355 return;
1359 void WW8AttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden )
1361 OutputWW8Attribute( 7, rHidden.GetValue() );
1364 void WW8AttributeOutput::CharBorder( const SvxBorderLine* pAllBorder, const sal_uInt16 /*nDist*/, const bool bShadow )
1366 WW8Export::Out_BorderLine( *m_rWW8Export.m_pO, pAllBorder, 0, NS_sprm::CBrc80::val, NS_sprm::CBrc::val, bShadow );
1369 void WW8AttributeOutput::CharHighlight( const SvxBrushItem& rBrush )
1371 sal_uInt8 nColor = msfilter::util::TransColToIco( rBrush.GetColor() );
1372 m_rWW8Export.InsUInt16( NS_sprm::CHighlight::val );
1373 m_rWW8Export.m_pO->push_back( nColor );
1376 void WW8AttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline )
1378 m_rWW8Export.InsUInt16( NS_sprm::CKul::val );
1380 // FIXME: this should likely be a StaticWhichCast(), but some we put something dirty in RES_CHRATR_WORDLINEMODE apparently
1381 const auto pItem = m_rWW8Export.HasItem(RES_CHRATR_WORDLINEMODE);
1382 bool bWord = false;
1383 if(pItem)
1385 const auto pWordline = pItem->DynamicWhichCast(RES_CHRATR_WORDLINEMODE);
1386 if(pWordline)
1387 bWord = pWordline->GetValue();
1388 else
1389 SAL_WARN("sw.ww8", "m_rWW8Export has an RES_CHRATR_WORDLINEMODE item, but it's of the wrong type.");
1392 // WW95 - parameters: 0 = none, 1 = single, 2 = by Word,
1393 // 3 = double, 4 = dotted, 5 = hidden
1394 // WW97 - additional parameters:
1395 // 6 = thick, 7 = dash, 8 = dot(not used)
1396 // 9 = dotdash 10 = dotdotdash, 11 = wave
1397 sal_uInt8 b = 0;
1398 switch ( rUnderline.GetLineStyle() )
1400 case LINESTYLE_SINGLE:
1401 b = bWord ? 2 : 1;
1402 break;
1403 case LINESTYLE_BOLD:
1404 b = 6;
1405 break;
1406 case LINESTYLE_DOUBLE:
1407 b = 3;
1408 break;
1409 case LINESTYLE_DOTTED:
1410 b = 4;
1411 break;
1412 case LINESTYLE_DASH:
1413 b = 7;
1414 break;
1415 case LINESTYLE_DASHDOT:
1416 b = 9;
1417 break;
1418 case LINESTYLE_DASHDOTDOT:
1419 b = 10;
1420 break;
1421 case LINESTYLE_WAVE:
1422 b = 11;
1423 break;
1424 // new in WW2000
1425 case LINESTYLE_BOLDDOTTED:
1426 b = 20;
1427 break;
1428 case LINESTYLE_BOLDDASH:
1429 b = 23;
1430 break;
1431 case LINESTYLE_LONGDASH:
1432 b = 39;
1433 break;
1434 case LINESTYLE_BOLDLONGDASH:
1435 b = 55;
1436 break;
1437 case LINESTYLE_BOLDDASHDOT:
1438 b = 25;
1439 break;
1440 case LINESTYLE_BOLDDASHDOTDOT:
1441 b = 26;
1442 break;
1443 case LINESTYLE_BOLDWAVE:
1444 b = 27;
1445 break;
1446 case LINESTYLE_DOUBLEWAVE:
1447 b = 43;
1448 break;
1449 case LINESTYLE_NONE:
1450 b = 0;
1451 break;
1452 default:
1453 OSL_ENSURE( rUnderline.GetLineStyle() == LINESTYLE_NONE, "Unhandled underline type" );
1454 break;
1457 m_rWW8Export.m_pO->push_back( b );
1458 Color aColor = rUnderline.GetColor();
1459 if( aColor != COL_TRANSPARENT )
1461 m_rWW8Export.InsUInt16( NS_sprm::CCvUl::val );
1463 m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( aColor ) );
1467 void WW8AttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage )
1469 sal_uInt16 nId = 0;
1470 switch ( rLanguage.Which() )
1472 case RES_CHRATR_LANGUAGE:
1473 nId = NS_sprm::CRgLid0_80::val;
1474 break;
1475 case RES_CHRATR_CJK_LANGUAGE:
1476 nId = NS_sprm::CRgLid1_80::val;
1477 break;
1478 case RES_CHRATR_CTL_LANGUAGE:
1479 nId = NS_sprm::CLidBi::val;
1480 break;
1483 if ( !nId )
1484 return;
1486 // use sprmCRgLid0_80 rather than sprmCLid
1487 m_rWW8Export.InsUInt16( nId );
1488 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1490 // Word 2000 and above apparently require both old and new versions of
1491 // these sprms to be set, without it spellchecking doesn't work
1492 if ( nId == NS_sprm::CRgLid0_80::val )
1494 m_rWW8Export.InsUInt16( NS_sprm::CRgLid0::val );
1495 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1497 else if ( nId == NS_sprm::CRgLid1_80::val )
1499 m_rWW8Export.InsUInt16( NS_sprm::CRgLid1::val );
1500 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1504 void WW8AttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement )
1506 sal_uInt8 b = 0xFF;
1507 short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight();
1508 if ( !nEsc )
1510 b = 0;
1511 nEsc = 0;
1512 nProp = 100;
1514 else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 )
1516 if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
1517 b = 2;
1518 else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
1519 b = 1;
1521 else if ( DFLT_ESC_AUTO_SUPER == nEsc )
1523 // Raised by the differences between the ascenders (ascent = baseline to top of highest letter).
1524 // The ascent is generally about 80% of the total font height.
1525 // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER)
1526 nEsc = .8 * (100 - nProp);
1528 else if ( DFLT_ESC_AUTO_SUB == nEsc )
1530 // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter).
1531 // The descent is generally about 20% of the total font height.
1532 // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB)
1533 nEsc = .2 * -(100 - nProp);
1536 if ( 0xFF != b )
1538 m_rWW8Export.InsUInt16( NS_sprm::CIss::val );
1540 m_rWW8Export.m_pO->push_back( b );
1543 if ( 0 != b && 0xFF != b )
1544 return;
1546 double fHeight = m_rWW8Export.GetItem( RES_CHRATR_FONTSIZE ).GetHeight();
1547 m_rWW8Export.InsUInt16( NS_sprm::CHpsPos::val );
1549 m_rWW8Export.InsUInt16(static_cast<short>( round(fHeight * nEsc / 1000) ));
1551 if( 100 != nProp || !b )
1553 m_rWW8Export.InsUInt16( NS_sprm::CHps::val );
1554 m_rWW8Export.InsUInt16(msword_cast<sal_uInt16>( round(fHeight * nProp / 1000) ));
1558 void WW8AttributeOutput::CharFontSize( const SvxFontHeightItem& rHeight )
1560 sal_uInt16 nId = 0;
1561 switch ( rHeight.Which() )
1563 case RES_CHRATR_FONTSIZE:
1564 case RES_CHRATR_CJK_FONTSIZE:
1565 nId = NS_sprm::CHps::val;
1566 break;
1567 case RES_CHRATR_CTL_FONTSIZE:
1568 nId = NS_sprm::CHpsBi::val;
1569 break;
1572 if ( nId )
1574 m_rWW8Export.InsUInt16( nId );
1576 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(( rHeight.GetHeight() + 5 ) / 10 ) );
1580 void WW8AttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth )
1582 m_rWW8Export.InsUInt16( NS_sprm::CCharScale::val );
1583 m_rWW8Export.InsUInt16( rScaleWidth.GetValue() );
1586 void WW8AttributeOutput::CharRelief( const SvxCharReliefItem& rRelief )
1588 sal_uInt16 nId;
1589 switch ( rRelief.GetValue() )
1591 case FontRelief::Embossed: nId = NS_sprm::CFEmboss::val; break;
1592 case FontRelief::Engraved: nId = NS_sprm::CFImprint::val; break;
1593 default: nId = 0; break;
1596 if( nId )
1598 m_rWW8Export.InsUInt16( nId );
1599 m_rWW8Export.m_pO->push_back( sal_uInt8(0x81) );
1601 else
1603 // switch both flags off
1604 m_rWW8Export.InsUInt16( NS_sprm::CFEmboss::val );
1605 m_rWW8Export.m_pO->push_back( sal_uInt8(0x0) );
1606 m_rWW8Export.InsUInt16( NS_sprm::CFImprint::val );
1607 m_rWW8Export.m_pO->push_back( sal_uInt8(0x0) );
1611 void WW8AttributeOutput::CharBidiRTL( const SfxPoolItem& rHt )
1613 const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
1614 if( rAttr.GetValue() == 1 )
1616 m_rWW8Export.InsUInt16(0x85a);
1617 m_rWW8Export.m_pO->push_back(sal_uInt8(1));
1621 void WW8AttributeOutput::CharIdctHint( const SfxPoolItem& rHt )
1623 const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
1624 m_rWW8Export.InsUInt16(0x286F);
1625 m_rWW8Export.m_pO->push_back(static_cast<sal_uInt8>(rAttr.GetValue()));
1628 void WW8AttributeOutput::CharRotate( const SvxCharRotateItem& rRotate )
1630 // #i28331# - check that a Value is set
1631 if ( !rRotate.GetValue() )
1632 return;
1634 if (m_rWW8Export.IsInTable())
1635 return;
1637 // #i36867 In word the text in a table is rotated via the TC or NS_sprm::TTextFlow::val
1638 // This means you can only rotate all or none of the text adding NS_sprm::CFELayout::val
1639 // here corrupts the table, hence !m_rWW8Export.bIsInTable
1641 m_rWW8Export.InsUInt16( NS_sprm::CFELayout::val );
1642 m_rWW8Export.m_pO->push_back( sal_uInt8(0x06) ); //len 6
1643 m_rWW8Export.m_pO->push_back( sal_uInt8(0x01) );
1645 m_rWW8Export.InsUInt16( rRotate.IsFitToLine() ? 1 : 0 );
1646 static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
1647 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aZeroArr, aZeroArr+3);
1650 void WW8AttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark )
1652 sal_uInt8 nVal;
1653 const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
1654 if (v == FontEmphasisMark::NONE)
1655 nVal = 0;
1656 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
1657 nVal = 2;
1658 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
1659 nVal = 3;
1660 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
1661 nVal = 4;
1662 else
1663 // case 1:
1664 nVal = 1;
1666 m_rWW8Export.InsUInt16( NS_sprm::CKcd::val );
1667 m_rWW8Export.m_pO->push_back( nVal );
1671 * TransBrush converts SW-Brushes to WW. The result is WW8_SHD.
1672 * Non-standard colours of SW won't be converted now to the mixed values
1673 * ( 0 .. 95% ) of WW.
1674 * Also if transparent, e.g. for tables a transparent brush is returned
1676 * @return real brush ( not transparent )
1678 bool WW8Export::TransBrush(const Color& rCol, WW8_SHD& rShd)
1680 if( rCol.IsTransparent() )
1681 rShd = WW8_SHD(); // all zeros: transparent
1682 else
1684 rShd.SetFore( 0);
1685 rShd.SetBack( msfilter::util::TransColToIco( rCol ) );
1686 rShd.SetStyle( 0 );
1688 return !rCol.IsTransparent();
1691 static sal_uInt32 SuitableBGColor(Color nIn)
1693 if (nIn == COL_AUTO)
1694 return 0xFF000000;
1695 return wwUtility::RGBToBGR(nIn);
1698 void WW8AttributeOutput::CharColor( const SvxColorItem& rColor )
1700 m_rWW8Export.InsUInt16( NS_sprm::CIco::val );
1702 sal_uInt8 nColor = msfilter::util::TransColToIco( rColor.GetValue() );
1703 m_rWW8Export.m_pO->push_back( nColor );
1705 if (nColor)
1707 m_rWW8Export.InsUInt16( NS_sprm::CCv::val );
1708 m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( rColor.GetValue() ) );
1712 void WW8AttributeOutput::CharBackground( const SvxBrushItem& rBrush )
1714 WW8_SHD aSHD;
1716 WW8Export::TransBrush( rBrush.GetColor(), aSHD );
1717 // sprmCShd80
1718 m_rWW8Export.InsUInt16( NS_sprm::CShd80::val );
1719 m_rWW8Export.InsUInt16( aSHD.GetValue() );
1721 //Quite a few unknowns, some might be transparency or something
1722 //of that nature...
1723 m_rWW8Export.InsUInt16( NS_sprm::CShd::val );
1724 m_rWW8Export.m_pO->push_back( 10 );
1725 m_rWW8Export.InsUInt32( 0xFF000000 );
1726 m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) );
1727 m_rWW8Export.InsUInt16( 0x0000);
1730 namespace sw { namespace util {
1732 const SwCharFormat* GetSwCharFormat(const SwFormatINetFormat& rINet, SwDoc& rDoc)
1734 if (rINet.GetValue().isEmpty())
1735 return nullptr;
1737 const sal_uInt16 nId = rINet.GetINetFormatId();
1738 const OUString& rStr = rINet.GetINetFormat();
1739 if (rStr.isEmpty())
1741 OSL_ENSURE( false, "WW8AttributeOutput::TextINetFormat(..) - missing unvisited character format at hyperlink attribute" );
1744 return IsPoolUserFormat( nId )
1745 ? rDoc.FindCharFormatByName( rStr )
1746 : rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nId );
1751 void WW8AttributeOutput::TextINetFormat( const SwFormatINetFormat& rINet )
1753 const SwCharFormat* pFormat = GetSwCharFormat(rINet, m_rWW8Export.m_rDoc);
1754 if (!pFormat)
1755 return;
1757 m_rWW8Export.InsUInt16( NS_sprm::CIstd::val );
1759 m_rWW8Export.InsUInt16( m_rWW8Export.GetId( pFormat ) );
1762 // #i43956# - add optional parameter <pLinkStr>
1763 // It's needed to write the hyperlink data for a certain cross-reference
1764 // - it contains the name of the link target, which is a bookmark.
1765 // add optional parameter <bIncludeEmptyPicLocation>
1766 // It is needed to write an empty picture location for page number field separators
1767 static void InsertSpecialChar( WW8Export& rWrt, sal_uInt8 c,
1768 OUString const * pLinkStr,
1769 bool bIncludeEmptyPicLocation = false )
1771 ww::bytes aItems;
1772 rWrt.GetCurrentItems(aItems);
1774 if (c == 0x13)
1775 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell());
1776 else
1777 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
1779 rWrt.WriteChar(c);
1781 // store empty sprmCPicLocation for field separator
1782 if ( bIncludeEmptyPicLocation &&
1783 ( c == 0x13 || c == 0x14 || c == 0x15 ) )
1785 SwWW8Writer::InsUInt16( aItems, NS_sprm::CPicLocation::val );
1786 SwWW8Writer::InsUInt32( aItems, 0x00000000 );
1789 // #i43956# - write hyperlink data and attributes
1790 if ( c == 0x01 && pLinkStr)
1792 // write hyperlink data to data stream
1793 SvStream& rStrm = *rWrt.m_pDataStrm;
1794 // position of hyperlink data
1795 const sal_uInt32 nLinkPosInDataStrm = rStrm.Tell();
1796 // write empty header
1797 const sal_uInt16 nEmptyHdrLen = 0x44;
1798 sal_uInt8 aEmptyHeader[ nEmptyHdrLen ] = { 0 };
1799 aEmptyHeader[ 4 ] = 0x44;
1800 rStrm.WriteBytes( aEmptyHeader, nEmptyHdrLen );
1801 // writer fixed header
1802 const sal_uInt16 nFixHdrLen = 0x19;
1803 sal_uInt8 const aFixHeader[ nFixHdrLen ] =
1805 0x08, 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE,
1806 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9,
1807 0x0B, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
1808 0x00,
1810 rStrm.WriteBytes( aFixHeader, nFixHdrLen );
1811 // write reference string including length+1
1812 sal_uInt32 nStrLen( pLinkStr->getLength() + 1 );
1813 SwWW8Writer::WriteLong( rStrm, nStrLen );
1814 SwWW8Writer::WriteString16( rStrm, *pLinkStr, false );
1815 // write additional two NULL Bytes
1816 SwWW8Writer::WriteLong( rStrm, 0 );
1817 // write length of hyperlink data
1818 const sal_uInt32 nCurrPos = rStrm.Tell();
1819 rStrm.Seek( nLinkPosInDataStrm );
1820 rStrm.WriteUInt32(nCurrPos - nLinkPosInDataStrm);
1821 rStrm.Seek( nCurrPos );
1823 // write attributes of hyperlink character 0x01
1824 SwWW8Writer::InsUInt16( aItems, NS_sprm::CFFldVanish::val );
1825 aItems.push_back( sal_uInt8(0x81) );
1826 SwWW8Writer::InsUInt16( aItems, NS_sprm::CPicLocation::val );
1827 SwWW8Writer::InsUInt32( aItems, nLinkPosInDataStrm );
1828 SwWW8Writer::InsUInt16( aItems, NS_sprm::CFData::val );
1829 aItems.push_back( sal_uInt8(0x01) );
1832 //Technically we should probably Remove all attribs
1833 //here for the 0x13, 0x14, 0x15, but our import
1834 //is slightly lacking
1835 //aItems.Remove(0, aItems.Count());
1836 // fSpec-Attribute true
1837 SwWW8Writer::InsUInt16( aItems, NS_sprm::CFSpec::val );
1838 aItems.push_back( 1 );
1840 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
1843 static OUString lcl_GetExpandedField(const SwField &rField)
1845 //replace LF 0x0A with VT 0x0B
1846 return rField.ExpandField(true, nullptr).replace(0x0A, 0x0B);
1849 WW8_WrPlcField* WW8Export::CurrentFieldPlc() const
1851 WW8_WrPlcField* pFieldP = nullptr;
1852 switch (m_nTextTyp)
1854 case TXT_MAINTEXT:
1855 pFieldP = m_pFieldMain.get();
1856 break;
1857 case TXT_HDFT:
1858 pFieldP = m_pFieldHdFt.get();
1859 break;
1860 case TXT_FTN:
1861 pFieldP = m_pFieldFootnote.get();
1862 break;
1863 case TXT_EDN:
1864 pFieldP = m_pFieldEdn.get();
1865 break;
1866 case TXT_ATN:
1867 pFieldP = m_pFieldAtn.get();
1868 break;
1869 case TXT_TXTBOX:
1870 pFieldP = m_pFieldTextBxs.get();
1871 break;
1872 case TXT_HFTXTBOX:
1873 pFieldP = m_pFieldHFTextBxs.get();
1874 break;
1875 default:
1876 OSL_ENSURE( false, "what type of SubDoc is that?" );
1878 return pFieldP;
1881 void WW8Export::OutputField( const SwField* pField, ww::eField eFieldType,
1882 const OUString& rFieldCmd, FieldFlags nMode )
1884 OUString sFieldCmd(rFieldCmd);
1885 switch (eFieldType)
1887 // map fields that are not supported in WW8 as of Word 2003
1888 case ww::eBIBLIOGRAPHY:
1889 eFieldType = ww::eQUOTE;
1890 assert(rFieldCmd == FieldString(ww::eBIBLIOGRAPHY));
1891 sFieldCmd = FieldString(ww::eQUOTE);
1892 break;
1893 case ww::eCITATION:
1894 eFieldType = ww::eQUOTE;
1895 assert(o3tl::starts_with(o3tl::trim(rFieldCmd), u"CITATION"));
1896 sFieldCmd = rFieldCmd.replaceFirst(FieldString(ww::eCITATION),
1897 FieldString(ww::eQUOTE));
1898 break;
1899 default:
1900 break;
1903 assert(eFieldType <= 0x5F); // 95 is the highest documented one
1905 WW8_WrPlcField* pFieldP = CurrentFieldPlc();
1907 const bool bIncludeEmptyPicLocation = ( eFieldType == ww::ePAGE );
1908 if (FieldFlags::Start & nMode)
1910 sal_uInt8 aField13[2] = { 0x13, 0x00 }; // will change
1911 //#i3958#, Needed to make this field work correctly in Word 2000
1912 if (eFieldType == ww::eSHAPE)
1913 aField13[0] |= 0x80;
1914 aField13[1] = static_cast< sal_uInt8 >(eFieldType); // add type
1915 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField13 );
1916 InsertSpecialChar( *this, 0x13, nullptr, bIncludeEmptyPicLocation );
1918 if (FieldFlags::CmdStart & nMode)
1920 SwWW8Writer::WriteString16(Strm(), sFieldCmd, false);
1921 // #i43956# - write hyperlink character including
1922 // attributes and corresponding binary data for certain reference fields.
1923 bool bHandleBookmark = false;
1925 if (pField)
1927 if (pField->GetTyp()->Which() == SwFieldIds::GetRef &&
1928 ( eFieldType == ww::ePAGEREF || eFieldType == ww::eREF ||
1929 eFieldType == ww::eNOTEREF || eFieldType == ww::eFOOTREF ))
1930 bHandleBookmark = true;
1933 if ( bHandleBookmark )
1935 // retrieve reference destination - the name of the bookmark
1936 OUString aLinkStr;
1937 const sal_uInt16 nSubType = pField->GetSubType();
1938 const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
1939 if ( nSubType == REF_SETREFATTR ||
1940 nSubType == REF_BOOKMARK )
1942 const OUString& aRefName(rRField.GetSetRefName());
1943 aLinkStr = GetBookmarkName( nSubType, &aRefName, 0 );
1945 else if ( nSubType == REF_FOOTNOTE ||
1946 nSubType == REF_ENDNOTE )
1948 aLinkStr = GetBookmarkName( nSubType, nullptr, rRField.GetSeqNo() );
1950 else if ( nSubType == REF_SEQUENCEFLD )
1952 aLinkStr = pField->GetPar2();
1954 // insert hyperlink character including attributes and data.
1955 InsertSpecialChar( *this, 0x01, &aLinkStr );
1958 if (FieldFlags::CmdEnd & nMode)
1960 static const sal_uInt8 aField14[2] = { 0x14, 0xff };
1961 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField14 );
1962 pFieldP->ResultAdded();
1963 InsertSpecialChar( *this, 0x14, nullptr, bIncludeEmptyPicLocation );
1965 if (FieldFlags::End & nMode)
1967 OUString sOut;
1968 if( pField )
1969 sOut = lcl_GetExpandedField(*pField);
1970 else
1971 sOut = sFieldCmd;
1972 if( !sOut.isEmpty() )
1974 SwWW8Writer::WriteString16(Strm(), sOut, false);
1976 if (pField)
1978 if (pField->GetTyp()->Which() == SwFieldIds::Input &&
1979 eFieldType == ww::eFORMTEXT)
1981 sal_uInt8 aArr[12];
1982 sal_uInt8 *pArr = aArr;
1984 Set_UInt16( pArr, NS_sprm::CPicLocation::val );
1985 Set_UInt32( pArr, 0x0 );
1987 Set_UInt16( pArr, NS_sprm::CFSpec::val );
1988 Set_UInt8( pArr, 1 );
1990 Set_UInt16( pArr, NS_sprm::CFNoProof::val );
1991 Set_UInt8( pArr, 1 );
1993 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
1998 if (!(FieldFlags::Close & nMode))
1999 return;
2001 sal_uInt8 aField15[2] = { 0x15, 0x80 };
2003 if (pField)
2005 if (pField->GetTyp()->Which() == SwFieldIds::Input &&
2006 eFieldType == ww::eFORMTEXT)
2008 sal_uInt16 nSubType = pField->GetSubType();
2010 if (nSubType == REF_SEQUENCEFLD)
2011 aField15[0] |= (0x4 << 5);
2013 // This ought to apply to any field, but just to be safe, start off with DATE/TIME only.
2014 if (pField->GetTyp()->Which() == SwFieldIds::DateTime
2015 && (pField->GetSubType() & FIXEDFLD))
2017 //bit 5 - Locked: do not recalculate field
2018 aField15[1] |= 0x10;
2022 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField15 );
2023 InsertSpecialChar( *this, 0x15, nullptr, bIncludeEmptyPicLocation );
2026 void WW8Export::StartCommentOutput(std::u16string_view rName)
2028 const OUString sStr{ FieldString(ww::eQUOTE) + "[" + rName + "] " };
2029 OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::Start | FieldFlags::CmdStart);
2032 void WW8Export::EndCommentOutput(std::u16string_view rName)
2034 const OUString sStr{ OUString::Concat(" [") + rName + "] " };
2035 OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::CmdEnd | FieldFlags::End |
2036 FieldFlags::Close);
2039 sal_uInt16 MSWordExportBase::GetId( const SwTOXType& rTOXType )
2041 std::vector<const SwTOXType*>::iterator it
2042 = std::find( m_aTOXArr.begin(), m_aTOXArr.end(), &rTOXType );
2043 if ( it != m_aTOXArr.end() )
2045 return it - m_aTOXArr.begin();
2047 m_aTOXArr.push_back( &rTOXType );
2048 return m_aTOXArr.size() - 1;
2051 // return values: 1 - no PageNum,
2052 // 2 - TabStop before PageNum,
2053 // 3 - Text before PageNum - rText hold the text
2054 // 4 - no Text and no TabStop before PageNum
2055 static int lcl_CheckForm( const SwForm& rForm, sal_uInt8 nLvl, OUString& rText )
2057 int nRet = 4;
2058 rText.clear();
2060 // #i21237#
2061 SwFormTokens aPattern = rForm.GetPattern(nLvl);
2062 SwFormTokens::iterator aIt = aPattern.begin();
2063 FormTokenType eTType;
2065 // #i61362#
2066 if (! aPattern.empty())
2068 bool bPgNumFnd = false;
2070 // #i21237#
2071 while( ++aIt != aPattern.end() && !bPgNumFnd )
2073 eTType = aIt->eTokenType;
2075 switch( eTType )
2077 case TOKEN_PAGE_NUMS:
2078 bPgNumFnd = true;
2079 break;
2081 case TOKEN_TAB_STOP:
2082 nRet = 2;
2083 break;
2084 case TOKEN_TEXT:
2086 nRet = 3;
2087 sal_Int32 nCount = std::min<sal_Int32>(5, aIt->sText.getLength());
2088 rText = aIt->sText.copy(0, nCount); // #i21237#
2089 break;
2091 case TOKEN_LINK_START:
2092 case TOKEN_LINK_END:
2093 break;
2095 default:
2096 nRet = 4;
2097 break;
2101 if( !bPgNumFnd )
2102 nRet = 1;
2105 return nRet;
2108 static bool lcl_IsHyperlinked(const SwForm& rForm, sal_uInt16 nTOXLvl)
2110 bool bRes = false;
2111 for (sal_uInt16 nI = 1; nI <= nTOXLvl; ++nI)
2113 // #i21237#
2114 SwFormTokens aPattern = rForm.GetPattern(nI);
2116 if ( !aPattern.empty() )
2118 SwFormTokens::iterator aIt = aPattern.begin();
2120 FormTokenType eTType;
2122 // #i21237#
2123 while ( ++aIt != aPattern.end() )
2125 eTType = aIt->eTokenType;
2126 switch (eTType)
2128 case TOKEN_LINK_START:
2129 case TOKEN_LINK_END:
2130 bRes = true;
2131 break;
2132 default:
2138 return bRes;
2141 void AttributeOutputBase::GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter)
2143 if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) // Not implemented for RTF
2144 return;
2146 const SwpHints* pTextAttrs = rNode.GetpSwpHints();
2147 if (!pTextAttrs)
2148 return;
2150 for( size_t i = 0; i < pTextAttrs->Count(); ++i )
2152 const SwTextAttr* pHt = pTextAttrs->Get(i);
2153 if (pHt->GetAttr().Which() == RES_TXTATR_FIELD)
2155 const SwFormatField& rField = static_cast<const SwFormatField&>(pHt->GetAttr());
2156 const SwField* pField = rField.GetField();
2157 // Need to have bookmarks only for sequence fields
2158 if (pField && pField->GetTyp()->Which() == SwFieldIds::SetExp && pField->GetSubType() == nsSwGetSetExpType::GSE_SEQ)
2160 const sal_uInt16 nSeqFieldNumber = static_cast<const SwSetExpField*>(pField)->GetSeqNumber();
2161 const OUString sObjectName = static_cast<const SwSetExpFieldType*>(pField->GetTyp())->GetName();
2162 const SwFieldTypes* pFieldTypes = GetExport().m_rDoc.getIDocumentFieldsAccess().GetFieldTypes();
2163 bool bHaveFullBkm = false;
2164 bool bHaveLabelAndNumberBkm = false;
2165 bool bHaveCaptionOnlyBkm = false;
2166 bool bHaveNumberOnlyBkm = false;
2167 bool bRunSplittedAtSep = false;
2168 for( auto const & pFieldType : *pFieldTypes )
2170 if( SwFieldIds::GetRef == pFieldType->Which() )
2172 SwIterator<SwFormatField,SwFieldType> aIter( *pFieldType );
2173 for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() )
2175 SwGetRefField* pRefField = static_cast<SwGetRefField*>(pFormatField->GetField());
2176 // If we have a reference to the current sequence field
2177 if(pRefField->GetSeqNo() == nSeqFieldNumber && pRefField->GetSetRefName() == sObjectName)
2179 // Need to create a separate run for separator character
2180 SwWW8AttrIter aLocalAttrIter( GetExport(), rNode ); // We need a local iterator having the right number of runs
2181 const OUString& aText = rNode.GetText();
2182 const sal_Int32 nCategoryStart = aText.indexOf(pRefField->GetSetRefName());
2183 const sal_Int32 nPosBeforeSeparator = std::max(nCategoryStart, pHt->GetStart());
2184 bool bCategoryFirst = nCategoryStart < pHt->GetStart();
2185 sal_Int32 nSeparatorPos = 0;
2186 if (bCategoryFirst)
2188 nSeparatorPos = aLocalAttrIter.WhereNext();
2189 while (nSeparatorPos <= nPosBeforeSeparator)
2191 aLocalAttrIter.NextPos();
2192 nSeparatorPos = aLocalAttrIter.WhereNext();
2195 else
2197 nSeparatorPos = nCategoryStart + pRefField->GetSetRefName().getLength();
2199 sal_Int32 nRefTextPos = 0;
2200 if(nSeparatorPos < aText.getLength())
2202 nRefTextPos = SwGetExpField::GetReferenceTextPos(pHt->GetFormatField(), GetExport().m_rDoc, nSeparatorPos);
2203 if(nRefTextPos != nSeparatorPos)
2205 if(!bRunSplittedAtSep)
2207 if(!bCategoryFirst)
2208 rAttrIter.SplitRun(nSeparatorPos);
2209 rAttrIter.SplitRun(nRefTextPos);
2210 bRunSplittedAtSep = true;
2212 if(!bCategoryFirst)
2213 aLocalAttrIter.SplitRun(nSeparatorPos);
2214 aLocalAttrIter.SplitRun(nRefTextPos);
2216 else if (bCategoryFirst)
2218 if(!bRunSplittedAtSep)
2220 rAttrIter.SplitRun(nSeparatorPos);
2221 bRunSplittedAtSep = true;
2223 aLocalAttrIter.SplitRun(nSeparatorPos);
2226 // Generate bookmarks on the right position
2227 OUString sName("Ref_" + pRefField->GetSetRefName() + OUString::number(pRefField->GetSeqNo()));
2228 switch (pRefField->GetFormat())
2230 case REF_PAGE:
2231 case REF_PAGE_PGDESC:
2232 case REF_CONTENT:
2233 case REF_UPDOWN:
2234 if(!bHaveFullBkm)
2236 sal_Int32 nLastAttrStart = 0;
2237 sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
2238 while (nActAttr < rNode.GetText().getLength())
2240 nLastAttrStart = nActAttr;
2241 aLocalAttrIter.NextPos();
2242 nActAttr = aLocalAttrIter.WhereNext();
2244 WriteBookmarkInActParagraph( sName + "_full", std::min(nCategoryStart, pHt->GetStart()), nLastAttrStart );
2245 bHaveFullBkm = true;
2247 break;
2248 case REF_ONLYNUMBER:
2250 if(!bHaveLabelAndNumberBkm)
2252 sName += "_label_and_number";
2253 if(bCategoryFirst)
2254 WriteBookmarkInActParagraph( sName, std::min(nCategoryStart, pHt->GetStart()), std::max(nCategoryStart, pHt->GetStart()) );
2255 else
2257 // Find the last run which contains category text
2258 SwWW8AttrIter aLocalAttrIter2( GetExport(), rNode );
2259 sal_Int32 nCatLastRun = 0;
2260 sal_Int32 nNextAttr = aLocalAttrIter2.WhereNext();
2261 while (nNextAttr < nSeparatorPos)
2263 nCatLastRun = nNextAttr;
2264 aLocalAttrIter2.NextPos();
2265 nNextAttr = aLocalAttrIter2.WhereNext();
2267 WriteBookmarkInActParagraph( sName, pHt->GetStart(), nCatLastRun );
2269 bHaveLabelAndNumberBkm = true;
2271 break;
2273 case REF_ONLYCAPTION:
2275 if(!bHaveCaptionOnlyBkm)
2277 // Find last run
2278 sal_Int32 nLastAttrStart = 0;
2279 sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
2280 while (nActAttr < rNode.GetText().getLength())
2282 nLastAttrStart = nActAttr;
2283 aLocalAttrIter.NextPos();
2284 nActAttr = aLocalAttrIter.WhereNext();
2286 WriteBookmarkInActParagraph( sName + "_caption_only", nRefTextPos, nLastAttrStart );
2287 bHaveCaptionOnlyBkm = true;
2289 break;
2291 case REF_ONLYSEQNO:
2293 if(!bHaveNumberOnlyBkm)
2295 WriteBookmarkInActParagraph( sName + "_number_only", pHt->GetStart(), pHt->GetStart() );
2296 bHaveNumberOnlyBkm = true;
2298 break;
2305 return;
2311 static auto GetSeparatorForLocale() -> OUString
2313 switch (sal_uInt16(MsLangId::getSystemLanguage()))
2315 case sal_uInt16(LANGUAGE_GERMAN):
2316 case sal_uInt16(LANGUAGE_GERMAN_AUSTRIAN):
2317 case sal_uInt16(LANGUAGE_GERMAN_LIECHTENSTEIN):
2318 case sal_uInt16(LANGUAGE_GERMAN_LUXEMBOURG):
2319 case sal_uInt16(LANGUAGE_GERMAN_SWISS):
2320 return ";";
2321 default:
2322 return ",";
2326 void AttributeOutputBase::StartTOX( const SwSection& rSect )
2328 if ( const SwTOXBase* pTOX = rSect.GetTOXBase() )
2330 static const char sEntryEnd[] = "\" ";
2332 ww::eField eCode = ww::eTOC;
2333 OUString sStr = pTOX ->GetMSTOCExpression();
2334 if ( sStr.isEmpty() )
2336 OUString sUserTypeName;
2337 auto aType = pTOX->GetType();
2338 // user index, it needs INDEX with \f
2339 if ( TOX_USER == aType )
2341 sUserTypeName = pTOX->GetTOXType()->GetTypeName();
2342 if ( !sUserTypeName.isEmpty() )
2343 aType = TOX_INDEX;
2345 switch (aType)
2347 case TOX_INDEX:
2348 eCode = ww::eINDEX;
2349 sStr = FieldString(eCode);
2352 const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
2353 const SwColumns& rColumns = rCol.GetColumns();
2354 sal_Int32 nCol = rColumns.size();
2356 if ( 0 < nCol )
2358 // Add a continuous section break
2359 if( GetExport().AddSectionBreaksForTOX() )
2361 SwSection *pParent = rSect.GetParent();
2362 WW8_SepInfo rInfo(&GetExport().m_rDoc.GetPageDesc(0),
2363 pParent ? pParent->GetFormat() : nullptr, 0/*nRstLnNum*/);
2364 GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
2367 sStr += "\\c \"" + OUString::number( nCol ) + "\"";
2371 if (pTOX->GetTOXForm().IsCommaSeparated())
2372 sStr += "\\r ";
2374 if (SwTOIOptions::AlphaDelimiter & pTOX->GetOptions())
2375 sStr += "\\h \"A\" ";
2377 if (!sUserTypeName.isEmpty())
2379 sStr += "\\f \"" + sUserTypeName + "\"";
2382 if (!pTOX->GetTOXForm().IsCommaSeparated())
2384 // In case of Run-in style no separators are added.
2385 OUString aFillText;
2386 for (sal_uInt8 n = 1; n <= 3; ++n)
2388 OUString aText;
2389 int nRet = ::lcl_CheckForm(pTOX->GetTOXForm(), n, aText);
2391 if( 3 == nRet )
2392 aFillText = aText;
2393 else if ((4 == nRet) || (2 == nRet))
2394 aFillText = "\t";
2395 else
2396 aFillText.clear();
2398 sStr += "\\e \"" + aFillText + sEntryEnd;
2400 break;
2402 case TOX_ILLUSTRATIONS:
2403 case TOX_OBJECTS:
2404 case TOX_TABLES:
2405 if (!pTOX->IsFromObjectNames())
2407 sStr = FieldString(eCode) + "\\c ";
2408 const OUString& seqName = pTOX->GetSequenceName();
2409 if(!seqName.isEmpty())
2411 sStr += "\"" + seqName + sEntryEnd;
2413 OUString aText;
2414 int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(), 1, aText );
2415 if (1 == nRet)
2416 sStr += "\\n ";
2417 else if( 3 == nRet || 4 == nRet )
2419 sStr += "\\p \"" + aText + sEntryEnd;
2422 if (lcl_IsHyperlinked(pTOX->GetTOXForm(), 1))
2424 sStr += "\\h ";
2426 if (pTOX->GetCreateType() & SwTOXElement::Template)
2428 OUString const& rStyle(pTOX->GetStyleNames(0));
2429 assert(rStyle.indexOf(TOX_STYLE_DELIMITER) == -1);
2430 SwTextFormatColl const*const pColl = GetExport().m_rDoc.FindTextFormatCollByName(rStyle);
2431 if (pColl)
2433 OUString const converted(GetExport().m_pStyles->GetStyleWWName(pColl));
2434 if (!converted.isEmpty())
2436 sStr += "\\t \"" + converted + sEntryEnd;
2440 break;
2442 case TOX_AUTHORITIES:
2443 eCode = ww::eBIBLIOGRAPHY;
2444 sStr = FieldString(eCode);
2445 break;
2446 // case TOX_USER:
2447 // case TOX_CONTENT:
2448 default:
2450 sStr = FieldString(eCode);
2452 OUString sTOption;
2453 // tdf#153082 Word's separator interpretation in DOCX
2454 // fields varies by system locale.
2455 auto const tsep(GetSeparatorForLocale());
2456 sal_uInt16 n, nTOXLvl = pTOX->GetLevel();
2457 if( !nTOXLvl )
2458 ++nTOXLvl;
2460 if(SwTOXElement::TableLeader & pTOX->GetCreateType())
2462 sStr +="\\z " ;
2463 GetExport( ).m_bHideTabLeaderAndPageNumbers = true ;
2465 if(SwTOXElement::TableInToc & pTOX->GetCreateType())
2467 sStr +="\\w " ;
2468 GetExport( ).m_bTabInTOC = true ;
2470 if(SwTOXElement::Newline & pTOX->GetCreateType())
2472 sStr +="\\x " ;
2474 if( SwTOXElement::Mark & pTOX->GetCreateType() )
2476 sStr += "\\f ";
2478 if( TOX_USER == pTOX->GetType() )
2480 sStr += "\""
2481 + OUStringChar(static_cast<char>( 'A' + GetExport( ).GetId( *pTOX->GetTOXType() ) ))
2482 + sEntryEnd;
2485 if(SwTOXElement::Bookmark & pTOX->GetCreateType())
2487 sStr += "\\b \"" + pTOX->GetBookmarkName() + sEntryEnd;
2490 if( SwTOXElement::OutlineLevel & pTOX->GetCreateType() )
2492 // Take the TOC value of the max level to evaluate to as
2493 // the starting point for the \o flag, but reduce it to the
2494 // value of the highest outline level filled by a *standard*
2495 // Heading 1 - 9 style because \o "Builds a table of
2496 // contents from paragraphs formatted with built-in heading
2497 // styles". And afterward fill in any outline styles left
2498 // uncovered by that range to the \t flag
2500 // i.e. for
2501 // Heading 1
2502 // Heading 2
2503 // custom-style
2504 // Heading 4
2505 // output
2506 // \o 1-2 \tcustom-style,3,Heading 3,4
2508 // Search over all the outline styles used and figure out
2509 // what is the minimum outline level (if any) filled by a
2510 // non-standard style for that level, i.e. ignore headline
2511 // styles 1-9 and find the lowest valid outline level
2512 sal_uInt8 nPosOfLowestNonStandardLvl = MAXLEVEL;
2513 const SwTextFormatColls& rColls = *GetExport().m_rDoc.GetTextFormatColls();
2514 for( n = rColls.size(); n; )
2516 const SwTextFormatColl* pColl = rColls[ --n ];
2517 sal_uInt16 nPoolId = pColl->GetPoolFormatId();
2518 if (
2519 //Is a Non-Standard Outline Style
2520 (RES_POOLCOLL_HEADLINE1 > nPoolId || RES_POOLCOLL_HEADLINE9 < nPoolId) &&
2521 //Has a valid outline level
2522 (pColl->IsAssignedToListLevelOfOutlineStyle()) &&
2523 // Is less than the lowest known non-standard level
2524 (pColl->GetAssignedOutlineStyleLevel() < nPosOfLowestNonStandardLvl)
2527 nPosOfLowestNonStandardLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
2531 sal_uInt8 nMaxMSAutoEvaluate = nPosOfLowestNonStandardLvl < nTOXLvl ? nPosOfLowestNonStandardLvl : static_cast<sal_uInt8>(nTOXLvl);
2533 //output \o 1-X where X is the highest normal outline style to be included in the toc
2534 if ( nMaxMSAutoEvaluate )
2536 if (nMaxMSAutoEvaluate > WW8ListManager::nMaxLevel)
2537 nMaxMSAutoEvaluate = WW8ListManager::nMaxLevel;
2539 sStr += "\\o \"1-" + OUString::number(nMaxMSAutoEvaluate) + sEntryEnd;
2542 //collect up any other styles in the writer TOC which will
2543 //not already appear in the MS TOC and place then into the
2544 //\t option
2545 if( nMaxMSAutoEvaluate < nTOXLvl )
2547 // collect this templates into the \t option
2548 for( n = rColls.size(); n;)
2550 const SwTextFormatColl* pColl = rColls[ --n ];
2551 if (!pColl->IsAssignedToListLevelOfOutlineStyle())
2552 continue;
2553 sal_uInt8 nTestLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
2554 if (nTestLvl < nTOXLvl && nTestLvl >= nMaxMSAutoEvaluate)
2556 if (!sTOption.isEmpty())
2557 sTOption += tsep;
2558 sTOption += pColl->GetName() + tsep + OUString::number(nTestLvl + 1);
2564 if( SwTOXElement::ParagraphOutlineLevel & pTOX->GetCreateType() )
2566 sStr +="\\u " ;
2569 if( SwTOXElement::Template & pTOX->GetCreateType() )
2571 // #i99641# - Consider additional styles regardless of TOX-outlinelevel
2572 for( n = 0; n < MAXLEVEL; ++n )
2574 const OUString& rStyles = pTOX->GetStyleNames( n );
2575 if( !rStyles.isEmpty() )
2577 sal_Int32 nPos = 0;
2578 const OUString sLvl{tsep + OUString::number(n + 1)};
2579 do {
2580 const OUString sStyle( rStyles.getToken( 0, TOX_STYLE_DELIMITER, nPos ));
2581 if( !sStyle.isEmpty() )
2583 SwTextFormatColl* pColl = GetExport().m_rDoc.FindTextFormatCollByName(sStyle);
2584 if (pColl)
2586 OUString const converted(GetExport().m_pStyles->GetStyleWWName(pColl));
2587 if (!converted.isEmpty() &&
2588 (!pColl->IsAssignedToListLevelOfOutlineStyle()
2589 || pColl->GetAssignedOutlineStyleLevel() < nTOXLvl))
2591 if( !sTOption.isEmpty() )
2592 sTOption += tsep;
2593 sTOption += converted + sLvl;
2597 } while( -1 != nPos );
2602 // No 'else' branch; why the below snippet is a block I have no idea.
2604 OUString aFillText;
2605 sal_uInt8 nNoPgStt = MAXLEVEL, nNoPgEnd = MAXLEVEL;
2606 bool bFirstFillText = true, bOnlyText = true;
2607 for( n = 0; n < nTOXLvl; ++n )
2609 OUString aText;
2610 int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(),
2611 static_cast< sal_uInt8 >(n+1), aText );
2612 if( 1 == nRet )
2614 bOnlyText = false;
2615 if( MAXLEVEL == nNoPgStt )
2616 nNoPgStt = static_cast< sal_uInt8 >(n+1);
2618 else
2620 if( MAXLEVEL != nNoPgStt &&
2621 MAXLEVEL == nNoPgEnd )
2622 nNoPgEnd = sal_uInt8(n);
2624 bOnlyText = bOnlyText && 3 == nRet;
2625 if( 3 == nRet || 4 == nRet )
2627 if( bFirstFillText )
2628 aFillText = aText;
2629 else if( aFillText != aText )
2630 aFillText.clear();
2631 bFirstFillText = false;
2635 if( MAXLEVEL != nNoPgStt )
2637 if (WW8ListManager::nMaxLevel < nNoPgEnd)
2638 nNoPgEnd = WW8ListManager::nMaxLevel;
2639 sStr += "\\n "
2640 + OUString::number( nNoPgStt )
2641 + "-"
2642 + OUString::number( nNoPgEnd )
2643 + " ";
2645 if( bOnlyText )
2647 sStr += "\\p \"" + aFillText + sEntryEnd;
2651 if( !sTOption.isEmpty() )
2653 sStr += "\\t \"" + sTOption + sEntryEnd;
2656 if (lcl_IsHyperlinked(pTOX->GetTOXForm(), nTOXLvl))
2657 sStr += "\\h";
2658 break;
2663 if (!sStr.isEmpty())
2665 GetExport( ).m_bInWriteTOX = true;
2666 if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
2667 { // tdf#129574: required for RTF; doesn't work with DOCX
2668 StartRun(nullptr, -42, true);
2670 GetExport( ).OutputField( nullptr, eCode, sStr, FieldFlags::Start | FieldFlags::CmdStart |
2671 FieldFlags::CmdEnd );
2672 if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
2674 EndRun(nullptr, -42, -1, true);
2679 GetExport( ).m_bStartTOX = false;
2682 void AttributeOutputBase::EndTOX( const SwSection& rSect,bool bCareEnd )
2684 const SwTOXBase* pTOX = rSect.GetTOXBase();
2685 if ( pTOX )
2687 ww::eField eCode = TOX_INDEX == pTOX->GetType() ? ww::eINDEX : ww::eTOC;
2688 GetExport( ).OutputField( nullptr, eCode, OUString(), FieldFlags::Close );
2690 if ( pTOX->GetType() == TOX_INDEX && GetExport().AddSectionBreaksForTOX() )
2692 const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
2693 const SwColumns& rColumns = rCol.GetColumns();
2694 sal_Int32 nCol = rColumns.size();
2696 if ( 0 < nCol )
2698 WW8_SepInfo rInfo( &GetExport().m_rDoc.GetPageDesc( 0 ), rSect.GetFormat(), 0/*nRstLnNum*/ );
2699 GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
2703 GetExport( ).m_bInWriteTOX = false;
2704 GetExport( ).m_bHideTabLeaderAndPageNumbers = false;
2705 if (bCareEnd)
2706 OnTOXEnding();
2709 bool MSWordExportBase::GetNumberFormat(const SwField& rField, OUString& rStr)
2711 // Returns a date or time format string by using the US NfKeywordTable
2712 bool bHasFormat = false;
2713 SvNumberFormatter* pNFormatr = m_rDoc.GetNumberFormatter();
2714 sal_uInt32 nFormatIdx = rField.GetFormat();
2715 const SvNumberformat* pNumFormat = pNFormatr->GetEntry( nFormatIdx );
2716 if( pNumFormat )
2718 LanguageType nLng = rField.GetLanguage();
2719 SAL_WARN_IF(nLng == LANGUAGE_DONTKNOW, "sw.ww8", "unexpected LANGUAGE_DONTKNOW");
2720 if (nLng == LANGUAGE_NONE || nLng == LANGUAGE_DONTKNOW)
2722 nLng = pNumFormat->GetLanguage();
2724 LocaleDataWrapper aLocDat(pNFormatr->GetComponentContext(),
2725 LanguageTag(nLng));
2727 OUString sFormat(pNumFormat->GetMappedFormatstring(GetNfKeywordTable(),
2728 aLocDat));
2730 if (!sFormat.isEmpty())
2732 sw::ms::SwapQuotesInField(sFormat);
2734 rStr = "\\@\"" + sFormat + "\" " ;
2735 bHasFormat = true;
2738 return bHasFormat;
2741 void AttributeOutputBase::GetNumberPara( OUString& rStr, const SwField& rField )
2743 switch(rField.GetFormat())
2745 case SVX_NUM_CHARS_UPPER_LETTER:
2746 case SVX_NUM_CHARS_UPPER_LETTER_N:
2747 rStr += "\\* ALPHABETIC ";
2748 break;
2749 case SVX_NUM_CHARS_LOWER_LETTER:
2750 case SVX_NUM_CHARS_LOWER_LETTER_N:
2751 rStr += "\\* alphabetic ";
2752 break;
2753 case SVX_NUM_ROMAN_UPPER:
2754 rStr += "\\* ROMAN ";
2755 break;
2756 case SVX_NUM_ROMAN_LOWER:
2757 rStr += "\\* roman ";
2758 break;
2759 case SVX_NUM_TEXT_NUMBER:
2760 rStr += "\\* Ordinal ";
2761 break;
2762 case SVX_NUM_TEXT_ORDINAL:
2763 rStr += "\\* Ordtext ";
2764 break;
2765 case SVX_NUM_TEXT_CARDINAL:
2766 rStr += "\\* Cardtext ";
2767 break;
2768 default:
2769 OSL_ENSURE(rField.GetFormat() == SVX_NUM_ARABIC,
2770 "Unknown numbering type exported as default of Arabic");
2771 [[fallthrough]];
2772 case SVX_NUM_ARABIC:
2773 rStr += "\\* ARABIC ";
2774 break;
2775 case SVX_NUM_PAGEDESC:
2776 //Nothing, use word's default
2777 break;
2781 void WW8Export::WritePostItBegin( ww::bytes* pOut )
2783 sal_uInt8 aArr[ 3 ];
2784 sal_uInt8* pArr = aArr;
2786 // sprmCFSpec true
2787 Set_UInt16( pArr, NS_sprm::CFSpec::val );
2788 Set_UInt8( pArr, 1 );
2790 m_pChpPlc->AppendFkpEntry( Strm().Tell() );
2791 WriteChar( 0x05 ); // Annotation reference
2793 if( pOut )
2794 pOut->insert( pOut->end(), aArr, pArr );
2795 else
2796 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
2799 OUString FieldString(ww::eField eIndex)
2801 if (const char *pField = ww::GetEnglishFieldName(eIndex))
2802 return " " + OUString::createFromAscii(pField) + " ";
2803 return " ";
2806 void WW8AttributeOutput::HiddenField( const SwField& rField )
2808 //replace LF 0x0A with VT 0x0B
2809 const OUString sExpand(rField.GetPar2().replace(0x0A, 0x0B));
2811 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell());
2812 SwWW8Writer::WriteString16(m_rWW8Export.Strm(), sExpand, false);
2813 static sal_uInt8 aArr[] =
2815 0x3C, 0x08, 0x1
2817 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), sizeof(aArr), aArr);
2820 void WW8AttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd )
2822 const SwSetExpField* pSet = static_cast<const SwSetExpField*>(&rField);
2823 const OUString &rVar = pSet->GetPar2();
2825 sal_uLong nFrom = m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell());
2827 GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Start |
2828 FieldFlags::CmdStart | FieldFlags::CmdEnd);
2831 Is there a bookmark at the start position of this field, if so
2832 move it to the 0x14 of the result of the field. This is what word
2833 does. MoveFieldMarks moves any bookmarks at this position to
2834 the beginning of the field result, and marks the bookmark as a
2835 fieldbookmark which is to be ended before the field end mark
2836 instead of after it like a normal bookmark.
2838 m_rWW8Export.MoveFieldMarks(nFrom,m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell()));
2840 if (!rVar.isEmpty())
2842 SwWW8Writer::WriteString16(m_rWW8Export.Strm(), rVar, false);
2844 GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Close);
2847 void WW8AttributeOutput::PostitField( const SwField* pField )
2849 const SwPostItField *pPField = static_cast<const SwPostItField*>(pField);
2850 m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pPField );
2851 m_rWW8Export.WritePostItBegin( m_rWW8Export.m_pO.get() );
2854 bool WW8AttributeOutput::DropdownField( const SwField* pField )
2856 const SwDropDownField& rField2 = *static_cast<const SwDropDownField*>(pField);
2857 uno::Sequence<OUString> aItems =
2858 rField2.GetItemSequence();
2859 GetExport().DoComboBox(rField2.GetName(),
2860 rField2.GetHelp(),
2861 rField2.GetToolTip(),
2862 rField2.GetSelectedItem(), aItems);
2863 return false;
2866 bool WW8AttributeOutput::PlaceholderField( const SwField* )
2868 return true; // expand to text?
2871 void WW8AttributeOutput::RefField( const SwField &rField, const OUString &rRef)
2873 const OUString sStr{ FieldString( ww::eREF ) + "\"" + rRef + "\" " };
2874 m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Start |
2875 FieldFlags::CmdStart | FieldFlags::CmdEnd );
2876 const OUString sVar = lcl_GetExpandedField( rField );
2877 if ( !sVar.isEmpty() )
2879 SwWW8Writer::WriteString16( m_rWW8Export.Strm(), sVar, false );
2881 m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Close );
2884 void WW8AttributeOutput::WriteExpand( const SwField* pField )
2886 SwWW8Writer::WriteString16( m_rWW8Export.Strm(), lcl_GetExpandedField( *pField ), false );
2889 namespace
2891 // Escapes a token string for storing in Word formats. Its import counterpart
2892 // is lcl_ExtractToken in writerfilter/source/dmapper/DomainMapper_Impl.cxx
2893 OUString EscapeToken(const OUString& rCommand)
2895 bool bWasEscaped = false;
2897 const int nBufferLen = rCommand.getLength()*1.5;
2898 OUStringBuffer sResult(nBufferLen);
2899 sResult.append('"'); // opening quote
2900 for (sal_Int32 i = 0; i < rCommand.getLength(); ++i)
2902 sal_Unicode ch = rCommand[i];
2903 switch (ch)
2905 case '\\':
2906 case '"':
2907 // Backslashes and doublequotes must be escaped
2908 bWasEscaped = true;
2909 sResult.append('\\');
2910 break;
2911 case ' ':
2912 // Spaces require quotation
2913 bWasEscaped = true;
2914 break;
2916 sResult.append(ch);
2919 if (bWasEscaped)
2921 sResult.append('"'); // closing quote
2922 return sResult.makeStringAndClear();
2924 // No escapement/quotation was required
2925 return rCommand;
2929 void AttributeOutputBase::TextField( const SwFormatField& rField )
2931 const SwField* pField = rField.GetField();
2932 bool bWriteExpand = false;
2933 const sal_uInt16 nSubType = pField->GetSubType();
2935 switch (pField->GetTyp()->Which())
2937 case SwFieldIds::GetExp:
2938 if (nSubType == nsSwGetSetExpType::GSE_STRING)
2940 const SwGetExpField *pGet = static_cast<const SwGetExpField*>(pField);
2941 RefField( *pGet, pGet->GetFormula() );
2943 else
2944 bWriteExpand = true;
2945 break;
2946 case SwFieldIds::SetExp:
2947 if (nsSwGetSetExpType::GSE_SEQ == nSubType)
2949 OUString sStr;
2950 if (GetExport().FieldsQuoted())
2951 sStr = FieldString(ww::eSEQ) + pField->GetTyp()->GetName() + " ";
2952 else
2953 sStr = FieldString(ww::eSEQ) + "\"" + pField->GetTyp()->GetName() +"\" ";
2954 GetNumberPara( sStr, *pField );
2955 GetExport().OutputField(pField, ww::eSEQ, sStr);
2957 else if (nSubType & nsSwGetSetExpType::GSE_STRING)
2959 bool bShowAsWell = false;
2960 ww::eField eFieldNo;
2961 const SwSetExpField *pSet = static_cast<const SwSetExpField*>(pField);
2962 const OUString sVar = pSet->GetPar2();
2963 OUString sStr;
2964 if (pSet->GetInputFlag())
2966 sStr = FieldString(ww::eASK) + "\""
2967 + pSet->GetPar1() + "\" "
2968 + pSet->GetPromptText() + " \\d "
2969 + sVar;
2970 eFieldNo = ww::eASK;
2972 else
2974 sStr = FieldString(ww::eSET)
2975 + pSet->GetPar1() + " \""
2976 + sVar + "\" ";
2977 eFieldNo = ww::eSET;
2978 bShowAsWell = (nSubType & nsSwExtendedSubType::SUB_INVISIBLE) == 0;
2981 SetField( *pField, eFieldNo, sStr );
2983 if (bShowAsWell)
2984 RefField( *pSet, pSet->GetPar1() );
2986 else
2987 bWriteExpand = true;
2988 break;
2989 case SwFieldIds::PageNumber:
2991 OUString sStr = FieldString(ww::ePAGE);
2992 GetNumberPara(sStr, *pField);
2993 GetExport().OutputField(pField, ww::ePAGE, sStr);
2995 break;
2996 case SwFieldIds::Filename:
2998 OUString sStr = FieldString(ww::eFILENAME);
2999 if (pField->GetFormat() == FF_PATHNAME)
3000 sStr += "\\p ";
3001 GetExport().OutputField(pField, ww::eFILENAME, sStr);
3003 break;
3004 case SwFieldIds::Database:
3006 OUString sStr = FieldString(ww::eMERGEFIELD)
3007 + EscapeToken(static_cast<SwDBFieldType *>(pField->GetTyp())->GetColumnName()) + " ";
3008 GetExport().OutputField(pField, ww::eMERGEFIELD, sStr);
3010 break;
3011 case SwFieldIds::DatabaseName:
3013 SwDBData aData = GetExport().m_rDoc.GetDBData();
3014 const OUString sStr = FieldString(ww::eDATABASE)
3015 + aData.sDataSource
3016 + OUStringChar(DB_DELIM)
3017 + aData.sCommand;
3018 GetExport().OutputField(pField, ww::eDATABASE, sStr);
3020 break;
3021 case SwFieldIds::Author:
3023 ww::eField eField =
3024 ((AF_SHORTCUT & pField->GetFormat()) ? ww::eUSERINITIALS : ww::eUSERNAME);
3025 GetExport().OutputField(pField, eField, FieldString(eField));
3027 break;
3028 case SwFieldIds::TemplateName:
3029 GetExport().OutputField(pField, ww::eTEMPLATE, FieldString(ww::eTEMPLATE));
3030 break;
3031 case SwFieldIds::DocInfo: // Last printed, last edited,...
3032 if( DI_SUB_FIXED & nSubType )
3033 bWriteExpand = true;
3036 OUString sStr;
3037 ww::eField eField(ww::eNONE);
3038 switch (0xff & nSubType)
3040 case DI_TITLE:
3041 eField = ww::eTITLE;
3042 break;
3043 case DI_SUBJECT:
3044 eField = ww::eSUBJECT;
3045 break;
3046 case DI_KEYS:
3047 eField = ww::eKEYWORDS;
3048 break;
3049 case DI_COMMENT:
3050 eField = ww::eCOMMENTS;
3051 break;
3052 case DI_DOCNO:
3053 eField = ww::eREVNUM;
3054 break;
3055 case DI_EDIT:
3056 eField = ww::eEDITTIME;
3057 break;
3058 case DI_CREATE:
3059 if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
3060 eField = ww::eAUTHOR;
3061 else if (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty())
3062 eField = ww::eCREATEDATE;
3064 // Create author/time are always imported as fixed. Safe to ignore on export
3065 bWriteExpand = false;
3066 break;
3068 case DI_CHANGE:
3069 if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
3071 eField = ww::eLASTSAVEDBY;
3072 bWriteExpand=false;
3074 else if (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty())
3075 eField = ww::eSAVEDATE;
3076 break;
3078 case DI_PRINT:
3079 if (DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK) &&
3080 (GetExport().GetNumberFormat(*pField, sStr) || sStr.isEmpty()))
3081 eField = ww::ePRINTDATE;
3082 break;
3083 case DI_CUSTOM:
3084 eField = ww::eDOCPROPERTY;
3086 const SwDocInfoField * pDocInfoField =
3087 dynamic_cast<const SwDocInfoField *> (pField);
3089 if (pDocInfoField != nullptr)
3090 sStr = "\"" + pDocInfoField->GetName() + "\"";
3092 bWriteExpand = false;
3094 break;
3095 default:
3096 break;
3099 if (!bWriteExpand && eField != ww::eNONE)
3101 GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
3103 else
3104 bWriteExpand = true;
3106 break;
3107 case SwFieldIds::DateTime:
3109 OUString sStr;
3110 if (!GetExport().GetNumberFormat(*pField, sStr))
3111 bWriteExpand = true;
3112 else
3114 ww::eField eField = (DATEFLD & nSubType) ? ww::eDATE : ww::eTIME;
3115 GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
3118 break;
3119 case SwFieldIds::DocStat:
3121 ww::eField eField = ww::eNONE;
3123 switch (nSubType)
3125 case DS_PAGE:
3126 eField = ww::eNUMPAGES;
3127 break;
3128 case DS_WORD:
3129 eField = ww::eNUMWORDS;
3130 break;
3131 case DS_CHAR:
3132 eField = ww::eNUMCHARS;
3133 break;
3136 if (eField != ww::eNONE)
3138 OUString sStr = FieldString(eField);
3139 GetNumberPara(sStr, *pField);
3140 GetExport().OutputField(pField, eField, sStr);
3142 else
3143 bWriteExpand = true;
3145 break;
3146 case SwFieldIds::ExtUser:
3148 ww::eField eField = ww::eNONE;
3149 switch (0xFF & nSubType)
3151 case EU_FIRSTNAME:
3152 case EU_NAME:
3153 eField = ww::eUSERNAME;
3154 break;
3155 case EU_SHORTCUT:
3156 eField = ww::eUSERINITIALS;
3157 break;
3158 case EU_STREET:
3159 case EU_COUNTRY:
3160 case EU_ZIP:
3161 case EU_CITY:
3162 eField = ww::eUSERADDRESS;
3163 break;
3166 if (eField != ww::eNONE)
3168 GetExport().OutputField(pField, eField, FieldString(eField));
3170 else
3171 bWriteExpand = true;
3173 break;
3174 case SwFieldIds::TableOfAuthorities:
3176 OUString sRet(static_cast<SwAuthorityField const*>(pField)
3177 ->ExpandCitation(AUTH_FIELD_IDENTIFIER, nullptr));
3178 // FIXME: DomainMapper_Impl::CloseFieldCommand() stuffs fully formed
3179 // field instructions in here, but if the field doesn't originate
3180 // from those filters it won't have that
3181 if (!o3tl::starts_with(o3tl::trim(sRet), u"CITATION"))
3183 sRet = FieldString(ww::eCITATION) + " \"" + sRet + "\"";
3185 GetExport().OutputField( pField, ww::eCITATION, sRet );
3187 break;
3188 case SwFieldIds::Postit:
3189 //Sadly only possible for word in main document text
3190 if (GetExport().m_nTextTyp == TXT_MAINTEXT)
3192 PostitField( pField );
3194 break;
3195 case SwFieldIds::Input:
3197 const SwInputField * pInputField = dynamic_cast<const SwInputField *>(pField);
3199 if (pInputField && pInputField->isFormField())
3200 GetExport().DoFormText(pInputField);
3201 else
3203 const OUString sStr = FieldString(ww::eFILLIN) + "\""
3204 + pField->GetPar2() + "\"";
3206 GetExport().OutputField(pField, ww::eFILLIN, sStr);
3209 break;
3210 case SwFieldIds::GetRef:
3212 ww::eField eField = ww::eNONE;
3213 OUString sStr;
3214 const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
3215 switch (nSubType)
3217 case REF_SETREFATTR:
3218 case REF_BOOKMARK:
3219 switch (pField->GetFormat())
3221 case REF_PAGE_PGDESC:
3222 case REF_PAGE:
3223 eField = ww::ePAGEREF;
3224 break;
3225 default:
3226 eField = ww::eREF;
3227 break;
3230 const OUString& aRefName(rRField.GetSetRefName());
3231 sStr = FieldString(eField)
3232 + GetExport().GetBookmarkName(nSubType, &aRefName, 0);
3234 switch (pField->GetFormat())
3236 case REF_NUMBER:
3237 sStr += " \\r";
3238 break;
3239 case REF_NUMBER_NO_CONTEXT:
3240 sStr += " \\n";
3241 break;
3242 case REF_NUMBER_FULL_CONTEXT:
3243 sStr += " \\w";
3244 break;
3246 break;
3247 case REF_SEQUENCEFLD:
3249 // Not implemented for RTF
3250 if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
3251 break;
3253 switch (pField->GetFormat())
3255 case REF_PAGE:
3256 case REF_PAGE_PGDESC:
3257 eField = ww::ePAGEREF;
3258 break;
3259 default:
3260 eField = ww::eREF;
3261 break;
3263 // Generate a unique bookmark name
3265 OUString sName{rRField.GetSetRefName() + OUString::number(rRField.GetSeqNo())};
3266 switch (pField->GetFormat())
3268 case REF_PAGE:
3269 case REF_PAGE_PGDESC:
3270 case REF_CONTENT:
3271 case REF_UPDOWN:
3272 sName += "_full";
3273 break;
3274 case REF_ONLYNUMBER:
3275 sName += "_label_and_number";
3276 break;
3277 case REF_ONLYCAPTION:
3278 sName += "_caption_only";
3279 break;
3280 case REF_ONLYSEQNO:
3281 sName += "_number_only";
3282 break;
3283 default: // Ignore other types of reference fields
3284 eField = ww::eNONE;
3285 break;
3287 sStr = FieldString(eField) + GetExport().GetBookmarkName(nSubType, &sName, 0);
3289 switch (pField->GetFormat())
3291 case REF_NUMBER:
3292 sStr += " \\r";
3293 break;
3294 case REF_NUMBER_NO_CONTEXT:
3295 sStr += " \\n";
3296 break;
3297 case REF_NUMBER_FULL_CONTEXT:
3298 sStr += " \\w";
3299 break;
3301 break;
3303 case REF_FOOTNOTE:
3304 case REF_ENDNOTE:
3305 switch (pField->GetFormat())
3307 case REF_PAGE_PGDESC:
3308 case REF_PAGE:
3309 eField = ww::ePAGEREF;
3310 break;
3311 case REF_UPDOWN:
3312 eField = ww::eREF;
3313 break;
3314 default:
3315 eField =
3316 REF_ENDNOTE == nSubType ? ww::eNOTEREF : ww::eFOOTREF;
3317 break;
3319 sStr = FieldString(eField)
3320 + GetExport().GetBookmarkName(nSubType, nullptr, rRField.GetSeqNo());
3321 break;
3324 if (eField != ww::eNONE)
3326 switch (pField->GetFormat())
3328 case REF_UPDOWN:
3329 sStr += " \\p \\h "; // with hyperlink
3330 break;
3331 case REF_CHAPTER:
3332 sStr += " \\n \\h "; // with hyperlink
3333 break;
3334 default:
3335 sStr += " \\h "; // insert hyperlink
3336 break;
3338 GetExport().OutputField(pField, eField, sStr);
3340 else
3341 bWriteExpand = true;
3343 break;
3344 case SwFieldIds::CombinedChars:
3347 We need a font size to fill in the defaults, if these are overridden
3348 (as they generally are) by character properties then those properties
3349 win.
3351 The fontsize that is used in MS for determining the defaults is always
3352 the CJK fontsize even if the text is not in that language, in OOo the
3353 largest fontsize used in the field is the one we should take, but
3354 whatever we do, word will actually render using the fontsize set for
3355 CJK text. Nevertheless we attempt to guess whether the script is in
3356 asian or western text based up on the first character and use the
3357 font size of that script as our default.
3359 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
3360 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( pField->GetPar1(), 0);
3362 TypedWhichId<SvxFontHeightItem> nFontHeightWhich = GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript);
3363 tools::Long nHeight = GetExport().GetItem(nFontHeightWhich).GetHeight();
3365 nHeight = (nHeight + 10) / 20; //Font Size in points;
3368 Divide the combined char string into its up and down part. Get the
3369 font size and fill in the defaults as up == half the font size and
3370 down == a fifth the font size
3372 const sal_Int32 nAbove = (pField->GetPar1().getLength()+1)/2;
3373 const OUString sStr = FieldString(ww::eEQ)
3374 + "\\o (\\s\\up "
3375 + OUString::number(nHeight/2)
3376 + "("
3377 + pField->GetPar1().subView(0, nAbove)
3378 + "), \\s\\do "
3379 + OUString::number(nHeight/5)
3380 + "("
3381 + pField->GetPar1().subView(nAbove)
3382 + "))";
3383 GetExport().OutputField(pField, ww::eEQ, sStr);
3385 break;
3386 case SwFieldIds::Dropdown:
3387 bWriteExpand = DropdownField( pField );
3388 break;
3389 case SwFieldIds::Chapter:
3390 bWriteExpand = true;
3391 if (GetExport().m_bOutKF && rField.GetTextField())
3393 const SwTextNode *pTextNd = GetExport().GetHdFtPageRoot();
3394 if (!pTextNd)
3396 pTextNd = GetExport().m_pCurPam->GetPointNode().GetTextNode();
3399 if (pTextNd)
3401 SwChapterField aCopy(*static_cast<const SwChapterField*>(pField));
3402 aCopy.ChangeExpansion(*pTextNd, false);
3403 const OUString sStr = FieldString(ww::eSTYLEREF)
3404 + " "
3405 + OUString::number(aCopy.GetLevel() + 1)
3406 + " \\* MERGEFORMAT ";
3407 GetExport().OutputField(pField, ww::eSTYLEREF, sStr);
3408 bWriteExpand = false;
3411 break;
3412 case SwFieldIds::HiddenText:
3414 OUString sExpand(pField->GetPar2());
3415 if (!sExpand.isEmpty())
3417 auto eSubType = static_cast<SwFieldTypesEnum>(pField->GetSubType());
3418 if (eSubType == SwFieldTypesEnum::ConditionalText)
3420 OUString aCond = pField->GetPar1();
3421 OUString aTrueFalse = pField->GetPar2();
3422 sal_Int32 nPos = aTrueFalse.indexOf('|');
3423 OUString aTrue;
3424 OUString aFalse;
3425 if (nPos == -1)
3427 aTrue = aTrueFalse;
3429 else
3431 aTrue = aTrueFalse.subView(0, nPos);
3432 aFalse = aTrueFalse.subView(nPos + 1);
3434 if (aTrue.getLength() > 1 && aTrue.startsWith("\"") && aTrue.endsWith("\""))
3435 aTrue = aTrue.copy(1, aTrue.getLength() - 2);
3436 if (aFalse.getLength() > 1 && aFalse.startsWith("\"") && aFalse.endsWith("\""))
3437 aFalse = aFalse.copy(1, aFalse.getLength() - 2);
3439 // Substitute a single quote for an illegal double quote if one exists
3440 OUString aCmd = FieldString(ww::eIF) + aCond + " \""
3441 + aTrue.replaceAll("\"", "'") + "\" \"" + aFalse.replaceAll("\"", "'")
3442 + "\"";
3443 GetExport().OutputField(pField, ww::eIF, aCmd);
3445 else
3446 HiddenField(*pField);
3449 break;
3450 case SwFieldIds::JumpEdit:
3451 bWriteExpand = PlaceholderField( pField );
3452 break;
3453 case SwFieldIds::Macro:
3455 const OUString sStr = " MACROBUTTON "
3456 + pField->GetPar1().replaceFirst("StarOffice.Standard.Modul1.", "")
3457 + " "
3458 + lcl_GetExpandedField(*pField);
3459 GetExport().OutputField( pField, ww::eMACROBUTTON, sStr );
3461 break;
3462 case SwFieldIds::Table:
3464 ww::eField eField = ww::eEquals;
3465 OUString aExpand = OUString::Concat(" =") + o3tl::trim(pField->GetFieldName());
3466 GetExport().OutputField(pField, eField, aExpand);
3468 break;
3469 case SwFieldIds::User:
3471 ww::eField eField = ww::eDOCVARIABLE;
3472 OUString aExpand = FieldString(eField) + pField->GetPar1() + " ";
3473 GetExport().OutputField(pField, eField, aExpand);
3475 break;
3476 default:
3477 bWriteExpand = true;
3478 break;
3481 if (bWriteExpand)
3482 WriteExpand( pField );
3485 void AttributeOutputBase::TextFlyContent( const SwFormatFlyCnt& rFlyContent )
3487 if ( auto pTextNd = dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) )
3489 Point const origin;
3490 Point aLayPos = pTextNd->FindLayoutRect( false, &origin ).Pos();
3492 SwPosition aPos( *pTextNd );
3493 ww8::Frame aFrame( *rFlyContent.GetFrameFormat(), std::move(aPos) );
3495 OutputFlyFrame_Impl( aFrame, aLayPos );
3499 // TOXMarks are still missing
3501 // WW allows detailed settings for hyphenation only for the whole document.
3502 // One could implement following mimic: The values of the style "Standard" will
3503 // be set in the Document Properties ( DOP ) if they exist.
3505 // ACK. This suggestion fits exactly to our implementation of the import,
3506 // therefore I'll implement that right now. (KHZ, 07/15/2000)
3507 void WW8AttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone )
3509 // sprmPFNoAutoHyph
3510 m_rWW8Export.InsUInt16( NS_sprm::PFNoAutoHyph::val );
3512 m_rWW8Export.m_pO->push_back( rHyphenZone.IsHyphen() ? 0 : 1 );
3515 void WW8AttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace )
3517 m_rWW8Export.InsUInt16( NS_sprm::PFAutoSpaceDE::val );
3518 m_rWW8Export.m_pO->push_back( rScriptSpace.GetValue() ? 1 : 0 );
3521 void WW8AttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem )
3523 m_rWW8Export.InsUInt16( NS_sprm::PFOverflowPunct::val );
3524 m_rWW8Export.m_pO->push_back( rItem.GetValue() ? 1 : 0 );
3527 void WW8AttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem )
3529 m_rWW8Export.InsUInt16( NS_sprm::PFKinsoku::val );
3530 m_rWW8Export.m_pO->push_back( rItem.GetValue() ? 1 : 0 );
3533 void WW8AttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid )
3535 // sprmPFUsePgsuSettings
3537 m_rWW8Export.InsUInt16( NS_sprm::PFUsePgsuSettings::val );
3538 m_rWW8Export.m_pO->push_back( rGrid.GetValue() ? 1 : 0 );
3541 void WW8AttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign )
3543 // sprmPWAlignFont
3545 m_rWW8Export.InsUInt16( NS_sprm::PWAlignFont::val );
3547 SvxParaVertAlignItem::Align nAlign = rAlign.GetValue();
3548 sal_uInt16 nVal;
3549 switch ( nAlign )
3551 case SvxParaVertAlignItem::Align::Baseline:
3552 nVal = 2;
3553 break;
3554 case SvxParaVertAlignItem::Align::Top:
3555 nVal = 0;
3556 break;
3557 case SvxParaVertAlignItem::Align::Center:
3558 nVal = 1;
3559 break;
3560 case SvxParaVertAlignItem::Align::Bottom:
3561 nVal = 3;
3562 break;
3563 case SvxParaVertAlignItem::Align::Automatic:
3564 nVal = 4;
3565 break;
3566 default:
3567 nVal = 4;
3568 OSL_FAIL( "Unknown vert alignment" );
3569 break;
3571 m_rWW8Export.InsUInt16( nVal );
3574 // NoHyphen: I didn't find an equal in the SW UI and WW UI
3576 // RefMark, NoLineBreakHere are still missing
3578 void WW8Export::WriteFootnoteBegin( const SwFormatFootnote& rFootnote, ww::bytes* pOutArr )
3580 ww::bytes aAttrArr;
3581 const bool bAutoNum = rFootnote.GetNumStr().isEmpty();
3582 if( bAutoNum )
3584 static const sal_uInt8 aSpec[] =
3586 0x03, 0x6a, 0, 0, 0, 0, // sprmCObjLocation
3587 0x55, 0x08, 1 // sprmCFSpec
3590 aAttrArr.insert(aAttrArr.end(), aSpec, aSpec+sizeof(aSpec));
3593 // sprmCIstd
3594 const SwEndNoteInfo* pInfo;
3595 if( rFootnote.IsEndNote() )
3596 pInfo = &m_rDoc.GetEndNoteInfo();
3597 else
3598 pInfo = &m_rDoc.GetFootnoteInfo();
3599 const SwCharFormat* pCFormat = pOutArr
3600 ? pInfo->GetAnchorCharFormat( m_rDoc )
3601 : pInfo->GetCharFormat( m_rDoc );
3602 SwWW8Writer::InsUInt16( aAttrArr, NS_sprm::CIstd::val );
3603 SwWW8Writer::InsUInt16( aAttrArr, GetId( pCFormat ) );
3605 // fSpec-Attribute true
3606 // For Auto-Number a special character must go
3607 // into the text and therefore a fSpec attribute
3608 m_pChpPlc->AppendFkpEntry( Strm().Tell() );
3609 if( bAutoNum )
3610 WriteChar( 0x02 ); // auto number character
3611 else
3612 // user numbering
3613 OutSwString(rFootnote.GetNumStr(), 0, rFootnote.GetNumStr().getLength());
3615 if( pOutArr )
3617 // insert at start of array, so the "hard" attribute overrule the
3618 // attributes of the character template
3619 pOutArr->insert( pOutArr->begin(), aAttrArr.begin(), aAttrArr.end() );
3621 else
3623 std::unique_ptr<ww::bytes> pOwnOutArr(new ww::bytes);
3625 // insert at start of array, so the "hard" attribute overrule the
3626 // attributes of the character template
3627 pOwnOutArr->insert(pOwnOutArr->begin(), aAttrArr.begin(), aAttrArr.end());
3629 // write for the ftn number in the content, the font of the anchor
3630 const SwTextFootnote* pTextFootnote = rFootnote.GetTextFootnote();
3631 if( pTextFootnote )
3633 std::unique_ptr<ww::bytes> pOld = std::move(m_pO);
3634 m_pO = std::move(pOwnOutArr);
3635 SfxItemSetFixed<RES_CHRATR_FONT, RES_CHRATR_FONT> aSet( m_rDoc.GetAttrPool() );
3637 pCFormat = pInfo->GetCharFormat( m_rDoc );
3639 pTextFootnote->GetTextNode().GetParaAttr(aSet,
3640 pTextFootnote->GetStart(), pTextFootnote->GetStart() + 1, true);
3641 if (aSet.Count())
3643 m_pAttrOutput->OutputItem( aSet.Get( RES_CHRATR_FONT ) );
3645 else
3647 m_pAttrOutput->OutputItem( pCFormat->GetAttrSet().Get(RES_CHRATR_FONT) );
3649 pOwnOutArr = std::move(m_pO);
3650 m_pO = std::move(pOld);
3652 m_pChpPlc->AppendFkpEntry( Strm().Tell(), pOwnOutArr->size(),
3653 pOwnOutArr->data() );
3657 static bool lcl_IsAtTextEnd(const SwFormatFootnote& rFootnote)
3659 bool bRet = true;
3660 if( rFootnote.GetTextFootnote() )
3662 sal_uInt16 nWh = rFootnote.IsEndNote() ? sal_uInt16(RES_END_AT_TXTEND)
3663 : sal_uInt16(RES_FTN_AT_TXTEND);
3664 const SwSectionNode* pSectNd = rFootnote.GetTextFootnote()->GetTextNode().
3665 FindSectionNode();
3666 while( pSectNd && FTNEND_ATPGORDOCEND ==
3667 static_cast<const SwFormatFootnoteEndAtTextEnd&>(pSectNd->GetSection().GetFormat()->
3668 GetFormatAttr( nWh)).GetValue() )
3669 pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
3671 if (!pSectNd)
3672 bRet = false; // the is ftn/end collected at Page- or Doc-End
3674 return bRet;
3677 void AttributeOutputBase::TextFootnote( const SwFormatFootnote& rFootnote )
3679 sal_uInt16 nTyp;
3680 if ( rFootnote.IsEndNote() )
3682 nTyp = REF_ENDNOTE;
3683 if ( GetExport().m_bEndAtTextEnd )
3684 GetExport().m_bEndAtTextEnd = lcl_IsAtTextEnd( rFootnote );
3686 else
3688 nTyp = REF_FOOTNOTE;
3689 if ( GetExport().m_bFootnoteAtTextEnd )
3690 GetExport().m_bFootnoteAtTextEnd = lcl_IsAtTextEnd( rFootnote );
3693 // if any reference to this footnote/endnote then insert an internal
3694 // Bookmark.
3695 OUString sBkmkNm;
3696 if ( GetExport().HasRefToFootOrEndnote( rFootnote.IsEndNote(), rFootnote.GetTextFootnote()->GetSeqRefNo()))
3698 sBkmkNm = GetExport().GetBookmarkName(nTyp, nullptr,
3699 rFootnote.GetTextFootnote()->GetSeqRefNo() );
3700 GetExport().AppendBookmark( sBkmkNm );
3703 TextFootnote_Impl( rFootnote );
3705 if ( !sBkmkNm.isEmpty() )
3706 GetExport().AppendBookmark( sBkmkNm ); // FIXME: Why is it added twice? Shouldn't this one go to WW8AttributeOutput::TextFootnote_Impl()?
3709 void WW8AttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote )
3711 WW8_WrPlcFootnoteEdn* pFootnoteEnd;
3712 if ( rFootnote.IsEndNote() || GetExport().m_rDoc.GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER )
3713 pFootnoteEnd = m_rWW8Export.m_pEdn.get();
3714 else
3715 pFootnoteEnd = m_rWW8Export.m_pFootnote.get();
3717 pFootnoteEnd->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), rFootnote );
3718 m_rWW8Export.WriteFootnoteBegin( rFootnote, m_rWW8Export.m_pO.get() );
3721 void WW8AttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat )
3723 if( rCharFormat.GetCharFormat() )
3725 m_rWW8Export.InsUInt16( NS_sprm::CIstd::val );
3727 m_rWW8Export.InsUInt16( m_rWW8Export.GetId( rCharFormat.GetCharFormat() ) );
3732 See ww8par6.cxx Read_DoubleLine for some more info
3734 void WW8AttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
3736 // #i28331# - check that bOn is set
3737 if ( !rTwoLines.GetValue() )
3738 return;
3740 m_rWW8Export.InsUInt16( NS_sprm::CFELayout::val );
3741 m_rWW8Export.m_pO->push_back( sal_uInt8(0x06) ); //len 6
3742 m_rWW8Export.m_pO->push_back( sal_uInt8(0x02) );
3744 sal_Unicode cStart = rTwoLines.GetStartBracket();
3745 sal_Unicode cEnd = rTwoLines.GetEndBracket();
3748 As per usual we have problems. We can have separate left and right brackets
3749 in OOo, it doesn't appear that you can in word. Also in word there appear
3750 to only be a limited number of possibilities, we can use pretty much
3751 anything.
3753 So if we have none, we export none, if either bracket is set to a known
3754 word type we export both as that type (with the bracket winning out in
3755 the case of a conflict simply being the order of test here.
3757 Upshot being a documented created in word will be reexported with no
3758 ill effects.
3761 sal_uInt16 nType;
3762 if (!cStart && !cEnd)
3763 nType = 0;
3764 else if ((cStart == '{') || (cEnd == '}'))
3765 nType = 4;
3766 else if ((cStart == '<') || (cEnd == '>'))
3767 nType = 3;
3768 else if ((cStart == '[') || (cEnd == ']'))
3769 nType = 2;
3770 else
3771 nType = 1;
3772 m_rWW8Export.InsUInt16( nType );
3773 static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
3774 m_rWW8Export.m_pO->insert( m_rWW8Export.m_pO->end(), aZeroArr, aZeroArr+3);
3777 void AttributeOutputBase::ParaOutlineLevelBase( const SfxUInt16Item& rItem )
3779 sal_uInt16 nOutLvl = rItem.GetValue();
3781 // Do not write out default level (Body Text) if there is no inheritance, or if the level matches the inherited value
3782 const SfxUInt16Item* pInherited = nullptr;
3783 if (auto pNd = dynamic_cast<const SwContentNode*>(GetExport().m_pOutFormatNode)) //paragraph
3784 pInherited = static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetItem<SfxUInt16Item>(RES_PARATR_OUTLINELEVEL);
3785 else if (GetExport().m_bStyDef && GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom()) //style
3786 pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SfxUInt16Item>(RES_PARATR_OUTLINELEVEL);
3787 if ((pInherited && pInherited->GetValue() == nOutLvl)
3788 || (!pInherited && !nOutLvl))
3789 return;
3791 ParaOutlineLevel(rItem);
3794 void AttributeOutputBase::ParaNumRule( const SwNumRuleItem& rNumRule )
3796 if (rNumRule.GetValue().isEmpty())
3798 ParaNumRule_Impl(nullptr, 0, 0);
3799 return;
3801 const SwNumRule* pRule = GetExport().m_rDoc.FindNumRulePtr(
3802 rNumRule.GetValue() );
3803 if (!pRule)
3804 return;
3806 sal_uInt16 nNumId = GetExport().GetNumberingId(*pRule) + 1;
3807 sal_uInt8 nLvl = 0;
3809 const SwTextNode* pTextNd = dynamic_cast<const SwTextNode*>(GetExport().m_pOutFormatNode);
3810 if (pTextNd)
3812 if( pTextNd->IsCountedInList())
3814 nLvl = std::clamp(pTextNd->GetActualListLevel(), 0, MAXLEVEL - 1);
3815 const bool bListRestart = pTextNd->IsListRestart();
3817 if (GetExport().GetExportFormat() == MSWordExportBase::DOCX) // FIXME
3819 // tdf#95848 find the abstract list definition
3820 OUString const listId(pTextNd->GetListId());
3821 if (!listId.isEmpty()
3822 && (listId != pRule->GetDefaultListId() // default list id uses the 1:1 mapping
3823 || bListRestart) // or restarting previous list
3826 SwList const*const pList(
3827 GetExport().m_rDoc.getIDocumentListsAccess().getListByName(listId));
3828 if (pList)
3830 SwNumRule const*const pAbstractRule(
3831 GetExport().m_rDoc.FindNumRulePtr(
3832 pList->GetDefaultListStyleName()));
3833 assert(pAbstractRule);
3834 if (pAbstractRule == pRule && !bListRestart)
3836 // different list, but no override
3837 nNumId = GetExport().DuplicateAbsNum(listId, *pAbstractRule) + 1;
3839 else
3841 nNumId = GetExport().OverrideNumRule(
3842 *pRule, listId, *pAbstractRule) + 1;
3844 if (bListRestart)
3846 // For restarted lists we should also keep value for
3847 // future w:lvlOverride / w:startOverride
3848 GetExport().AddListLevelOverride(nNumId-1, pTextNd->GetActualListLevel(),
3849 pTextNd->GetActualListStartValue());
3855 else if (bListRestart)
3857 sal_uInt16 nStartWith = static_cast<sal_uInt16>(pTextNd->GetActualListStartValue());
3858 nNumId = GetExport().DuplicateNumRule(pRule, nLvl, nStartWith);
3859 if (USHRT_MAX != nNumId)
3860 ++nNumId;
3863 else
3865 // #i44815# adjust numbering for numbered paragraphs
3866 // without number. These paragraphs will receive a
3867 // list id 0, which WW interprets as 'no number'.
3868 nNumId = 0;
3871 else if ( auto pC = dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
3873 if (pC->IsAssignedToListLevelOfOutlineStyle())
3874 nLvl = static_cast< sal_uInt8 >( pC->GetAssignedOutlineStyleLevel() );
3875 else
3877 const SfxItemSet* pSet = GetExport().m_pISet;
3878 if (pSet && pSet->HasItem(RES_PARATR_LIST_LEVEL))
3880 const SfxInt16Item* pItem = pSet->GetItem(RES_PARATR_LIST_LEVEL);
3881 nLvl = pItem->GetValue();
3886 if ( nLvl >= WW8ListManager::nMaxLevel )
3887 nLvl = WW8ListManager::nMaxLevel - 1;
3889 ParaNumRule_Impl( pTextNd, nLvl, nNumId);
3892 void WW8AttributeOutput::ParaNumRule_Impl(const SwTextNode* /*pTextNd*/,
3893 sal_Int32 const nLvl, sal_Int32 const nNumId)
3895 if (USHRT_MAX == nNumId)
3896 return;
3898 // write sprmPIlvl and sprmPIlfo
3899 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::PIlvl::val );
3900 m_rWW8Export.m_pO->push_back( ::sal::static_int_cast<sal_uInt8>(nLvl) );
3901 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, NS_sprm::PIlfo::val );
3902 SwWW8Writer::InsUInt16( *m_rWW8Export.m_pO, ::sal::static_int_cast<sal_uInt16>(nNumId) );
3905 /* File FRMATR.HXX */
3907 void WW8AttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize )
3909 if( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
3911 if( m_rWW8Export.m_bOutGrf )
3912 return; // Fly around graphic -> Auto-size
3914 //???? What about percentages ???
3915 if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed)
3917 //"sprmPDxaWidth"
3918 m_rWW8Export.InsUInt16( NS_sprm::PDxaWidth::val );
3919 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rSize.GetWidth()) );
3922 if ( rSize.GetHeight() )
3924 // sprmPWHeightAbs
3925 m_rWW8Export.InsUInt16( NS_sprm::PWHeightAbs::val );
3927 sal_uInt16 nH = 0;
3928 switch ( rSize.GetHeightSizeType() )
3930 case SwFrameSize::Variable: break;
3931 case SwFrameSize::Fixed: nH = o3tl::narrowing<sal_uInt16>(rSize.GetHeight()) & 0x7fff; break;
3932 default: nH = o3tl::narrowing<sal_uInt16>(rSize.GetHeight()) | 0x8000; break;
3934 m_rWW8Export.InsUInt16( nH );
3937 else if( m_rWW8Export.m_bOutPageDescs ) // PageDesc : width + height
3939 if( m_rWW8Export.m_pCurrentPageDesc->GetLandscape() )
3941 /*sprmSBOrientation*/
3942 m_rWW8Export.InsUInt16( NS_sprm::SBOrientation::val );
3943 m_rWW8Export.m_pO->push_back( 2 );
3946 /*sprmSXaPage*/
3947 m_rWW8Export.InsUInt16( NS_sprm::SXaPage::val );
3948 m_rWW8Export.InsUInt16(
3949 msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetWidth())));
3951 /*sprmSYaPage*/
3952 m_rWW8Export.InsUInt16( NS_sprm::SYaPage::val );
3953 m_rWW8Export.InsUInt16(
3954 msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetHeight())));
3958 // FillOrder is still missing
3961 * ReplaceCr() is used for Pagebreaks and Pagedescs. An already written CR
3962 * will be replaced by a break character. Replace must be called right after
3963 * the writing of CR.
3965 * @return FilePos + 1 of the replaced CR or 0 if nothing was replaced.
3967 sal_uInt64 WW8Export::ReplaceCr( sal_uInt8 nChar )
3969 OSL_ENSURE( nChar, "replaced with 0 crashes WW97/95" );
3971 bool bReplaced = false;
3972 SvStream& rStrm = Strm();
3973 sal_uInt64 nRetPos = 0, nPos = rStrm.Tell();
3974 //If there is at least two characters already output
3975 if (nPos - 2 >= o3tl::make_unsigned(m_pFib->m_fcMin))
3977 sal_uInt16 nUCode=0;
3979 rStrm.SeekRel(-2);
3980 rStrm.ReadUInt16( nUCode );
3981 //If the last char was a cr
3982 if (nUCode == 0x0d) // CR ?
3984 if ((nChar == 0x0c) &&
3985 (nPos - 4 >= o3tl::make_unsigned(m_pFib->m_fcMin)))
3987 rStrm.SeekRel(-4);
3988 rStrm.ReadUInt16( nUCode );
3990 else
3992 rStrm.SeekRel(-2);
3993 nUCode = 0x0;
3995 //And the para is not of len 0, then replace this cr with the mark
3996 //#120140# If there is a cr before a column break, need replace the cr. So remove the "nChar==0x0e" check.
3997 if( nUCode == 0x0d )
3998 bReplaced = false;
3999 else
4001 bReplaced = true;
4002 WriteChar(nChar);
4003 nRetPos = nPos;
4006 else if ((nUCode == 0x0c) && (nChar == 0x0e))
4008 // a column break after a section has no effect in writer
4009 bReplaced = true;
4011 rStrm.Seek( nPos );
4013 else
4014 bReplaced = true;
4016 if (!bReplaced)
4018 // then write as normal char
4019 WriteChar(nChar);
4020 m_pPiece->SetParaBreak();
4021 m_pPapPlc->AppendFkpEntry(rStrm.Tell());
4022 m_pChpPlc->AppendFkpEntry(rStrm.Tell());
4023 nRetPos = rStrm.Tell();
4025 return nRetPos;
4028 void WW8AttributeOutput::TableRowEnd(sal_uInt32 nDepth)
4030 if ( nDepth == 1 )
4031 m_rWW8Export.WriteChar( 0x07 );
4032 else if ( nDepth > 1 )
4033 m_rWW8Export.WriteChar( 0x0d );
4035 //Technically in a word document this is a different value for a row ends
4036 //that are not row ends directly after a cell with a graphic. But it
4037 //doesn't seem to make a difference
4038 //pMagicTable->Append(Fc2Cp(Strm().Tell()),0x1B6);
4041 void AttributeOutputBase::FormatPageDescription( const SwFormatPageDesc& rPageDesc )
4043 if ( GetExport().m_bStyDef )
4044 if (auto pC = dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
4046 if ( (SfxItemState::SET != pC->GetItemState( RES_BREAK, false ) ) && rPageDesc.KnowsPageDesc() )
4047 FormatBreak( SvxFormatBreakItem( SvxBreak::PageBefore, RES_BREAK ) );
4051 void WW8AttributeOutput::PageBreakBefore( bool bBreak )
4053 // sprmPPageBreakBefore/sprmPFPageBreakBefore
4054 m_rWW8Export.InsUInt16( NS_sprm::PFPageBreakBefore::val );
4056 m_rWW8Export.m_pO->push_back( bBreak ? 1 : 0 );
4060 * breaks write nothing in the output field rWrt.pO,
4061 * but only in the text stream (requirement so they can
4062 * be called from Out_Break...)
4064 void AttributeOutputBase::FormatBreak( const SvxFormatBreakItem& rBreak )
4066 if ( GetExport().m_bStyDef )
4068 switch ( rBreak.GetBreak() )
4070 case SvxBreak::NONE:
4071 case SvxBreak::PageBefore:
4072 case SvxBreak::PageBoth:
4073 PageBreakBefore( rBreak.GetValue() != SvxBreak::NONE );
4074 break;
4075 default:
4076 break;
4079 else if ( !GetExport().m_pParentFrame )
4081 sal_uInt8 nC = 0;
4082 bool bBefore = false;
4083 // #i76300# - Note: Can only be <true>, if <bBefore> equals <false>.
4084 bool bCheckForFollowPageDesc = false;
4086 switch ( rBreak.GetBreak() )
4088 case SvxBreak::NONE: // disabled
4089 if ( !GetExport().m_bBreakBefore )
4090 PageBreakBefore( false );
4091 return;
4093 case SvxBreak::ColumnBefore: // ColumnBreak
4094 bBefore = true;
4095 [[fallthrough]];
4096 case SvxBreak::ColumnAfter:
4097 case SvxBreak::ColumnBoth:
4098 if ( GetExport().m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK )
4099 || GetExport().Sections().CurrentNumberOfColumns( GetExport().m_rDoc ) > 1 )
4101 nC = msword::ColumnBreak;
4103 break;
4105 case SvxBreak::PageBefore: // PageBreak
4106 // From now on(fix for #i77900#) we prefer to save a page break
4107 // as paragraph attribute (if the exporter is OK with that),
4108 // this has to be done after the export of the paragraph ( =>
4109 // !GetExport().bBreakBefore )
4110 if (GetExport().PreferPageBreakBefore())
4112 if (!GetExport().m_bBreakBefore)
4113 PageBreakBefore(true);
4115 else
4117 bBefore = true;
4118 nC = msword::PageBreak;
4120 break;
4121 case SvxBreak::PageAfter:
4122 case SvxBreak::PageBoth:
4123 nC = msword::PageBreak;
4124 // #i76300# - check for follow page description,
4125 // if current writing attributes of a paragraph.
4126 if ( dynamic_cast< const SwTextNode* >( GetExport().m_pOutFormatNode ) &&
4127 GetExport().GetCurItemSet() )
4129 bCheckForFollowPageDesc = true;
4131 break;
4133 default:
4134 break;
4137 if ( ( bBefore == GetExport().m_bBreakBefore ) && nC )
4139 // #i76300#
4140 bool bFollowPageDescWritten = false;
4141 if ( bCheckForFollowPageDesc )
4143 bFollowPageDescWritten =
4144 GetExport().OutputFollowPageDesc( GetExport().GetCurItemSet(),
4145 dynamic_cast<const SwTextNode*>( GetExport().m_pOutFormatNode ) );
4147 if ( !bFollowPageDescWritten )
4149 SectionBreak(nC, !bBefore);
4155 void WW8AttributeOutput::SectionBreak( sal_uInt8 nC, bool /*bBreakAfter*/, const WW8_SepInfo* /*pSectionInfo*/, bool /*bExtraPageBreak*/ )
4157 m_rWW8Export.ReplaceCr( nC );
4160 sal_uInt32 AttributeOutputBase::GridCharacterPitch( const SwTextGridItem& rGrid ) const
4162 MSWordStyles * pStyles = GetExport().m_pStyles.get();
4163 const SwFormat * pSwFormat = pStyles->GetSwFormat(0);
4165 sal_uInt32 nPageCharSize = 0;
4167 if (pSwFormat != nullptr)
4169 nPageCharSize = pSwFormat->GetFormatAttr(RES_CHRATR_FONTSIZE).GetHeight();
4171 sal_uInt16 nPitch = rGrid.IsSquaredMode() ? rGrid.GetBaseHeight() :
4172 rGrid.GetBaseWidth( );
4174 sal_Int32 nCharWidth = nPitch - nPageCharSize;
4175 sal_Int32 nFraction = nCharWidth % 20;
4176 if ( nCharWidth < 0 )
4177 nFraction = 20 + nFraction;
4178 nFraction = ( nFraction * 0xFFF ) / 20;
4179 nFraction = ( nFraction & 0x00000FFF );
4181 sal_Int32 nMain = nCharWidth / 20;
4182 if ( nCharWidth < 0 )
4183 nMain -= 1;
4184 nMain = nMain * 0x1000;
4185 nMain = ( nMain & 0xFFFFF000 );
4187 return sal_uInt32( nFraction + nMain );
4190 void WW8AttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid )
4192 if (!m_rWW8Export.m_bOutPageDescs)
4193 return;
4195 sal_uInt16 nGridType = 0;
4196 switch ( rGrid.GetGridType() )
4198 default:
4199 OSL_FAIL("Unknown grid type");
4200 [[fallthrough]];
4201 case GRID_NONE:
4202 nGridType = 0;
4203 break;
4204 case GRID_LINES_ONLY:
4205 nGridType = 2;
4206 break;
4207 case GRID_LINES_CHARS:
4208 if ( rGrid.IsSnapToChars() )
4209 nGridType = 3;
4210 else
4211 nGridType = 1;
4212 break;
4214 m_rWW8Export.InsUInt16( NS_sprm::SClm::val );
4215 m_rWW8Export.InsUInt16( nGridType );
4217 sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
4218 m_rWW8Export.InsUInt16( NS_sprm::SDyaLinePitch::val );
4219 m_rWW8Export.InsUInt16( nHeight );
4221 m_rWW8Export.InsUInt16( NS_sprm::SDxtCharSpace::val );
4222 m_rWW8Export.InsUInt32( GridCharacterPitch( rGrid ) );
4225 void WW8AttributeOutput::FormatPaperBin( const SvxPaperBinItem& rPaperBin )
4227 if ( !m_rWW8Export.m_bOutPageDescs )
4228 return;
4230 sal_uInt16 nVal;
4231 switch ( rPaperBin.GetValue() )
4233 case 0: nVal = 15; break; // Automatically select
4234 case 1: nVal = 1; break; // Upper paper tray
4235 case 2: nVal = 4; break; // Manual paper feed
4236 default: nVal = 0; break;
4239 if ( nVal )
4241 m_rWW8Export.InsUInt16( m_rWW8Export.m_bOutFirstPage
4242 ? NS_sprm::SDmBinFirst::val : NS_sprm::SDmBinOther::val );
4244 m_rWW8Export.InsUInt16( nVal );
4248 void WW8AttributeOutput::FormatFirstLineIndent(SvxFirstLineIndentItem const& rFirstLine)
4250 // sprmPDxaLeft1
4251 m_rWW8Export.InsUInt16( 0x8460 ); //asian version ?
4252 m_rWW8Export.InsUInt16( rFirstLine.GetTextFirstLineOffset() );
4255 void WW8AttributeOutput::FormatTextLeftMargin(SvxTextLeftMarginItem const& rTextLeftMargin)
4256 { // normal paragraphs
4257 // sprmPDxaLeft
4258 m_rWW8Export.InsUInt16( 0x845E ); //asian version ?
4259 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rTextLeftMargin.GetTextLeft()) );
4262 void WW8AttributeOutput::FormatRightMargin(SvxRightMarginItem const& rRightMargin)
4264 // (paragraph case, this will be an else branch once others are converted)
4265 #if 0
4266 else
4267 #endif
4268 { // normal paragraphs
4269 // sprmPDxaRight
4270 m_rWW8Export.InsUInt16( 0x845D ); //asian version ?
4271 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rRightMargin.GetRight()) );
4275 void WW8AttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLR )
4277 // Flys are still missing ( see RTF )
4279 if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
4281 // sprmPDxaFromText10
4282 m_rWW8Export.InsUInt16( NS_sprm::LN_PDxaFromText10 );
4283 // use average, since WW only knows one value
4284 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>( ( rLR.GetLeft() + rLR.GetRight() ) / 2 ) );
4286 else if ( m_rWW8Export.m_bOutPageDescs ) // PageDescs
4288 m_pageMargins.nLeft = 0;
4289 m_pageMargins.nRight = 0;
4291 if ( const SvxBoxItem* pBoxItem = m_rWW8Export.HasItem( RES_BOX ) )
4293 m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
4294 m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
4297 m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLR.GetLeft());
4298 m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLR.GetRight());
4299 sal_uInt16 nGutter = rLR.GetGutterMargin();
4301 // sprmSDxaLeft
4302 m_rWW8Export.InsUInt16( NS_sprm::SDxaLeft::val );
4303 m_rWW8Export.InsUInt16( m_pageMargins.nLeft );
4305 // sprmSDxaRight
4306 m_rWW8Export.InsUInt16( NS_sprm::SDxaRight::val );
4307 m_rWW8Export.InsUInt16( m_pageMargins.nRight );
4309 if (nGutter)
4311 // sprmSDzaGutter
4312 m_rWW8Export.InsUInt16(NS_sprm::SDzaGutter::val);
4313 m_rWW8Export.InsUInt16(nGutter);
4316 else
4317 { // normal paragraphs
4318 // sprmPDxaLeft
4319 m_rWW8Export.InsUInt16( 0x845E ); //asian version ?
4320 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rLR.GetTextLeft()) );
4322 // sprmPDxaRight
4323 m_rWW8Export.InsUInt16( 0x845D ); //asian version ?
4324 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>(rLR.GetRight()) );
4326 // sprmPDxaLeft1
4327 m_rWW8Export.InsUInt16( 0x8460 ); //asian version ?
4328 m_rWW8Export.InsUInt16( rLR.GetTextFirstLineOffset() );
4332 void WW8AttributeOutput::SectionRtlGutter(const SfxBoolItem& rRtlGutter)
4334 if (!rRtlGutter.GetValue())
4336 return;
4339 // sprmSFRTLGutter
4340 m_rWW8Export.InsUInt16(NS_sprm::SFRTLGutter::val);
4341 m_rWW8Export.m_pO->push_back(1);
4344 void WW8AttributeOutput::TextLineBreak(const SwFormatLineBreak& rLineBreak)
4346 // Write the linebreak itself.
4347 m_rWW8Export.WriteChar(0x0b);
4349 // sprmCLbcCRJ
4350 m_rWW8Export.InsUInt16(NS_sprm::CLbcCRJ::val);
4351 m_rWW8Export.m_pO->push_back(rLineBreak.GetEnumValue());
4354 void WW8AttributeOutput::FormatULSpace( const SvxULSpaceItem& rUL )
4356 // Flys are still missing ( see RTF )
4358 if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
4360 // sprmPDyaFromText
4361 m_rWW8Export.InsUInt16( NS_sprm::PDyaFromText::val );
4362 // use average, since WW only knows one value
4363 m_rWW8Export.InsUInt16( o3tl::narrowing<sal_uInt16>( ( rUL.GetUpper() + rUL.GetLower() ) / 2 ) );
4365 else if ( m_rWW8Export.m_bOutPageDescs ) // Page-UL
4367 OSL_ENSURE( m_rWW8Export.GetCurItemSet(), "Impossible" );
4368 if ( !m_rWW8Export.GetCurItemSet() )
4369 return;
4371 HdFtDistanceGlue aDistances( *m_rWW8Export.GetCurItemSet() );
4373 if ( aDistances.HasHeader() )
4375 //sprmSDyaHdrTop
4376 m_rWW8Export.InsUInt16( NS_sprm::SDyaHdrTop::val );
4377 m_rWW8Export.InsUInt16( aDistances.m_DyaHdrTop );
4380 // sprmSDyaTop
4381 m_rWW8Export.InsUInt16( NS_sprm::SDyaTop::val );
4382 m_rWW8Export.InsUInt16( aDistances.m_DyaTop );
4383 m_pageMargins.nTop = aDistances.m_DyaTop;
4385 if ( aDistances.HasFooter() )
4387 //sprmSDyaHdrBottom
4388 m_rWW8Export.InsUInt16( NS_sprm::SDyaHdrBottom::val );
4389 m_rWW8Export.InsUInt16( aDistances.m_DyaHdrBottom );
4392 //sprmSDyaBottom
4393 m_rWW8Export.InsUInt16( NS_sprm::SDyaBottom::val );
4394 m_rWW8Export.InsUInt16( aDistances.m_DyaBottom );
4395 m_pageMargins.nBottom = aDistances.m_DyaBottom;
4397 else
4399 // sprmPDyaBefore
4400 m_rWW8Export.InsUInt16( NS_sprm::PDyaBefore::val );
4401 m_rWW8Export.InsUInt16( rUL.GetUpper() );
4402 // sprmPDyaAfter
4403 m_rWW8Export.InsUInt16( NS_sprm::PDyaAfter::val );
4404 m_rWW8Export.InsUInt16( rUL.GetLower() );
4405 // sprmPFContextualSpacing
4407 // Write out Contextual Spacing = false if it would have inherited a true.
4408 const SvxULSpaceItem* pInherited = nullptr;
4409 if (!rUL.GetContext())
4411 if (auto pNd = dynamic_cast<const SwContentNode*>(m_rWW8Export.m_pOutFormatNode)) //paragraph
4412 pInherited = &static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetULSpace();
4413 else if (m_rWW8Export.m_bStyDef && m_rWW8Export.m_pCurrentStyle && m_rWW8Export.m_pCurrentStyle->DerivedFrom()) //style
4414 pInherited = &m_rWW8Export.m_pCurrentStyle->DerivedFrom()->GetULSpace();
4416 if (rUL.GetContext() || (pInherited && pInherited->GetContext()))
4418 m_rWW8Export.InsUInt16(NS_sprm::PFContextualSpacing::val);
4419 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(rUL.GetContext()) );
4424 // print, opaque, protect are still missing
4426 void WW8AttributeOutput::FormatSurround( const SwFormatSurround& rSurround )
4428 if ( m_rWW8Export.m_bOutFlyFrameAttrs )
4430 m_rWW8Export.InsUInt16( NS_sprm::PWr::val );
4432 m_rWW8Export.m_pO->push_back(
4433 ( css::text::WrapTextMode_NONE != rSurround.GetSurround() ) ? 2 : 1 );
4437 void WW8AttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert )
4440 //!!!! anchor type and corresponding borders are still missing
4441 if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
4442 return;
4444 short nPos;
4445 switch( rFlyVert.GetVertOrient() )
4447 case text::VertOrientation::NONE:
4448 nPos = static_cast<short>(rFlyVert.GetPos());
4449 break;
4450 case text::VertOrientation::CENTER:
4451 case text::VertOrientation::LINE_CENTER:
4452 nPos = -8;
4453 break;
4454 case text::VertOrientation::BOTTOM:
4455 case text::VertOrientation::LINE_BOTTOM:
4456 nPos = -12;
4457 break;
4458 case text::VertOrientation::TOP:
4459 case text::VertOrientation::LINE_TOP:
4460 default:
4461 nPos = -4;
4462 break;
4465 // sprmPDyaAbs
4466 m_rWW8Export.InsUInt16( NS_sprm::PDyaAbs::val );
4467 m_rWW8Export.InsUInt16( nPos );
4470 void WW8AttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori )
4472 if ( !m_rWW8Export.m_pParentFrame )
4474 OSL_ENSURE( m_rWW8Export.m_pParentFrame, "HoriOrient without mpParentFrame !!" );
4475 return;
4478 //!!!! anchor type and corresponding borders are still missing
4479 if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
4480 return;
4482 short nPos;
4483 switch( rFlyHori.GetHoriOrient() )
4485 case text::HoriOrientation::NONE:
4486 nPos = static_cast<short>(rFlyHori.GetPos());
4487 if( !nPos )
4488 nPos = 1; // WW: 0 is reserved
4489 break;
4490 case text::HoriOrientation::LEFT:
4491 nPos = rFlyHori.IsPosToggle() ? -12 : 0;
4492 break;
4493 case text::HoriOrientation::RIGHT:
4494 nPos = rFlyHori.IsPosToggle() ? -16 : -8;
4495 break;
4496 case text::HoriOrientation::CENTER:
4497 case text::HoriOrientation::FULL: // FULL only for tables
4498 default:
4499 nPos = -4;
4500 break;
4503 // sprmPDxaAbs
4504 m_rWW8Export.InsUInt16( NS_sprm::PDxaAbs::val );
4505 m_rWW8Export.InsUInt16( nPos );
4508 void WW8AttributeOutput::FormatAnchor( const SwFormatAnchor& rAnchor )
4510 OSL_ENSURE( m_rWW8Export.m_pParentFrame, "Anchor without mpParentFrame !!" );
4512 if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
4513 return;
4515 sal_uInt8 nP = 0;
4516 switch ( rAnchor.GetAnchorId() )
4518 case RndStdIds::FLY_AT_PAGE:
4519 // vertical: page | horizontal: page
4520 nP |= (1 << 4) | (2 << 6);
4521 break;
4522 // in case of Fly as characters: set paragraph-bound!!!
4523 case RndStdIds::FLY_AT_FLY:
4524 case RndStdIds::FLY_AT_CHAR:
4525 case RndStdIds::FLY_AT_PARA:
4526 case RndStdIds::FLY_AS_CHAR:
4527 // vertical: page | horizontal: page
4528 nP |= (2 << 4) | (0 << 6);
4529 break;
4530 default:
4531 break;
4534 // sprmPPc
4535 m_rWW8Export.InsUInt16( NS_sprm::PPc::val );
4536 m_rWW8Export.m_pO->push_back( nP );
4539 void WW8AttributeOutput::FormatBackground( const SvxBrushItem& rBrush )
4541 // WW cannot have background in a section
4542 if ( m_rWW8Export.m_bOutPageDescs )
4543 return;
4545 WW8_SHD aSHD;
4546 WW8Export::TransBrush( rBrush.GetColor(), aSHD );
4548 m_rWW8Export.InsUInt16( NS_sprm::PShd80::val );
4549 m_rWW8Export.InsUInt16( aSHD.GetValue() );
4551 m_rWW8Export.InsUInt16( NS_sprm::PShd::val );
4552 m_rWW8Export.m_pO->push_back( 10 ); //size of operand: MUST be 10
4553 m_rWW8Export.InsUInt32( 0xFF000000 ); //cvFore: Foreground BGR = cvAuto
4554 m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) ); //cvBack
4555 m_rWW8Export.InsUInt16( 0x0000 ); //iPat: specifies the pattern used for shading = clear/100% background
4558 void WW8AttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle )
4560 // WW cannot have background in a section
4561 if ( m_rWW8Export.m_bOutPageDescs )
4562 return;
4564 // see MSWordExportBase::OutputItemSet for how _SOLID is handled
4565 if ( rFillStyle.GetValue() != drawing::FillStyle_NONE )
4566 return;
4568 //Shd80Nil
4569 m_rWW8Export.InsUInt16( NS_sprm::PShd80::val );
4570 m_rWW8Export.InsUInt16( 0xffff );
4572 //cvAuto
4573 m_rWW8Export.InsUInt16( NS_sprm::PShd::val );
4574 m_rWW8Export.m_pO->push_back( 10 );
4575 m_rWW8Export.InsUInt32( 0xFF000000 );
4576 m_rWW8Export.InsUInt32( 0xFF000000 );
4577 m_rWW8Export.InsUInt16( 0x0000 );
4580 void WW8AttributeOutput::FormatFillGradient( const XFillGradientItem& /*rFillGradient*/ )
4584 WW8_BRCVer9 WW8Export::TranslateBorderLine(const SvxBorderLine& rLine,
4585 sal_uInt16 nDist, bool bShadow)
4587 sal_uInt32 nColBGR = 0;
4588 sal_uInt16 nWidth = ::editeng::ConvertBorderWidthToWord(
4589 rLine.GetBorderLineStyle(), rLine.GetWidth());
4590 sal_uInt8 brcType = 0;
4592 if( nWidth ) // line ?
4594 // BRC.brcType
4595 brcType = 0;
4596 // All the border types values are available on
4597 // http://msdn.microsoft.com/en-us/library/dd908142%28v=office.12%29.aspx
4598 switch (rLine.GetBorderLineStyle())
4600 case SvxBorderLineStyle::SOLID:
4602 if ( rLine.GetWidth( ) == SvxBorderLineWidth::Hairline )
4603 brcType = 5;
4604 else
4605 brcType = 1;
4607 break;
4608 case SvxBorderLineStyle::DOTTED:
4609 brcType = 6;
4610 break;
4611 case SvxBorderLineStyle::DASHED:
4612 brcType = 7;
4613 break;
4614 case SvxBorderLineStyle::DOUBLE:
4615 case SvxBorderLineStyle::DOUBLE_THIN:
4616 brcType = 3;
4617 break;
4618 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
4619 brcType = 11;
4620 break;
4621 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
4622 brcType = 14;
4623 break;
4624 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
4625 brcType = 17;
4626 break;
4627 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
4628 brcType = 12;
4629 break;
4630 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
4631 brcType = 15;
4632 break;
4633 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
4634 brcType = 18;
4635 break;
4636 case SvxBorderLineStyle::EMBOSSED:
4637 brcType = 24;
4638 break;
4639 case SvxBorderLineStyle::ENGRAVED:
4640 brcType = 25;
4641 break;
4642 case SvxBorderLineStyle::OUTSET:
4643 brcType = 26;
4644 break;
4645 case SvxBorderLineStyle::INSET:
4646 brcType = 27;
4647 break;
4648 case SvxBorderLineStyle::FINE_DASHED:
4649 brcType = 22;
4650 break;
4651 case SvxBorderLineStyle::DASH_DOT:
4652 brcType = 8;
4653 break;
4654 case SvxBorderLineStyle::DASH_DOT_DOT:
4655 brcType = 9;
4656 break;
4657 default:
4658 break;
4661 // convert width from twips (1/20 pt) to eighths of a point
4662 nWidth = (( nWidth * 8 ) + 10 ) / 20;
4663 if( 0xff < nWidth )
4664 nWidth = 0xff;
4666 if( 0 == nWidth ) // really thin line
4667 nWidth = 1; // don't omit
4669 // BRC.cv
4670 nColBGR = wwUtility::RGBToBGR(rLine.GetColor().GetRGBColor());
4673 // BRC.dptSpace
4674 sal_uInt16 nLDist = rtl::math::round(nDist / 20.0); // unit of measurement: pt
4675 if( nLDist > 0x1f )
4676 nLDist = 0x1f;
4678 return WW8_BRCVer9(nColBGR, sal_uInt8(nWidth), brcType, sal_uInt8(nLDist),
4679 bShadow, false);
4683 * Gets passed a WW8Bytes*, so the function also can be used for the table border.
4685 * @param nSprmNo If nSprmNo == 0, then the opcode isn't outputted.
4686 * @param bShadow SHOULDN'T be set for table cells !
4688 void WW8Export::Out_BorderLine(ww::bytes& rO, const SvxBorderLine* pLine,
4689 sal_uInt16 nDist, sal_uInt16 nSprmNo, sal_uInt16 nSprmNoVer9, bool bShadow)
4691 OSL_ENSURE( ( nSprmNo == 0 ) ||
4692 ( nSprmNo >= 38 && nSprmNo <= 41 ) ||
4693 ( nSprmNo >= NS_sprm::PBrcTop80::val
4694 && nSprmNo <= NS_sprm::PBrcRight80::val ) ||
4695 ( nSprmNo >= NS_sprm::SBrcTop80::val
4696 && nSprmNo <= NS_sprm::SBrcRight80::val ),
4697 "Sprm for border out is of range" );
4699 WW8_BRCVer9 aBrcVer9;
4700 WW8_BRC aBrcVer8;
4702 if( pLine && pLine->GetBorderLineStyle() != SvxBorderLineStyle::NONE )
4704 aBrcVer9 = TranslateBorderLine( *pLine, nDist, bShadow );
4705 sal_uInt8 ico = msfilter::util::TransColToIco( msfilter::util::BGRToRGB(aBrcVer9.cv()) );
4706 aBrcVer8 = WW8_BRC( aBrcVer9.dptLineWidth(), aBrcVer9.brcType(), ico,
4707 aBrcVer9.dptSpace(), aBrcVer9.fShadow(), aBrcVer9.fFrame() );
4710 // WW97-SprmIds
4711 if ( nSprmNo != 0 )
4712 SwWW8Writer::InsUInt16( rO, nSprmNo );
4714 rO.insert( rO.end(), aBrcVer8.aBits1, aBrcVer8.aBits2+2 );
4716 if ( nSprmNoVer9 != 0 )
4718 SwWW8Writer::InsUInt16( rO, nSprmNoVer9 );
4719 rO.push_back(sizeof(WW8_BRCVer9));
4720 rO.insert( rO.end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4);
4725 * is for all boxes except in tables. pO of the WW8Writer is used
4727 * @param rBox
4728 * @param bShadow
4730 void WW8Export::Out_SwFormatBox(const SvxBoxItem& rBox, bool bShadow)
4732 static const SvxBoxItemLine aBorders[] =
4734 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4736 static const sal_uInt16 aPBrc[] =
4738 // WW8 SPRMs
4739 NS_sprm::PBrcTop80::val, NS_sprm::PBrcLeft80::val,
4740 NS_sprm::PBrcBottom80::val, NS_sprm::PBrcRight80::val,
4741 // WW9 SPRMs
4742 NS_sprm::PBrcTop::val, NS_sprm::PBrcLeft::val,
4743 NS_sprm::PBrcBottom::val, NS_sprm::PBrcRight::val
4745 static const sal_uInt16 aSBrc[] =
4747 // WW8 SPRMs
4748 NS_sprm::SBrcTop80::val, NS_sprm::SBrcLeft80::val,
4749 NS_sprm::SBrcBottom80::val, NS_sprm::SBrcRight80::val,
4750 // WW9 SPRMs
4751 NS_sprm::SBrcTop::val, NS_sprm::SBrcLeft::val,
4752 NS_sprm::SBrcBottom::val, NS_sprm::SBrcRight::val
4755 const SvxBoxItemLine* pBrd = aBorders;
4756 for( sal_uInt16 i = 0; i < 4; ++i, ++pBrd )
4758 const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
4760 sal_uInt16 nSprmNo, nSprmNoVer9 = 0;
4761 if (m_bOutPageDescs)
4763 nSprmNo = aSBrc[i];
4764 nSprmNoVer9 = aSBrc[i+4];
4766 else
4768 nSprmNo = aPBrc[i];
4769 nSprmNoVer9 = aPBrc[i+4];
4772 Out_BorderLine( *m_pO, pLn, rBox.GetDistance( *pBrd ), nSprmNo,
4773 nSprmNoVer9, bShadow );
4778 * FormatBox2() is for TC structures in tables. The Sprm opcode isn't written
4779 * because it is packed into the TC structure without opcode.
4780 * dxpSpace always becomes 0, because WW requires that in tables
4781 * ( table borders otherwise will fray )
4783 * @param rO A WW8Bytes pointer is passed in as output parameter
4785 void WW8Export::Out_SwFormatTableBox( ww::bytes& rO, const SvxBoxItem * pBox )
4787 // possible and maybe better would be 0xffff
4788 static const SvxBoxItemLine aBorders[] =
4790 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4792 static const SvxBorderLine aBorderLine;
4794 for(const SvxBoxItemLine & rBorder : aBorders)
4796 const SvxBorderLine* pLn;
4797 if (pBox != nullptr)
4798 pLn = pBox->GetLine( rBorder );
4799 else
4800 pLn = & aBorderLine;
4802 Out_BorderLine(rO, pLn, 0, 0, 0, false);
4806 void WW8Export::Out_CellRangeBorders( const SvxBoxItem * pBox, sal_uInt8 nStart,
4807 sal_uInt8 nLimit )
4809 if ( !pBox )
4810 return;
4812 static const SvxBoxItemLine aBorders[] =
4814 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4817 for( int i = 0; i < 4; ++i )
4819 const SvxBorderLine* pLn = pBox->GetLine( aBorders[i] );
4820 if (!pLn)
4821 continue;
4823 InsUInt16( NS_sprm::TSetBrc::val );
4824 m_pO->push_back( 11 );
4825 m_pO->push_back( nStart );
4826 m_pO->push_back( nLimit );
4827 m_pO->push_back( 1<<i );
4828 WW8_BRCVer9 aBrcVer9 = TranslateBorderLine( *pLn, 0, false );
4829 m_pO->insert( m_pO->end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4 );
4833 void WW8AttributeOutput::FormatBox( const SvxBoxItem& rBox )
4835 // Fly around graphic -> here no border, because the
4836 // graphics header already has the border
4837 if ( m_rWW8Export.m_bOutGrf )
4838 return;
4840 bool bShadow = false;
4841 const SvxShadowItem* pShadowItem = m_rWW8Export.HasItem( RES_SHADOW );
4842 if ( pShadowItem )
4844 bShadow = ( pShadowItem->GetLocation() != SvxShadowLocation::NONE )
4845 && ( pShadowItem->GetWidth() != 0 );
4848 SvxBoxItem aBox(rBox);
4849 if (m_rWW8Export.m_bOutPageDescs)
4851 editeng::WordBorderDistances aDistances;
4852 editeng::BorderDistancesToWord(aBox, m_pageMargins, aDistances);
4854 aBox.SetDistance(aDistances.nTop, SvxBoxItemLine::TOP);
4855 aBox.SetDistance(aDistances.nLeft, SvxBoxItemLine::LEFT);
4856 aBox.SetDistance(aDistances.nBottom, SvxBoxItemLine::BOTTOM);
4857 aBox.SetDistance(aDistances.nRight, SvxBoxItemLine::RIGHT);
4859 m_bFromEdge = aDistances.bFromEdge;
4862 m_rWW8Export.Out_SwFormatBox( aBox, bShadow );
4865 SwTwips WW8Export::CurrentPageWidth(SwTwips &rLeft, SwTwips &rRight) const
4867 const SwFrameFormat* pFormat = m_pCurrentPageDesc ? &m_pCurrentPageDesc->GetMaster()
4868 : &m_rDoc.GetPageDesc(0).GetMaster();
4870 const SvxLRSpaceItem& rLR = pFormat->GetLRSpace();
4871 SwTwips nPageSize = pFormat->GetFrameSize().GetWidth();
4872 rLeft = rLR.GetLeft();
4873 rRight = rLR.GetRight();
4874 return nPageSize;
4877 void WW8AttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize )
4879 // CColumns
4880 m_rWW8Export.InsUInt16( NS_sprm::SCcolumns::val );
4881 m_rWW8Export.InsUInt16( nCols - 1 );
4883 // DxaColumns
4884 m_rWW8Export.InsUInt16( NS_sprm::SDxaColumns::val );
4885 m_rWW8Export.InsUInt16( rCol.GetGutterWidth( true ) );
4887 // LBetween
4888 m_rWW8Export.InsUInt16( NS_sprm::SLBetween::val );
4889 m_rWW8Export.m_pO->push_back( COLADJ_NONE == rCol.GetLineAdj( )? 0 : 1 );
4891 const SwColumns & rColumns = rCol.GetColumns( );
4893 // FEvenlySpaced
4894 m_rWW8Export.InsUInt16( NS_sprm::SFEvenlySpaced::val );
4895 m_rWW8Export.m_pO->push_back( bEven ? 1 : 0 );
4897 if ( bEven )
4898 return;
4900 for ( sal_uInt16 n = 0; n < nCols; ++n )
4902 //sprmSDxaColWidth
4903 m_rWW8Export.InsUInt16( NS_sprm::SDxaColWidth::val );
4904 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(n) );
4905 m_rWW8Export.InsUInt16( rCol.
4906 CalcPrtColWidth( n,
4907 o3tl::narrowing<sal_uInt16>(nPageSize) ) );
4909 if ( n + 1 != nCols )
4911 //sprmSDxaColSpacing
4912 m_rWW8Export.InsUInt16( NS_sprm::SDxaColSpacing::val );
4913 m_rWW8Export.m_pO->push_back( static_cast<sal_uInt8>(n) );
4914 m_rWW8Export.InsUInt16( rColumns[n].GetRight( ) +
4915 rColumns[n + 1].GetLeft( ) );
4920 void AttributeOutputBase::FormatColumns( const SwFormatCol& rCol )
4922 const SwColumns& rColumns = rCol.GetColumns();
4924 sal_uInt16 nCols = rColumns.size();
4925 if (1 >= nCols || GetExport( ).m_bOutFlyFrameAttrs)
4926 return;
4928 // get the page width without borders !!
4930 const SwFrameFormat* pFormat = GetExport( ).m_pCurrentPageDesc ? &GetExport( ).m_pCurrentPageDesc->GetMaster() : &const_cast<const SwDoc&>(GetExport().m_rDoc).GetPageDesc(0).GetMaster();
4931 const SvxFrameDirectionItem &frameDirection = pFormat->GetFrameDir();
4932 SwTwips nPageSize;
4933 if ( frameDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB || frameDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB )
4935 const SvxULSpaceItem &rUL = pFormat->GetULSpace();
4936 nPageSize = pFormat->GetFrameSize().GetHeight();
4937 nPageSize -= rUL.GetUpper() + rUL.GetLower();
4939 const SwFormatHeader *header = pFormat->GetAttrSet().GetItem(RES_HEADER);
4940 if ( header )
4942 const SwFrameFormat *headerFormat = header->GetHeaderFormat();
4943 if (headerFormat)
4945 nPageSize -= headerFormat->GetFrameSize().GetHeight();
4948 const SwFormatFooter *footer = pFormat->GetAttrSet().GetItem(RES_FOOTER);
4949 if ( footer )
4951 const SwFrameFormat *footerFormat = footer->GetFooterFormat();
4952 if ( footerFormat )
4954 nPageSize -= footerFormat->GetFrameSize().GetHeight();
4958 else
4960 const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
4961 nPageSize = pFormat->GetFrameSize().GetWidth();
4962 nPageSize -= rLR.GetLeft() + rLR.GetRight();
4963 //i120133: The Section width should consider page indent value.
4964 nPageSize -= rCol.GetAdjustValue();
4968 // look if all columns are equal
4969 bool bEven = rCol.IsOrtho();
4970 if (!bEven)
4972 bEven = true;
4973 sal_uInt16 n;
4974 sal_uInt16 nColWidth = rCol.CalcPrtColWidth( 0, o3tl::narrowing<sal_uInt16>(nPageSize) );
4975 for ( n = 1; n < nCols; n++ )
4977 short nDiff = nColWidth -
4978 rCol.CalcPrtColWidth( n, o3tl::narrowing<sal_uInt16>(nPageSize) );
4980 if ( nDiff > 10 || nDiff < -10 ) // Tolerance: 10 tw
4982 bEven = false;
4983 break;
4988 FormatColumns_Impl( nCols, rCol, bEven, nPageSize );
4991 // "Paragraphs together"
4992 void WW8AttributeOutput::FormatKeep( const SvxFormatKeepItem& rKeep )
4994 // sprmFKeepFollow
4995 m_rWW8Export.InsUInt16( NS_sprm::PFKeepFollow::val );
4997 m_rWW8Export.m_pO->push_back( rKeep.GetValue() ? 1 : 0 );
5000 // exclude a paragraph from Line Numbering
5001 void WW8AttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering )
5003 // sprmPFNoLineNumb
5004 m_rWW8Export.InsUInt16( NS_sprm::PFNoLineNumb::val );
5006 m_rWW8Export.m_pO->push_back( rNumbering.IsCount() ? 0 : 1 );
5009 /* File PARATR.HXX */
5011 void WW8AttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti )
5013 // sprmPDyaLine
5014 m_rWW8Export.InsUInt16( NS_sprm::PDyaLine::val );
5016 m_rWW8Export.InsUInt16( nSpace );
5017 m_rWW8Export.InsUInt16( nMulti );
5020 void AttributeOutputBase::ParaLineSpacing( const SvxLineSpacingItem& rSpacing )
5022 short nSpace = 240, nMulti = 0;
5024 switch ( rSpacing.GetLineSpaceRule() )
5026 default:
5027 break;
5028 case SvxLineSpaceRule::Fix: // Fix
5029 nSpace = -static_cast<short>(rSpacing.GetLineHeight());
5030 break;
5031 case SvxLineSpaceRule::Min: // At least
5032 nSpace = static_cast<short>(rSpacing.GetLineHeight());
5033 break;
5034 case SvxLineSpaceRule::Auto:
5036 if( rSpacing.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) // Leading
5038 // doesn't exist in WW - how do you get the MaxLineHeight?
5039 nSpace = rSpacing.GetInterLineSpace();
5040 sal_uInt16 nScript =
5041 i18n::ScriptType::LATIN;
5042 const SwAttrSet *pSet = nullptr;
5043 if ( auto pFormat = dynamic_cast< const SwFormat *>( GetExport().m_pOutFormatNode ) )
5045 pSet = &pFormat->GetAttrSet();
5047 else if ( auto pNd = dynamic_cast< const SwTextNode *>( GetExport().m_pOutFormatNode ) )
5049 pSet = &pNd->GetSwAttrSet();
5050 nScript = g_pBreakIt->GetBreakIter()->getScriptType(pNd->GetText(), 0);
5052 OSL_ENSURE( pSet, "No attrset for lineheight :-(" );
5053 if ( pSet )
5055 nSpace = nSpace + static_cast<short>( AttrSetToLineHeight( GetExport().m_rDoc.getIDocumentSettingAccess(),
5056 *pSet, *Application::GetDefaultDevice(), nScript ) );
5059 else // Proportional
5061 if ( rSpacing.GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off )
5062 nSpace = static_cast<short>( ( 240L * rSpacing.GetPropLineSpace() ) / 100L );
5063 nMulti = 1;
5066 break;
5068 // if nSpace is negative, it is a fixed size in 1/20 of a point
5069 // if nSpace is positive and nMulti is 1, it is 1/240 of a single line height
5070 // otherwise, it is a minimum size in 1/20 of a point
5071 ParaLineSpacing_Impl( nSpace, nMulti );
5074 void WW8AttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust )
5076 // sprmPJc
5077 sal_uInt8 nAdj;
5078 sal_uInt8 nAdjBiDi;
5079 switch ( rAdjust.GetAdjust() )
5081 case SvxAdjust::Left:
5082 nAdj = 0;
5083 nAdjBiDi = 2;
5084 break;
5085 case SvxAdjust::Right:
5086 nAdj = 2;
5087 nAdjBiDi = 0;
5088 break;
5089 case SvxAdjust::BlockLine:
5090 case SvxAdjust::Block:
5091 nAdj = nAdjBiDi = rAdjust.GetLastBlock() == SvxAdjust::Block ? 4 : 3;
5092 break;
5093 case SvxAdjust::Center:
5094 nAdj = nAdjBiDi = 1;
5095 break;
5096 default:
5097 return; // not a supported Attribute
5100 m_rWW8Export.InsUInt16(NS_sprm::PJc80::val);
5101 m_rWW8Export.m_pO->push_back(nAdj);
5104 Sadly for left to right paragraphs both these values are the same,
5105 for right to left paragraphs the bidi one is the reverse of the
5106 normal one.
5108 m_rWW8Export.InsUInt16(NS_sprm::PJc::val); //bidi version ?
5109 bool bBiDiSwap = false;
5110 if (m_rWW8Export.m_pOutFormatNode)
5112 SvxFrameDirection nDirection = SvxFrameDirection::Horizontal_LR_TB;
5113 if (auto pTN = dynamic_cast<const SwTextNode*>(m_rWW8Export.m_pOutFormatNode))
5115 SwPosition aPos(*pTN);
5116 nDirection = m_rWW8Export.m_rDoc.GetTextDirection(aPos);
5118 else if (auto pC = dynamic_cast<const SwTextFormatColl*>(m_rWW8Export.m_pOutFormatNode))
5120 const SvxFrameDirectionItem &rItem = pC->GetFormatAttr(RES_FRAMEDIR);
5121 nDirection = rItem.GetValue();
5123 if ( ( nDirection == SvxFrameDirection::Horizontal_RL_TB ) ||
5124 ( nDirection == SvxFrameDirection::Environment && AllSettings::GetLayoutRTL() ) )
5126 bBiDiSwap = true;
5130 if (bBiDiSwap)
5131 m_rWW8Export.m_pO->push_back(nAdjBiDi);
5132 else
5133 m_rWW8Export.m_pO->push_back(nAdj);
5136 void WW8AttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection )
5138 sal_uInt16 nTextFlow=0;
5139 bool bBiDi = false;
5140 SvxFrameDirection nDir = rDirection.GetValue();
5142 if ( nDir == SvxFrameDirection::Environment )
5143 nDir = GetExport( ).GetDefaultFrameDirection( );
5146 switch ( nDir )
5148 default:
5149 //Can't get an unknown type here
5150 OSL_FAIL("Unknown frame direction");
5151 [[fallthrough]];
5152 case SvxFrameDirection::Horizontal_LR_TB:
5153 nTextFlow = 0;
5154 break;
5155 case SvxFrameDirection::Horizontal_RL_TB:
5156 nTextFlow = 0;
5157 bBiDi = true;
5158 break;
5159 case SvxFrameDirection::Vertical_LR_TB: //word doesn't have this
5160 case SvxFrameDirection::Vertical_RL_TB:
5161 nTextFlow = 1;
5162 break;
5165 if ( m_rWW8Export.m_bOutPageDescs )
5167 m_rWW8Export.InsUInt16( NS_sprm::STextFlow::val );
5168 m_rWW8Export.InsUInt16( nTextFlow );
5169 m_rWW8Export.InsUInt16( NS_sprm::SFBiDi::val );
5170 m_rWW8Export.m_pO->push_back( bBiDi ? 1 : 0 );
5172 else if ( !m_rWW8Export.m_bOutFlyFrameAttrs ) //paragraph/style
5174 m_rWW8Export.InsUInt16( NS_sprm::PFBiDi::val );
5175 m_rWW8Export.m_pO->push_back( bBiDi ? 1 : 0 );
5179 void WW8AttributeOutput::ParaGrabBag(const SfxGrabBagItem& /*rItem*/)
5183 void WW8AttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/)
5187 void WW8AttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem)
5189 sal_uInt16 nOutLvl = std::min(rItem.GetValue(), sal_uInt16(WW8ListManager::nMaxLevel));
5190 // Outline Level: in LO Body Text = 0, in MS Body Text = 9
5191 nOutLvl = nOutLvl ? nOutLvl - 1 : 9;
5192 m_rWW8Export.InsUInt16( NS_sprm::POutLvl::val );
5193 m_rWW8Export.m_pO->push_back( nOutLvl );
5196 // "Separate paragraphs"
5197 void WW8AttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit )
5199 // sprmPFKeep
5200 m_rWW8Export.InsUInt16( NS_sprm::PFKeep::val );
5201 m_rWW8Export.m_pO->push_back( rSplit.GetValue() ? 0 : 1 );
5205 * Only convert the item "SvxWidowItem" and not the orphans, because
5206 * in WW only one attribute "paragraph control" exists for both and
5207 * in SW probably both or none is set by the user.
5209 void WW8AttributeOutput::ParaWidows( const SvxWidowsItem& rWidows )
5211 // sprmPFWidowControl
5212 m_rWW8Export.InsUInt16( NS_sprm::PFWidowControl::val );
5213 m_rWW8Export.m_pO->push_back( rWidows.GetValue() ? 1 : 0 );
5216 namespace {
5218 class SwWW8WrTabu
5220 std::unique_ptr<sal_uInt8[]> m_pDel; // DelArray
5221 std::unique_ptr<sal_uInt8[]> m_pAddPos; // AddPos-Array
5222 std::unique_ptr<sal_uInt8[]> m_pAddTyp; // AddTyp-Array
5223 sal_uInt16 m_nAdd; // number of tabs to be added
5224 sal_uInt16 m_nDel; // number of tabs to be deleted
5226 SwWW8WrTabu(const SwWW8WrTabu&) = delete;
5227 SwWW8WrTabu& operator=(const SwWW8WrTabu&) = delete;
5229 public:
5230 SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax);
5232 void Add(const SvxTabStop &rTS, tools::Long nAdjustment);
5233 void Del(const SvxTabStop &rTS, tools::Long nAdjustment);
5234 void PutAll(WW8Export& rWW8Wrt);
5239 SwWW8WrTabu::SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax)
5240 : m_nAdd(0), m_nDel(0)
5242 if (nDelMax)
5243 m_pDel.reset( new sal_uInt8[nDelMax * 2] );
5244 m_pAddPos.reset( new sal_uInt8[nAddMax * 2] );
5245 m_pAddTyp.reset( new sal_uInt8[nAddMax] );
5249 * insert a tab in the WW structure
5251 void SwWW8WrTabu::Add(const SvxTabStop & rTS, tools::Long nAdjustment)
5253 // insert tab position
5254 ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment),
5255 m_pAddPos.get() + (m_nAdd * 2));
5257 // insert tab type
5258 sal_uInt8 nPara = 0;
5259 switch (rTS.GetAdjustment())
5261 case SvxTabAdjust::Right:
5262 nPara = 2;
5263 break;
5264 case SvxTabAdjust::Center:
5265 nPara = 1;
5266 break;
5267 case SvxTabAdjust::Decimal:
5269 There is nothing we can do btw the decimal separator has been
5270 customized, but if you think different remember that different
5271 locales have different separators, i.e. german is a , while english
5272 is a .
5274 nPara = 3;
5275 break;
5276 default:
5277 break;
5280 switch( rTS.GetFill() )
5282 case '.': // dotted leader
5283 nPara |= 1 << 3;
5284 break;
5285 case '_': // Single line leader
5286 nPara |= 3 << 3;
5287 break;
5288 case '-': // hyphenated leader
5289 nPara |= 2 << 3;
5290 break;
5291 case '=': // heavy line leader
5292 nPara |= 4 << 3;
5293 break;
5296 m_pAddTyp[m_nAdd] = nPara;
5297 ++m_nAdd;
5301 * Insert a to be deleted tab in the WW structure
5303 void SwWW8WrTabu::Del(const SvxTabStop &rTS, tools::Long nAdjustment)
5305 // insert tab position
5306 ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment),
5307 m_pDel.get() + (m_nDel * 2));
5308 ++m_nDel;
5312 * Writes the attribute to rWrt.pO
5314 void SwWW8WrTabu::PutAll(WW8Export& rWrt)
5316 if (!m_nAdd && !m_nDel) //If it's a no-op
5317 return;
5318 OSL_ENSURE(m_nAdd <= 255, "more than 255 added tabstops?");
5319 OSL_ENSURE(m_nDel <= 255, "more than 244 removed tabstops?");
5320 if (m_nAdd > 255)
5321 m_nAdd = 255;
5322 if (m_nDel > 255)
5323 m_nDel = 255;
5325 sal_uInt16 nSiz = 2 * m_nDel + 3 * m_nAdd + 2;
5326 if (nSiz > 255)
5327 nSiz = 255;
5329 rWrt.InsUInt16(NS_sprm::PChgTabsPapx::val);
5330 // insert cch
5331 rWrt.m_pO->push_back(msword_cast<sal_uInt8>(nSiz));
5332 // write DelArr
5333 rWrt.m_pO->push_back(msword_cast<sal_uInt8>(m_nDel));
5334 rWrt.OutSprmBytes(m_pDel.get(), m_nDel * 2);
5335 // write InsArr
5336 rWrt.m_pO->push_back(msword_cast<sal_uInt8>(m_nAdd));
5337 rWrt.OutSprmBytes(m_pAddPos.get(), 2 * m_nAdd); // AddPosArray
5338 rWrt.OutSprmBytes(m_pAddTyp.get(), m_nAdd); // AddTypArray
5341 static void ParaTabStopAdd( WW8Export& rWrt,
5342 const SvxTabStopItem& rTStops,
5343 const tools::Long nLParaMgn )
5345 SwWW8WrTabu aTab( 0, rTStops.Count());
5347 for( sal_uInt16 n = 0; n < rTStops.Count(); n++ )
5349 const SvxTabStop& rTS = rTStops[n];
5350 // ignore default tabs
5351 if (SvxTabAdjust::Default != rTS.GetAdjustment())
5352 aTab.Add(rTS, nLParaMgn);
5354 aTab.PutAll( rWrt );
5357 static bool lcl_IsEqual(tools::Long nOneLeft, const SvxTabStop &rOne,
5358 tools::Long nTwoLeft, const SvxTabStop &rTwo)
5360 return(
5361 nOneLeft == nTwoLeft &&
5362 rOne.GetAdjustment() == rTwo.GetAdjustment() &&
5363 rOne.GetDecimal() == rTwo.GetDecimal() &&
5364 rOne.GetFill() == rTwo.GetFill()
5368 static void ParaTabStopDelAdd( WW8Export& rWrt,
5369 const SvxTabStopItem& rTStyle,
5370 const tools::Long nLStypeMgn,
5371 const SvxTabStopItem& rTNew,
5372 const tools::Long nLParaMgn )
5374 SwWW8WrTabu aTab(rTStyle.Count(), rTNew.Count());
5376 sal_uInt16 nO = 0; // rTStyle Index
5377 sal_uInt16 nN = 0; // rTNew Index
5379 do {
5380 const SvxTabStop* pTO;
5381 tools::Long nOP;
5382 if( nO < rTStyle.Count() ) // old not yet at the end?
5384 pTO = &rTStyle[ nO ];
5385 nOP = pTO->GetTabPos() + nLStypeMgn;
5386 if( SvxTabAdjust::Default == pTO->GetAdjustment() )
5388 nO++; // ignore default tab
5389 continue;
5392 else
5394 pTO = nullptr;
5395 nOP = LONG_MAX;
5398 const SvxTabStop* pTN;
5399 tools::Long nNP;
5400 if( nN < rTNew.Count() ) // new not yet at the end
5402 pTN = &rTNew[ nN ];
5403 nNP = pTN->GetTabPos() + nLParaMgn;
5404 if( SvxTabAdjust::Default == pTN->GetAdjustment() )
5406 nN++; // ignore default tab
5407 continue;
5410 else
5412 pTN = nullptr;
5413 nNP = LONG_MAX;
5416 if( nOP == LONG_MAX && nNP == LONG_MAX )
5417 break; // everything done
5419 if( nOP < nNP ) // next tab is old
5421 assert(pTO);
5422 aTab.Del(*pTO, nLStypeMgn); // must be deleted
5423 nO++;
5425 else if( nNP < nOP ) // next tab is new
5427 assert(pTN);
5428 aTab.Add(*pTN, nLParaMgn); // must be inserted
5429 nN++;
5431 else if (lcl_IsEqual(nOP, *pTO, nNP, *pTN)) // tabs are equal
5433 nO++; // nothing to do
5434 nN++;
5436 else // tabs same position, different type
5438 aTab.Del(*pTO, nLStypeMgn); // delete old one
5439 aTab.Add(*pTN, nLParaMgn); // insert new one
5440 nO++;
5441 nN++;
5443 } while( true );
5445 aTab.PutAll( rWrt );
5448 void WW8AttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStops )
5450 const bool bTabsRelativeToIndex = m_rWW8Export.m_pCurPam->GetDoc().getIDocumentSettingAccess().get( DocumentSettingId::TABS_RELATIVE_TO_INDENT );
5452 tools::Long nCurrentLeft = 0;
5453 if ( bTabsRelativeToIndex )
5455 if (const SfxPoolItem* pItem = m_rWW8Export.HasItem(RES_MARGIN_TEXTLEFT))
5457 if (const auto pLeft = pItem->DynamicWhichCast(RES_MARGIN_TEXTLEFT))
5459 nCurrentLeft = pLeft->GetTextLeft();
5461 else
5462 // FIXME: This fails in sw.ww8export/testCommentExport::Load_Verify_Reload_Verify
5463 SAL_WARN("sw.ww8", "m_rWW8Export has an RES_LR_SPACE item, but it's of the wrong type.");
5467 // #i100264#
5468 if ( m_rWW8Export.m_bStyDef &&
5469 m_rWW8Export.m_pCurrentStyle != nullptr &&
5470 m_rWW8Export.m_pCurrentStyle->DerivedFrom() != nullptr )
5472 SvxTabStopItem aParentTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
5473 const SwFormat *pParentStyle = m_rWW8Export.m_pCurrentStyle->DerivedFrom();
5475 if (const SvxTabStopItem* pParentTabs = pParentStyle->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP))
5477 aParentTabs.Insert( pParentTabs );
5481 // #i120938# - consider left indentation of style and its parent style
5482 tools::Long nParentLeft = 0;
5483 if ( bTabsRelativeToIndex )
5485 SvxTextLeftMarginItem const& rLeftMargin(pParentStyle->GetAttrSet().Get(RES_MARGIN_TEXTLEFT));
5486 nParentLeft = rLeftMargin.GetTextLeft();
5489 ParaTabStopDelAdd( m_rWW8Export, aParentTabs, nParentLeft, rTabStops, nCurrentLeft );
5490 return;
5493 const SvxTabStopItem* pStyleTabs = nullptr;
5494 if ( !m_rWW8Export.m_bStyDef && m_rWW8Export.m_pStyAttr )
5496 pStyleTabs = m_rWW8Export.m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
5499 if ( !pStyleTabs )
5501 ParaTabStopAdd(m_rWW8Export, rTabStops, nCurrentLeft);
5503 else
5505 tools::Long nStyleLeft = 0;
5506 if ( bTabsRelativeToIndex )
5508 SvxTextLeftMarginItem const& rLeftMargin(m_rWW8Export.m_pStyAttr->Get(RES_MARGIN_TEXTLEFT));
5509 nStyleLeft = rLeftMargin.GetTextLeft();
5512 ParaTabStopDelAdd( m_rWW8Export,
5513 *pStyleTabs, nStyleLeft,
5514 rTabStops, nCurrentLeft);
5518 void AttributeOutputBase::OutputItem( const SfxPoolItem& rHt )
5520 // FIXME maybe use 'item_cast', like 'item_cast<SvxCharHiddenItem>( rHt )'?
5521 switch ( rHt.Which() )
5523 case RES_CHRATR_CASEMAP:
5524 CharCaseMap(rHt.StaticWhichCast(RES_CHRATR_CASEMAP));
5525 break;
5526 case RES_CHRATR_COLOR:
5527 CharColor(rHt.StaticWhichCast(RES_CHRATR_COLOR));
5528 break;
5529 case RES_CHRATR_CONTOUR:
5530 CharContour(rHt.StaticWhichCast(RES_CHRATR_CONTOUR));
5531 break;
5532 case RES_CHRATR_CROSSEDOUT:
5533 CharCrossedOut(rHt.StaticWhichCast(RES_CHRATR_CROSSEDOUT));
5534 break;
5535 case RES_CHRATR_ESCAPEMENT:
5536 CharEscapement(rHt.StaticWhichCast(RES_CHRATR_ESCAPEMENT));
5537 break;
5538 case RES_CHRATR_FONT:
5539 CharFont(rHt.StaticWhichCast(RES_CHRATR_FONT));
5540 break;
5541 case RES_CHRATR_FONTSIZE:
5542 CharFontSize(rHt.StaticWhichCast(RES_CHRATR_FONTSIZE));
5543 break;
5544 case RES_CHRATR_KERNING:
5545 CharKerning(rHt.StaticWhichCast(RES_CHRATR_KERNING));
5546 break;
5547 case RES_CHRATR_LANGUAGE:
5548 CharLanguage(rHt.StaticWhichCast(RES_CHRATR_LANGUAGE));
5549 break;
5550 case RES_CHRATR_POSTURE:
5551 CharPosture(rHt.StaticWhichCast(RES_CHRATR_POSTURE));
5552 break;
5553 case RES_CHRATR_SHADOWED:
5554 CharShadow(rHt.StaticWhichCast(RES_CHRATR_SHADOWED));
5555 break;
5556 case RES_CHRATR_UNDERLINE:
5557 CharUnderline(rHt.StaticWhichCast(RES_CHRATR_UNDERLINE));
5558 break;
5559 case RES_CHRATR_WEIGHT:
5560 CharWeight(rHt.StaticWhichCast(RES_CHRATR_WEIGHT));
5561 break;
5562 case RES_CHRATR_AUTOKERN:
5563 CharAutoKern(rHt.StaticWhichCast(RES_CHRATR_AUTOKERN));
5564 break;
5565 case RES_CHRATR_BLINK:
5566 CharAnimatedText(rHt.StaticWhichCast(RES_CHRATR_BLINK));
5567 break;
5568 case RES_CHRATR_BACKGROUND:
5569 CharBackgroundBase(rHt.StaticWhichCast(RES_CHRATR_BACKGROUND));
5570 break;
5572 case RES_CHRATR_CJK_FONT:
5573 CharFontCJK( static_cast< const SvxFontItem& >( rHt ) );
5574 break;
5575 case RES_CHRATR_CJK_FONTSIZE:
5576 CharFontSizeCJK( static_cast< const SvxFontHeightItem& >( rHt ) );
5577 break;
5578 case RES_CHRATR_CJK_LANGUAGE:
5579 CharLanguageCJK( static_cast< const SvxLanguageItem& >( rHt ) );
5580 break;
5581 case RES_CHRATR_CJK_POSTURE:
5582 CharPostureCJK( static_cast< const SvxPostureItem& >( rHt ) );
5583 break;
5584 case RES_CHRATR_CJK_WEIGHT:
5585 CharWeightCJK( static_cast< const SvxWeightItem& >( rHt ) );
5586 break;
5588 case RES_CHRATR_CTL_FONT:
5589 CharFontCTL( static_cast< const SvxFontItem& >( rHt ) );
5590 break;
5591 case RES_CHRATR_CTL_FONTSIZE:
5592 CharFontSizeCTL( static_cast< const SvxFontHeightItem& >( rHt ) );
5593 break;
5594 case RES_CHRATR_CTL_LANGUAGE:
5595 CharLanguageCTL( static_cast< const SvxLanguageItem& >( rHt ) );
5596 break;
5597 case RES_CHRATR_CTL_POSTURE:
5598 CharPostureCTL( static_cast< const SvxPostureItem& >( rHt ) );
5599 break;
5600 case RES_CHRATR_CTL_WEIGHT:
5601 CharWeightCTL( static_cast< const SvxWeightItem& >( rHt ) );
5602 break;
5604 case RES_CHRATR_ROTATE:
5605 CharRotate(rHt.StaticWhichCast(RES_CHRATR_ROTATE));
5606 break;
5607 case RES_CHRATR_EMPHASIS_MARK:
5608 CharEmphasisMark(rHt.StaticWhichCast(RES_CHRATR_EMPHASIS_MARK));
5609 break;
5610 case RES_CHRATR_TWO_LINES:
5611 CharTwoLines(rHt.StaticWhichCast(RES_CHRATR_TWO_LINES));
5612 break;
5613 case RES_CHRATR_SCALEW:
5614 CharScaleWidth(rHt.StaticWhichCast(RES_CHRATR_SCALEW));
5615 break;
5616 case RES_CHRATR_RELIEF:
5617 CharRelief(rHt.StaticWhichCast(RES_CHRATR_RELIEF));
5618 break;
5619 case RES_CHRATR_HIDDEN:
5620 CharHidden(rHt.StaticWhichCast(RES_CHRATR_HIDDEN));
5621 break;
5622 case RES_CHRATR_BOX:
5623 FormatCharBorder(rHt.StaticWhichCast(RES_CHRATR_BOX));
5624 break;
5625 case RES_CHRATR_HIGHLIGHT:
5626 CharHighlight(rHt.StaticWhichCast(RES_CHRATR_HIGHLIGHT));
5627 break;
5628 case RES_CHRATR_BIDIRTL:
5629 CharBidiRTL( rHt );
5630 break;
5631 case RES_CHRATR_IDCTHINT:
5632 CharIdctHint( rHt );
5633 break;
5634 case RES_TXTATR_INETFMT:
5635 TextINetFormat( static_cast< const SwFormatINetFormat& >( rHt ) );
5636 break;
5637 case RES_TXTATR_CHARFMT:
5638 TextCharFormat( static_cast< const SwFormatCharFormat& >( rHt ) );
5639 break;
5641 case RES_TXTATR_FIELD:
5642 case RES_TXTATR_ANNOTATION:
5643 case RES_TXTATR_INPUTFIELD:
5644 TextField( static_cast< const SwFormatField& >( rHt ) );
5645 break;
5647 case RES_TXTATR_FLYCNT:
5648 TextFlyContent( static_cast< const SwFormatFlyCnt& >( rHt ) );
5649 break;
5650 case RES_TXTATR_FTN:
5651 TextFootnote( static_cast< const SwFormatFootnote& >( rHt ) );
5652 break;
5654 case RES_PARATR_LINESPACING:
5655 ParaLineSpacing( static_cast< const SvxLineSpacingItem& >( rHt ) );
5656 break;
5657 case RES_PARATR_ADJUST:
5658 ParaAdjust( static_cast< const SvxAdjustItem& >( rHt ) );
5659 break;
5660 case RES_PARATR_SPLIT:
5661 ParaSplit( static_cast< const SvxFormatSplitItem& >( rHt ) );
5662 break;
5663 case RES_PARATR_WIDOWS:
5664 ParaWidows( static_cast< const SvxWidowsItem& >( rHt ) );
5665 break;
5666 case RES_PARATR_TABSTOP:
5667 ParaTabStop( static_cast< const SvxTabStopItem& >( rHt ) );
5668 break;
5669 case RES_PARATR_HYPHENZONE:
5670 ParaHyphenZone( static_cast< const SvxHyphenZoneItem& >( rHt ) );
5671 break;
5672 case RES_PARATR_NUMRULE:
5673 ParaNumRule( static_cast< const SwNumRuleItem& >( rHt ) );
5674 break;
5675 case RES_PARATR_SCRIPTSPACE:
5676 ParaScriptSpace( static_cast< const SfxBoolItem& >( rHt ) );
5677 break;
5678 case RES_PARATR_HANGINGPUNCTUATION:
5679 ParaHangingPunctuation( static_cast< const SfxBoolItem& >( rHt ) );
5680 break;
5681 case RES_PARATR_FORBIDDEN_RULES:
5682 ParaForbiddenRules( static_cast< const SfxBoolItem& >( rHt ) );
5683 break;
5684 case RES_PARATR_VERTALIGN:
5685 ParaVerticalAlign( static_cast< const SvxParaVertAlignItem& >( rHt ) );
5686 break;
5687 case RES_PARATR_SNAPTOGRID:
5688 ParaSnapToGrid( static_cast< const SvxParaGridItem& >( rHt ) );
5689 break;
5691 case RES_FRM_SIZE:
5692 FormatFrameSize( static_cast< const SwFormatFrameSize& >( rHt ) );
5693 break;
5694 case RES_PAPER_BIN:
5695 FormatPaperBin( static_cast< const SvxPaperBinItem& >( rHt ) );
5696 break;
5697 case RES_MARGIN_FIRSTLINE:
5698 FormatFirstLineIndent(static_cast<const SvxFirstLineIndentItem &>(rHt));
5699 break;
5700 case RES_MARGIN_TEXTLEFT:
5701 FormatTextLeftMargin(static_cast<const SvxTextLeftMarginItem &>(rHt));
5702 break;
5703 case RES_MARGIN_RIGHT:
5704 FormatRightMargin(static_cast<const SvxRightMarginItem &>(rHt));
5705 break;
5706 case RES_LR_SPACE:
5707 FormatLRSpace( static_cast< const SvxLRSpaceItem& >( rHt ) );
5708 break;
5709 case RES_UL_SPACE:
5710 FormatULSpace( static_cast< const SvxULSpaceItem& >( rHt ) );
5711 break;
5712 case RES_PAGEDESC:
5713 FormatPageDescription( static_cast< const SwFormatPageDesc& >( rHt ) );
5714 break;
5715 case RES_BREAK:
5716 FormatBreak( static_cast< const SvxFormatBreakItem& >( rHt ) );
5717 break;
5718 case RES_SURROUND:
5719 FormatSurround( static_cast< const SwFormatSurround& >( rHt ) );
5720 break;
5721 case RES_VERT_ORIENT:
5722 FormatVertOrientation( static_cast< const SwFormatVertOrient& >( rHt ) );
5723 break;
5724 case RES_HORI_ORIENT:
5725 FormatHorizOrientation( static_cast< const SwFormatHoriOrient& >( rHt ) );
5726 break;
5727 case RES_ANCHOR:
5728 FormatAnchor( static_cast< const SwFormatAnchor& >( rHt ) );
5729 break;
5730 case RES_BACKGROUND:
5731 FormatBackground( static_cast< const SvxBrushItem& >( rHt ) );
5732 break;
5733 case XATTR_FILLSTYLE:
5734 FormatFillStyle( static_cast< const XFillStyleItem& >( rHt ) );
5735 break;
5736 case XATTR_FILLGRADIENT:
5737 FormatFillGradient( static_cast< const XFillGradientItem& >( rHt ) );
5738 break;
5739 case RES_BOX:
5740 FormatBox( static_cast< const SvxBoxItem& >( rHt ) );
5741 break;
5742 case RES_COL:
5743 FormatColumns( static_cast< const SwFormatCol& >( rHt ) );
5744 break;
5745 case RES_KEEP:
5746 FormatKeep( static_cast< const SvxFormatKeepItem& >( rHt ) );
5747 break;
5748 case RES_TEXTGRID:
5749 FormatTextGrid( static_cast< const SwTextGridItem& >( rHt ) );
5750 break;
5751 case RES_LINENUMBER:
5752 FormatLineNumbering( static_cast< const SwFormatLineNumber& >( rHt ) );
5753 break;
5754 case RES_FRAMEDIR:
5755 FormatFrameDirection( static_cast< const SvxFrameDirectionItem& >( rHt ) );
5756 break;
5757 case RES_PARATR_GRABBAG:
5758 ParaGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
5759 break;
5760 case RES_PARATR_OUTLINELEVEL:
5761 ParaOutlineLevelBase(static_cast<const SfxUInt16Item&>(rHt));
5762 break;
5763 case RES_CHRATR_GRABBAG:
5764 CharGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
5765 break;
5766 case RES_RTL_GUTTER:
5767 SectionRtlGutter(static_cast<const SfxBoolItem&>(rHt));
5768 break;
5769 case RES_TXTATR_LINEBREAK:
5770 TextLineBreak(static_cast<const SwFormatLineBreak&>(rHt));
5771 break;
5773 default:
5774 SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() );
5775 break;
5779 void AttributeOutputBase::OutputStyleItemSet( const SfxItemSet& rSet, bool bTestForDefault )
5781 // based on OutputItemSet() from wrt_fn.cxx
5783 const SfxItemPool& rPool = *rSet.GetPool();
5784 const SfxItemSet* pSet = &rSet;
5785 if ( !pSet->Count() )
5787 while ( nullptr != ( pSet = pSet->GetParent() ) && !pSet->Count() )
5790 if ( !pSet )
5791 return;
5794 const SfxPoolItem* pItem;
5795 if ( !pSet->GetParent() )
5797 assert(rSet.Count() && "Was already handled or?");
5798 SfxItemIter aIter( *pSet );
5799 pItem = aIter.GetCurItem();
5800 do {
5801 OutputItem( *pItem );
5802 } while ((pItem = aIter.NextItem()));
5804 else
5806 SfxWhichIter aIter( *pSet );
5807 sal_uInt16 nWhich = aIter.FirstWhich();
5808 while ( nWhich )
5810 if ( SfxItemState::SET == aIter.GetItemState( true/*bDeep*/, &pItem ) &&
5811 ( !bTestForDefault ||
5812 nWhich == RES_UL_SPACE ||
5813 nWhich == RES_LR_SPACE ||
5814 *pItem != rPool.GetDefaultItem( nWhich ) ||
5815 ( pSet->GetParent() && *pItem != pSet->GetParent()->Get( nWhich ) ) ) )
5817 OutputItem( *pItem );
5819 nWhich = aIter.NextWhich();
5824 void AttributeOutputBase::FormatCharBorder( const SvxBoxItem& rBox )
5826 // Get one of the borders (if there is any border then in docx also will be)
5827 const SvxBorderLine* pBorderLine = nullptr;
5828 sal_uInt16 nDist = 0;
5829 if( rBox.GetTop() )
5831 pBorderLine = rBox.GetTop();
5832 nDist = rBox.GetDistance( SvxBoxItemLine::TOP );
5834 else if( rBox.GetLeft() )
5836 pBorderLine = rBox.GetLeft();
5837 nDist = rBox.GetDistance( SvxBoxItemLine::LEFT );
5839 else if( rBox.GetBottom() )
5841 pBorderLine = rBox.GetBottom();
5842 nDist = rBox.GetDistance( SvxBoxItemLine::BOTTOM );
5844 else if( rBox.GetRight() )
5846 pBorderLine = rBox.GetRight();
5847 nDist = rBox.GetDistance( SvxBoxItemLine::RIGHT );
5850 // RTF: avoid regressions since RTF doesn't know how to export a border_NONE style-override
5851 if( pBorderLine || GetExport().GetExportFormat() != MSWordExportBase::ExportFormat::RTF )
5853 const SfxPoolItem* pItem = GetExport().HasItem( RES_CHRATR_SHADOW );
5854 const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
5855 const bool bShadow = pBorderLine &&
5856 pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE &&
5857 pShadowItem->GetWidth() > 0;
5859 CharBorder( pBorderLine, nDist, bShadow );
5864 * This function is used to check if the current SwTextNode (paragraph) has a redline object
5865 * that is attached to the paragraph marker.
5866 * This is done by checking if the range (SwPaM) of the redline is :
5867 * - Start = the last character of the current paragraph
5868 * - End = the first character of the next paragraph
5870 const SwRedlineData* AttributeOutputBase::GetParagraphMarkerRedline( const SwTextNode& rNode, RedlineType aRedlineType)
5872 // ToDo : this is not the most ideal ... should start maybe from 'nCurRedlinePos'
5873 for(SwRangeRedline* pRedl : GetExport().m_rDoc.getIDocumentRedlineAccess().GetRedlineTable())
5875 // Only check redlines that are of type 'Delete'
5876 if ( pRedl->GetRedlineData().GetType() != aRedlineType )
5877 continue;
5879 SwNodeOffset uStartNodeIndex = pRedl->Start()->GetNodeIndex();
5880 SwNodeOffset uEndNodeIndex = pRedl->End()->GetNodeIndex();
5881 SwNodeOffset uNodeIndex = rNode.GetIndex();
5883 if( uStartNodeIndex <= uNodeIndex && uNodeIndex < uEndNodeIndex )
5884 return &( pRedl->GetRedlineData() );
5886 return nullptr;
5889 void AttributeOutputBase::CharBackgroundBase( const SvxBrushItem& rBrush )
5891 bool bConvertToShading = SvtFilterOptions::Get().IsCharBackground2Shading();
5892 bool bHasShadingMarker = false;
5894 // MS Word doesn't support highlight in character styles. Always export those as shading.
5895 if ( !bConvertToShading && GetExport().m_bStyDef )
5897 const SwFormat* pFormat = dynamic_cast<const SwFormat*>( GetExport().m_pOutFormatNode );
5898 bConvertToShading = pFormat && pFormat->Which() == RES_CHRFMT;
5901 // Check shading marker
5902 const SfxPoolItem* pItem = GetExport().HasItem(RES_CHRATR_GRABBAG);
5903 if( pItem )
5905 const SfxGrabBagItem aGrabBag = static_cast< const SfxGrabBagItem& >(*pItem);
5906 const std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
5907 auto aIterator = rMap.find("CharShadingMarker");
5908 if( aIterator != rMap.end() )
5910 aIterator->second >>= bHasShadingMarker;
5914 if( bConvertToShading || bHasShadingMarker )
5916 CharBackground(rBrush);
5918 else
5920 // Don't create a duplicate entry when converting to highlight. An existing one has priority.
5921 // Character runs seem to need a different method to detect duplicates? Just continue to ignore that situation.
5922 if (GetExport().m_aCurrentCharPropStarts.size() || !GetExport().HasItem(RES_CHRATR_HIGHLIGHT))
5923 CharHighlight(rBrush);
5927 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */