nss: upgrade to release 3.73
[LibreOffice.git] / sw / source / filter / ww8 / ww8atr.cxx
blob14afa9919405f11dffa97f176dce85a3bdffce65
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/zformat.hxx>
35 #include <svl/itemiter.hxx>
36 #include <svl/whiter.hxx>
37 #include <svl/grabbagitem.hxx>
38 #include <editeng/fontitem.hxx>
39 #include <editeng/tstpitem.hxx>
40 #include <editeng/adjustitem.hxx>
41 #include <editeng/spltitem.hxx>
42 #include <editeng/widwitem.hxx>
43 #include <editeng/lspcitem.hxx>
44 #include <editeng/keepitem.hxx>
45 #include <editeng/shaditem.hxx>
46 #include <editeng/brushitem.hxx>
47 #include <editeng/postitem.hxx>
48 #include <editeng/wghtitem.hxx>
49 #include <editeng/kernitem.hxx>
50 #include <editeng/crossedoutitem.hxx>
51 #include <editeng/cmapitem.hxx>
52 #include <editeng/wrlmitem.hxx>
53 #include <editeng/udlnitem.hxx>
54 #include <editeng/langitem.hxx>
55 #include <editeng/escapementitem.hxx>
56 #include <editeng/fhgtitem.hxx>
57 #include <editeng/colritem.hxx>
58 #include <editeng/hyphenzoneitem.hxx>
59 #include <editeng/formatbreakitem.hxx>
60 #include <editeng/lrspitem.hxx>
61 #include <editeng/ulspitem.hxx>
62 #include <editeng/boxitem.hxx>
63 #include <editeng/contouritem.hxx>
64 #include <editeng/shdditem.hxx>
65 #include <editeng/autokernitem.hxx>
66 #include <editeng/pbinitem.hxx>
67 #include <editeng/emphasismarkitem.hxx>
68 #include <editeng/twolinesitem.hxx>
69 #include <editeng/charscaleitem.hxx>
70 #include <editeng/charrotateitem.hxx>
71 #include <editeng/charreliefitem.hxx>
72 #include <editeng/paravertalignitem.hxx>
73 #include <editeng/pgrditem.hxx>
74 #include <editeng/frmdiritem.hxx>
75 #include <editeng/blinkitem.hxx>
76 #include <editeng/charhiddenitem.hxx>
77 #include <editeng/paperinf.hxx>
78 #include <svx/xfillit0.hxx>
79 #include <svx/xflgrit.hxx>
80 #include <fmtfld.hxx>
81 #include <fchrfmt.hxx>
82 #include <fmtfsize.hxx>
83 #include <fmtpdsc.hxx>
84 #include <fmtornt.hxx>
85 #include <fmtanchr.hxx>
86 #include <fmtclds.hxx>
87 #include <fmtsrnd.hxx>
88 #include <fmtftn.hxx>
89 #include <fmtflcnt.hxx>
90 #include <frmatr.hxx>
91 #include <swtable.hxx>
92 #include <fmtinfmt.hxx>
93 #include <txtfld.hxx>
94 #include <txtftn.hxx>
95 #include <poolfmt.hxx>
96 #include <doc.hxx>
97 #include <IDocumentSettingAccess.hxx>
98 #include <IDocumentFieldsAccess.hxx>
99 #include <IDocumentStylePoolAccess.hxx>
100 #include <IDocumentListsAccess.hxx>
101 #include <list.hxx>
102 #include <docary.hxx>
103 #include <pam.hxx>
104 #include <paratr.hxx>
105 #include <fldbas.hxx>
106 #include <docufld.hxx>
107 #include <expfld.hxx>
108 #include <pagedesc.hxx>
109 #include <ndtxt.hxx>
110 #include <swrect.hxx>
111 #include <redline.hxx>
112 #include <reffld.hxx>
113 #include <ftninfo.hxx>
114 #include <charfmt.hxx>
115 #include <section.hxx>
116 #include <fmtline.hxx>
117 #include <tox.hxx>
118 #include <fmtftntx.hxx>
119 #include <breakit.hxx>
120 #include <com/sun/star/i18n/ScriptType.hpp>
121 #include <com/sun/star/i18n/XBreakIterator.hpp>
122 #include <unotools/localedatawrapper.hxx>
123 #include <svx/unobrushitemhelper.hxx>
124 #include <tgrditem.hxx>
125 #include <flddropdown.hxx>
126 #include <chpfld.hxx>
127 #include <fmthdft.hxx>
128 #include <authfld.hxx>
129 #include <dbfld.hxx>
131 #include "sprmids.hxx"
133 #include <fmtcntnt.hxx>
134 #include "writerhelper.hxx"
135 #include "writerwordglue.hxx"
136 #include "wrtww8.hxx"
137 #include "ww8par.hxx"
138 #include "ww8attributeoutput.hxx"
139 #include "fields.hxx"
140 #include <i18nlangtag/languagetag.hxx>
141 #include <unotools/fltrcfg.hxx>
144 using ::editeng::SvxBorderLine;
145 using namespace ::com::sun::star;
146 using namespace nsSwDocInfoSubType;
147 using namespace sw::util;
148 using namespace sw::types;
150 bool WW8Export::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich )
152 bool bRet = true;
153 if ( nScript == i18n::ScriptType::ASIAN )
155 //for asian in ww8, there is only one fontsize
156 //and one fontstyle (posture/weight) for ww6
157 //there is the additional problem that there
158 //is only one font setting for all three scripts
159 switch ( nWhich )
161 case RES_CHRATR_FONTSIZE:
162 case RES_CHRATR_POSTURE:
163 case RES_CHRATR_WEIGHT:
164 bRet = false;
165 break;
166 case RES_CHRATR_LANGUAGE:
167 case RES_CHRATR_CTL_FONT:
168 case RES_CHRATR_CTL_FONTSIZE:
169 case RES_CHRATR_CTL_LANGUAGE:
170 case RES_CHRATR_CTL_POSTURE:
171 case RES_CHRATR_CTL_WEIGHT:
172 default:
173 break;
176 else if ( nScript == i18n::ScriptType::COMPLEX )
178 //Complex is ok in ww8, but for ww6 there is only
179 //one font, one fontsize, one fontsize (weight/posture)
180 //and only one language
182 else
184 //for western in ww8, there is only one fontsize
185 //and one fontstyle (posture/weight) for ww6
186 //there is the additional problem that there
187 //is only one font setting for all three scripts
188 switch ( nWhich )
190 case RES_CHRATR_CJK_FONTSIZE:
191 case RES_CHRATR_CJK_POSTURE:
192 case RES_CHRATR_CJK_WEIGHT:
193 bRet = false;
194 break;
195 case RES_CHRATR_CJK_LANGUAGE:
196 case RES_CHRATR_CTL_FONT:
197 case RES_CHRATR_CTL_FONTSIZE:
198 case RES_CHRATR_CTL_LANGUAGE:
199 case RES_CHRATR_CTL_POSTURE:
200 case RES_CHRATR_CTL_WEIGHT:
201 default:
202 break;
205 return bRet;
209 void MSWordExportBase::ExportPoolItemsToCHP( ww8::PoolItems &rItems, sal_uInt16 nScript, const SvxFontItem *pFont, bool bWriteCombChars )
211 for ( const auto& rItem : rItems )
213 const SfxPoolItem *pItem = rItem.second;
214 sal_uInt16 nWhich = pItem->Which();
215 if ( ( isCHRATR( nWhich ) || isTXTATR( nWhich ) ) && CollapseScriptsforWordOk( nScript, nWhich ) )
217 //In the id definition, RES_TXTATR_INETFMT must precede RES_TXTATR_CHARFMT, so that link style can overwrite char style.
218 //and in #i24291# it describes "All we want to do is ensure for now is that if a charfmt exist in the character
219 //properties that it rises to the top and is exported first."
220 //In bug 119649, it is in such situation, so we need to ignore the link style when doing ms word filter exports and
221 //add the second judgement for #i24291# definition.
222 if (nWhich == RES_TXTATR_CHARFMT)
224 const SfxPoolItem* pINetItem = SearchPoolItems(rItems, RES_TXTATR_INETFMT);
226 if (pINetItem)
228 const SwFormatINetFormat& rINet = static_cast<const SwFormatINetFormat&>(*pINetItem);
229 const SwCharFormat* pINetFormat = GetSwCharFormat(rINet, m_rDoc);
230 if (!pINetFormat)
231 continue;
233 const SwCharFormat* pFormat = static_cast<const SwFormatCharFormat&>(*pItem).GetCharFormat();
234 ww8::PoolItems aCharItems, aINetItems;
235 GetPoolItems(pFormat->GetAttrSet(), aCharItems, false);
236 GetPoolItems(pINetFormat->GetAttrSet(), aINetItems, false);
237 for (const auto& rCharItem : aCharItems)
239 const SfxPoolItem* pCharItem = rCharItem.second;
240 sal_uInt16 nCharWhich = pCharItem->Which();
241 if (!SearchPoolItems(aINetItems, nCharWhich) && !SearchPoolItems(rItems, nCharWhich))
242 AttrOutput().OutputItem(*pCharItem);
244 continue;
248 // tdf#38778 Fix output of the font in DOC run for fields
249 if (pFont &&
250 nWhich == RES_TXTATR_FIELD)
252 AttrOutput().OutputItem( *pFont );
255 // tdf#66401 For Combined Characters in docx, MS Word uses half the normal font-size for the field's
256 // font-size, but only for <w:sz>. Therefore, we check if we are currently writing a field of type
257 // Combined Characters and if so, we half the font size.
258 if (bWriteCombChars &&
259 nWhich == RES_CHRATR_FONTSIZE)
261 SvxFontHeightItem fontHeight(item_cast<SvxFontHeightItem>( *pItem ));
262 fontHeight.SetHeight( fontHeight.GetHeight() / 2 );
264 AttrOutput().OutputItem( fontHeight );
266 else if (nWhich == RES_CHRATR_COLOR)
268 const SvxColorItem& rColor = static_cast<const SvxColorItem&>(*pItem);
269 const SfxPoolItem* pBackgroundItem = SearchPoolItems(rItems, RES_CHRATR_BACKGROUND);
270 if (rColor.GetValue() == COL_AUTO && pBackgroundItem)
272 const SvxBrushItem& rBrushBackground = static_cast<const SvxBrushItem&>(*pBackgroundItem);
273 SvxColorItem aForeground(rBrushBackground.GetColor().IsDark() ? COL_WHITE : COL_BLACK, RES_CHRATR_COLOR);
274 AttrOutput().OutputItem(aForeground);
276 else
278 // default
279 AttrOutput().OutputItem( *pItem );
282 else
284 AttrOutput().OutputItem( *pItem );
291 * Output format as follows:
292 * - output the attributes; without parents!
295 void MSWordExportBase::OutputItemSet( const SfxItemSet& rSet, bool bPapFormat, bool bChpFormat, sal_uInt16 nScript,
296 bool bExportParentItemSet )
298 if( !(bExportParentItemSet || rSet.Count()) )
299 return;
301 const SfxPoolItem* pItem;
302 m_pISet = &rSet; // for double attributes
304 // If frame dir is set, but not adjust, then force adjust as well
305 if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_FRAMEDIR, bExportParentItemSet ) )
307 // No explicit adjust set ?
308 if ( SfxItemState::SET != rSet.GetItemState( RES_PARATR_ADJUST, bExportParentItemSet ) )
310 pItem = rSet.GetItem( RES_PARATR_ADJUST, bExportParentItemSet );
311 if ( nullptr != pItem )
313 // then set the adjust used by the parent format
314 AttrOutput().OutputItem( *pItem );
319 if ( bPapFormat && SfxItemState::SET == rSet.GetItemState( RES_PARATR_NUMRULE, bExportParentItemSet, &pItem ) )
321 AttrOutput().OutputItem( *pItem );
323 // switch off the numbering?
324 if ( static_cast<const SwNumRuleItem*>(pItem)->GetValue().isEmpty() &&
325 SfxItemState::SET != rSet.GetItemState( RES_LR_SPACE, false) &&
326 SfxItemState::SET == rSet.GetItemState( RES_LR_SPACE, true, &pItem ) )
328 // the set the LR-Space of the parentformat!
329 AttrOutput().OutputItem( *pItem );
333 ww8::PoolItems aItems;
334 GetPoolItems( rSet, aItems, bExportParentItemSet );
335 if ( bChpFormat )
336 ExportPoolItemsToCHP(aItems, nScript, nullptr);
337 if ( bPapFormat )
339 AttrOutput().MaybeOutputBrushItem(rSet);
341 for ( const auto& rItem : aItems )
343 pItem = rItem.second;
344 sal_uInt16 nWhich = pItem->Which();
345 // Handle fill attributes just like frame attributes for now.
346 if ( (nWhich >= RES_PARATR_BEGIN && nWhich < RES_FRMATR_END && nWhich != RES_PARATR_NUMRULE ) ||
347 (nWhich >= XATTR_FILL_FIRST && nWhich < XATTR_FILL_LAST))
348 AttrOutput().OutputItem( *pItem );
351 // Has to be called after RES_PARATR_GRABBAG is processed.
352 const XFillStyleItem* pXFillStyleItem(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
353 if (pXFillStyleItem && pXFillStyleItem->GetValue() == drawing::FillStyle_SOLID && !rSet.HasItem(RES_BACKGROUND))
355 // Construct an SvxBrushItem, as expected by the exporters.
356 std::unique_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
357 AttrOutput().OutputItem(*aBrush);
359 #if 0
360 else
362 // note: *does not work* due to undocumented Word behavior: must be before a:ln element at least
363 AttrOutput().MaybeOutputBrushItem(rSet);
365 #endif
367 m_pISet = nullptr; // for double attributes
370 void MSWordExportBase::GatherChapterFields()
372 //If the header/footer contains a chapter field
373 SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter );
374 pType->GatherNodeIndex(m_aChapterFieldLocs);
377 bool MSWordExportBase::ContentContainsChapterField(const SwFormatContent &rContent) const
379 bool bRet = false;
380 if ( const SwNodeIndex* pSttIdx = rContent.GetContentIdx() )
382 SwNodeIndex aIdx( *pSttIdx, 1 );
383 SwNodeIndex aEnd( *pSttIdx->GetNode().EndOfSectionNode() );
384 sal_uLong nStart = aIdx.GetIndex();
385 sal_uLong nEnd = aEnd.GetIndex();
386 //If the header/footer contains a chapter field
387 bRet = std::any_of(m_aChapterFieldLocs.cbegin(), m_aChapterFieldLocs.cend(),
388 [nStart, nEnd](sal_uLong i) { return ( nStart <= i ) && ( i <= nEnd ); });
390 return bRet;
393 bool MSWordExportBase::FormatHdFtContainsChapterField(const SwFrameFormat &rFormat) const
395 if ( m_aChapterFieldLocs.empty() )
396 return false;
398 const SwFrameFormat *pFormat = nullptr;
400 pFormat = rFormat.GetHeader().GetHeaderFormat();
401 if ( pFormat && ContentContainsChapterField( pFormat->GetContent() ) )
402 return true;
404 pFormat = rFormat.GetFooter().GetFooterFormat();
405 return pFormat && ContentContainsChapterField( pFormat->GetContent() );
408 bool MSWordExportBase::SetCurrentPageDescFromNode(const SwNode &rNd)
410 bool bNewPageDesc = false;
411 const SwPageDesc* pCurrent = SwPageDesc::GetPageDescOfNode(rNd);
412 OSL_ENSURE(pCurrent && m_pCurrentPageDesc, "Not possible surely");
413 if (m_pCurrentPageDesc && pCurrent)
415 if (pCurrent != m_pCurrentPageDesc)
417 if (m_pCurrentPageDesc->GetFollow() != pCurrent)
418 bNewPageDesc = true;
419 else
421 const SwFrameFormat& rTitleFormat = m_pCurrentPageDesc->GetFirstMaster();
422 const SwFrameFormat& rFollowFormat = pCurrent->GetMaster();
424 bNewPageDesc = !IsPlausableSingleWordSection(rTitleFormat,
425 rFollowFormat);
427 m_pCurrentPageDesc = pCurrent;
429 else
431 const SwFrameFormat &rFormat = pCurrent->GetMaster();
432 bNewPageDesc = FormatHdFtContainsChapterField(rFormat);
435 return bNewPageDesc;
439 * WW only knows Break-After (page break and section breaks),
440 * whereas in SW page breaks exist both "before" and "after" and PageDesc exists
441 * only "before". Therefore the breaks are iterated two times, namely before
442 * and after every line.
443 * Depending on the break type they're set before or after the line.
444 * Only functions can be called, which do not write in output area pO,
445 * because that one only exits once for CHP and PAP and therefore end up in
446 * the wrong one.
448 void MSWordExportBase::OutputSectionBreaks( const SfxItemSet *pSet, const SwNode& rNd, bool isCellOpen )
450 if ( m_bStyDef || m_bOutKF || m_bInWriteEscher || m_bOutPageDescs )
451 return;
453 m_bBreakBefore = true;
454 bool bNewPageDesc = false;
455 const SfxPoolItem* pItem=nullptr;
456 const SwFormatPageDesc *pPgDesc=nullptr;
458 //Output a sectionbreak if there's a new pagedescriptor. Otherwise output a
459 //pagebreak if there is a pagebreak here, unless the new page (follow
460 //style) is different to the current one, in which case plump for a
461 //section.
462 bool bBreakSet = false;
464 const SwPageDesc * pPageDesc = rNd.FindPageDesc();
466 // Even if m_pCurrentPageDesc != pPageDesc ,it might be because of the different header & footer types.
467 if (m_pCurrentPageDesc != pPageDesc)
469 if (isCellOpen && ( m_pCurrentPageDesc->GetName() != pPageDesc->GetName() ))
472 * If Table cell is open and page header types are different
473 * set pSet to NULL as we don't want to add any section breaks inside a table.
475 pSet = nullptr;
477 else if (!sw::util::IsPlausableSingleWordSection(m_pCurrentPageDesc->GetFirstMaster(), pPageDesc->GetMaster()))
479 bBreakSet = true;
480 bNewPageDesc = true;
481 m_pCurrentPageDesc = pPageDesc;
485 if ( pSet && pSet->Count() )
487 if ( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem ) &&
488 static_cast<const SwFormatPageDesc*>(pItem)->GetRegisteredIn() != nullptr)
490 bBreakSet = true;
491 bNewPageDesc = true;
492 pPgDesc = static_cast<const SwFormatPageDesc*>(pItem);
493 m_pCurrentPageDesc = pPgDesc->GetPageDesc();
495 else if ( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem ) )
497 // Word does not like hard break attributes in some table cells
498 bool bRemoveHardBreakInsideTable = false;
499 if ( m_bOutTable )
501 const SwTableNode* pTableNode = rNd.FindTableNode();
502 if ( pTableNode )
504 const SwTableBox* pBox = rNd.GetTableBox();
505 const SwTableLine* pLine = pBox ? pBox->GetUpper() : nullptr;
506 // but only for non-complex tables
507 if ( pLine && !pLine->GetUpper() )
509 // check if box is not first in that line:
510 if ( 0 < pLine->GetBoxPos( pBox ) && pBox->GetSttNd() )
512 bRemoveHardBreakInsideTable = true;
517 bBreakSet = true;
519 if ( !bRemoveHardBreakInsideTable )
521 OSL_ENSURE(m_pCurrentPageDesc, "should not be possible");
523 If because of this pagebreak the page desc following the page
524 break is the follow style of the current page desc then output a
525 section break using that style instead. At least in those cases
526 we end up with the same style in word and writer, nothing can be
527 done when it happens when we get a new pagedesc because we
528 overflow from the first page style.
530 if ( m_pCurrentPageDesc )
532 // #i76301# - assure that there is a page break before set at the node.
533 const SvxFormatBreakItem* pBreak = dynamic_cast<const SvxFormatBreakItem*>(pItem);
534 if ( pBreak &&
535 pBreak->GetBreak() == SvxBreak::PageBefore )
537 bNewPageDesc |= SetCurrentPageDescFromNode( rNd );
540 if ( !bNewPageDesc )
541 AttrOutput().OutputItem( *pItem );
547 #i9301#
548 No explicit page break, lets see if the style had one and we've moved to a
549 new page style because of it, if we have to then we take the opportunity to
550 set the equivalent word section here. We *could* do it for every paragraph
551 that moves onto a new page because of layout, but that would be insane.
553 bool bHackInBreak = false;
554 if ( !bBreakSet )
556 if ( const SwContentNode *pNd = rNd.GetContentNode() )
558 const SvxFormatBreakItem &rBreak =
559 ItemGet<SvxFormatBreakItem>( *pNd, RES_BREAK );
560 if ( rBreak.GetBreak() == SvxBreak::PageBefore )
561 bHackInBreak = true;
562 else
563 { // Even a pagedesc item is set, the break item can be set 'NONE',
564 // but a pagedesc item is an implicit page break before...
565 const SwFormatPageDesc &rPageDesc =
566 ItemGet<SwFormatPageDesc>( *pNd, RES_PAGEDESC );
567 if ( rPageDesc.KnowsPageDesc() )
568 bHackInBreak = true;
573 if ( bHackInBreak )
575 OSL_ENSURE( m_pCurrentPageDesc, "should not be possible" );
576 if ( m_pCurrentPageDesc )
577 bNewPageDesc = SetCurrentPageDescFromNode( rNd );
580 if ( bNewPageDesc && m_pCurrentPageDesc )
582 PrepareNewPageDesc( pSet, rNd, pPgDesc, m_pCurrentPageDesc );
584 m_bBreakBefore = false;
587 // #i76300#
588 bool MSWordExportBase::OutputFollowPageDesc( const SfxItemSet* pSet, const SwTextNode* pNd )
590 bool bRet = false;
592 if ( pNd &&
593 m_pCurrentPageDesc &&
594 m_pCurrentPageDesc != m_pCurrentPageDesc->GetFollow() )
596 PrepareNewPageDesc( pSet, *pNd, nullptr, m_pCurrentPageDesc->GetFollow() );
597 bRet = true;
600 return bRet;
603 const SwSectionFormat* MSWordExportBase::GetSectionFormat( const SwNode& rNd )
605 const SwSectionFormat* pFormat = nullptr;
606 const SwSectionNode* pSect = rNd.FindSectionNode();
607 if ( pSect &&
608 SectionType::Content == pSect->GetSection().GetType() )
610 pFormat = pSect->GetSection().GetFormat();
613 return pFormat;
616 sal_uLong MSWordExportBase::GetSectionLineNo( const SfxItemSet* pSet, const SwNode& rNd )
618 const SwFormatLineNumber* pNItem = nullptr;
619 if ( pSet )
621 pNItem = &( ItemGet<SwFormatLineNumber>( *pSet, RES_LINENUMBER ) );
623 else if ( const SwContentNode *pNd = rNd.GetContentNode() )
625 pNItem = &( ItemGet<SwFormatLineNumber>( *pNd, RES_LINENUMBER ) );
628 return pNItem? pNItem->GetStartValue() : 0;
631 void WW8Export::PrepareNewPageDesc( const SfxItemSet*pSet,
632 const SwNode& rNd,
633 const SwFormatPageDesc* pNewPgDescFormat,
634 const SwPageDesc* pNewPgDesc )
636 // The PageDescs will only be inserted in WW8Writer::pSepx with the corresponding
637 // position by the occurrences of PageDesc attributes. The construction and
638 // output of the attributes and header/footer of the PageDesc are done
639 // after the main text and its attributes.
641 sal_uLong nFcPos = ReplaceCr( msword::PageBreak ); // Page/Section-Break
643 // actually nothing is outputted here, rather the arrays aCps, aSects
644 // accordingly completed
645 if ( !nFcPos )
646 return;
648 const SwSectionFormat* pFormat = GetSectionFormat( rNd );
649 const sal_uLong nLnNm = GetSectionLineNo( pSet, rNd );
651 OSL_ENSURE( pNewPgDescFormat || pNewPgDesc, "Neither page desc format nor page desc provided." );
653 if ( pNewPgDescFormat )
655 pSepx->AppendSep( Fc2Cp( nFcPos ), *pNewPgDescFormat, rNd, pFormat, nLnNm );
657 else if ( pNewPgDesc )
659 pSepx->AppendSep( Fc2Cp( nFcPos ), pNewPgDesc, rNd, pFormat, nLnNm );
663 void MSWordExportBase::CorrectTabStopInSet( SfxItemSet& rSet, sal_Int32 nAbsLeft )
665 const SvxTabStopItem *pItem = rSet.GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
666 if (!pItem)
667 return;
669 // then it must be corrected for the output
670 SvxTabStopItem aTStop(*pItem);
671 for ( sal_uInt16 nCnt = 0; nCnt < aTStop.Count(); ++nCnt )
673 SvxTabStop& rTab = const_cast<SvxTabStop&>(aTStop[ nCnt ]);
674 if ( SvxTabAdjust::Default != rTab.GetAdjustment() &&
675 rTab.GetTabPos() >= nAbsLeft )
677 rTab.GetTabPos() -= nAbsLeft;
679 else
681 aTStop.Remove( nCnt );
682 --nCnt;
685 rSet.Put( aTStop );
688 sal_uInt8 WW8Export::GetNumId( sal_uInt16 eNumType )
690 sal_uInt8 nRet = 0;
691 switch( eNumType )
693 case SVX_NUM_CHARS_UPPER_LETTER:
694 case SVX_NUM_CHARS_UPPER_LETTER_N: nRet = 3; break;
695 case SVX_NUM_CHARS_LOWER_LETTER:
696 case SVX_NUM_CHARS_LOWER_LETTER_N: nRet = 4; break;
697 case SVX_NUM_ROMAN_UPPER: nRet = 1; break;
698 case SVX_NUM_ROMAN_LOWER: nRet = 2; break;
700 case SVX_NUM_BITMAP:
701 case SVX_NUM_CHAR_SPECIAL: nRet = 23; break;
703 // nothing, WW does the same (undocumented)
704 case SVX_NUM_NUMBER_NONE: nRet = 0xff; break;
705 case SVX_NUM_SYMBOL_CHICAGO:
706 // 0x09, msonfcChiManSty
707 nRet = 9;
708 break;
709 case SVX_NUM_ARABIC_ZERO:
710 // 0x16, msonfcArabicLZ
711 nRet = 22;
712 break;
714 return nRet;
717 void WW8AttributeOutput::OutlineNumbering(sal_uInt8 nLvl)
719 if ( nLvl >= WW8ListManager::nMaxLevel )
720 nLvl = WW8ListManager::nMaxLevel-1;
722 // write sprmPOutLvl sprmPIlvl and sprmPIlfo
723 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::POutLvl::val );
724 m_rWW8Export.pO->push_back( nLvl );
725 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::PIlvl::val );
726 m_rWW8Export.pO->push_back( nLvl );
727 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::PIlfo::val );
728 SwWW8Writer::InsUInt16( *m_rWW8Export.pO,
729 1 + m_rWW8Export.GetNumberingId(*m_rWW8Export.m_rDoc.GetOutlineNumRule()) );
732 // #i77805#
733 bool WW8Export::DisallowInheritingOutlineNumbering(const SwFormat &rFormat)
735 bool bRet( false );
737 //If there is no numbering on this fmt, but its parent was outline
738 //numbered, then in writer this is no inheritied, but in word it would
739 //be, so we must export "no numbering" and "body level" to make word
740 //behave like writer (see #i25755)
741 if (SfxItemState::SET != rFormat.GetItemState(RES_PARATR_NUMRULE, false))
743 if (const SwFormat *pParent = rFormat.DerivedFrom())
745 if (static_cast<const SwTextFormatColl*>(pParent)->IsAssignedToListLevelOfOutlineStyle())
747 SwWW8Writer::InsUInt16(*pO, NS_sprm::POutLvl::val);
748 pO->push_back(sal_uInt8(9));
749 SwWW8Writer::InsUInt16(*pO, NS_sprm::PIlfo::val);
750 SwWW8Writer::InsUInt16(*pO, 0);
752 bRet = true;
757 return bRet;
760 void MSWordExportBase::OutputFormat( const SwFormat& rFormat, bool bPapFormat, bool bChpFormat, bool bFlyFormat )
762 bool bCallOutSet = true;
763 const sw::BroadcastingModify* pOldMod = m_pOutFormatNode;
764 m_pOutFormatNode = &rFormat;
766 switch( rFormat.Which() )
768 case RES_CONDTXTFMTCOLL:
769 case RES_TXTFMTCOLL:
770 if( bPapFormat )
772 int nLvl = MAXLEVEL;
774 if (static_cast<const SwTextFormatColl&>(rFormat).IsAssignedToListLevelOfOutlineStyle())
775 nLvl = static_cast<const SwTextFormatColl&>(rFormat).GetAssignedOutlineStyleLevel();
777 if (nLvl >= 0 && nLvl < MAXLEVEL)
779 //if outline numbered
780 // if Write StyleDefinition then write the OutlineRule
781 const SwNumFormat& rNFormat = m_rDoc.GetOutlineNumRule()->Get( static_cast<sal_uInt16>( nLvl ) );
782 if ( m_bStyDef )
783 AttrOutput().OutlineNumbering(static_cast<sal_uInt8>(nLvl));
785 if ( rNFormat.GetPositionAndSpaceMode() ==
786 SvxNumberFormat::LABEL_WIDTH_AND_POSITION &&
787 rNFormat.GetAbsLSpace() )
789 SfxItemSet aSet( rFormat.GetAttrSet() );
790 SvxLRSpaceItem aLR(
791 ItemGet<SvxLRSpaceItem>(aSet, RES_LR_SPACE));
793 aLR.SetTextLeft( aLR.GetTextLeft() + rNFormat.GetAbsLSpace() );
794 aLR.SetTextFirstLineOffset( GetWordFirstLineOffset(rNFormat));
796 aSet.Put( aLR );
797 CorrectTabStopInSet( aSet, rNFormat.GetAbsLSpace() );
798 OutputItemSet( aSet, bPapFormat, bChpFormat,
799 i18n::ScriptType::LATIN, m_bExportModeRTF);
800 bCallOutSet = false;
803 else
805 //otherwise we might have to remove outline numbering from
806 //what gets exported if the parent style was outline numbered
807 // #i77805#
808 // If inherited outline numbering is suppress, the left/right
809 // margins has to be exported explicitly.
810 if ( m_bStyDef && DisallowInheritingOutlineNumbering(rFormat) )
812 SfxItemSet aSet( rFormat.GetAttrSet() );
813 const SvxLRSpaceItem& aLR(
814 ItemGet<SvxLRSpaceItem>(aSet, RES_LR_SPACE));
815 aSet.Put( aLR );
816 OutputItemSet( aSet, bPapFormat, bChpFormat,
817 css::i18n::ScriptType::LATIN, m_bExportModeRTF);
818 bCallOutSet = false;
822 break;
824 case RES_CHRFMT:
825 break;
826 case RES_FLYFRMFMT:
827 if (bFlyFormat)
829 OSL_ENSURE(m_pParentFrame, "No parent frame, all broken");
831 if (m_pParentFrame)
833 const SwFrameFormat &rFrameFormat = m_pParentFrame->GetFrameFormat();
835 SfxItemSet aSet(m_rDoc.GetAttrPool(), svl::Items<RES_FRMATR_BEGIN,
836 RES_FRMATR_END-1,
837 XATTR_FILL_FIRST, XATTR_FILL_LAST>{});
838 aSet.Set(rFrameFormat.GetAttrSet());
840 // Fly as character becomes a paragraph bound
841 // now set the distance to paragraph margin
842 if (m_pFlyOffset)
844 aSet.Put(SwFormatHoriOrient(m_pFlyOffset->X()));
845 aSet.Put(SwFormatVertOrient(m_pFlyOffset->Y()));
846 SwFormatAnchor aAnchor(rFrameFormat.GetAnchor());
847 aAnchor.SetType(m_eNewAnchorType);
848 aSet.Put(aAnchor);
851 if (SfxItemState::SET != aSet.GetItemState(RES_SURROUND))
852 aSet.Put(SwFormatSurround(css::text::WrapTextMode_NONE));
854 const XFillStyleItem* pXFillStyleItem(rFrameFormat.GetAttrSet().GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
855 if (pXFillStyleItem)
857 switch (pXFillStyleItem->GetValue())
859 case drawing::FillStyle_NONE:
860 break;
861 case drawing::FillStyle_SOLID:
863 // Construct an SvxBrushItem, as expected by the exporters.
864 std::unique_ptr<SvxBrushItem> aBrush(getSvxBrushItemFromSourceSet(rFrameFormat.GetAttrSet(), RES_BACKGROUND));
865 aSet.Put(*aBrush);
866 break;
868 default:
869 break;
873 m_bOutFlyFrameAttrs = true;
874 //script doesn't matter if not exporting chp
875 OutputItemSet(aSet, true, false,
876 i18n::ScriptType::LATIN, m_bExportModeRTF);
877 m_bOutFlyFrameAttrs = false;
879 bCallOutSet = false;
882 break;
883 case RES_FRMFMT:
884 break;
885 default:
886 OSL_ENSURE( false, "Which format is exported here?" );
887 break;
890 if( bCallOutSet )
891 OutputItemSet( rFormat.GetAttrSet(), bPapFormat, bChpFormat,
892 i18n::ScriptType::LATIN, m_bExportModeRTF);
893 m_pOutFormatNode = pOldMod;
896 bool MSWordExportBase::HasRefToAttr(const OUString& rName)
898 SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef);
899 std::vector<SwGetRefField*> vpRFields;
900 pType->GatherRefFields(vpRFields, REF_SETREFATTR);
901 return std::any_of(vpRFields.begin(), vpRFields.end(),
902 [rName](SwGetRefField* pF) { return rName == pF->GetSetRefName(); });
905 bool MSWordExportBase::HasRefToFootOrEndnote(const bool isEndNote, const sal_uInt16 nSeqNo)
907 SwFieldType* pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef);
908 std::vector<SwGetRefField*> vpRFields;
909 pType->GatherRefFields(vpRFields, isEndNote ? REF_ENDNOTE : REF_FOOTNOTE);
910 return std::any_of(vpRFields.begin(), vpRFields.end(),
911 [nSeqNo](SwGetRefField* pF) { return nSeqNo == pF->GetSeqNo(); });
914 OUString MSWordExportBase::GetBookmarkName( sal_uInt16 nTyp, const OUString* pName, sal_uInt16 nSeqNo )
916 OUString sRet;
917 switch ( nTyp )
919 case REF_SETREFATTR:
920 if ( pName )
922 sRet = "Ref_" + *pName;
924 break;
925 case REF_SEQUENCEFLD:
927 assert(pName);
928 sRet = "Ref_" + *pName;
929 break;
931 case REF_BOOKMARK:
932 if ( pName )
933 sRet = *pName;
934 break;
935 case REF_OUTLINE:
936 break; // ???
937 case REF_FOOTNOTE:
938 sRet = "_RefF" + OUString::number( nSeqNo );
939 break;
940 case REF_ENDNOTE:
941 sRet = "_RefE" + OUString::number( nSeqNo );
942 break;
944 return BookmarkToWord( sRet ); // #i43956# - encode bookmark accordingly
947 /* File CHRATR.HXX: */
948 void WW8AttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript )
950 if (bIsRTL)
952 if( m_rWW8Export.m_rDoc.GetDocumentType() != SwDoc::DOCTYPE_MSWORD )
954 m_rWW8Export.InsUInt16( NS_sprm::CFBiDi::val );
955 m_rWW8Export.pO->push_back( sal_uInt8(1) );
959 // #i46087# patch from james_clark; complex texts needs the undocumented SPRM CComplexScript with param 0x81.
960 if (nScript == i18n::ScriptType::COMPLEX && !bIsRTL)
962 m_rWW8Export.InsUInt16( NS_sprm::CFComplexScripts::val );
963 m_rWW8Export.pO->push_back( sal_uInt8(0x81) );
964 m_rWW8Export.pDop->bUseThaiLineBreakingRules = true;
968 void WW8AttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
970 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell() - (mbOnTOXEnding?2:0), m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
971 mbOnTOXEnding = false;
972 m_rWW8Export.pO->clear();
974 if ( pTextNodeInfoInner )
976 if ( pTextNodeInfoInner->isEndOfLine() )
978 TableRowEnd( pTextNodeInfoInner->getDepth() );
980 SVBT16 nSty;
981 ShortToSVBT16( 0, nSty );
982 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nSty, nSty+2 ); // Style #
983 TableInfoRow( pTextNodeInfoInner );
984 m_rWW8Export.m_pPapPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(), m_rWW8Export.pO->size(), m_rWW8Export.pO->data());
985 m_rWW8Export.pO->clear();
989 // Clear bookmarks of the current paragraph
990 m_aBookmarksOfParagraphStart.clear();
991 m_aBookmarksOfParagraphEnd.clear();
994 void WW8AttributeOutput::StartRunProperties()
996 WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
997 m_nFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
1000 void WW8AttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 nPos, bool /*bSingleEmptyRun*/ )
1002 if (pRedlineData)
1004 const OUString &rComment = pRedlineData->GetComment();
1005 //Only possible to export to main text
1006 if (!rComment.isEmpty() && (m_rWW8Export.m_nTextTyp == TXT_MAINTEXT))
1008 if (m_rWW8Export.m_pAtn->IsNewRedlineComment(pRedlineData))
1010 m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pRedlineData );
1011 m_rWW8Export.WritePostItBegin( m_rWW8Export.pO.get() );
1016 /// Insert bookmarks started at this run
1017 auto aRange = m_aBookmarksOfParagraphStart.equal_range(nPos);
1018 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1020 GetExport().AppendBookmark(BookmarkToWord(aIter->second));
1024 void WW8AttributeOutput::OnTOXEnding()
1026 mbOnTOXEnding = true;
1029 void WW8AttributeOutput::EndRun( const SwTextNode* /*pNode*/, sal_Int32 nPos, bool bLastRun )
1031 /// Insert bookmarks ended after this run
1032 auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nPos);
1033 for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1035 if(bLastRun)
1036 GetExport().AppendBookmarkEndWithCorrection(BookmarkToWord(aIter->second));
1037 else
1038 GetExport().AppendBookmark(BookmarkToWord(aIter->second));
1042 void WW8AttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData )
1044 Redline( pRedlineData );
1046 WW8_WrPlcField* pCurrentFields = m_rWW8Export.CurrentFieldPlc();
1047 sal_uInt16 nNewFieldResults = pCurrentFields ? pCurrentFields->ResultCount() : 0;
1049 bool bExportedFieldResult = ( m_nFieldResults != nNewFieldResults );
1051 // If we have exported a field result, then we will have been forced to
1052 // split up the text into a 0x13, 0x14, <result> 0x15 sequence with the
1053 // properties forced out at the end of the result, so the 0x15 itself
1054 // should remain clean of all other attributes to avoid #iXXXXX#
1055 if ( !bExportedFieldResult )
1057 m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
1058 m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
1060 m_rWW8Export.pO->clear();
1063 void WW8AttributeOutput::RunText( const OUString& rText, rtl_TextEncoding eCharSet )
1065 RawText(rText, eCharSet);
1068 void WW8AttributeOutput::RawText(const OUString& rText, rtl_TextEncoding)
1070 m_rWW8Export.OutSwString(rText, 0, rText.getLength());
1073 void WW8AttributeOutput::OutputFKP(bool bForce)
1075 if (!m_rWW8Export.pO->empty() || bForce)
1077 m_rWW8Export.m_pChpPlc->AppendFkpEntry( m_rWW8Export.Strm().Tell(),
1078 m_rWW8Export.pO->size(), m_rWW8Export.pO->data() );
1079 m_rWW8Export.pO->clear();
1083 void WW8AttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
1085 OSL_ENSURE( m_rWW8Export.pO->empty(), " pO is not empty at line end" );
1087 SVBT16 nSty;
1088 ShortToSVBT16( nStyle, nSty );
1089 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), nSty, nSty+2 ); // style #
1092 void WW8AttributeOutput::OutputWW8Attribute( sal_uInt8 nId, bool bVal )
1094 m_rWW8Export.InsUInt16( 8 == nId ? NS_sprm::CFDStrike::val : NS_sprm::CFBold::val + nId );
1096 m_rWW8Export.pO->push_back( bVal ? 1 : 0 );
1099 void WW8AttributeOutput::OutputWW8AttributeCTL( sal_uInt8 nId, bool bVal )
1101 OSL_ENSURE( nId <= 1, "out of range" );
1102 if (nId > 1)
1103 return;
1105 m_rWW8Export.InsUInt16( NS_sprm::CFBoldBi::val + nId );
1106 m_rWW8Export.pO->push_back( bVal ? 1 : 0 );
1109 void WW8AttributeOutput::CharFont( const SvxFontItem& rFont )
1111 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1113 m_rWW8Export.InsUInt16( NS_sprm::CRgFtc0::val );
1114 m_rWW8Export.InsUInt16( nFontID );
1115 m_rWW8Export.InsUInt16( NS_sprm::CRgFtc2::val );
1117 m_rWW8Export.InsUInt16( nFontID );
1120 void WW8AttributeOutput::CharFontCTL( const SvxFontItem& rFont )
1122 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1123 m_rWW8Export.InsUInt16( NS_sprm::CFtcBi::val );
1124 m_rWW8Export.InsUInt16( nFontID );
1127 void WW8AttributeOutput::CharFontCJK( const SvxFontItem& rFont )
1129 sal_uInt16 nFontID = m_rWW8Export.GetId( rFont );
1130 m_rWW8Export.InsUInt16( NS_sprm::CRgFtc1::val );
1131 m_rWW8Export.InsUInt16( nFontID );
1134 void WW8AttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight )
1136 OutputWW8AttributeCTL( 0, WEIGHT_BOLD == rWeight.GetWeight());
1139 void WW8AttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture )
1141 OutputWW8AttributeCTL( 1, ITALIC_NONE != rPosture.GetPosture() );
1144 void WW8AttributeOutput::CharPosture( const SvxPostureItem& rPosture )
1146 OutputWW8Attribute( 1, ITALIC_NONE != rPosture.GetPosture() );
1149 void WW8AttributeOutput::CharWeight( const SvxWeightItem& rWeight )
1151 OutputWW8Attribute( 0, WEIGHT_BOLD == rWeight.GetWeight() );
1154 // Shadowed and Contour are not in WW-UI. JP: ??
1155 void WW8AttributeOutput::CharContour( const SvxContourItem& rContour )
1157 OutputWW8Attribute( 3, rContour.GetValue() );
1160 void WW8AttributeOutput::CharShadow( const SvxShadowedItem& rShadow )
1162 OutputWW8Attribute( 4, rShadow.GetValue() );
1165 void WW8AttributeOutput::CharKerning( const SvxKerningItem& rKerning )
1167 m_rWW8Export.InsUInt16( NS_sprm::CDxaSpace::val );
1169 m_rWW8Export.InsUInt16( rKerning.GetValue() );
1172 void WW8AttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern )
1174 m_rWW8Export.InsUInt16( NS_sprm::CHpsKern::val );
1176 m_rWW8Export.InsUInt16( rAutoKern.GetValue() ? 2 : 0 );
1179 void WW8AttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
1181 m_rWW8Export.InsUInt16( NS_sprm::CSfxText::val );
1182 // At the moment the only animated text effect we support is blinking
1183 m_rWW8Export.pO->push_back( rBlink.GetValue() ? 2 : 0 );
1186 void WW8AttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossed )
1188 FontStrikeout eSt = rCrossed.GetStrikeout();
1189 if ( STRIKEOUT_DOUBLE == eSt )
1191 OutputWW8Attribute( 8, true );
1192 return;
1194 if ( STRIKEOUT_NONE != eSt )
1196 OutputWW8Attribute( 2, true );
1197 return;
1200 // otherwise both off
1201 OutputWW8Attribute( 8, false );
1202 OutputWW8Attribute( 2, false );
1205 void WW8AttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap )
1207 SvxCaseMap eSt = rCaseMap.GetValue();
1208 switch ( eSt )
1210 case SvxCaseMap::SmallCaps:
1211 OutputWW8Attribute( 5, true );
1212 return;
1213 case SvxCaseMap::Uppercase:
1214 OutputWW8Attribute( 6, true );
1215 return;
1216 case SvxCaseMap::Capitalize:
1217 // no such feature in word
1218 break;
1219 default:
1220 // otherwise both off
1221 OutputWW8Attribute( 5, false );
1222 OutputWW8Attribute( 6, false );
1223 return;
1227 void WW8AttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden )
1229 OutputWW8Attribute( 7, rHidden.GetValue() );
1232 void WW8AttributeOutput::CharBorder( const SvxBorderLine* pAllBorder, const sal_uInt16 /*nDist*/, const bool bShadow )
1234 WW8Export::Out_BorderLine( *m_rWW8Export.pO, pAllBorder, 0, NS_sprm::CBrc80::val, NS_sprm::CBrc::val, bShadow );
1237 void WW8AttributeOutput::CharHighlight( const SvxBrushItem& rBrush )
1239 if (rBrush.GetColor() != COL_TRANSPARENT)
1241 sal_uInt8 nColor = msfilter::util::TransColToIco( rBrush.GetColor() );
1242 // sprmCHighlight
1243 m_rWW8Export.InsUInt16( NS_sprm::CHighlight::val );
1244 m_rWW8Export.pO->push_back( nColor );
1248 void WW8AttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline )
1250 m_rWW8Export.InsUInt16( NS_sprm::CKul::val );
1252 const SfxPoolItem* pItem = m_rWW8Export.HasItem( RES_CHRATR_WORDLINEMODE );
1253 bool bWord = false;
1254 if (pItem)
1255 bWord = static_cast<const SvxWordLineModeItem*>(pItem)->GetValue();
1257 // WW95 - parameters: 0 = none, 1 = single, 2 = by Word,
1258 // 3 = double, 4 = dotted, 5 = hidden
1259 // WW97 - additional parameters:
1260 // 6 = thick, 7 = dash, 8 = dot(not used)
1261 // 9 = dotdash 10 = dotdotdash, 11 = wave
1262 sal_uInt8 b = 0;
1263 switch ( rUnderline.GetLineStyle() )
1265 case LINESTYLE_SINGLE:
1266 b = bWord ? 2 : 1;
1267 break;
1268 case LINESTYLE_BOLD:
1269 b = 6;
1270 break;
1271 case LINESTYLE_DOUBLE:
1272 b = 3;
1273 break;
1274 case LINESTYLE_DOTTED:
1275 b = 4;
1276 break;
1277 case LINESTYLE_DASH:
1278 b = 7;
1279 break;
1280 case LINESTYLE_DASHDOT:
1281 b = 9;
1282 break;
1283 case LINESTYLE_DASHDOTDOT:
1284 b = 10;
1285 break;
1286 case LINESTYLE_WAVE:
1287 b = 11;
1288 break;
1289 // new in WW2000
1290 case LINESTYLE_BOLDDOTTED:
1291 b = 20;
1292 break;
1293 case LINESTYLE_BOLDDASH:
1294 b = 23;
1295 break;
1296 case LINESTYLE_LONGDASH:
1297 b = 39;
1298 break;
1299 case LINESTYLE_BOLDLONGDASH:
1300 b = 55;
1301 break;
1302 case LINESTYLE_BOLDDASHDOT:
1303 b = 25;
1304 break;
1305 case LINESTYLE_BOLDDASHDOTDOT:
1306 b = 26;
1307 break;
1308 case LINESTYLE_BOLDWAVE:
1309 b = 27;
1310 break;
1311 case LINESTYLE_DOUBLEWAVE:
1312 b = 43;
1313 break;
1314 case LINESTYLE_NONE:
1315 b = 0;
1316 break;
1317 default:
1318 OSL_ENSURE( rUnderline.GetLineStyle() == LINESTYLE_NONE, "Unhandled underline type" );
1319 break;
1322 m_rWW8Export.pO->push_back( b );
1323 Color aColor = rUnderline.GetColor();
1324 if( aColor != COL_TRANSPARENT )
1326 m_rWW8Export.InsUInt16( NS_sprm::CCvUl::val );
1328 m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( aColor ) );
1332 void WW8AttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage )
1334 sal_uInt16 nId = 0;
1335 switch ( rLanguage.Which() )
1337 case RES_CHRATR_LANGUAGE:
1338 nId = NS_sprm::CRgLid0_80::val;
1339 break;
1340 case RES_CHRATR_CJK_LANGUAGE:
1341 nId = NS_sprm::CRgLid1_80::val;
1342 break;
1343 case RES_CHRATR_CTL_LANGUAGE:
1344 nId = NS_sprm::CLidBi::val;
1345 break;
1348 if ( !nId )
1349 return;
1351 // use sprmCRgLid0_80 rather than sprmCLid
1352 m_rWW8Export.InsUInt16( nId );
1353 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1355 // Word 2000 and above apparently require both old and new versions of
1356 // these sprms to be set, without it spellchecking doesn't work
1357 if ( nId == NS_sprm::CRgLid0_80::val )
1359 m_rWW8Export.InsUInt16( NS_sprm::CRgLid0::val );
1360 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1362 else if ( nId == NS_sprm::CRgLid1_80::val )
1364 m_rWW8Export.InsUInt16( NS_sprm::CRgLid1::val );
1365 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLanguage.GetLanguage()) );
1369 void WW8AttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement )
1371 sal_uInt8 b = 0xFF;
1372 short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight();
1373 if ( !nEsc )
1375 b = 0;
1376 nEsc = 0;
1377 nProp = 100;
1379 else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 )
1381 if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
1382 b = 2;
1383 else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
1384 b = 1;
1386 else if ( DFLT_ESC_AUTO_SUPER == nEsc )
1388 // Raised by the differences between the ascenders (ascent = baseline to top of highest letter).
1389 // The ascent is generally about 80% of the total font height.
1390 // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER)
1391 nEsc = .8 * (100 - nProp);
1393 else if ( DFLT_ESC_AUTO_SUB == nEsc )
1395 // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter).
1396 // The descent is generally about 20% of the total font height.
1397 // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB)
1398 nEsc = .2 * -(100 - nProp);
1401 if ( 0xFF != b )
1403 m_rWW8Export.InsUInt16( NS_sprm::CIss::val );
1405 m_rWW8Export.pO->push_back( b );
1408 if ( 0 != b && 0xFF != b )
1409 return;
1411 double fHeight = m_rWW8Export.GetItem( RES_CHRATR_FONTSIZE ).GetHeight();
1412 m_rWW8Export.InsUInt16( NS_sprm::CHpsPos::val );
1414 m_rWW8Export.InsUInt16(static_cast<short>( round(fHeight * nEsc / 1000) ));
1416 if( 100 != nProp || !b )
1418 m_rWW8Export.InsUInt16( NS_sprm::CHps::val );
1419 m_rWW8Export.InsUInt16(msword_cast<sal_uInt16>( round(fHeight * nProp / 1000) ));
1423 void WW8AttributeOutput::CharFontSize( const SvxFontHeightItem& rHeight )
1425 sal_uInt16 nId = 0;
1426 switch ( rHeight.Which() )
1428 case RES_CHRATR_FONTSIZE:
1429 case RES_CHRATR_CJK_FONTSIZE:
1430 nId = NS_sprm::CHps::val;
1431 break;
1432 case RES_CHRATR_CTL_FONTSIZE:
1433 nId = NS_sprm::CHpsBi::val;
1434 break;
1437 if ( nId )
1439 m_rWW8Export.InsUInt16( nId );
1441 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(( rHeight.GetHeight() + 5 ) / 10 ) );
1445 void WW8AttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth )
1447 m_rWW8Export.InsUInt16( NS_sprm::CCharScale::val );
1448 m_rWW8Export.InsUInt16( rScaleWidth.GetValue() );
1451 void WW8AttributeOutput::CharRelief( const SvxCharReliefItem& rRelief )
1453 sal_uInt16 nId;
1454 switch ( rRelief.GetValue() )
1456 case FontRelief::Embossed: nId = NS_sprm::CFEmboss::val; break;
1457 case FontRelief::Engraved: nId = NS_sprm::CFImprint::val; break;
1458 default: nId = 0; break;
1461 if( nId )
1463 m_rWW8Export.InsUInt16( nId );
1464 m_rWW8Export.pO->push_back( sal_uInt8(0x81) );
1466 else
1468 // switch both flags off
1469 m_rWW8Export.InsUInt16( NS_sprm::CFEmboss::val );
1470 m_rWW8Export.pO->push_back( sal_uInt8(0x0) );
1471 m_rWW8Export.InsUInt16( NS_sprm::CFImprint::val );
1472 m_rWW8Export.pO->push_back( sal_uInt8(0x0) );
1476 void WW8AttributeOutput::CharBidiRTL( const SfxPoolItem& rHt )
1478 const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
1479 if( rAttr.GetValue() == 1 )
1481 m_rWW8Export.InsUInt16(0x85a);
1482 m_rWW8Export.pO->push_back(sal_uInt8(1));
1486 void WW8AttributeOutput::CharIdctHint( const SfxPoolItem& rHt )
1488 const SfxInt16Item& rAttr = static_cast<const SfxInt16Item&>(rHt);
1489 m_rWW8Export.InsUInt16(0x286F);
1490 m_rWW8Export.pO->push_back(static_cast<sal_uInt8>(rAttr.GetValue()));
1493 void WW8AttributeOutput::CharRotate( const SvxCharRotateItem& rRotate )
1495 // #i28331# - check that a Value is set
1496 if ( !rRotate.GetValue() )
1497 return;
1499 if (m_rWW8Export.IsInTable())
1500 return;
1502 // #i36867 In word the text in a table is rotated via the TC or NS_sprm::TTextFlow::val
1503 // This means you can only rotate all or none of the text adding NS_sprm::CFELayout::val
1504 // here corrupts the table, hence !m_rWW8Export.bIsInTable
1506 m_rWW8Export.InsUInt16( NS_sprm::CFELayout::val );
1507 m_rWW8Export.pO->push_back( sal_uInt8(0x06) ); //len 6
1508 m_rWW8Export.pO->push_back( sal_uInt8(0x01) );
1510 m_rWW8Export.InsUInt16( rRotate.IsFitToLine() ? 1 : 0 );
1511 static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
1512 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aZeroArr, aZeroArr+3);
1515 void WW8AttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark )
1517 sal_uInt8 nVal;
1518 const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
1519 if (v == FontEmphasisMark::NONE)
1520 nVal = 0;
1521 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
1522 nVal = 2;
1523 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
1524 nVal = 3;
1525 else if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosBelow))
1526 nVal = 4;
1527 else
1528 // case 1:
1529 nVal = 1;
1531 m_rWW8Export.InsUInt16( NS_sprm::CKcd::val );
1532 m_rWW8Export.pO->push_back( nVal );
1536 * TransBrush converts SW-Brushes to WW. The result is WW8_SHD.
1537 * Non-standard colours of SW won't be converted now to the mixed values
1538 * ( 0 .. 95% ) of WW.
1539 * Also if transparent, e.g. for tables a transparent brush is returned
1541 * @return real brush ( not transparent )
1543 bool WW8Export::TransBrush(const Color& rCol, WW8_SHD& rShd)
1545 if( rCol.GetTransparency() )
1546 rShd = WW8_SHD(); // all zeros: transparent
1547 else
1549 rShd.SetFore( 0);
1550 rShd.SetBack( msfilter::util::TransColToIco( rCol ) );
1551 rShd.SetStyle( 0 );
1553 return !rCol.GetTransparency();
1556 static sal_uInt32 SuitableBGColor(Color nIn)
1558 if (nIn == COL_AUTO)
1559 return 0xFF000000;
1560 return wwUtility::RGBToBGR(nIn);
1563 void WW8AttributeOutput::CharColor( const SvxColorItem& rColor )
1565 m_rWW8Export.InsUInt16( NS_sprm::CIco::val );
1567 sal_uInt8 nColor = msfilter::util::TransColToIco( rColor.GetValue() );
1568 m_rWW8Export.pO->push_back( nColor );
1570 if (nColor)
1572 m_rWW8Export.InsUInt16( NS_sprm::CCv::val );
1573 m_rWW8Export.InsUInt32( wwUtility::RGBToBGR( rColor.GetValue() ) );
1577 void WW8AttributeOutput::CharBackground( const SvxBrushItem& rBrush )
1579 WW8_SHD aSHD;
1581 WW8Export::TransBrush( rBrush.GetColor(), aSHD );
1582 // sprmCShd80
1583 m_rWW8Export.InsUInt16( NS_sprm::CShd80::val );
1584 m_rWW8Export.InsUInt16( aSHD.GetValue() );
1586 //Quite a few unknowns, some might be transparency or something
1587 //of that nature...
1588 m_rWW8Export.InsUInt16( NS_sprm::CShd::val );
1589 m_rWW8Export.pO->push_back( 10 );
1590 m_rWW8Export.InsUInt32( 0xFF000000 );
1591 m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) );
1592 m_rWW8Export.InsUInt16( 0x0000);
1595 namespace sw { namespace util {
1597 const SwCharFormat* GetSwCharFormat(const SwFormatINetFormat& rINet, SwDoc& rDoc)
1599 if (rINet.GetValue().isEmpty())
1600 return nullptr;
1602 const sal_uInt16 nId = rINet.GetINetFormatId();
1603 const OUString& rStr = rINet.GetINetFormat();
1604 if (rStr.isEmpty())
1606 OSL_ENSURE( false, "WW8AttributeOutput::TextINetFormat(..) - missing unvisited character format at hyperlink attribute" );
1609 return IsPoolUserFormat( nId )
1610 ? rDoc.FindCharFormatByName( rStr )
1611 : rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nId );
1616 void WW8AttributeOutput::TextINetFormat( const SwFormatINetFormat& rINet )
1618 const SwCharFormat* pFormat = GetSwCharFormat(rINet, m_rWW8Export.m_rDoc);
1619 if (!pFormat)
1620 return;
1622 m_rWW8Export.InsUInt16( NS_sprm::CIstd::val );
1624 m_rWW8Export.InsUInt16( m_rWW8Export.GetId( pFormat ) );
1627 // #i43956# - add optional parameter <pLinkStr>
1628 // It's needed to write the hyperlink data for a certain cross-reference
1629 // - it contains the name of the link target, which is a bookmark.
1630 // add optional parameter <bIncludeEmptyPicLocation>
1631 // It is needed to write an empty picture location for page number field separators
1632 static void InsertSpecialChar( WW8Export& rWrt, sal_uInt8 c,
1633 OUString const * pLinkStr,
1634 bool bIncludeEmptyPicLocation = false )
1636 ww::bytes aItems;
1637 rWrt.GetCurrentItems(aItems);
1639 if (c == 0x13)
1640 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell());
1641 else
1642 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
1644 rWrt.WriteChar(c);
1646 // store empty sprmCPicLocation for field separator
1647 if ( bIncludeEmptyPicLocation &&
1648 ( c == 0x13 || c == 0x14 || c == 0x15 ) )
1650 SwWW8Writer::InsUInt16( aItems, NS_sprm::CPicLocation::val );
1651 SwWW8Writer::InsUInt32( aItems, 0x00000000 );
1654 // #i43956# - write hyperlink data and attributes
1655 if ( c == 0x01 && pLinkStr)
1657 // write hyperlink data to data stream
1658 SvStream& rStrm = *rWrt.pDataStrm;
1659 // position of hyperlink data
1660 const sal_uInt32 nLinkPosInDataStrm = rStrm.Tell();
1661 // write empty header
1662 const sal_uInt16 nEmptyHdrLen = 0x44;
1663 sal_uInt8 aEmptyHeader[ nEmptyHdrLen ] = { 0 };
1664 aEmptyHeader[ 4 ] = 0x44;
1665 rStrm.WriteBytes( aEmptyHeader, nEmptyHdrLen );
1666 // writer fixed header
1667 const sal_uInt16 nFixHdrLen = 0x19;
1668 sal_uInt8 const aFixHeader[ nFixHdrLen ] =
1670 0x08, 0xD0, 0xC9, 0xEA, 0x79, 0xF9, 0xBA, 0xCE,
1671 0x11, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9,
1672 0x0B, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
1673 0x00,
1675 rStrm.WriteBytes( aFixHeader, nFixHdrLen );
1676 // write reference string including length+1
1677 sal_uInt32 nStrLen( pLinkStr->getLength() + 1 );
1678 SwWW8Writer::WriteLong( rStrm, nStrLen );
1679 SwWW8Writer::WriteString16( rStrm, *pLinkStr, false );
1680 // write additional two NULL Bytes
1681 SwWW8Writer::WriteLong( rStrm, 0 );
1682 // write length of hyperlink data
1683 const sal_uInt32 nCurrPos = rStrm.Tell();
1684 rStrm.Seek( nLinkPosInDataStrm );
1685 rStrm.WriteUInt32(nCurrPos - nLinkPosInDataStrm);
1686 rStrm.Seek( nCurrPos );
1688 // write attributes of hyperlink character 0x01
1689 SwWW8Writer::InsUInt16( aItems, NS_sprm::CFFldVanish::val );
1690 aItems.push_back( sal_uInt8(0x81) );
1691 SwWW8Writer::InsUInt16( aItems, NS_sprm::CPicLocation::val );
1692 SwWW8Writer::InsUInt32( aItems, nLinkPosInDataStrm );
1693 SwWW8Writer::InsUInt16( aItems, NS_sprm::CFData::val );
1694 aItems.push_back( sal_uInt8(0x01) );
1697 //Technically we should probably Remove all attribs
1698 //here for the 0x13, 0x14, 0x15, but our import
1699 //is slightly lacking
1700 //aItems.Remove(0, aItems.Count());
1701 // fSpec-Attribute true
1702 SwWW8Writer::InsUInt16( aItems, NS_sprm::CFSpec::val );
1703 aItems.push_back( 1 );
1705 rWrt.m_pChpPlc->AppendFkpEntry(rWrt.Strm().Tell(), aItems.size(), aItems.data());
1708 static OUString lcl_GetExpandedField(const SwField &rField)
1710 //replace LF 0x0A with VT 0x0B
1711 return rField.ExpandField(true, nullptr).replace(0x0A, 0x0B);
1714 WW8_WrPlcField* WW8Export::CurrentFieldPlc() const
1716 WW8_WrPlcField* pFieldP = nullptr;
1717 switch (m_nTextTyp)
1719 case TXT_MAINTEXT:
1720 pFieldP = m_pFieldMain.get();
1721 break;
1722 case TXT_HDFT:
1723 pFieldP = m_pFieldHdFt.get();
1724 break;
1725 case TXT_FTN:
1726 pFieldP = m_pFieldFootnote.get();
1727 break;
1728 case TXT_EDN:
1729 pFieldP = m_pFieldEdn.get();
1730 break;
1731 case TXT_ATN:
1732 pFieldP = m_pFieldAtn.get();
1733 break;
1734 case TXT_TXTBOX:
1735 pFieldP = m_pFieldTextBxs.get();
1736 break;
1737 case TXT_HFTXTBOX:
1738 pFieldP = m_pFieldHFTextBxs.get();
1739 break;
1740 default:
1741 OSL_ENSURE( false, "what type of SubDoc is that?" );
1743 return pFieldP;
1746 void WW8Export::OutputField( const SwField* pField, ww::eField eFieldType,
1747 const OUString& rFieldCmd, FieldFlags nMode )
1749 OUString sFieldCmd(rFieldCmd);
1750 switch (eFieldType)
1752 // map fields that are not supported in WW8 as of Word 2003
1753 case ww::eBIBLIOGRAPHY:
1754 eFieldType = ww::eQUOTE;
1755 assert(rFieldCmd == FieldString(ww::eBIBLIOGRAPHY));
1756 sFieldCmd = FieldString(ww::eQUOTE);
1757 break;
1758 case ww::eCITATION:
1759 eFieldType = ww::eQUOTE;
1760 assert(rFieldCmd.trim().startsWith("CITATION"));
1761 sFieldCmd = rFieldCmd.replaceFirst(FieldString(ww::eCITATION),
1762 FieldString(ww::eQUOTE));
1763 break;
1764 default:
1765 break;
1768 assert(eFieldType <= 0x5F); // 95 is the highest documented one
1770 WW8_WrPlcField* pFieldP = CurrentFieldPlc();
1772 const bool bIncludeEmptyPicLocation = ( eFieldType == ww::ePAGE );
1773 if (FieldFlags::Start & nMode)
1775 sal_uInt8 aField13[2] = { 0x13, 0x00 }; // will change
1776 //#i3958#, Needed to make this field work correctly in Word 2000
1777 if (eFieldType == ww::eSHAPE)
1778 aField13[0] |= 0x80;
1779 aField13[1] = static_cast< sal_uInt8 >(eFieldType); // add type
1780 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField13 );
1781 InsertSpecialChar( *this, 0x13, nullptr, bIncludeEmptyPicLocation );
1783 if (FieldFlags::CmdStart & nMode)
1785 SwWW8Writer::WriteString16(Strm(), sFieldCmd, false);
1786 // #i43956# - write hyperlink character including
1787 // attributes and corresponding binary data for certain reference fields.
1788 bool bHandleBookmark = false;
1790 if (pField)
1792 if (pField->GetTyp()->Which() == SwFieldIds::GetRef &&
1793 ( eFieldType == ww::ePAGEREF || eFieldType == ww::eREF ||
1794 eFieldType == ww::eNOTEREF || eFieldType == ww::eFOOTREF ))
1795 bHandleBookmark = true;
1798 if ( bHandleBookmark )
1800 // retrieve reference destination - the name of the bookmark
1801 OUString aLinkStr;
1802 const sal_uInt16 nSubType = pField->GetSubType();
1803 const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
1804 if ( nSubType == REF_SETREFATTR ||
1805 nSubType == REF_BOOKMARK )
1807 const OUString& aRefName(rRField.GetSetRefName());
1808 aLinkStr = GetBookmarkName( nSubType, &aRefName, 0 );
1810 else if ( nSubType == REF_FOOTNOTE ||
1811 nSubType == REF_ENDNOTE )
1813 aLinkStr = GetBookmarkName( nSubType, nullptr, rRField.GetSeqNo() );
1815 else if ( nSubType == REF_SEQUENCEFLD )
1817 aLinkStr = pField->GetPar2();
1819 // insert hyperlink character including attributes and data.
1820 InsertSpecialChar( *this, 0x01, &aLinkStr );
1823 if (FieldFlags::CmdEnd & nMode)
1825 static const sal_uInt8 aField14[2] = { 0x14, 0xff };
1826 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField14 );
1827 pFieldP->ResultAdded();
1828 InsertSpecialChar( *this, 0x14, nullptr, bIncludeEmptyPicLocation );
1830 if (FieldFlags::End & nMode)
1832 OUString sOut;
1833 if( pField )
1834 sOut = lcl_GetExpandedField(*pField);
1835 else
1836 sOut = sFieldCmd;
1837 if( !sOut.isEmpty() )
1839 SwWW8Writer::WriteString16(Strm(), sOut, false);
1841 if (pField)
1843 if (pField->GetTyp()->Which() == SwFieldIds::Input &&
1844 eFieldType == ww::eFORMTEXT)
1846 sal_uInt8 aArr[12];
1847 sal_uInt8 *pArr = aArr;
1849 Set_UInt16( pArr, NS_sprm::CPicLocation::val );
1850 Set_UInt32( pArr, 0x0 );
1852 Set_UInt16( pArr, NS_sprm::CFSpec::val );
1853 Set_UInt8( pArr, 1 );
1855 Set_UInt16( pArr, NS_sprm::CFNoProof::val );
1856 Set_UInt8( pArr, 1 );
1858 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
1863 if (!(FieldFlags::Close & nMode))
1864 return;
1866 sal_uInt8 aField15[2] = { 0x15, 0x80 };
1868 if (pField)
1870 if (pField->GetTyp()->Which() == SwFieldIds::Input &&
1871 eFieldType == ww::eFORMTEXT)
1873 sal_uInt16 nSubType = pField->GetSubType();
1875 if (nSubType == REF_SEQUENCEFLD)
1876 aField15[0] |= (0x4 << 5);
1880 pFieldP->Append( Fc2Cp( Strm().Tell() ), aField15 );
1881 InsertSpecialChar( *this, 0x15, nullptr, bIncludeEmptyPicLocation );
1884 void WW8Export::StartCommentOutput(const OUString& rName)
1886 const OUString sStr{ FieldString(ww::eQUOTE) + "[" + rName + "] " };
1887 OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::Start | FieldFlags::CmdStart);
1890 void WW8Export::EndCommentOutput(const OUString& rName)
1892 const OUString sStr{ " [" + rName + "] " };
1893 OutputField(nullptr, ww::eQUOTE, sStr, FieldFlags::CmdEnd | FieldFlags::End |
1894 FieldFlags::Close);
1897 sal_uInt16 MSWordExportBase::GetId( const SwTOXType& rTOXType )
1899 std::vector<const SwTOXType*>::iterator it
1900 = std::find( m_aTOXArr.begin(), m_aTOXArr.end(), &rTOXType );
1901 if ( it != m_aTOXArr.end() )
1903 return it - m_aTOXArr.begin();
1905 m_aTOXArr.push_back( &rTOXType );
1906 return m_aTOXArr.size() - 1;
1909 // return values: 1 - no PageNum,
1910 // 2 - TabStop before PageNum,
1911 // 3 - Text before PageNum - rText hold the text
1912 // 4 - no Text and no TabStop before PageNum
1913 static int lcl_CheckForm( const SwForm& rForm, sal_uInt8 nLvl, OUString& rText )
1915 int nRet = 4;
1916 rText.clear();
1918 // #i21237#
1919 SwFormTokens aPattern = rForm.GetPattern(nLvl);
1920 SwFormTokens::iterator aIt = aPattern.begin();
1921 FormTokenType eTType;
1923 // #i61362#
1924 if (! aPattern.empty())
1926 bool bPgNumFnd = false;
1928 // #i21237#
1929 while( ++aIt != aPattern.end() && !bPgNumFnd )
1931 eTType = aIt->eTokenType;
1933 switch( eTType )
1935 case TOKEN_PAGE_NUMS:
1936 bPgNumFnd = true;
1937 break;
1939 case TOKEN_TAB_STOP:
1940 nRet = 2;
1941 break;
1942 case TOKEN_TEXT:
1944 nRet = 3;
1945 sal_Int32 nCount = std::min<sal_Int32>(5, aIt->sText.getLength());
1946 rText = aIt->sText.copy(0, nCount); // #i21237#
1947 break;
1949 case TOKEN_LINK_START:
1950 case TOKEN_LINK_END:
1951 break;
1953 default:
1954 nRet = 4;
1955 break;
1959 if( !bPgNumFnd )
1960 nRet = 1;
1963 return nRet;
1966 static bool lcl_IsHyperlinked(const SwForm& rForm, sal_uInt16 nTOXLvl)
1968 bool bRes = false;
1969 for (sal_uInt16 nI = 1; nI < nTOXLvl; ++nI)
1971 // #i21237#
1972 SwFormTokens aPattern = rForm.GetPattern(nI);
1974 if ( !aPattern.empty() )
1976 SwFormTokens::iterator aIt = aPattern.begin();
1978 FormTokenType eTType;
1980 // #i21237#
1981 while ( ++aIt != aPattern.end() )
1983 eTType = aIt->eTokenType;
1984 switch (eTType)
1986 case TOKEN_LINK_START:
1987 case TOKEN_LINK_END:
1988 bRes = true;
1989 break;
1990 default:
1996 return bRes;
1999 void AttributeOutputBase::GenerateBookmarksForSequenceField(const SwTextNode& rNode, SwWW8AttrIter& rAttrIter)
2001 if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF) // Not implemented for RTF
2002 return;
2004 const SwpHints* pTextAttrs = rNode.GetpSwpHints();
2005 if (!pTextAttrs)
2006 return;
2008 for( size_t i = 0; i < pTextAttrs->Count(); ++i )
2010 const SwTextAttr* pHt = pTextAttrs->Get(i);
2011 if (pHt->GetAttr().Which() == RES_TXTATR_FIELD)
2013 const SwFormatField& rField = static_cast<const SwFormatField&>(pHt->GetAttr());
2014 const SwField* pField = rField.GetField();
2015 // Need to have bookmarks only for sequence fields
2016 if (pField && pField->GetTyp()->Which() == SwFieldIds::SetExp && pField->GetSubType() == nsSwGetSetExpType::GSE_SEQ)
2018 const sal_uInt16 nSeqFieldNumber = static_cast<const SwSetExpField*>(pField)->GetSeqNumber();
2019 const OUString sObjectName = static_cast<const SwSetExpFieldType*>(pField->GetTyp())->GetName();
2020 const SwFieldTypes* pFieldTypes = GetExport().m_rDoc.getIDocumentFieldsAccess().GetFieldTypes();
2021 bool bHaveFullBkm = false;
2022 bool bHaveLabelAndNumberBkm = false;
2023 bool bHaveCaptionOnlyBkm = false;
2024 bool bHaveNumberOnlyBkm = false;
2025 bool bRunSplittedAtSep = false;
2026 for( auto const & pFieldType : *pFieldTypes )
2028 if( SwFieldIds::GetRef == pFieldType->Which() )
2030 SwIterator<SwFormatField,SwFieldType> aIter( *pFieldType );
2031 for( SwFormatField* pFormatField = aIter.First(); pFormatField; pFormatField = aIter.Next() )
2033 SwGetRefField* pRefField = static_cast<SwGetRefField*>(pFormatField->GetField());
2034 // If we have a reference to the current sequence field
2035 if(pRefField->GetSeqNo() == nSeqFieldNumber && pRefField->GetSetRefName() == sObjectName)
2037 // Need to create a separate run for separator character
2038 SwWW8AttrIter aLocalAttrIter( GetExport(), rNode ); // We need a local iterator having the right number of runs
2039 const OUString& aText = rNode.GetText();
2040 const sal_Int32 nCategoryStart = aText.indexOf(pRefField->GetSetRefName());
2041 const sal_Int32 nPosBeforeSeparator = std::max(nCategoryStart, pHt->GetStart());
2042 bool bCategoryFirst = nCategoryStart < pHt->GetStart();
2043 sal_Int32 nSeparatorPos = 0;
2044 if (bCategoryFirst)
2046 nSeparatorPos = aLocalAttrIter.WhereNext();
2047 while (nSeparatorPos <= nPosBeforeSeparator)
2049 aLocalAttrIter.NextPos();
2050 nSeparatorPos = aLocalAttrIter.WhereNext();
2053 else
2055 nSeparatorPos = nCategoryStart + pRefField->GetSetRefName().getLength();
2057 sal_Int32 nRefTextPos = 0;
2058 if(nSeparatorPos < aText.getLength())
2060 nRefTextPos = SwGetExpField::GetReferenceTextPos(pHt->GetFormatField(), GetExport().m_rDoc, nSeparatorPos);
2061 if(nRefTextPos != nSeparatorPos)
2063 if(!bRunSplittedAtSep)
2065 if(!bCategoryFirst)
2066 rAttrIter.SplitRun(nSeparatorPos);
2067 rAttrIter.SplitRun(nRefTextPos);
2068 bRunSplittedAtSep = true;
2070 if(!bCategoryFirst)
2071 aLocalAttrIter.SplitRun(nSeparatorPos);
2072 aLocalAttrIter.SplitRun(nRefTextPos);
2074 else if (bCategoryFirst)
2076 if(!bRunSplittedAtSep)
2078 rAttrIter.SplitRun(nSeparatorPos);
2079 bRunSplittedAtSep = true;
2081 aLocalAttrIter.SplitRun(nSeparatorPos);
2084 // Generate bookmarks on the right position
2085 OUString sName("Ref_" + pRefField->GetSetRefName() + OUString::number(pRefField->GetSeqNo()));
2086 switch (pRefField->GetFormat())
2088 case REF_PAGE:
2089 case REF_PAGE_PGDESC:
2090 case REF_CONTENT:
2091 case REF_UPDOWN:
2092 if(!bHaveFullBkm)
2094 sal_Int32 nLastAttrStart = 0;
2095 sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
2096 while (nActAttr < rNode.GetText().getLength())
2098 nLastAttrStart = nActAttr;
2099 aLocalAttrIter.NextPos();
2100 nActAttr = aLocalAttrIter.WhereNext();
2102 WriteBookmarkInActParagraph( sName + "_full", std::min(nCategoryStart, pHt->GetStart()), nLastAttrStart );
2103 bHaveFullBkm = true;
2105 break;
2106 case REF_ONLYNUMBER:
2108 if(!bHaveLabelAndNumberBkm)
2110 sName += "_label_and_number";
2111 if(bCategoryFirst)
2112 WriteBookmarkInActParagraph( sName, std::min(nCategoryStart, pHt->GetStart()), std::max(nCategoryStart, pHt->GetStart()) );
2113 else
2115 // Find the last run which contains category text
2116 SwWW8AttrIter aLocalAttrIter2( GetExport(), rNode );
2117 sal_Int32 nCatLastRun = 0;
2118 sal_Int32 nNextAttr = aLocalAttrIter2.WhereNext();
2119 while (nNextAttr < nSeparatorPos)
2121 nCatLastRun = nNextAttr;
2122 aLocalAttrIter2.NextPos();
2123 nNextAttr = aLocalAttrIter2.WhereNext();
2125 WriteBookmarkInActParagraph( sName, pHt->GetStart(), nCatLastRun );
2127 bHaveLabelAndNumberBkm = true;
2129 break;
2131 case REF_ONLYCAPTION:
2133 if(!bHaveCaptionOnlyBkm)
2135 // Find last run
2136 sal_Int32 nLastAttrStart = 0;
2137 sal_Int32 nActAttr = aLocalAttrIter.WhereNext();
2138 while (nActAttr < rNode.GetText().getLength())
2140 nLastAttrStart = nActAttr;
2141 aLocalAttrIter.NextPos();
2142 nActAttr = aLocalAttrIter.WhereNext();
2144 WriteBookmarkInActParagraph( sName + "_caption_only", nRefTextPos, nLastAttrStart );
2145 bHaveCaptionOnlyBkm = true;
2147 break;
2149 case REF_ONLYSEQNO:
2151 if(!bHaveNumberOnlyBkm)
2153 WriteBookmarkInActParagraph( sName + "_number_only", pHt->GetStart(), pHt->GetStart() );
2154 bHaveNumberOnlyBkm = true;
2156 break;
2163 return;
2169 void AttributeOutputBase::StartTOX( const SwSection& rSect )
2171 if ( const SwTOXBase* pTOX = rSect.GetTOXBase() )
2173 static const char sEntryEnd[] = "\" ";
2175 ww::eField eCode = ww::eTOC;
2176 OUString sStr = pTOX ->GetMSTOCExpression();
2177 if ( sStr.isEmpty() )
2179 switch (pTOX->GetType())
2181 case TOX_INDEX:
2182 eCode = ww::eINDEX;
2183 sStr = FieldString(eCode);
2186 const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
2187 const SwColumns& rColumns = rCol.GetColumns();
2188 sal_Int32 nCol = rColumns.size();
2190 if ( 0 < nCol )
2192 // Add a continuous section break
2193 if( GetExport().AddSectionBreaksForTOX() )
2195 SwSection *pParent = rSect.GetParent();
2196 WW8_SepInfo rInfo(&GetExport().m_rDoc.GetPageDesc(0),
2197 pParent ? pParent->GetFormat() : nullptr, 0/*nRstLnNum*/);
2198 GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
2201 sStr += "\\c \"" + OUString::number( nCol ) + "\"";
2205 if (pTOX->GetTOXForm().IsCommaSeparated())
2206 sStr += "\\r ";
2208 if (SwTOIOptions::AlphaDelimiter & pTOX->GetOptions())
2209 sStr += "\\h \"A\" ";
2211 if(SwTOXElement::IndexEntryType & pTOX->GetCreateType())
2213 sStr += "\\f ";
2214 const OUString& sName = pTOX->GetEntryTypeName();
2215 if(!sName.isEmpty())
2217 sStr += sName + sEntryEnd;
2221 if (!pTOX->GetTOXForm().IsCommaSeparated())
2223 // In case of Run-in style no separators are added.
2224 OUString aFillText;
2225 for (sal_uInt8 n = 1; n <= 3; ++n)
2227 OUString aText;
2228 int nRet = ::lcl_CheckForm(pTOX->GetTOXForm(), n, aText);
2230 if( 3 == nRet )
2231 aFillText = aText;
2232 else if ((4 == nRet) || (2 == nRet))
2233 aFillText = "\t";
2234 else
2235 aFillText.clear();
2237 sStr += "\\e \"" + aFillText + sEntryEnd;
2239 break;
2241 case TOX_ILLUSTRATIONS:
2242 case TOX_OBJECTS:
2243 case TOX_TABLES:
2244 if (!pTOX->IsFromObjectNames())
2246 sStr = FieldString(eCode) + "\\c ";
2247 const OUString& seqName = pTOX->GetSequenceName();
2248 if(!seqName.isEmpty())
2250 sStr += "\"" + seqName + sEntryEnd;
2252 OUString aText;
2253 int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(), 1, aText );
2254 if (1 == nRet)
2255 sStr += "\\n ";
2256 else if( 3 == nRet || 4 == nRet )
2258 sStr += "\\p \"" + aText + sEntryEnd;
2261 break;
2263 case TOX_AUTHORITIES:
2264 eCode = ww::eBIBLIOGRAPHY;
2265 sStr = FieldString(eCode);
2266 break;
2267 // case TOX_USER:
2268 // case TOX_CONTENT:
2269 default:
2271 sStr = FieldString(eCode);
2273 OUString sTOption;
2274 sal_uInt16 n, nTOXLvl = pTOX->GetLevel();
2275 if( !nTOXLvl )
2276 ++nTOXLvl;
2278 if(SwTOXElement::TableLeader & pTOX->GetCreateType())
2280 sStr +="\\z " ;
2281 GetExport( ).m_bHideTabLeaderAndPageNumbers = true ;
2283 if(SwTOXElement::TableInToc & pTOX->GetCreateType())
2285 sStr +="\\w " ;
2286 GetExport( ).m_bTabInTOC = true ;
2288 if(SwTOXElement::Newline & pTOX->GetCreateType())
2290 sStr +="\\x " ;
2292 if( SwTOXElement::Mark & pTOX->GetCreateType() )
2294 sStr += "\\f ";
2296 if( TOX_USER == pTOX->GetType() )
2298 sStr += "\""
2299 + OUStringChar(static_cast<char>( 'A' + GetExport( ).GetId( *pTOX->GetTOXType() ) ))
2300 + sEntryEnd;
2303 if(SwTOXElement::Bookmark & pTOX->GetCreateType())
2305 sStr += "\\b \"" + pTOX->GetBookmarkName() + sEntryEnd;
2308 if( SwTOXElement::OutlineLevel & pTOX->GetCreateType() )
2310 // Take the TOC value of the max level to evaluate to as
2311 // the starting point for the \o flag, but reduce it to the
2312 // value of the highest outline level filled by a *standard*
2313 // Heading 1 - 9 style because \o "Builds a table of
2314 // contents from paragraphs formatted with built-in heading
2315 // styles". And afterward fill in any outline styles left
2316 // uncovered by that range to the \t flag
2318 // i.e. for
2319 // Heading 1
2320 // Heading 2
2321 // custom-style
2322 // Heading 4
2323 // output
2324 // \o 1-2 \tcustom-style,3,Heading 3,4
2326 // Search over all the outline styles used and figure out
2327 // what is the minimum outline level (if any) filled by a
2328 // non-standard style for that level, i.e. ignore headline
2329 // styles 1-9 and find the lowest valid outline level
2330 sal_uInt8 nPosOfLowestNonStandardLvl = MAXLEVEL;
2331 const SwTextFormatColls& rColls = *GetExport().m_rDoc.GetTextFormatColls();
2332 for( n = rColls.size(); n; )
2334 const SwTextFormatColl* pColl = rColls[ --n ];
2335 sal_uInt16 nPoolId = pColl->GetPoolFormatId();
2336 if (
2337 //Is a Non-Standard Outline Style
2338 (RES_POOLCOLL_HEADLINE1 > nPoolId || RES_POOLCOLL_HEADLINE9 < nPoolId) &&
2339 //Has a valid outline level
2340 (pColl->IsAssignedToListLevelOfOutlineStyle()) &&
2341 // Is less than the lowest known non-standard level
2342 (pColl->GetAssignedOutlineStyleLevel() < nPosOfLowestNonStandardLvl)
2345 nPosOfLowestNonStandardLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
2349 sal_uInt8 nMaxMSAutoEvaluate = nPosOfLowestNonStandardLvl < nTOXLvl ? nPosOfLowestNonStandardLvl : static_cast<sal_uInt8>(nTOXLvl);
2351 //output \o 1-X where X is the highest normal outline style to be included in the toc
2352 if ( nMaxMSAutoEvaluate )
2354 if (nMaxMSAutoEvaluate > WW8ListManager::nMaxLevel)
2355 nMaxMSAutoEvaluate = WW8ListManager::nMaxLevel;
2357 sStr += "\\o \"1-" + OUString::number(nMaxMSAutoEvaluate) + sEntryEnd;
2360 //collect up any other styles in the writer TOC which will
2361 //not already appear in the MS TOC and place then into the
2362 //\t option
2363 if( nMaxMSAutoEvaluate < nTOXLvl )
2365 // collect this templates into the \t option
2366 for( n = rColls.size(); n;)
2368 const SwTextFormatColl* pColl = rColls[ --n ];
2369 if (!pColl->IsAssignedToListLevelOfOutlineStyle())
2370 continue;
2371 sal_uInt8 nTestLvl = ::sal::static_int_cast<sal_uInt8>(pColl->GetAssignedOutlineStyleLevel());
2372 if (nTestLvl < nTOXLvl && nTestLvl >= nMaxMSAutoEvaluate)
2374 if (!sTOption.isEmpty())
2375 sTOption += ",";
2376 sTOption += pColl->GetName() + "," + OUString::number( nTestLvl + 1 );
2382 if( SwTOXElement::ParagraphOutlineLevel & pTOX->GetCreateType() )
2384 sStr +="\\u " ;
2387 if( SwTOXElement::Template & pTOX->GetCreateType() )
2389 // #i99641# - Consider additional styles regardless of TOX-outlinelevel
2390 for( n = 0; n < MAXLEVEL; ++n )
2392 const OUString& rStyles = pTOX->GetStyleNames( n );
2393 if( !rStyles.isEmpty() )
2395 sal_Int32 nPos = 0;
2396 const OUString sLvl{ "," + OUString::number( n + 1 ) };
2397 do {
2398 const OUString sStyle( rStyles.getToken( 0, TOX_STYLE_DELIMITER, nPos ));
2399 if( !sStyle.isEmpty() )
2401 SwTextFormatColl* pColl = GetExport().m_rDoc.FindTextFormatCollByName(sStyle);
2402 if (pColl)
2404 if (!pColl->IsAssignedToListLevelOfOutlineStyle() || pColl->GetAssignedOutlineStyleLevel() < nTOXLvl)
2406 if( !sTOption.isEmpty() )
2407 sTOption += ",";
2408 sTOption += sStyle + sLvl;
2412 } while( -1 != nPos );
2417 // No 'else' branch; why the below snippet is a block I have no idea.
2419 OUString aFillText;
2420 sal_uInt8 nNoPgStt = MAXLEVEL, nNoPgEnd = MAXLEVEL;
2421 bool bFirstFillText = true, bOnlyText = true;
2422 for( n = 0; n < nTOXLvl; ++n )
2424 OUString aText;
2425 int nRet = ::lcl_CheckForm( pTOX->GetTOXForm(),
2426 static_cast< sal_uInt8 >(n+1), aText );
2427 if( 1 == nRet )
2429 bOnlyText = false;
2430 if( MAXLEVEL == nNoPgStt )
2431 nNoPgStt = static_cast< sal_uInt8 >(n+1);
2433 else
2435 if( MAXLEVEL != nNoPgStt &&
2436 MAXLEVEL == nNoPgEnd )
2437 nNoPgEnd = sal_uInt8(n);
2439 bOnlyText = bOnlyText && 3 == nRet;
2440 if( 3 == nRet || 4 == nRet )
2442 if( bFirstFillText )
2443 aFillText = aText;
2444 else if( aFillText != aText )
2445 aFillText.clear();
2446 bFirstFillText = false;
2450 if( MAXLEVEL != nNoPgStt )
2452 if (WW8ListManager::nMaxLevel < nNoPgEnd)
2453 nNoPgEnd = WW8ListManager::nMaxLevel;
2454 sStr += "\\n "
2455 + OUString::number( nNoPgStt )
2456 + "-"
2457 + OUString::number( nNoPgEnd )
2458 + " ";
2460 if( bOnlyText )
2462 sStr += "\\p \"" + aFillText + sEntryEnd;
2466 if( !sTOption.isEmpty() )
2468 sStr += "\\t \"" + sTOption + sEntryEnd;
2471 if (lcl_IsHyperlinked(pTOX->GetTOXForm(), nTOXLvl))
2472 sStr += "\\h";
2473 break;
2478 if (!sStr.isEmpty())
2480 GetExport( ).m_bInWriteTOX = true;
2481 if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
2482 { // tdf#129574: required for RTF; doesn't work with DOCX
2483 StartRun(nullptr, -42, true);
2485 GetExport( ).OutputField( nullptr, eCode, sStr, FieldFlags::Start | FieldFlags::CmdStart |
2486 FieldFlags::CmdEnd );
2487 if (GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
2489 EndRun(nullptr, -42, true);
2494 GetExport( ).m_bStartTOX = false;
2497 void AttributeOutputBase::EndTOX( const SwSection& rSect,bool bCareEnd )
2499 const SwTOXBase* pTOX = rSect.GetTOXBase();
2500 if ( pTOX )
2502 ww::eField eCode = TOX_INDEX == pTOX->GetType() ? ww::eINDEX : ww::eTOC;
2503 GetExport( ).OutputField( nullptr, eCode, OUString(), FieldFlags::Close );
2505 if ( pTOX->GetType() == TOX_INDEX && GetExport().AddSectionBreaksForTOX() )
2507 const SwFormatCol& rCol = rSect.GetFormat()->GetFormatAttr( RES_COL );
2508 const SwColumns& rColumns = rCol.GetColumns();
2509 sal_Int32 nCol = rColumns.size();
2511 if ( 0 < nCol )
2513 WW8_SepInfo rInfo( &GetExport().m_rDoc.GetPageDesc( 0 ), rSect.GetFormat(), 0/*nRstLnNum*/ );
2514 GetExport( ).AttrOutput().SectionBreak( msword::PageBreak, false, &rInfo );
2518 GetExport( ).m_bInWriteTOX = false;
2519 GetExport( ).m_bHideTabLeaderAndPageNumbers = false;
2520 if (bCareEnd)
2521 OnTOXEnding();
2524 bool MSWordExportBase::GetNumberFormat(const SwField& rField, OUString& rStr)
2526 // Returns a date or time format string by using the US NfKeywordTable
2527 bool bHasFormat = false;
2528 SvNumberFormatter* pNFormatr = m_rDoc.GetNumberFormatter();
2529 sal_uInt32 nFormatIdx = rField.GetFormat();
2530 const SvNumberformat* pNumFormat = pNFormatr->GetEntry( nFormatIdx );
2531 if( pNumFormat )
2533 LanguageType nLng = rField.GetLanguage();
2534 LocaleDataWrapper aLocDat(pNFormatr->GetComponentContext(),
2535 LanguageTag(nLng));
2537 OUString sFormat(pNumFormat->GetMappedFormatstring(GetNfKeywordTable(),
2538 aLocDat));
2540 if (!sFormat.isEmpty())
2542 sw::ms::SwapQuotesInField(sFormat);
2544 rStr = "\\@\"" + sFormat + "\" " ;
2545 bHasFormat = true;
2548 return bHasFormat;
2551 void AttributeOutputBase::GetNumberPara( OUString& rStr, const SwField& rField )
2553 switch(rField.GetFormat())
2555 case SVX_NUM_CHARS_UPPER_LETTER:
2556 case SVX_NUM_CHARS_UPPER_LETTER_N:
2557 rStr += "\\* ALPHABETIC ";
2558 break;
2559 case SVX_NUM_CHARS_LOWER_LETTER:
2560 case SVX_NUM_CHARS_LOWER_LETTER_N:
2561 rStr += "\\* alphabetic ";
2562 break;
2563 case SVX_NUM_ROMAN_UPPER:
2564 rStr += "\\* ROMAN ";
2565 break;
2566 case SVX_NUM_ROMAN_LOWER:
2567 rStr += "\\* roman ";
2568 break;
2569 default:
2570 OSL_ENSURE(rField.GetFormat() == SVX_NUM_ARABIC,
2571 "Unknown numbering type exported as default of Arabic");
2572 [[fallthrough]];
2573 case SVX_NUM_ARABIC:
2574 rStr += "\\* ARABIC ";
2575 break;
2576 case SVX_NUM_PAGEDESC:
2577 //Nothing, use word's default
2578 break;
2582 void WW8Export::WritePostItBegin( ww::bytes* pOut )
2584 sal_uInt8 aArr[ 3 ];
2585 sal_uInt8* pArr = aArr;
2587 // sprmCFSpec true
2588 Set_UInt16( pArr, NS_sprm::CFSpec::val );
2589 Set_UInt8( pArr, 1 );
2591 m_pChpPlc->AppendFkpEntry( Strm().Tell() );
2592 WriteChar( 0x05 ); // Annotation reference
2594 if( pOut )
2595 pOut->insert( pOut->end(), aArr, pArr );
2596 else
2597 m_pChpPlc->AppendFkpEntry( Strm().Tell(), static_cast< short >(pArr - aArr), aArr );
2600 OUString FieldString(ww::eField eIndex)
2602 if (const char *pField = ww::GetEnglishFieldName(eIndex))
2603 return " " + OUString::createFromAscii(pField) + " ";
2604 return " ";
2607 void WW8AttributeOutput::HiddenField( const SwField& rField )
2609 //replace LF 0x0A with VT 0x0B
2610 const OUString sExpand(rField.GetPar2().replace(0x0A, 0x0B));
2612 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell());
2613 SwWW8Writer::WriteString16(m_rWW8Export.Strm(), sExpand, false);
2614 static sal_uInt8 aArr[] =
2616 0x3C, 0x08, 0x1
2618 m_rWW8Export.m_pChpPlc->AppendFkpEntry(m_rWW8Export.Strm().Tell(), sizeof(aArr), aArr);
2621 void WW8AttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd )
2623 const SwSetExpField* pSet = static_cast<const SwSetExpField*>(&rField);
2624 const OUString &rVar = pSet->GetPar2();
2626 sal_uLong nFrom = m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell());
2628 GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Start |
2629 FieldFlags::CmdStart | FieldFlags::CmdEnd);
2632 Is there a bookmark at the start position of this field, if so
2633 move it to the 0x14 of the result of the field. This is what word
2634 does. MoveFieldMarks moves any bookmarks at this position to
2635 the beginning of the field result, and marks the bookmark as a
2636 fieldbookmark which is to be ended before the field end mark
2637 instead of after it like a normal bookmark.
2639 m_rWW8Export.MoveFieldMarks(nFrom,m_rWW8Export.Fc2Cp(m_rWW8Export.Strm().Tell()));
2641 if (!rVar.isEmpty())
2643 SwWW8Writer::WriteString16(m_rWW8Export.Strm(), rVar, false);
2645 GetExport().OutputField(&rField, eType, rCmd, FieldFlags::Close);
2648 void WW8AttributeOutput::PostitField( const SwField* pField )
2650 const SwPostItField *pPField = static_cast<const SwPostItField*>(pField);
2651 m_rWW8Export.m_pAtn->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), pPField );
2652 m_rWW8Export.WritePostItBegin( m_rWW8Export.pO.get() );
2655 bool WW8AttributeOutput::DropdownField( const SwField* pField )
2657 const SwDropDownField& rField2 = *static_cast<const SwDropDownField*>(pField);
2658 uno::Sequence<OUString> aItems =
2659 rField2.GetItemSequence();
2660 GetExport().DoComboBox(rField2.GetName(),
2661 rField2.GetHelp(),
2662 rField2.GetToolTip(),
2663 rField2.GetSelectedItem(), aItems);
2664 return false;
2667 bool WW8AttributeOutput::PlaceholderField( const SwField* )
2669 return true; // expand to text?
2672 void WW8AttributeOutput::RefField( const SwField &rField, const OUString &rRef)
2674 const OUString sStr{ FieldString( ww::eREF ) + "\"" + rRef + "\" " };
2675 m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Start |
2676 FieldFlags::CmdStart | FieldFlags::CmdEnd );
2677 const OUString sVar = lcl_GetExpandedField( rField );
2678 if ( !sVar.isEmpty() )
2680 SwWW8Writer::WriteString16( m_rWW8Export.Strm(), sVar, false );
2682 m_rWW8Export.OutputField( &rField, ww::eREF, sStr, FieldFlags::Close );
2685 void WW8AttributeOutput::WriteExpand( const SwField* pField )
2687 SwWW8Writer::WriteString16( m_rWW8Export.Strm(), lcl_GetExpandedField( *pField ), false );
2690 namespace
2692 // Escapes a token string for storing in Word formats. Its import counterpart
2693 // is lcl_ExtractToken in writerfilter/source/dmapper/DomainMapper_Impl.cxx
2694 OUString EscapeToken(const OUString& rCommand)
2696 bool bWasEscaped = false;
2698 const int nBufferLen = rCommand.getLength()*1.5;
2699 OUStringBuffer sResult(nBufferLen);
2700 sResult.append('"'); // opening quote
2701 for (sal_Int32 i = 0; i < rCommand.getLength(); ++i)
2703 sal_Unicode ch = rCommand[i];
2704 switch (ch)
2706 case '\\':
2707 case '"':
2708 // Backslashes and doublequotes must be escaped
2709 bWasEscaped = true;
2710 sResult.append('\\');
2711 break;
2712 case ' ':
2713 // Spaces require quotation
2714 bWasEscaped = true;
2715 break;
2717 sResult.append(ch);
2720 if (bWasEscaped)
2722 sResult.append('"'); // closing quote
2723 return sResult.makeStringAndClear();
2725 // No escapement/quotation was required
2726 return rCommand;
2730 void AttributeOutputBase::TextField( const SwFormatField& rField )
2732 const SwField* pField = rField.GetField();
2733 bool bWriteExpand = false;
2734 const sal_uInt16 nSubType = pField->GetSubType();
2736 switch (pField->GetTyp()->Which())
2738 case SwFieldIds::GetExp:
2739 if (nSubType == nsSwGetSetExpType::GSE_STRING)
2741 const SwGetExpField *pGet = static_cast<const SwGetExpField*>(pField);
2742 RefField( *pGet, pGet->GetFormula() );
2744 else
2745 bWriteExpand = true;
2746 break;
2747 case SwFieldIds::SetExp:
2748 if (nsSwGetSetExpType::GSE_SEQ == nSubType)
2750 OUString sStr;
2751 if (GetExport().FieldsQuoted())
2752 sStr = FieldString(ww::eSEQ) + pField->GetTyp()->GetName() + " ";
2753 else
2754 sStr = FieldString(ww::eSEQ) + "\"" + pField->GetTyp()->GetName() +"\" ";
2755 GetNumberPara( sStr, *pField );
2756 GetExport().OutputField(pField, ww::eSEQ, sStr);
2758 else if (nSubType & nsSwGetSetExpType::GSE_STRING)
2760 bool bShowAsWell = false;
2761 ww::eField eFieldNo;
2762 const SwSetExpField *pSet = static_cast<const SwSetExpField*>(pField);
2763 const OUString sVar = pSet->GetPar2();
2764 OUString sStr;
2765 if (pSet->GetInputFlag())
2767 sStr = FieldString(ww::eASK) + "\""
2768 + pSet->GetPar1() + "\" "
2769 + pSet->GetPromptText() + " \\d "
2770 + sVar;
2771 eFieldNo = ww::eASK;
2773 else
2775 sStr = FieldString(ww::eSET)
2776 + pSet->GetPar1() + " \""
2777 + sVar + "\" ";
2778 eFieldNo = ww::eSET;
2779 bShowAsWell = (nSubType & nsSwExtendedSubType::SUB_INVISIBLE) == 0;
2782 SetField( *pField, eFieldNo, sStr );
2784 if (bShowAsWell)
2785 RefField( *pSet, pSet->GetPar1() );
2787 else
2788 bWriteExpand = true;
2789 break;
2790 case SwFieldIds::PageNumber:
2792 OUString sStr = FieldString(ww::ePAGE);
2793 GetNumberPara(sStr, *pField);
2794 GetExport().OutputField(pField, ww::ePAGE, sStr);
2796 break;
2797 case SwFieldIds::Filename:
2799 OUString sStr = FieldString(ww::eFILENAME);
2800 if (pField->GetFormat() == FF_PATHNAME)
2801 sStr += "\\p ";
2802 GetExport().OutputField(pField, ww::eFILENAME, sStr);
2804 break;
2805 case SwFieldIds::Database:
2807 OUString sStr = FieldString(ww::eMERGEFIELD)
2808 + EscapeToken(static_cast<SwDBFieldType *>(pField->GetTyp())->GetColumnName()) + " ";
2809 GetExport().OutputField(pField, ww::eMERGEFIELD, sStr);
2811 break;
2812 case SwFieldIds::DatabaseName:
2814 SwDBData aData = GetExport().m_rDoc.GetDBData();
2815 const OUString sStr = FieldString(ww::eDATABASE)
2816 + aData.sDataSource
2817 + OUStringChar(DB_DELIM)
2818 + aData.sCommand;
2819 GetExport().OutputField(pField, ww::eDATABASE, sStr);
2821 break;
2822 case SwFieldIds::Author:
2824 ww::eField eField =
2825 ((AF_SHORTCUT & pField->GetFormat()) ? ww::eUSERINITIALS : ww::eUSERNAME);
2826 GetExport().OutputField(pField, eField, FieldString(eField));
2828 break;
2829 case SwFieldIds::TemplateName:
2830 GetExport().OutputField(pField, ww::eTEMPLATE, FieldString(ww::eTEMPLATE));
2831 break;
2832 case SwFieldIds::DocInfo: // Last printed, last edited,...
2833 if( DI_SUB_FIXED & nSubType )
2834 bWriteExpand = true;
2835 else
2837 OUString sStr;
2838 ww::eField eField(ww::eNONE);
2839 switch (0xff & nSubType)
2841 case DI_TITLE:
2842 eField = ww::eTITLE;
2843 break;
2844 case DI_SUBJECT:
2845 eField = ww::eSUBJECT;
2846 break;
2847 case DI_KEYS:
2848 eField = ww::eKEYWORDS;
2849 break;
2850 case DI_COMMENT:
2851 eField = ww::eCOMMENTS;
2852 break;
2853 case DI_DOCNO:
2854 eField = ww::eREVNUM;
2855 break;
2856 case DI_CREATE:
2857 if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
2858 eField = ww::eAUTHOR;
2859 else if (GetExport().GetNumberFormat(*pField, sStr))
2860 eField = ww::eCREATEDATE;
2861 break;
2863 case DI_CHANGE:
2864 if (DI_SUB_AUTHOR == (nSubType & DI_SUB_MASK))
2865 eField = ww::eLASTSAVEDBY;
2866 else if (GetExport().GetNumberFormat(*pField, sStr))
2867 eField = ww::eSAVEDATE;
2868 break;
2870 case DI_PRINT:
2871 if (DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK) &&
2872 GetExport().GetNumberFormat(*pField, sStr))
2873 eField = ww::ePRINTDATE;
2874 break;
2875 case DI_EDIT:
2876 if( DI_SUB_AUTHOR != (nSubType & DI_SUB_MASK ) &&
2877 GetExport().GetNumberFormat( *pField, sStr ))
2878 eField = ww::eSAVEDATE;
2879 else
2880 eField = ww::eEDITTIME;
2881 break;
2882 case DI_CUSTOM:
2883 eField = ww::eDOCPROPERTY;
2885 const SwDocInfoField * pDocInfoField =
2886 dynamic_cast<const SwDocInfoField *> (pField);
2888 if (pDocInfoField != nullptr)
2890 OUString sFieldname = pDocInfoField->GetFieldName();
2892 const sal_Int32 nIndex = sFieldname.indexOf(':');
2893 if (nIndex >= 0)
2894 sFieldname = sFieldname.copy(nIndex + 1);
2896 sStr = "\"" + sFieldname + "\"";
2899 break;
2900 default:
2901 break;
2904 if (eField != ww::eNONE)
2906 GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
2908 else
2909 bWriteExpand = true;
2911 break;
2912 case SwFieldIds::DateTime:
2914 OUString sStr;
2915 if (!GetExport().GetNumberFormat(*pField, sStr))
2916 bWriteExpand = true;
2917 else
2919 ww::eField eField = (DATEFLD & nSubType) ? ww::eDATE : ww::eTIME;
2920 GetExport().OutputField(pField, eField, FieldString(eField) + sStr);
2923 break;
2924 case SwFieldIds::DocStat:
2926 ww::eField eField = ww::eNONE;
2928 switch (nSubType)
2930 case DS_PAGE:
2931 eField = ww::eNUMPAGES;
2932 break;
2933 case DS_WORD:
2934 eField = ww::eNUMWORDS;
2935 break;
2936 case DS_CHAR:
2937 eField = ww::eNUMCHARS;
2938 break;
2941 if (eField != ww::eNONE)
2943 OUString sStr = FieldString(eField);
2944 GetNumberPara(sStr, *pField);
2945 GetExport().OutputField(pField, eField, sStr);
2947 else
2948 bWriteExpand = true;
2950 break;
2951 case SwFieldIds::ExtUser:
2953 ww::eField eField = ww::eNONE;
2954 switch (0xFF & nSubType)
2956 case EU_FIRSTNAME:
2957 case EU_NAME:
2958 eField = ww::eUSERNAME;
2959 break;
2960 case EU_SHORTCUT:
2961 eField = ww::eUSERINITIALS;
2962 break;
2963 case EU_STREET:
2964 case EU_COUNTRY:
2965 case EU_ZIP:
2966 case EU_CITY:
2967 eField = ww::eUSERADDRESS;
2968 break;
2971 if (eField != ww::eNONE)
2973 GetExport().OutputField(pField, eField, FieldString(eField));
2975 else
2976 bWriteExpand = true;
2978 break;
2979 case SwFieldIds::TableOfAuthorities:
2981 OUString sRet(static_cast<SwAuthorityField const*>(pField)
2982 ->ExpandCitation(AUTH_FIELD_IDENTIFIER, nullptr));
2983 // FIXME: DomainMapper_Impl::CloseFieldCommand() stuffs fully formed
2984 // field instructions in here, but if the field doesn't originate
2985 // from those filters it won't have that
2986 if (!sRet.trim().startsWith("CITATION"))
2988 sRet = FieldString(ww::eCITATION) + " \"" + sRet + "\"";
2990 GetExport().OutputField( pField, ww::eCITATION, sRet );
2992 break;
2993 case SwFieldIds::Postit:
2994 //Sadly only possible for word in main document text
2995 if (GetExport().m_nTextTyp == TXT_MAINTEXT)
2997 PostitField( pField );
2999 break;
3000 case SwFieldIds::Input:
3002 const SwInputField * pInputField = dynamic_cast<const SwInputField *>(pField);
3004 if (pInputField && pInputField->isFormField())
3005 GetExport().DoFormText(pInputField);
3006 else
3008 const OUString sStr = FieldString(ww::eFILLIN) + "\""
3009 + pField->GetPar2() + "\"";
3011 GetExport().OutputField(pField, ww::eFILLIN, sStr);
3014 break;
3015 case SwFieldIds::GetRef:
3017 ww::eField eField = ww::eNONE;
3018 OUString sStr;
3019 const SwGetRefField& rRField = *static_cast<const SwGetRefField*>(pField);
3020 switch (nSubType)
3022 case REF_SETREFATTR:
3023 case REF_BOOKMARK:
3024 switch (pField->GetFormat())
3026 case REF_PAGE_PGDESC:
3027 case REF_PAGE:
3028 eField = ww::ePAGEREF;
3029 break;
3030 default:
3031 eField = ww::eREF;
3032 break;
3035 const OUString& aRefName(rRField.GetSetRefName());
3036 sStr = FieldString(eField)
3037 + MSWordExportBase::GetBookmarkName(nSubType, &aRefName, 0);
3039 switch (pField->GetFormat())
3041 case REF_NUMBER:
3042 sStr += " \\r";
3043 break;
3044 case REF_NUMBER_NO_CONTEXT:
3045 sStr += " \\n";
3046 break;
3047 case REF_NUMBER_FULL_CONTEXT:
3048 sStr += " \\w";
3049 break;
3051 break;
3052 case REF_SEQUENCEFLD:
3054 // Not implemented for RTF
3055 if(GetExport().GetExportFormat() == MSWordExportBase::ExportFormat::RTF)
3056 break;
3058 switch (pField->GetFormat())
3060 case REF_PAGE:
3061 case REF_PAGE_PGDESC:
3062 eField = ww::ePAGEREF;
3063 break;
3064 default:
3065 eField = ww::eREF;
3066 break;
3068 // Generate a unique bookmark name
3070 OUString sName{rRField.GetSetRefName() + OUString::number(rRField.GetSeqNo())};
3071 switch (pField->GetFormat())
3073 case REF_PAGE:
3074 case REF_PAGE_PGDESC:
3075 case REF_CONTENT:
3076 case REF_UPDOWN:
3077 sName += "_full";
3078 break;
3079 case REF_ONLYNUMBER:
3080 sName += "_label_and_number";
3081 break;
3082 case REF_ONLYCAPTION:
3083 sName += "_caption_only";
3084 break;
3085 case REF_ONLYSEQNO:
3086 sName += "_number_only";
3087 break;
3088 default: // Ignore other types of reference fields
3089 eField = ww::eNONE;
3090 break;
3092 sStr = FieldString(eField) + MSWordExportBase::GetBookmarkName(nSubType, &sName, 0);
3094 switch (pField->GetFormat())
3096 case REF_NUMBER:
3097 sStr += " \\r";
3098 break;
3099 case REF_NUMBER_NO_CONTEXT:
3100 sStr += " \\n";
3101 break;
3102 case REF_NUMBER_FULL_CONTEXT:
3103 sStr += " \\w";
3104 break;
3106 break;
3108 case REF_FOOTNOTE:
3109 case REF_ENDNOTE:
3110 switch (pField->GetFormat())
3112 case REF_PAGE_PGDESC:
3113 case REF_PAGE:
3114 eField = ww::ePAGEREF;
3115 break;
3116 case REF_UPDOWN:
3117 eField = ww::eREF;
3118 break;
3119 default:
3120 eField =
3121 REF_ENDNOTE == nSubType ? ww::eNOTEREF : ww::eFOOTREF;
3122 break;
3124 sStr = FieldString(eField)
3125 + MSWordExportBase::GetBookmarkName(nSubType, nullptr, rRField.GetSeqNo());
3126 break;
3129 if (eField != ww::eNONE)
3131 switch (pField->GetFormat())
3133 case REF_UPDOWN:
3134 sStr += " \\p \\h "; // with hyperlink
3135 break;
3136 case REF_CHAPTER:
3137 sStr += " \\n \\h "; // with hyperlink
3138 break;
3139 default:
3140 sStr += " \\h "; // insert hyperlink
3141 break;
3143 GetExport().OutputField(pField, eField, sStr);
3145 else
3146 bWriteExpand = true;
3148 break;
3149 case SwFieldIds::CombinedChars:
3152 We need a font size to fill in the defaults, if these are overridden
3153 (as they generally are) by character properties then those properties
3154 win.
3156 The fontsize that is used in MS for determining the defaults is always
3157 the CJK fontsize even if the text is not in that language, in OOo the
3158 largest fontsize used in the field is the one we should take, but
3159 whatever we do, word will actually render using the fontsize set for
3160 CJK text. Nevertheless we attempt to guess whether the script is in
3161 asian or western text based up on the first character and use the
3162 font size of that script as our default.
3164 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
3165 sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( pField->GetPar1(), 0);
3167 tools::Long nHeight = static_cast<const SvxFontHeightItem&>((GetExport().GetItem(
3168 GetWhichOfScript(RES_CHRATR_FONTSIZE,nScript)))).GetHeight();
3170 nHeight = (nHeight + 10) / 20; //Font Size in points;
3173 Divide the combined char string into its up and down part. Get the
3174 font size and fill in the defaults as up == half the font size and
3175 down == a fifth the font size
3177 const sal_Int32 nAbove = (pField->GetPar1().getLength()+1)/2;
3178 const OUString sStr = FieldString(ww::eEQ)
3179 + "\\o (\\s\\up "
3180 + OUString::number(nHeight/2)
3181 + "("
3182 + pField->GetPar1().subView(0, nAbove)
3183 + "), \\s\\do "
3184 + OUString::number(nHeight/5)
3185 + "("
3186 + pField->GetPar1().subView(nAbove)
3187 + "))";
3188 GetExport().OutputField(pField, ww::eEQ, sStr);
3190 break;
3191 case SwFieldIds::Dropdown:
3192 bWriteExpand = DropdownField( pField );
3193 break;
3194 case SwFieldIds::Chapter:
3195 bWriteExpand = true;
3196 if (GetExport().m_bOutKF && rField.GetTextField())
3198 const SwTextNode *pTextNd = GetExport().GetHdFtPageRoot();
3199 if (!pTextNd)
3201 pTextNd = GetExport().m_pCurPam->GetNode().GetTextNode();
3204 if (pTextNd)
3206 SwChapterField aCopy(*static_cast<const SwChapterField*>(pField));
3207 aCopy.ChangeExpansion(*pTextNd, false);
3208 const OUString sStr = FieldString(ww::eSTYLEREF)
3209 + " "
3210 + OUString::number(aCopy.GetLevel() + 1)
3211 + " \\* MERGEFORMAT ";
3212 GetExport().OutputField(pField, ww::eSTYLEREF, sStr);
3213 bWriteExpand = false;
3216 break;
3217 case SwFieldIds::HiddenText:
3219 OUString sExpand(pField->GetPar2());
3220 if (!sExpand.isEmpty())
3222 HiddenField( *pField );
3225 break;
3226 case SwFieldIds::JumpEdit:
3227 bWriteExpand = PlaceholderField( pField );
3228 break;
3229 case SwFieldIds::Macro:
3231 const OUString sStr = " MACROBUTTON"
3232 + pField->GetPar1().replaceFirst("StarOffice.Standard.Modul1.", " ")
3233 + " "
3234 + lcl_GetExpandedField(*pField);
3235 GetExport().OutputField( pField, ww::eMACROBUTTON, sStr );
3237 break;
3238 case SwFieldIds::Table:
3240 ww::eField eField = ww::eEquals;
3241 OUString aExpand = " =" + pField->GetFieldName().trim();
3242 GetExport().OutputField(pField, eField, aExpand);
3244 break;
3245 case SwFieldIds::User:
3247 ww::eField eField = ww::eDOCVARIABLE;
3248 OUString aExpand = FieldString(eField) + pField->GetPar1() + " ";
3249 GetExport().OutputField(pField, eField, aExpand);
3251 break;
3252 default:
3253 bWriteExpand = true;
3254 break;
3257 if (bWriteExpand)
3258 WriteExpand( pField );
3261 void AttributeOutputBase::TextFlyContent( const SwFormatFlyCnt& rFlyContent )
3263 if ( auto pTextNd = dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) )
3265 Point const origin;
3266 Point aLayPos = pTextNd->FindLayoutRect( false, &origin ).Pos();
3268 SwPosition aPos( *pTextNd );
3269 ww8::Frame aFrame( *rFlyContent.GetFrameFormat(), aPos );
3271 OutputFlyFrame_Impl( aFrame, aLayPos );
3275 // TOXMarks are still missing
3277 // WW allows detailed settings for hyphenation only for the whole document.
3278 // One could implement following mimic: The values of the style "Standard" will
3279 // be set in the Document Properties ( DOP ) if they exist.
3281 // ACK. This suggestion fits exactly to our implementation of the import,
3282 // therefore I'll implement that right now. (KHZ, 07/15/2000)
3283 void WW8AttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone )
3285 // sprmPFNoAutoHyph
3286 m_rWW8Export.InsUInt16( NS_sprm::PFNoAutoHyph::val );
3288 m_rWW8Export.pO->push_back( rHyphenZone.IsHyphen() ? 0 : 1 );
3291 void WW8AttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace )
3293 m_rWW8Export.InsUInt16( NS_sprm::PFAutoSpaceDE::val );
3294 m_rWW8Export.pO->push_back( rScriptSpace.GetValue() ? 1 : 0 );
3297 void WW8AttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem )
3299 m_rWW8Export.InsUInt16( NS_sprm::PFOverflowPunct::val );
3300 m_rWW8Export.pO->push_back( rItem.GetValue() ? 1 : 0 );
3303 void WW8AttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem )
3305 m_rWW8Export.InsUInt16( NS_sprm::PFKinsoku::val );
3306 m_rWW8Export.pO->push_back( rItem.GetValue() ? 1 : 0 );
3309 void WW8AttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid )
3311 // sprmPFUsePgsuSettings
3313 m_rWW8Export.InsUInt16( NS_sprm::PFUsePgsuSettings::val );
3314 m_rWW8Export.pO->push_back( rGrid.GetValue() ? 1 : 0 );
3317 void WW8AttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign )
3319 // sprmPWAlignFont
3321 m_rWW8Export.InsUInt16( NS_sprm::PWAlignFont::val );
3323 SvxParaVertAlignItem::Align nAlign = rAlign.GetValue();
3324 sal_uInt16 nVal;
3325 switch ( nAlign )
3327 case SvxParaVertAlignItem::Align::Baseline:
3328 nVal = 2;
3329 break;
3330 case SvxParaVertAlignItem::Align::Top:
3331 nVal = 0;
3332 break;
3333 case SvxParaVertAlignItem::Align::Center:
3334 nVal = 1;
3335 break;
3336 case SvxParaVertAlignItem::Align::Bottom:
3337 nVal = 3;
3338 break;
3339 case SvxParaVertAlignItem::Align::Automatic:
3340 nVal = 4;
3341 break;
3342 default:
3343 nVal = 4;
3344 OSL_FAIL( "Unknown vert alignment" );
3345 break;
3347 m_rWW8Export.InsUInt16( nVal );
3350 // NoHyphen: I didn't find an equal in the SW UI and WW UI
3352 // RefMark, NoLineBreakHere are still missing
3354 void WW8Export::WriteFootnoteBegin( const SwFormatFootnote& rFootnote, ww::bytes* pOutArr )
3356 ww::bytes aAttrArr;
3357 const bool bAutoNum = rFootnote.GetNumStr().isEmpty();
3358 if( bAutoNum )
3360 static const sal_uInt8 aSpec[] =
3362 0x03, 0x6a, 0, 0, 0, 0, // sprmCObjLocation
3363 0x55, 0x08, 1 // sprmCFSpec
3366 aAttrArr.insert(aAttrArr.end(), aSpec, aSpec+sizeof(aSpec));
3369 // sprmCIstd
3370 const SwEndNoteInfo* pInfo;
3371 if( rFootnote.IsEndNote() )
3372 pInfo = &m_rDoc.GetEndNoteInfo();
3373 else
3374 pInfo = &m_rDoc.GetFootnoteInfo();
3375 const SwCharFormat* pCFormat = pOutArr
3376 ? pInfo->GetAnchorCharFormat( m_rDoc )
3377 : pInfo->GetCharFormat( m_rDoc );
3378 SwWW8Writer::InsUInt16( aAttrArr, NS_sprm::CIstd::val );
3379 SwWW8Writer::InsUInt16( aAttrArr, GetId( pCFormat ) );
3381 // fSpec-Attribut true
3382 // For Auto-Number a special character must go
3383 // into the text and therefore a fSpec attribute
3384 m_pChpPlc->AppendFkpEntry( Strm().Tell() );
3385 if( bAutoNum )
3386 WriteChar( 0x02 ); // auto number character
3387 else
3388 // user numbering
3389 OutSwString(rFootnote.GetNumStr(), 0, rFootnote.GetNumStr().getLength());
3391 if( pOutArr )
3393 // insert at start of array, so the "hard" attribute overrule the
3394 // attributes of the character template
3395 pOutArr->insert( pOutArr->begin(), aAttrArr.begin(), aAttrArr.end() );
3397 else
3399 std::unique_ptr<ww::bytes> pOwnOutArr(new ww::bytes);
3401 // insert at start of array, so the "hard" attribute overrule the
3402 // attributes of the character template
3403 pOwnOutArr->insert(pOwnOutArr->begin(), aAttrArr.begin(), aAttrArr.end());
3405 // write for the ftn number in the content, the font of the anchor
3406 const SwTextFootnote* pTextFootnote = rFootnote.GetTextFootnote();
3407 if( pTextFootnote )
3409 std::unique_ptr<ww::bytes> pOld = std::move(pO);
3410 pO = std::move(pOwnOutArr);
3411 SfxItemSet aSet( m_rDoc.GetAttrPool(), svl::Items<RES_CHRATR_FONT,
3412 RES_CHRATR_FONT>{} );
3414 pCFormat = pInfo->GetCharFormat( m_rDoc );
3416 pTextFootnote->GetTextNode().GetParaAttr(aSet,
3417 pTextFootnote->GetStart(), pTextFootnote->GetStart() + 1, true);
3418 if (aSet.Count())
3420 m_pAttrOutput->OutputItem( aSet.Get( RES_CHRATR_FONT ) );
3422 else
3424 m_pAttrOutput->OutputItem( pCFormat->GetAttrSet().Get(RES_CHRATR_FONT) );
3426 pOwnOutArr = std::move(pO);
3427 pO = std::move(pOld);
3429 m_pChpPlc->AppendFkpEntry( Strm().Tell(), pOwnOutArr->size(),
3430 pOwnOutArr->data() );
3434 static bool lcl_IsAtTextEnd(const SwFormatFootnote& rFootnote)
3436 bool bRet = true;
3437 if( rFootnote.GetTextFootnote() )
3439 sal_uInt16 nWh = rFootnote.IsEndNote() ? sal_uInt16(RES_END_AT_TXTEND)
3440 : sal_uInt16(RES_FTN_AT_TXTEND);
3441 const SwSectionNode* pSectNd = rFootnote.GetTextFootnote()->GetTextNode().
3442 FindSectionNode();
3443 while( pSectNd && FTNEND_ATPGORDOCEND ==
3444 static_cast<const SwFormatFootnoteEndAtTextEnd&>(pSectNd->GetSection().GetFormat()->
3445 GetFormatAttr( nWh)).GetValue() )
3446 pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
3448 if (!pSectNd)
3449 bRet = false; // the is ftn/end collected at Page- or Doc-End
3451 return bRet;
3454 void AttributeOutputBase::TextFootnote( const SwFormatFootnote& rFootnote )
3456 sal_uInt16 nTyp;
3457 if ( rFootnote.IsEndNote() )
3459 nTyp = REF_ENDNOTE;
3460 if ( GetExport().m_bEndAtTextEnd )
3461 GetExport().m_bEndAtTextEnd = lcl_IsAtTextEnd( rFootnote );
3463 else
3465 nTyp = REF_FOOTNOTE;
3466 if ( GetExport().m_bFootnoteAtTextEnd )
3467 GetExport().m_bFootnoteAtTextEnd = lcl_IsAtTextEnd( rFootnote );
3470 // if any reference to this footnote/endnote then insert an internal
3471 // Bookmark.
3472 OUString sBkmkNm;
3473 if ( GetExport().HasRefToFootOrEndnote( rFootnote.IsEndNote(), rFootnote.GetTextFootnote()->GetSeqRefNo()))
3475 sBkmkNm = MSWordExportBase::GetBookmarkName( nTyp, nullptr,
3476 rFootnote.GetTextFootnote()->GetSeqRefNo() );
3477 GetExport().AppendBookmark( sBkmkNm );
3480 TextFootnote_Impl( rFootnote );
3482 if ( !sBkmkNm.isEmpty() )
3483 GetExport().AppendBookmark( sBkmkNm ); // FIXME: Why is it added twice? Shouldn't this one go to WW8AttributeOutput::TextFootnote_Impl()?
3486 void WW8AttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote )
3488 WW8_WrPlcFootnoteEdn* pFootnoteEnd;
3489 if ( rFootnote.IsEndNote() || GetExport().m_rDoc.GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER )
3490 pFootnoteEnd = m_rWW8Export.pEdn.get();
3491 else
3492 pFootnoteEnd = m_rWW8Export.pFootnote.get();
3494 pFootnoteEnd->Append( m_rWW8Export.Fc2Cp( m_rWW8Export.Strm().Tell() ), rFootnote );
3495 m_rWW8Export.WriteFootnoteBegin( rFootnote, m_rWW8Export.pO.get() );
3498 void WW8AttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat )
3500 if( rCharFormat.GetCharFormat() )
3502 m_rWW8Export.InsUInt16( NS_sprm::CIstd::val );
3504 m_rWW8Export.InsUInt16( m_rWW8Export.GetId( rCharFormat.GetCharFormat() ) );
3509 See ww8par6.cxx Read_DoubleLine for some more info
3511 void WW8AttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
3513 // #i28331# - check that bOn is set
3514 if ( !rTwoLines.GetValue() )
3515 return;
3517 m_rWW8Export.InsUInt16( NS_sprm::CFELayout::val );
3518 m_rWW8Export.pO->push_back( sal_uInt8(0x06) ); //len 6
3519 m_rWW8Export.pO->push_back( sal_uInt8(0x02) );
3521 sal_Unicode cStart = rTwoLines.GetStartBracket();
3522 sal_Unicode cEnd = rTwoLines.GetEndBracket();
3525 As per usual we have problems. We can have separate left and right brackets
3526 in OOo, it doesn't appear that you can in word. Also in word there appear
3527 to only be a limited number of possibilities, we can use pretty much
3528 anything.
3530 So if we have none, we export none, if either bracket is set to a known
3531 word type we export both as that type (with the bracket winning out in
3532 the case of a conflict simply being the order of test here.
3534 Upshot being a documented created in word will be reexported with no
3535 ill effects.
3538 sal_uInt16 nType;
3539 if (!cStart && !cEnd)
3540 nType = 0;
3541 else if ((cStart == '{') || (cEnd == '}'))
3542 nType = 4;
3543 else if ((cStart == '<') || (cEnd == '>'))
3544 nType = 3;
3545 else if ((cStart == '[') || (cEnd == ']'))
3546 nType = 2;
3547 else
3548 nType = 1;
3549 m_rWW8Export.InsUInt16( nType );
3550 static const sal_uInt8 aZeroArr[ 3 ] = { 0, 0, 0 };
3551 m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aZeroArr, aZeroArr+3);
3554 void AttributeOutputBase::ParaNumRule( const SwNumRuleItem& rNumRule )
3556 const SwTextNode* pTextNd = nullptr;
3557 if (rNumRule.GetValue().isEmpty())
3559 ParaNumRule_Impl(pTextNd, 0, 0);
3560 return;
3562 const SwNumRule* pRule = GetExport().m_rDoc.FindNumRulePtr(
3563 rNumRule.GetValue() );
3564 if (!pRule)
3565 return;
3567 sal_uInt16 nNumId = GetExport().GetNumberingId(*pRule) + 1;
3568 sal_uInt8 nLvl = 0;
3570 if (!GetExport().m_pOutFormatNode)
3572 ParaNumRule_Impl(pTextNd, nLvl, nNumId);
3573 return;
3576 if ( dynamic_cast< const SwContentNode *>( GetExport().m_pOutFormatNode ) != nullptr )
3578 pTextNd = static_cast<const SwTextNode*>(GetExport().m_pOutFormatNode);
3580 if( pTextNd->IsCountedInList())
3582 int nLevel = pTextNd->GetActualListLevel();
3584 if (nLevel < 0)
3585 nLevel = 0;
3587 if (nLevel >= MAXLEVEL)
3588 nLevel = MAXLEVEL - 1;
3590 nLvl = static_cast< sal_uInt8 >(nLevel);
3592 if (GetExport().GetExportFormat() == MSWordExportBase::DOCX) // FIXME
3594 // tdf#95848 find the abstract list definition
3595 OUString const listId(pTextNd->GetListId());
3596 if (!listId.isEmpty()
3597 && (listId != pRule->GetDefaultListId() // default list id uses the 1:1 mapping
3598 || pTextNd->IsListRestart()) // or restarting previous list
3601 SwList const*const pList(
3602 GetExport().m_rDoc.getIDocumentListsAccess().getListByName(listId));
3603 if (pList)
3605 SwNumRule const*const pAbstractRule(
3606 GetExport().m_rDoc.FindNumRulePtr(
3607 pList->GetDefaultListStyleName()));
3608 assert(pAbstractRule);
3609 if (pAbstractRule == pRule && !pTextNd->IsListRestart())
3611 // different list, but no override
3612 nNumId = GetExport().DuplicateAbsNum(listId, *pAbstractRule) + 1;
3614 else
3616 nNumId = GetExport().OverrideNumRule(
3617 *pRule, listId, *pAbstractRule) + 1;
3619 if (pTextNd->IsListRestart())
3621 // For restarted lists we should also keep value for
3622 // future w:lvlOverride / w:startOverride
3623 GetExport().AddListLevelOverride(nNumId-1, pTextNd->GetActualListLevel(),
3624 pTextNd->GetActualListStartValue());
3630 else if (pTextNd->IsListRestart())
3632 sal_uInt16 nStartWith = static_cast<sal_uInt16>(pTextNd->GetActualListStartValue());
3633 nNumId = GetExport().DuplicateNumRule(pRule, nLvl, nStartWith);
3634 if (USHRT_MAX != nNumId)
3635 ++nNumId;
3638 else
3640 // #i44815# adjust numbering for numbered paragraphs
3641 // without number (NO_NUMLEVEL). These paragraphs
3642 // will receive a list id 0, which WW interprets as
3643 // 'no number'.
3644 nNumId = 0;
3647 else if ( auto pC = dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
3649 if ( pC && pC->IsAssignedToListLevelOfOutlineStyle() )
3650 nLvl = static_cast< sal_uInt8 >( pC->GetAssignedOutlineStyleLevel() );
3653 if ( nLvl >= WW8ListManager::nMaxLevel )
3654 nLvl = WW8ListManager::nMaxLevel - 1;
3656 ParaNumRule_Impl( pTextNd, nLvl, nNumId);
3659 void WW8AttributeOutput::ParaNumRule_Impl(const SwTextNode* /*pTextNd*/,
3660 sal_Int32 const nLvl, sal_Int32 const nNumId)
3662 if (USHRT_MAX == nNumId)
3663 return;
3665 // write sprmPIlvl and sprmPIlfo
3666 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::PIlvl::val );
3667 m_rWW8Export.pO->push_back( ::sal::static_int_cast<sal_uInt8>(nLvl) );
3668 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::PIlfo::val );
3669 SwWW8Writer::InsUInt16( *m_rWW8Export.pO, ::sal::static_int_cast<sal_uInt16>(nNumId) );
3672 /* File FRMATR.HXX */
3674 void WW8AttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize )
3676 if( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
3678 if( m_rWW8Export.m_bOutGrf )
3679 return; // Fly around graphic -> Auto-size
3681 //???? What about percentages ???
3682 if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed)
3684 //"sprmPDxaWidth"
3685 m_rWW8Export.InsUInt16( NS_sprm::PDxaWidth::val );
3686 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rSize.GetWidth()) );
3689 if ( rSize.GetHeight() )
3691 // sprmPWHeightAbs
3692 m_rWW8Export.InsUInt16( NS_sprm::PWHeightAbs::val );
3694 sal_uInt16 nH = 0;
3695 switch ( rSize.GetHeightSizeType() )
3697 case SwFrameSize::Variable: break;
3698 case SwFrameSize::Fixed: nH = static_cast<sal_uInt16>(rSize.GetHeight()) & 0x7fff; break;
3699 default: nH = static_cast<sal_uInt16>(rSize.GetHeight()) | 0x8000; break;
3701 m_rWW8Export.InsUInt16( nH );
3704 else if( m_rWW8Export.m_bOutPageDescs ) // PageDesc : width + height
3706 if( m_rWW8Export.m_pCurrentPageDesc->GetLandscape() )
3708 /*sprmSBOrientation*/
3709 m_rWW8Export.InsUInt16( NS_sprm::SBOrientation::val );
3710 m_rWW8Export.pO->push_back( 2 );
3713 /*sprmSXaPage*/
3714 m_rWW8Export.InsUInt16( NS_sprm::SXaPage::val );
3715 m_rWW8Export.InsUInt16(
3716 msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetWidth())));
3718 /*sprmSYaPage*/
3719 m_rWW8Export.InsUInt16( NS_sprm::SYaPage::val );
3720 m_rWW8Export.InsUInt16(
3721 msword_cast<sal_uInt16>(SvxPaperInfo::GetSloppyPaperDimension(rSize.GetHeight())));
3725 // FillOrder is still missing
3728 * ReplaceCr() is used for Pagebreaks and Pagedescs. An already written CR
3729 * will be replaced by a break character. Replace must be called right after
3730 * the writing of CR.
3732 * @return FilePos + 1 of the replaced CR or 0 if nothing was replaced.
3734 sal_uLong WW8Export::ReplaceCr( sal_uInt8 nChar )
3736 OSL_ENSURE( nChar, "replaced with 0 crashes WW97/95" );
3738 bool bReplaced = false;
3739 SvStream& rStrm = Strm();
3740 sal_uLong nRetPos = 0, nPos = rStrm.Tell();
3741 //If there is at least two characters already output
3742 if (nPos - 2 >= o3tl::make_unsigned(pFib->m_fcMin))
3744 sal_uInt16 nUCode=0;
3746 rStrm.SeekRel(-2);
3747 rStrm.ReadUInt16( nUCode );
3748 //If the last char was a cr
3749 if (nUCode == 0x0d) // CR ?
3751 if ((nChar == 0x0c) &&
3752 (nPos - 4 >= o3tl::make_unsigned(pFib->m_fcMin)))
3754 rStrm.SeekRel(-4);
3755 rStrm.ReadUInt16( nUCode );
3757 else
3759 rStrm.SeekRel(-2);
3760 nUCode = 0x0;
3762 //And the para is not of len 0, then replace this cr with the mark
3763 //#120140# If there is a cr before a column break, need replace the cr. So remove the "nChar==0x0e" check.
3764 if( nUCode == 0x0d )
3765 bReplaced = false;
3766 else
3768 bReplaced = true;
3769 WriteChar(nChar);
3770 nRetPos = nPos;
3773 else if ((nUCode == 0x0c) && (nChar == 0x0e))
3775 // a column break after a section has no effect in writer
3776 bReplaced = true;
3778 rStrm.Seek( nPos );
3780 else
3781 bReplaced = true;
3783 if (!bReplaced)
3785 // then write as normal char
3786 WriteChar(nChar);
3787 m_pPiece->SetParaBreak();
3788 m_pPapPlc->AppendFkpEntry(rStrm.Tell());
3789 m_pChpPlc->AppendFkpEntry(rStrm.Tell());
3790 nRetPos = rStrm.Tell();
3792 return nRetPos;
3795 void WW8AttributeOutput::TableRowEnd(sal_uInt32 nDepth)
3797 if ( nDepth == 1 )
3798 m_rWW8Export.WriteChar( 0x07 );
3799 else if ( nDepth > 1 )
3800 m_rWW8Export.WriteChar( 0x0d );
3802 //Technically in a word document this is a different value for a row ends
3803 //that are not row ends directly after a cell with a graphic. But it
3804 //doesn't seem to make a difference
3805 //pMagicTable->Append(Fc2Cp(Strm().Tell()),0x1B6);
3808 void AttributeOutputBase::FormatPageDescription( const SwFormatPageDesc& rPageDesc )
3810 if ( GetExport().m_bStyDef )
3811 if (auto pC = dynamic_cast< const SwTextFormatColl *>( GetExport().m_pOutFormatNode ) )
3813 if ( (SfxItemState::SET != pC->GetItemState( RES_BREAK, false ) ) && rPageDesc.KnowsPageDesc() )
3814 FormatBreak( SvxFormatBreakItem( SvxBreak::PageBefore, RES_BREAK ) );
3818 void WW8AttributeOutput::PageBreakBefore( bool bBreak )
3820 // sprmPPageBreakBefore/sprmPFPageBreakBefore
3821 m_rWW8Export.InsUInt16( NS_sprm::PFPageBreakBefore::val );
3823 m_rWW8Export.pO->push_back( bBreak ? 1 : 0 );
3827 * breaks write nothing in the output field rWrt.pO,
3828 * but only in the text stream (requirement so they can
3829 * be called from Out_Break...)
3831 void AttributeOutputBase::FormatBreak( const SvxFormatBreakItem& rBreak )
3833 if ( GetExport().m_bStyDef )
3835 switch ( rBreak.GetBreak() )
3837 case SvxBreak::NONE:
3838 case SvxBreak::PageBefore:
3839 case SvxBreak::PageBoth:
3840 PageBreakBefore( rBreak.GetValue() != SvxBreak::NONE );
3841 break;
3842 default:
3843 break;
3846 else if ( !GetExport().m_pParentFrame )
3848 sal_uInt8 nC = 0;
3849 bool bBefore = false;
3850 // #i76300# - Note: Can only be <true>, if <bBefore> equals <false>.
3851 bool bCheckForFollowPageDesc = false;
3853 switch ( rBreak.GetBreak() )
3855 case SvxBreak::NONE: // disabled
3856 if ( !GetExport().m_bBreakBefore )
3857 PageBreakBefore( false );
3858 return;
3860 case SvxBreak::ColumnBefore: // ColumnBreak
3861 bBefore = true;
3862 [[fallthrough]];
3863 case SvxBreak::ColumnAfter:
3864 case SvxBreak::ColumnBoth:
3865 if ( GetExport().m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK )
3866 || GetExport().Sections().CurrentNumberOfColumns( GetExport().m_rDoc ) > 1 )
3868 nC = msword::ColumnBreak;
3870 break;
3872 case SvxBreak::PageBefore: // PageBreak
3873 // From now on(fix for #i77900#) we prefer to save a page break
3874 // as paragraph attribute (if the exporter is OK with that),
3875 // this has to be done after the export of the paragraph ( =>
3876 // !GetExport().bBreakBefore )
3877 if (GetExport().PreferPageBreakBefore())
3879 if (!GetExport().m_bBreakBefore)
3880 PageBreakBefore(true);
3882 else
3884 bBefore = true;
3885 nC = msword::PageBreak;
3887 break;
3888 case SvxBreak::PageAfter:
3889 case SvxBreak::PageBoth:
3890 nC = msword::PageBreak;
3891 // #i76300# - check for follow page description,
3892 // if current writing attributes of a paragraph.
3893 if ( dynamic_cast< const SwTextNode* >( GetExport().m_pOutFormatNode ) &&
3894 GetExport().GetCurItemSet() )
3896 bCheckForFollowPageDesc = true;
3898 break;
3900 default:
3901 break;
3904 if ( ( bBefore == GetExport().m_bBreakBefore ) && nC )
3906 // #i76300#
3907 bool bFollowPageDescWritten = false;
3908 if ( bCheckForFollowPageDesc )
3910 bFollowPageDescWritten =
3911 GetExport().OutputFollowPageDesc( GetExport().GetCurItemSet(),
3912 dynamic_cast<const SwTextNode*>( GetExport().m_pOutFormatNode ) );
3914 if ( !bFollowPageDescWritten )
3916 SectionBreak(nC, !bBefore);
3922 void WW8AttributeOutput::SectionBreak( sal_uInt8 nC, bool /*bBreakAfter*/, const WW8_SepInfo* /*pSectionInfo*/ )
3924 m_rWW8Export.ReplaceCr( nC );
3927 sal_uInt32 AttributeOutputBase::GridCharacterPitch( const SwTextGridItem& rGrid ) const
3929 MSWordStyles * pStyles = GetExport().m_pStyles.get();
3930 const SwFormat * pSwFormat = pStyles->GetSwFormat(0);
3932 sal_uInt32 nPageCharSize = 0;
3934 if (pSwFormat != nullptr)
3936 nPageCharSize = ItemGet<SvxFontHeightItem>
3937 (*pSwFormat, RES_CHRATR_FONTSIZE).GetHeight();
3939 sal_uInt16 nPitch = rGrid.IsSquaredMode() ? rGrid.GetBaseHeight() :
3940 rGrid.GetBaseWidth( );
3942 sal_Int32 nCharWidth = nPitch - nPageCharSize;
3943 sal_Int32 nFraction = nCharWidth % 20;
3944 if ( nCharWidth < 0 )
3945 nFraction = 20 + nFraction;
3946 nFraction = ( nFraction * 0xFFF ) / 20;
3947 nFraction = ( nFraction & 0x00000FFF );
3949 sal_Int32 nMain = nCharWidth / 20;
3950 if ( nCharWidth < 0 )
3951 nMain -= 1;
3952 nMain = nMain * 0x1000;
3953 nMain = ( nMain & 0xFFFFF000 );
3955 return sal_uInt32( nFraction + nMain );
3958 void WW8AttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid )
3960 if (!m_rWW8Export.m_bOutPageDescs)
3961 return;
3963 sal_uInt16 nGridType = 0;
3964 switch ( rGrid.GetGridType() )
3966 default:
3967 OSL_FAIL("Unknown grid type");
3968 [[fallthrough]];
3969 case GRID_NONE:
3970 nGridType = 0;
3971 break;
3972 case GRID_LINES_ONLY:
3973 nGridType = 2;
3974 break;
3975 case GRID_LINES_CHARS:
3976 if ( rGrid.IsSnapToChars() )
3977 nGridType = 3;
3978 else
3979 nGridType = 1;
3980 break;
3982 m_rWW8Export.InsUInt16( NS_sprm::SClm::val );
3983 m_rWW8Export.InsUInt16( nGridType );
3985 sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
3986 m_rWW8Export.InsUInt16( NS_sprm::SDyaLinePitch::val );
3987 m_rWW8Export.InsUInt16( nHeight );
3989 m_rWW8Export.InsUInt16( NS_sprm::SDxtCharSpace::val );
3990 m_rWW8Export.InsUInt32( GridCharacterPitch( rGrid ) );
3993 void WW8AttributeOutput::FormatPaperBin( const SvxPaperBinItem& rPaperBin )
3995 if ( !m_rWW8Export.m_bOutPageDescs )
3996 return;
3998 sal_uInt16 nVal;
3999 switch ( rPaperBin.GetValue() )
4001 case 0: nVal = 15; break; // Automatically select
4002 case 1: nVal = 1; break; // Upper paper tray
4003 case 2: nVal = 4; break; // Manual paper feed
4004 default: nVal = 0; break;
4007 if ( nVal )
4009 m_rWW8Export.InsUInt16( m_rWW8Export.m_bOutFirstPage
4010 ? NS_sprm::SDmBinFirst::val : NS_sprm::SDmBinOther::val );
4012 m_rWW8Export.InsUInt16( nVal );
4016 void WW8AttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLR )
4018 // Flys are still missing ( see RTF )
4020 if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
4022 // sprmPDxaFromText10
4023 m_rWW8Export.InsUInt16( NS_sprm::LN_PDxaFromText10 );
4024 // use average, since WW only knows one value
4025 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>( ( rLR.GetLeft() + rLR.GetRight() ) / 2 ) );
4027 else if ( m_rWW8Export.m_bOutPageDescs ) // PageDescs
4029 m_pageMargins.nLeft = 0;
4030 m_pageMargins.nRight = 0;
4032 if ( auto pBoxItem = static_cast<const SvxBoxItem*>(m_rWW8Export.HasItem( RES_BOX )) )
4034 m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
4035 m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
4038 m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLR.GetLeft());
4039 m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLR.GetRight());
4041 // sprmSDxaLeft
4042 m_rWW8Export.InsUInt16( NS_sprm::SDxaLeft::val );
4043 m_rWW8Export.InsUInt16( m_pageMargins.nLeft );
4045 // sprmSDxaRight
4046 m_rWW8Export.InsUInt16( NS_sprm::SDxaRight::val );
4047 m_rWW8Export.InsUInt16( m_pageMargins.nRight );
4049 else
4050 { // normal paragraphs
4051 // sprmPDxaLeft
4052 m_rWW8Export.InsUInt16( 0x845E ); //asian version ?
4053 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLR.GetTextLeft()) );
4055 // sprmPDxaRight
4056 m_rWW8Export.InsUInt16( 0x845D ); //asian version ?
4057 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>(rLR.GetRight()) );
4059 // sprmPDxaLeft1
4060 m_rWW8Export.InsUInt16( 0x8460 ); //asian version ?
4061 m_rWW8Export.InsUInt16( rLR.GetTextFirstLineOffset() );
4065 void WW8AttributeOutput::FormatULSpace( const SvxULSpaceItem& rUL )
4067 // Flys are still missing ( see RTF )
4069 if ( m_rWW8Export.m_bOutFlyFrameAttrs ) // Flys
4071 // sprmPDyaFromText
4072 m_rWW8Export.InsUInt16( NS_sprm::PDyaFromText::val );
4073 // use average, since WW only knows one value
4074 m_rWW8Export.InsUInt16( static_cast<sal_uInt16>( ( rUL.GetUpper() + rUL.GetLower() ) / 2 ) );
4076 else if ( m_rWW8Export.m_bOutPageDescs ) // Page-UL
4078 OSL_ENSURE( m_rWW8Export.GetCurItemSet(), "Impossible" );
4079 if ( !m_rWW8Export.GetCurItemSet() )
4080 return;
4082 HdFtDistanceGlue aDistances( *m_rWW8Export.GetCurItemSet() );
4084 if ( aDistances.HasHeader() )
4086 //sprmSDyaHdrTop
4087 m_rWW8Export.InsUInt16( NS_sprm::SDyaHdrTop::val );
4088 m_rWW8Export.InsUInt16( aDistances.dyaHdrTop );
4091 // sprmSDyaTop
4092 m_rWW8Export.InsUInt16( NS_sprm::SDyaTop::val );
4093 m_rWW8Export.InsUInt16( aDistances.dyaTop );
4094 m_pageMargins.nTop = aDistances.dyaTop;
4096 if ( aDistances.HasFooter() )
4098 //sprmSDyaHdrBottom
4099 m_rWW8Export.InsUInt16( NS_sprm::SDyaHdrBottom::val );
4100 m_rWW8Export.InsUInt16( aDistances.dyaHdrBottom );
4103 //sprmSDyaBottom
4104 m_rWW8Export.InsUInt16( NS_sprm::SDyaBottom::val );
4105 m_rWW8Export.InsUInt16( aDistances.dyaBottom );
4106 m_pageMargins.nBottom = aDistances.dyaBottom;
4108 else
4110 // sprmPDyaBefore
4111 m_rWW8Export.InsUInt16( NS_sprm::PDyaBefore::val );
4112 m_rWW8Export.InsUInt16( rUL.GetUpper() );
4113 // sprmPDyaAfter
4114 m_rWW8Export.InsUInt16( NS_sprm::PDyaAfter::val );
4115 m_rWW8Export.InsUInt16( rUL.GetLower() );
4116 // sprmPFContextualSpacing
4117 if (rUL.GetContext())
4119 m_rWW8Export.InsUInt16(NS_sprm::PFContextualSpacing::val);
4120 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(rUL.GetContext()) );
4125 // print, opaque, protect are still missing
4127 void WW8AttributeOutput::FormatSurround( const SwFormatSurround& rSurround )
4129 if ( m_rWW8Export.m_bOutFlyFrameAttrs )
4131 m_rWW8Export.InsUInt16( NS_sprm::PWr::val );
4133 m_rWW8Export.pO->push_back(
4134 ( css::text::WrapTextMode_NONE != rSurround.GetSurround() ) ? 2 : 1 );
4138 void WW8AttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert )
4141 //!!!! anchor type and corresponding borders are still missing
4142 if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
4143 return;
4145 short nPos;
4146 switch( rFlyVert.GetVertOrient() )
4148 case text::VertOrientation::NONE:
4149 nPos = static_cast<short>(rFlyVert.GetPos());
4150 break;
4151 case text::VertOrientation::CENTER:
4152 case text::VertOrientation::LINE_CENTER:
4153 nPos = -8;
4154 break;
4155 case text::VertOrientation::BOTTOM:
4156 case text::VertOrientation::LINE_BOTTOM:
4157 nPos = -12;
4158 break;
4159 case text::VertOrientation::TOP:
4160 case text::VertOrientation::LINE_TOP:
4161 default:
4162 nPos = -4;
4163 break;
4166 // sprmPDyaAbs
4167 m_rWW8Export.InsUInt16( NS_sprm::PDyaAbs::val );
4168 m_rWW8Export.InsUInt16( nPos );
4171 void WW8AttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori )
4173 if ( !m_rWW8Export.m_pParentFrame )
4175 OSL_ENSURE( m_rWW8Export.m_pParentFrame, "HoriOrient without mpParentFrame !!" );
4176 return;
4179 //!!!! anchor type and corresponding borders are still missing
4180 if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
4181 return;
4183 short nPos;
4184 switch( rFlyHori.GetHoriOrient() )
4186 case text::HoriOrientation::NONE:
4187 nPos = static_cast<short>(rFlyHori.GetPos());
4188 if( !nPos )
4189 nPos = 1; // WW: 0 is reserved
4190 break;
4191 case text::HoriOrientation::LEFT:
4192 nPos = rFlyHori.IsPosToggle() ? -12 : 0;
4193 break;
4194 case text::HoriOrientation::RIGHT:
4195 nPos = rFlyHori.IsPosToggle() ? -16 : -8;
4196 break;
4197 case text::HoriOrientation::CENTER:
4198 case text::HoriOrientation::FULL: // FULL only for tables
4199 default:
4200 nPos = -4;
4201 break;
4204 // sprmPDxaAbs
4205 m_rWW8Export.InsUInt16( NS_sprm::PDxaAbs::val );
4206 m_rWW8Export.InsUInt16( nPos );
4209 void WW8AttributeOutput::FormatAnchor( const SwFormatAnchor& rAnchor )
4211 OSL_ENSURE( m_rWW8Export.m_pParentFrame, "Anchor without mpParentFrame !!" );
4213 if ( !m_rWW8Export.m_bOutFlyFrameAttrs )
4214 return;
4216 sal_uInt8 nP = 0;
4217 switch ( rAnchor.GetAnchorId() )
4219 case RndStdIds::FLY_AT_PAGE:
4220 // vertical: page | horizontal: page
4221 nP |= (1 << 4) | (2 << 6);
4222 break;
4223 // in case of Fly as characters: set paragraph-bound!!!
4224 case RndStdIds::FLY_AT_FLY:
4225 case RndStdIds::FLY_AT_CHAR:
4226 case RndStdIds::FLY_AT_PARA:
4227 case RndStdIds::FLY_AS_CHAR:
4228 // vertical: page | horizontal: page
4229 nP |= (2 << 4) | (0 << 6);
4230 break;
4231 default:
4232 break;
4235 // sprmPPc
4236 m_rWW8Export.InsUInt16( NS_sprm::PPc::val );
4237 m_rWW8Export.pO->push_back( nP );
4240 void WW8AttributeOutput::FormatBackground( const SvxBrushItem& rBrush )
4242 // WW cannot have background in a section
4243 if ( m_rWW8Export.m_bOutPageDescs )
4244 return;
4246 WW8_SHD aSHD;
4247 WW8Export::TransBrush( rBrush.GetColor(), aSHD );
4249 m_rWW8Export.InsUInt16( NS_sprm::PShd80::val );
4250 m_rWW8Export.InsUInt16( aSHD.GetValue() );
4252 m_rWW8Export.InsUInt16( NS_sprm::PShd::val );
4253 m_rWW8Export.pO->push_back( 10 ); //size of operand: MUST be 10
4254 m_rWW8Export.InsUInt32( 0xFF000000 ); //cvFore: Foreground BGR = cvAuto
4255 m_rWW8Export.InsUInt32( SuitableBGColor( rBrush.GetColor() ) ); //cvBack
4256 m_rWW8Export.InsUInt16( 0x0000 ); //iPat: specifies the pattern used for shading = clear/100% background
4259 void WW8AttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle )
4261 // WW cannot have background in a section
4262 if ( m_rWW8Export.m_bOutPageDescs )
4263 return;
4265 // see MSWordExportBase::OutputItemSet for how _SOLID is handled
4266 if ( rFillStyle.GetValue() != drawing::FillStyle_NONE )
4267 return;
4269 //Shd80Nil
4270 m_rWW8Export.InsUInt16( NS_sprm::PShd80::val );
4271 m_rWW8Export.InsUInt16( 0xffff );
4273 //cvAuto
4274 m_rWW8Export.InsUInt16( NS_sprm::PShd::val );
4275 m_rWW8Export.pO->push_back( 10 );
4276 m_rWW8Export.InsUInt32( 0xFF000000 );
4277 m_rWW8Export.InsUInt32( 0xFF000000 );
4278 m_rWW8Export.InsUInt16( 0x0000 );
4281 void WW8AttributeOutput::FormatFillGradient( const XFillGradientItem& /*rFillGradient*/ )
4285 WW8_BRCVer9 WW8Export::TranslateBorderLine(const SvxBorderLine& rLine,
4286 sal_uInt16 nDist, bool bShadow)
4288 sal_uInt32 nColBGR = 0;
4289 sal_uInt16 nWidth = ::editeng::ConvertBorderWidthToWord(
4290 rLine.GetBorderLineStyle(), rLine.GetWidth());
4291 sal_uInt8 brcType = 0;
4293 if( nWidth ) // line ?
4295 // BRC.brcType
4296 brcType = 0;
4297 // All the border types values are available on
4298 // http://msdn.microsoft.com/en-us/library/dd908142%28v=office.12%29.aspx
4299 switch (rLine.GetBorderLineStyle())
4301 case SvxBorderLineStyle::SOLID:
4303 if ( rLine.GetWidth( ) == DEF_LINE_WIDTH_0 )
4304 brcType = 5;
4305 else
4306 brcType = 1;
4308 break;
4309 case SvxBorderLineStyle::DOTTED:
4310 brcType = 6;
4311 break;
4312 case SvxBorderLineStyle::DASHED:
4313 brcType = 7;
4314 break;
4315 case SvxBorderLineStyle::DOUBLE:
4316 case SvxBorderLineStyle::DOUBLE_THIN:
4317 brcType = 3;
4318 break;
4319 case SvxBorderLineStyle::THINTHICK_SMALLGAP:
4320 brcType = 11;
4321 break;
4322 case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
4323 brcType = 14;
4324 break;
4325 case SvxBorderLineStyle::THINTHICK_LARGEGAP:
4326 brcType = 17;
4327 break;
4328 case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
4329 brcType = 12;
4330 break;
4331 case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
4332 brcType = 15;
4333 break;
4334 case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
4335 brcType = 18;
4336 break;
4337 case SvxBorderLineStyle::EMBOSSED:
4338 brcType = 24;
4339 break;
4340 case SvxBorderLineStyle::ENGRAVED:
4341 brcType = 25;
4342 break;
4343 case SvxBorderLineStyle::OUTSET:
4344 brcType = 26;
4345 break;
4346 case SvxBorderLineStyle::INSET:
4347 brcType = 27;
4348 break;
4349 case SvxBorderLineStyle::FINE_DASHED:
4350 brcType = 22;
4351 break;
4352 case SvxBorderLineStyle::DASH_DOT:
4353 brcType = 8;
4354 break;
4355 case SvxBorderLineStyle::DASH_DOT_DOT:
4356 brcType = 9;
4357 break;
4358 default:
4359 break;
4362 // convert width from twips (1/20 pt) to eighths of a point
4363 nWidth = (( nWidth * 8 ) + 10 ) / 20;
4364 if( 0xff < nWidth )
4365 nWidth = 0xff;
4367 if( 0 == nWidth ) // really thin line
4368 nWidth = 1; // don't omit
4370 // BRC.cv
4371 nColBGR = wwUtility::RGBToBGR(rLine.GetColor().GetRGBColor());
4374 // BRC.dptSpace
4375 sal_uInt16 nLDist = rtl::math::round(nDist / 20.0); // unit of measurement: pt
4376 if( nLDist > 0x1f )
4377 nLDist = 0x1f;
4379 return WW8_BRCVer9(nColBGR, sal_uInt8(nWidth), brcType, sal_uInt8(nLDist),
4380 bShadow, false);
4384 * Gets passed a WW8Bytes*, so the function also can be used for the table border.
4386 * @param nSprmNo If nSprmNo == 0, then the opcode isn't outputted.
4387 * @param bShadow SHOULDN'T be set for table cells !
4389 void WW8Export::Out_BorderLine(ww::bytes& rO, const SvxBorderLine* pLine,
4390 sal_uInt16 nDist, sal_uInt16 nSprmNo, sal_uInt16 nSprmNoVer9, bool bShadow)
4392 OSL_ENSURE( ( nSprmNo == 0 ) ||
4393 ( nSprmNo >= 38 && nSprmNo <= 41 ) ||
4394 ( nSprmNo >= NS_sprm::PBrcTop80::val
4395 && nSprmNo <= NS_sprm::PBrcRight80::val ) ||
4396 ( nSprmNo >= NS_sprm::SBrcTop80::val
4397 && nSprmNo <= NS_sprm::SBrcRight80::val ),
4398 "Sprm for border out is of range" );
4400 WW8_BRCVer9 aBrcVer9;
4401 WW8_BRC aBrcVer8;
4403 if( pLine && pLine->GetBorderLineStyle() != SvxBorderLineStyle::NONE )
4405 aBrcVer9 = TranslateBorderLine( *pLine, nDist, bShadow );
4406 sal_uInt8 ico = msfilter::util::TransColToIco( msfilter::util::BGRToRGB(aBrcVer9.cv()) );
4407 aBrcVer8 = WW8_BRC( aBrcVer9.dptLineWidth(), aBrcVer9.brcType(), ico,
4408 aBrcVer9.dptSpace(), aBrcVer9.fShadow(), aBrcVer9.fFrame() );
4411 // WW97-SprmIds
4412 if ( nSprmNo != 0 )
4413 SwWW8Writer::InsUInt16( rO, nSprmNo );
4415 rO.insert( rO.end(), aBrcVer8.aBits1, aBrcVer8.aBits2+2 );
4417 if ( nSprmNoVer9 != 0 )
4419 SwWW8Writer::InsUInt16( rO, nSprmNoVer9 );
4420 rO.push_back(sizeof(WW8_BRCVer9));
4421 rO.insert( rO.end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4);
4426 * is for all boxes except in tables. pO of the WW8Writer is used
4428 * @param rBox
4429 * @param bShadow
4431 void WW8Export::Out_SwFormatBox(const SvxBoxItem& rBox, bool bShadow)
4433 static const SvxBoxItemLine aBorders[] =
4435 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4437 static const sal_uInt16 aPBrc[] =
4439 // WW8 SPRMs
4440 NS_sprm::PBrcTop80::val, NS_sprm::PBrcLeft80::val,
4441 NS_sprm::PBrcBottom80::val, NS_sprm::PBrcRight80::val,
4442 // WW9 SPRMs
4443 NS_sprm::PBrcTop::val, NS_sprm::PBrcLeft::val,
4444 NS_sprm::PBrcBottom::val, NS_sprm::PBrcRight::val
4446 static const sal_uInt16 aSBrc[] =
4448 // WW8 SPRMs
4449 NS_sprm::SBrcTop80::val, NS_sprm::SBrcLeft80::val,
4450 NS_sprm::SBrcBottom80::val, NS_sprm::SBrcRight80::val,
4451 // WW9 SPRMs
4452 NS_sprm::SBrcTop::val, NS_sprm::SBrcLeft::val,
4453 NS_sprm::SBrcBottom::val, NS_sprm::SBrcRight::val
4456 const SvxBoxItemLine* pBrd = aBorders;
4457 for( sal_uInt16 i = 0; i < 4; ++i, ++pBrd )
4459 const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
4461 sal_uInt16 nSprmNo, nSprmNoVer9 = 0;
4462 if (m_bOutPageDescs)
4464 nSprmNo = aSBrc[i];
4465 nSprmNoVer9 = aSBrc[i+4];
4467 else
4469 nSprmNo = aPBrc[i];
4470 nSprmNoVer9 = aPBrc[i+4];
4473 Out_BorderLine( *pO, pLn, rBox.GetDistance( *pBrd ), nSprmNo,
4474 nSprmNoVer9, bShadow );
4479 * FormatBox2() is for TC structures in tables. The Sprm opcode isn't written
4480 * because it is packed into the TC structure without opcode.
4481 * dxpSpace always becomes 0, because WW requires that in tables
4482 * ( table borders otherwise will fray )
4484 * @param rO A WW8Bytes pointer is passed in as output parameter
4486 void WW8Export::Out_SwFormatTableBox( ww::bytes& rO, const SvxBoxItem * pBox )
4488 // possible and maybe better would be 0xffff
4489 static const SvxBoxItemLine aBorders[] =
4491 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4493 static const SvxBorderLine aBorderLine;
4495 for(const SvxBoxItemLine & rBorder : aBorders)
4497 const SvxBorderLine* pLn;
4498 if (pBox != nullptr)
4499 pLn = pBox->GetLine( rBorder );
4500 else
4501 pLn = & aBorderLine;
4503 Out_BorderLine(rO, pLn, 0, 0, 0, false);
4507 void WW8Export::Out_CellRangeBorders( const SvxBoxItem * pBox, sal_uInt8 nStart,
4508 sal_uInt8 nLimit )
4510 if ( !pBox )
4511 return;
4513 static const SvxBoxItemLine aBorders[] =
4515 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
4518 for( int i = 0; i < 4; ++i )
4520 const SvxBorderLine* pLn = pBox->GetLine( aBorders[i] );
4521 if (!pLn)
4522 continue;
4524 InsUInt16( NS_sprm::TSetBrc::val );
4525 pO->push_back( 11 );
4526 pO->push_back( nStart );
4527 pO->push_back( nLimit );
4528 pO->push_back( 1<<i );
4529 WW8_BRCVer9 aBrcVer9 = TranslateBorderLine( *pLn, 0, false );
4530 pO->insert( pO->end(), aBrcVer9.aBits1, aBrcVer9.aBits2+4 );
4534 void WW8AttributeOutput::FormatBox( const SvxBoxItem& rBox )
4536 // Fly around graphic -> here no border, because the
4537 // graphics header already has the border
4538 if ( m_rWW8Export.m_bOutGrf )
4539 return;
4541 bool bShadow = false;
4542 const SfxPoolItem* pItem = m_rWW8Export.HasItem( RES_SHADOW );
4543 if ( pItem )
4545 const SvxShadowItem* p = static_cast<const SvxShadowItem*>(pItem);
4546 bShadow = ( p->GetLocation() != SvxShadowLocation::NONE )
4547 && ( p->GetWidth() != 0 );
4550 SvxBoxItem aBox(rBox);
4551 if (m_rWW8Export.m_bOutPageDescs)
4553 editeng::WordBorderDistances aDistances;
4554 editeng::BorderDistancesToWord(aBox, m_pageMargins, aDistances);
4556 aBox.SetDistance(aDistances.nTop, SvxBoxItemLine::TOP);
4557 aBox.SetDistance(aDistances.nLeft, SvxBoxItemLine::LEFT);
4558 aBox.SetDistance(aDistances.nBottom, SvxBoxItemLine::BOTTOM);
4559 aBox.SetDistance(aDistances.nRight, SvxBoxItemLine::RIGHT);
4561 m_bFromEdge = aDistances.bFromEdge;
4564 m_rWW8Export.Out_SwFormatBox( aBox, bShadow );
4567 SwTwips WW8Export::CurrentPageWidth(SwTwips &rLeft, SwTwips &rRight) const
4569 const SwFrameFormat* pFormat = m_pCurrentPageDesc ? &m_pCurrentPageDesc->GetMaster()
4570 : &m_rDoc.GetPageDesc(0).GetMaster();
4572 const SvxLRSpaceItem& rLR = pFormat->GetLRSpace();
4573 SwTwips nPageSize = pFormat->GetFrameSize().GetWidth();
4574 rLeft = rLR.GetLeft();
4575 rRight = rLR.GetRight();
4576 return nPageSize;
4579 void WW8AttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol & rCol, bool bEven, SwTwips nPageSize )
4581 // CColumns
4582 m_rWW8Export.InsUInt16( NS_sprm::SCcolumns::val );
4583 m_rWW8Export.InsUInt16( nCols - 1 );
4585 // DxaColumns
4586 m_rWW8Export.InsUInt16( NS_sprm::SDxaColumns::val );
4587 m_rWW8Export.InsUInt16( rCol.GetGutterWidth( true ) );
4589 // LBetween
4590 m_rWW8Export.InsUInt16( NS_sprm::SLBetween::val );
4591 m_rWW8Export.pO->push_back( COLADJ_NONE == rCol.GetLineAdj( )? 0 : 1 );
4593 const SwColumns & rColumns = rCol.GetColumns( );
4595 // FEvenlySpaced
4596 m_rWW8Export.InsUInt16( NS_sprm::SFEvenlySpaced::val );
4597 m_rWW8Export.pO->push_back( bEven ? 1 : 0 );
4599 if ( bEven )
4600 return;
4602 for ( sal_uInt16 n = 0; n < nCols; ++n )
4604 //sprmSDxaColWidth
4605 m_rWW8Export.InsUInt16( NS_sprm::SDxaColWidth::val );
4606 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(n) );
4607 m_rWW8Export.InsUInt16( rCol.
4608 CalcPrtColWidth( n,
4609 static_cast<sal_uInt16>(nPageSize) ) );
4611 if ( n + 1 != nCols )
4613 //sprmSDxaColSpacing
4614 m_rWW8Export.InsUInt16( NS_sprm::SDxaColSpacing::val );
4615 m_rWW8Export.pO->push_back( static_cast<sal_uInt8>(n) );
4616 m_rWW8Export.InsUInt16( rColumns[n].GetRight( ) +
4617 rColumns[n + 1].GetLeft( ) );
4622 void AttributeOutputBase::FormatColumns( const SwFormatCol& rCol )
4624 const SwColumns& rColumns = rCol.GetColumns();
4626 sal_uInt16 nCols = rColumns.size();
4627 if (1 >= nCols || GetExport( ).m_bOutFlyFrameAttrs)
4628 return;
4630 // get the page width without borders !!
4632 const SwFrameFormat* pFormat = GetExport( ).m_pCurrentPageDesc ? &GetExport( ).m_pCurrentPageDesc->GetMaster() : &const_cast<const SwDoc&>(GetExport().m_rDoc).GetPageDesc(0).GetMaster();
4633 const SvxFrameDirectionItem &frameDirection = pFormat->GetFrameDir();
4634 SwTwips nPageSize;
4635 if ( frameDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB || frameDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB )
4637 const SvxULSpaceItem &rUL = pFormat->GetULSpace();
4638 nPageSize = pFormat->GetFrameSize().GetHeight();
4639 nPageSize -= rUL.GetUpper() + rUL.GetLower();
4641 const SwFormatHeader *header = pFormat->GetAttrSet().GetItem(RES_HEADER);
4642 if ( header )
4644 const SwFrameFormat *headerFormat = header->GetHeaderFormat();
4645 if (headerFormat)
4647 nPageSize -= headerFormat->GetFrameSize().GetHeight();
4650 const SwFormatFooter *footer = pFormat->GetAttrSet().GetItem(RES_FOOTER);
4651 if ( footer )
4653 const SwFrameFormat *footerFormat = footer->GetFooterFormat();
4654 if ( footerFormat )
4656 nPageSize -= footerFormat->GetFrameSize().GetHeight();
4660 else
4662 const SvxLRSpaceItem &rLR = pFormat->GetLRSpace();
4663 nPageSize = pFormat->GetFrameSize().GetWidth();
4664 nPageSize -= rLR.GetLeft() + rLR.GetRight();
4665 //i120133: The Section width should consider page indent value.
4666 nPageSize -= rCol.GetAdjustValue();
4670 // look if all columns are equal
4671 bool bEven = rCol.IsOrtho();
4672 if (!bEven)
4674 bEven = true;
4675 sal_uInt16 n;
4676 sal_uInt16 nColWidth = rCol.CalcPrtColWidth( 0, static_cast<sal_uInt16>(nPageSize) );
4677 for ( n = 1; n < nCols; n++ )
4679 short nDiff = nColWidth -
4680 rCol.CalcPrtColWidth( n, static_cast<sal_uInt16>(nPageSize) );
4682 if ( nDiff > 10 || nDiff < -10 ) // Tolerance: 10 tw
4684 bEven = false;
4685 break;
4690 FormatColumns_Impl( nCols, rCol, bEven, nPageSize );
4693 // "Paragraphs together"
4694 void WW8AttributeOutput::FormatKeep( const SvxFormatKeepItem& rKeep )
4696 // sprmFKeepFollow
4697 m_rWW8Export.InsUInt16( NS_sprm::PFKeepFollow::val );
4699 m_rWW8Export.pO->push_back( rKeep.GetValue() ? 1 : 0 );
4702 // exclude a paragraph from Line Numbering
4703 void WW8AttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering )
4705 // sprmPFNoLineNumb
4706 m_rWW8Export.InsUInt16( NS_sprm::PFNoLineNumb::val );
4708 m_rWW8Export.pO->push_back( rNumbering.IsCount() ? 0 : 1 );
4711 /* File PARATR.HXX */
4713 void WW8AttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti )
4715 // sprmPDyaLine
4716 m_rWW8Export.InsUInt16( NS_sprm::PDyaLine::val );
4718 m_rWW8Export.InsUInt16( nSpace );
4719 m_rWW8Export.InsUInt16( nMulti );
4722 void AttributeOutputBase::ParaLineSpacing( const SvxLineSpacingItem& rSpacing )
4724 short nSpace = 240, nMulti = 0;
4726 switch ( rSpacing.GetLineSpaceRule() )
4728 default:
4729 break;
4730 case SvxLineSpaceRule::Fix: // Fix
4731 nSpace = -static_cast<short>(rSpacing.GetLineHeight());
4732 break;
4733 case SvxLineSpaceRule::Min: // At least
4734 nSpace = static_cast<short>(rSpacing.GetLineHeight());
4735 break;
4736 case SvxLineSpaceRule::Auto:
4738 if( rSpacing.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix ) // Leading
4740 // doesn't exist in WW - how do you get the MaxLineHeight?
4741 nSpace = rSpacing.GetInterLineSpace();
4742 sal_uInt16 nScript =
4743 i18n::ScriptType::LATIN;
4744 const SwAttrSet *pSet = nullptr;
4745 if ( auto pFormat = dynamic_cast< const SwFormat *>( GetExport().m_pOutFormatNode ) )
4747 pSet = &pFormat->GetAttrSet();
4749 else if ( auto pNd = dynamic_cast< const SwTextNode *>( GetExport().m_pOutFormatNode ) )
4751 pSet = &pNd->GetSwAttrSet();
4752 nScript = g_pBreakIt->GetBreakIter()->getScriptType(pNd->GetText(), 0);
4754 OSL_ENSURE( pSet, "No attrset for lineheight :-(" );
4755 if ( pSet )
4757 nSpace = nSpace + static_cast<short>( AttrSetToLineHeight( GetExport().m_rDoc.getIDocumentSettingAccess(),
4758 *pSet, *Application::GetDefaultDevice(), nScript ) );
4761 else // Proportional
4763 if ( rSpacing.GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off )
4764 nSpace = static_cast<short>( ( 240L * rSpacing.GetPropLineSpace() ) / 100L );
4765 nMulti = 1;
4768 break;
4770 // if nSpace is negative, it is a fixed size in 1/20 of a point
4771 // if nSpace is positive and nMulti is 1, it is 1/240 of a single line height
4772 // otherwise, it is a minimum size in 1/20 of a point
4773 ParaLineSpacing_Impl( nSpace, nMulti );
4776 void WW8AttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust )
4778 // sprmPJc
4779 sal_uInt8 nAdj;
4780 sal_uInt8 nAdjBiDi;
4781 switch ( rAdjust.GetAdjust() )
4783 case SvxAdjust::Left:
4784 nAdj = 0;
4785 nAdjBiDi = 2;
4786 break;
4787 case SvxAdjust::Right:
4788 nAdj = 2;
4789 nAdjBiDi = 0;
4790 break;
4791 case SvxAdjust::BlockLine:
4792 case SvxAdjust::Block:
4793 nAdj = nAdjBiDi = 3;
4794 break;
4795 case SvxAdjust::Center:
4796 nAdj = nAdjBiDi = 1;
4797 break;
4798 default:
4799 return; // not a supported Attribute
4802 m_rWW8Export.InsUInt16(NS_sprm::PJc80::val);
4803 m_rWW8Export.pO->push_back(nAdj);
4806 Sadly for left to right paragraphs both these values are the same,
4807 for right to left paragraphs the bidi one is the reverse of the
4808 normal one.
4810 m_rWW8Export.InsUInt16(NS_sprm::PJc::val); //bidi version ?
4811 bool bBiDiSwap = false;
4812 if (m_rWW8Export.m_pOutFormatNode)
4814 SvxFrameDirection nDirection = SvxFrameDirection::Horizontal_LR_TB;
4815 if (auto pTN = dynamic_cast<const SwTextNode*>(m_rWW8Export.m_pOutFormatNode))
4817 SwPosition aPos(*pTN);
4818 nDirection = m_rWW8Export.m_rDoc.GetTextDirection(aPos);
4820 else if (auto pC = dynamic_cast<const SwTextFormatColl*>(m_rWW8Export.m_pOutFormatNode))
4822 const SvxFrameDirectionItem &rItem =
4823 ItemGet<SvxFrameDirectionItem>(*pC, RES_FRAMEDIR);
4824 nDirection = rItem.GetValue();
4826 if ( ( nDirection == SvxFrameDirection::Horizontal_RL_TB ) ||
4827 ( nDirection == SvxFrameDirection::Environment && AllSettings::GetLayoutRTL() ) )
4829 bBiDiSwap = true;
4833 if (bBiDiSwap)
4834 m_rWW8Export.pO->push_back(nAdjBiDi);
4835 else
4836 m_rWW8Export.pO->push_back(nAdj);
4839 void WW8AttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection )
4841 sal_uInt16 nTextFlow=0;
4842 bool bBiDi = false;
4843 SvxFrameDirection nDir = rDirection.GetValue();
4845 if ( nDir == SvxFrameDirection::Environment )
4846 nDir = GetExport( ).GetDefaultFrameDirection( );
4849 switch ( nDir )
4851 default:
4852 //Can't get an unknown type here
4853 OSL_FAIL("Unknown frame direction");
4854 [[fallthrough]];
4855 case SvxFrameDirection::Horizontal_LR_TB:
4856 nTextFlow = 0;
4857 break;
4858 case SvxFrameDirection::Horizontal_RL_TB:
4859 nTextFlow = 0;
4860 bBiDi = true;
4861 break;
4862 case SvxFrameDirection::Vertical_LR_TB: //word doesn't have this
4863 case SvxFrameDirection::Vertical_RL_TB:
4864 nTextFlow = 1;
4865 break;
4868 if ( m_rWW8Export.m_bOutPageDescs )
4870 m_rWW8Export.InsUInt16( NS_sprm::STextFlow::val );
4871 m_rWW8Export.InsUInt16( nTextFlow );
4872 m_rWW8Export.InsUInt16( NS_sprm::SFBiDi::val );
4873 m_rWW8Export.pO->push_back( bBiDi ? 1 : 0 );
4875 else if ( !m_rWW8Export.m_bOutFlyFrameAttrs ) //paragraph/style
4877 m_rWW8Export.InsUInt16( NS_sprm::PFBiDi::val );
4878 m_rWW8Export.pO->push_back( bBiDi ? 1 : 0 );
4882 void WW8AttributeOutput::ParaGrabBag(const SfxGrabBagItem& /*rItem*/)
4886 void WW8AttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/)
4890 void WW8AttributeOutput::ParaOutlineLevel(const SfxUInt16Item& /*rItem*/)
4894 // "Separate paragraphs"
4895 void WW8AttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit )
4897 // sprmPFKeep
4898 m_rWW8Export.InsUInt16( NS_sprm::PFKeep::val );
4899 m_rWW8Export.pO->push_back( rSplit.GetValue() ? 0 : 1 );
4903 * Only convert the item "SvxWidowItem" and not the orphans, because
4904 * in WW only one attribute "paragraph control" exists for both and
4905 * in SW probably both or none is set by the user.
4907 void WW8AttributeOutput::ParaWidows( const SvxWidowsItem& rWidows )
4909 // sprmPFWidowControl
4910 m_rWW8Export.InsUInt16( NS_sprm::PFWidowControl::val );
4911 m_rWW8Export.pO->push_back( rWidows.GetValue() ? 1 : 0 );
4914 namespace {
4916 class SwWW8WrTabu
4918 std::unique_ptr<sal_uInt8[]> pDel; // DelArray
4919 std::unique_ptr<sal_uInt8[]> pAddPos; // AddPos-Array
4920 std::unique_ptr<sal_uInt8[]> pAddTyp; // AddTyp-Array
4921 sal_uInt16 nAdd; // number of tabs to be added
4922 sal_uInt16 nDel; // number of tabs to be deleted
4924 SwWW8WrTabu(const SwWW8WrTabu&) = delete;
4925 SwWW8WrTabu& operator=(const SwWW8WrTabu&) = delete;
4927 public:
4928 SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax);
4930 void Add(const SvxTabStop &rTS, tools::Long nAdjustment);
4931 void Del(const SvxTabStop &rTS, tools::Long nAdjustment);
4932 void PutAll(WW8Export& rWW8Wrt);
4937 SwWW8WrTabu::SwWW8WrTabu(sal_uInt16 nDelMax, sal_uInt16 nAddMax)
4938 : nAdd(0), nDel(0)
4940 if (nDelMax)
4941 pDel.reset( new sal_uInt8[nDelMax * 2] );
4942 pAddPos.reset( new sal_uInt8[nAddMax * 2] );
4943 pAddTyp.reset( new sal_uInt8[nAddMax] );
4947 * insert a tab in the WW structure
4949 void SwWW8WrTabu::Add(const SvxTabStop & rTS, tools::Long nAdjustment)
4951 // insert tab position
4952 ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment),
4953 pAddPos.get() + (nAdd * 2));
4955 // insert tab type
4956 sal_uInt8 nPara = 0;
4957 switch (rTS.GetAdjustment())
4959 case SvxTabAdjust::Right:
4960 nPara = 2;
4961 break;
4962 case SvxTabAdjust::Center:
4963 nPara = 1;
4964 break;
4965 case SvxTabAdjust::Decimal:
4967 There is nothing we can do btw the decimal separator has been
4968 customized, but if you think different remember that different
4969 locales have different separators, i.e. german is a , while english
4970 is a .
4972 nPara = 3;
4973 break;
4974 default:
4975 break;
4978 switch( rTS.GetFill() )
4980 case '.': // dotted leader
4981 nPara |= 1 << 3;
4982 break;
4983 case '_': // Single line leader
4984 nPara |= 3 << 3;
4985 break;
4986 case '-': // hyphenated leader
4987 nPara |= 2 << 3;
4988 break;
4989 case '=': // heavy line leader
4990 nPara |= 4 << 3;
4991 break;
4994 pAddTyp[nAdd] = nPara;
4995 ++nAdd;
4999 * Insert a to be deleted tab in the WW structure
5001 void SwWW8WrTabu::Del(const SvxTabStop &rTS, tools::Long nAdjustment)
5003 // insert tab position
5004 ShortToSVBT16(msword_cast<sal_Int16>(rTS.GetTabPos() + nAdjustment),
5005 pDel.get() + (nDel * 2));
5006 ++nDel;
5010 * Writes the attribute to rWrt.pO
5012 void SwWW8WrTabu::PutAll(WW8Export& rWrt)
5014 if (!nAdd && !nDel) //If it's a no-op
5015 return;
5016 OSL_ENSURE(nAdd <= 255, "more than 255 added tabstops?");
5017 OSL_ENSURE(nDel <= 255, "more than 244 removed tabstops?");
5018 if (nAdd > 255)
5019 nAdd = 255;
5020 if (nDel > 255)
5021 nDel = 255;
5023 sal_uInt16 nSiz = 2 * nDel + 3 * nAdd + 2;
5024 if (nSiz > 255)
5025 nSiz = 255;
5027 rWrt.InsUInt16(NS_sprm::PChgTabsPapx::val);
5028 // insert cch
5029 rWrt.pO->push_back(msword_cast<sal_uInt8>(nSiz));
5030 // write DelArr
5031 rWrt.pO->push_back(msword_cast<sal_uInt8>(nDel));
5032 rWrt.OutSprmBytes(pDel.get(), nDel * 2);
5033 // write InsArr
5034 rWrt.pO->push_back(msword_cast<sal_uInt8>(nAdd));
5035 rWrt.OutSprmBytes(pAddPos.get(), 2 * nAdd); // AddPosArray
5036 rWrt.OutSprmBytes(pAddTyp.get(), nAdd); // AddTypArray
5039 static void ParaTabStopAdd( WW8Export& rWrt,
5040 const SvxTabStopItem& rTStops,
5041 const tools::Long nLParaMgn )
5043 SwWW8WrTabu aTab( 0, rTStops.Count());
5045 for( sal_uInt16 n = 0; n < rTStops.Count(); n++ )
5047 const SvxTabStop& rTS = rTStops[n];
5048 // ignore default tabs
5049 if (SvxTabAdjust::Default != rTS.GetAdjustment())
5050 aTab.Add(rTS, nLParaMgn);
5052 aTab.PutAll( rWrt );
5055 static bool lcl_IsEqual(tools::Long nOneLeft, const SvxTabStop &rOne,
5056 tools::Long nTwoLeft, const SvxTabStop &rTwo)
5058 return(
5059 nOneLeft == nTwoLeft &&
5060 rOne.GetAdjustment() == rTwo.GetAdjustment() &&
5061 rOne.GetDecimal() == rTwo.GetDecimal() &&
5062 rOne.GetFill() == rTwo.GetFill()
5066 static void ParaTabStopDelAdd( WW8Export& rWrt,
5067 const SvxTabStopItem& rTStyle,
5068 const tools::Long nLStypeMgn,
5069 const SvxTabStopItem& rTNew,
5070 const tools::Long nLParaMgn )
5072 SwWW8WrTabu aTab(rTStyle.Count(), rTNew.Count());
5074 sal_uInt16 nO = 0; // rTStyle Index
5075 sal_uInt16 nN = 0; // rTNew Index
5077 do {
5078 const SvxTabStop* pTO;
5079 tools::Long nOP;
5080 if( nO < rTStyle.Count() ) // old not yet at the end?
5082 pTO = &rTStyle[ nO ];
5083 nOP = pTO->GetTabPos() + nLStypeMgn;
5084 if( SvxTabAdjust::Default == pTO->GetAdjustment() )
5086 nO++; // ignore default tab
5087 continue;
5090 else
5092 pTO = nullptr;
5093 nOP = LONG_MAX;
5096 const SvxTabStop* pTN;
5097 tools::Long nNP;
5098 if( nN < rTNew.Count() ) // new not yet at the end
5100 pTN = &rTNew[ nN ];
5101 nNP = pTN->GetTabPos() + nLParaMgn;
5102 if( SvxTabAdjust::Default == pTN->GetAdjustment() )
5104 nN++; // ignore default tab
5105 continue;
5108 else
5110 pTN = nullptr;
5111 nNP = LONG_MAX;
5114 if( nOP == LONG_MAX && nNP == LONG_MAX )
5115 break; // everything done
5117 if( nOP < nNP ) // next tab is old
5119 assert(pTO);
5120 aTab.Del(*pTO, nLStypeMgn); // must be deleted
5121 nO++;
5123 else if( nNP < nOP ) // next tab is new
5125 assert(pTN);
5126 aTab.Add(*pTN, nLParaMgn); // must be inserted
5127 nN++;
5129 else if (lcl_IsEqual(nOP, *pTO, nNP, *pTN)) // tabs are equal
5131 nO++; // nothing to do
5132 nN++;
5134 else // tabs same position, different type
5136 aTab.Del(*pTO, nLStypeMgn); // delete old one
5137 aTab.Add(*pTN, nLParaMgn); // insert new one
5138 nO++;
5139 nN++;
5141 } while( true );
5143 aTab.PutAll( rWrt );
5146 void WW8AttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStops )
5148 const bool bTabsRelativeToIndex = m_rWW8Export.m_pCurPam->GetDoc().getIDocumentSettingAccess().get( DocumentSettingId::TABS_RELATIVE_TO_INDENT );
5150 tools::Long nCurrentLeft = 0;
5151 if ( bTabsRelativeToIndex )
5153 const SfxPoolItem* pLR = m_rWW8Export.HasItem( RES_LR_SPACE );
5155 if ( pLR != nullptr )
5156 nCurrentLeft = static_cast<const SvxLRSpaceItem*>(pLR)->GetTextLeft();
5159 // #i100264#
5160 if ( m_rWW8Export.m_bStyDef &&
5161 m_rWW8Export.m_pCurrentStyle != nullptr &&
5162 m_rWW8Export.m_pCurrentStyle->DerivedFrom() != nullptr )
5164 SvxTabStopItem aParentTabs( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
5165 const SwFormat *pParentStyle = m_rWW8Export.m_pCurrentStyle->DerivedFrom();
5167 if (const SvxTabStopItem* pParentTabs = pParentStyle->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP))
5169 aParentTabs.Insert( pParentTabs );
5173 // #i120938# - consider left indentation of style and its parent style
5174 tools::Long nParentLeft = 0;
5175 if ( bTabsRelativeToIndex )
5177 const SvxLRSpaceItem &rStyleLR = ItemGet<SvxLRSpaceItem>( pParentStyle->GetAttrSet(), RES_LR_SPACE );
5178 nParentLeft = rStyleLR.GetTextLeft();
5181 ParaTabStopDelAdd( m_rWW8Export, aParentTabs, nParentLeft, rTabStops, nCurrentLeft );
5182 return;
5185 const SvxTabStopItem* pStyleTabs = nullptr;
5186 if ( !m_rWW8Export.m_bStyDef && m_rWW8Export.m_pStyAttr )
5188 pStyleTabs = m_rWW8Export.m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
5191 if ( !pStyleTabs )
5193 ParaTabStopAdd(m_rWW8Export, rTabStops, nCurrentLeft);
5195 else
5197 tools::Long nStyleLeft = 0;
5198 if ( bTabsRelativeToIndex )
5200 const SvxLRSpaceItem &rStyleLR = ItemGet<SvxLRSpaceItem>(*m_rWW8Export.m_pStyAttr, RES_LR_SPACE);
5201 nStyleLeft = rStyleLR.GetTextLeft();
5204 ParaTabStopDelAdd( m_rWW8Export,
5205 *pStyleTabs, nStyleLeft,
5206 rTabStops, nCurrentLeft);
5210 void AttributeOutputBase::OutputItem( const SfxPoolItem& rHt )
5212 // FIXME maybe use 'item_cast', like 'item_cast<SvxCharHiddenItem>( rHt )'?
5213 switch ( rHt.Which() )
5215 case RES_CHRATR_CASEMAP:
5216 CharCaseMap( static_cast< const SvxCaseMapItem& >( rHt ) );
5217 break;
5218 case RES_CHRATR_COLOR:
5219 CharColor( static_cast< const SvxColorItem& >( rHt ) );
5220 break;
5221 case RES_CHRATR_CONTOUR:
5222 CharContour( static_cast< const SvxContourItem& >( rHt ) );
5223 break;
5224 case RES_CHRATR_CROSSEDOUT:
5225 CharCrossedOut( static_cast< const SvxCrossedOutItem& >( rHt ) );
5226 break;
5227 case RES_CHRATR_ESCAPEMENT:
5228 CharEscapement( static_cast< const SvxEscapementItem& >( rHt ) );
5229 break;
5230 case RES_CHRATR_FONT:
5231 CharFont( static_cast< const SvxFontItem& >( rHt ) );
5232 break;
5233 case RES_CHRATR_FONTSIZE:
5234 CharFontSize( static_cast< const SvxFontHeightItem& >( rHt ) );
5235 break;
5236 case RES_CHRATR_KERNING:
5237 CharKerning( static_cast< const SvxKerningItem& >( rHt ) );
5238 break;
5239 case RES_CHRATR_LANGUAGE:
5240 CharLanguage( static_cast< const SvxLanguageItem& >( rHt ) );
5241 break;
5242 case RES_CHRATR_POSTURE:
5243 CharPosture( static_cast< const SvxPostureItem& >( rHt ) );
5244 break;
5245 case RES_CHRATR_SHADOWED:
5246 CharShadow( static_cast< const SvxShadowedItem& >( rHt ) );
5247 break;
5248 case RES_CHRATR_UNDERLINE:
5249 CharUnderline( static_cast< const SvxUnderlineItem& >( rHt ) );
5250 break;
5251 case RES_CHRATR_WEIGHT:
5252 CharWeight( static_cast< const SvxWeightItem& >( rHt ) );
5253 break;
5254 case RES_CHRATR_AUTOKERN:
5255 CharAutoKern( static_cast< const SvxAutoKernItem& >( rHt ) );
5256 break;
5257 case RES_CHRATR_BLINK:
5258 CharAnimatedText( static_cast< const SvxBlinkItem& >( rHt ) );
5259 break;
5260 case RES_CHRATR_BACKGROUND:
5261 CharBackgroundBase( static_cast< const SvxBrushItem& >( rHt ) );
5262 break;
5264 case RES_CHRATR_CJK_FONT:
5265 CharFontCJK( static_cast< const SvxFontItem& >( rHt ) );
5266 break;
5267 case RES_CHRATR_CJK_FONTSIZE:
5268 CharFontSizeCJK( static_cast< const SvxFontHeightItem& >( rHt ) );
5269 break;
5270 case RES_CHRATR_CJK_LANGUAGE:
5271 CharLanguageCJK( static_cast< const SvxLanguageItem& >( rHt ) );
5272 break;
5273 case RES_CHRATR_CJK_POSTURE:
5274 CharPostureCJK( static_cast< const SvxPostureItem& >( rHt ) );
5275 break;
5276 case RES_CHRATR_CJK_WEIGHT:
5277 CharWeightCJK( static_cast< const SvxWeightItem& >( rHt ) );
5278 break;
5280 case RES_CHRATR_CTL_FONT:
5281 CharFontCTL( static_cast< const SvxFontItem& >( rHt ) );
5282 break;
5283 case RES_CHRATR_CTL_FONTSIZE:
5284 CharFontSizeCTL( static_cast< const SvxFontHeightItem& >( rHt ) );
5285 break;
5286 case RES_CHRATR_CTL_LANGUAGE:
5287 CharLanguageCTL( static_cast< const SvxLanguageItem& >( rHt ) );
5288 break;
5289 case RES_CHRATR_CTL_POSTURE:
5290 CharPostureCTL( static_cast< const SvxPostureItem& >( rHt ) );
5291 break;
5292 case RES_CHRATR_CTL_WEIGHT:
5293 CharWeightCTL( static_cast< const SvxWeightItem& >( rHt ) );
5294 break;
5296 case RES_CHRATR_ROTATE:
5297 CharRotate( static_cast< const SvxCharRotateItem& >( rHt ) );
5298 break;
5299 case RES_CHRATR_EMPHASIS_MARK:
5300 CharEmphasisMark( static_cast< const SvxEmphasisMarkItem& >( rHt ) );
5301 break;
5302 case RES_CHRATR_TWO_LINES:
5303 CharTwoLines( static_cast< const SvxTwoLinesItem& >( rHt ) );
5304 break;
5305 case RES_CHRATR_SCALEW:
5306 CharScaleWidth( static_cast< const SvxCharScaleWidthItem& >( rHt ) );
5307 break;
5308 case RES_CHRATR_RELIEF:
5309 CharRelief( static_cast< const SvxCharReliefItem& >( rHt ) );
5310 break;
5311 case RES_CHRATR_HIDDEN:
5312 CharHidden( static_cast< const SvxCharHiddenItem& >( rHt ) );
5313 break;
5314 case RES_CHRATR_BOX:
5315 FormatCharBorder( static_cast< const SvxBoxItem& >( rHt ) );
5316 break;
5317 case RES_CHRATR_HIGHLIGHT:
5318 CharHighlight( static_cast< const SvxBrushItem& >( rHt ) );
5319 break;
5320 case RES_CHRATR_BIDIRTL:
5321 CharBidiRTL( rHt );
5322 break;
5323 case RES_CHRATR_IDCTHINT:
5324 CharIdctHint( rHt );
5325 break;
5326 case RES_TXTATR_INETFMT:
5327 TextINetFormat( static_cast< const SwFormatINetFormat& >( rHt ) );
5328 break;
5329 case RES_TXTATR_CHARFMT:
5330 TextCharFormat( static_cast< const SwFormatCharFormat& >( rHt ) );
5331 break;
5333 case RES_TXTATR_FIELD:
5334 case RES_TXTATR_ANNOTATION:
5335 case RES_TXTATR_INPUTFIELD:
5336 TextField( static_cast< const SwFormatField& >( rHt ) );
5337 break;
5339 case RES_TXTATR_FLYCNT:
5340 TextFlyContent( static_cast< const SwFormatFlyCnt& >( rHt ) );
5341 break;
5342 case RES_TXTATR_FTN:
5343 TextFootnote( static_cast< const SwFormatFootnote& >( rHt ) );
5344 break;
5346 case RES_PARATR_LINESPACING:
5347 ParaLineSpacing( static_cast< const SvxLineSpacingItem& >( rHt ) );
5348 break;
5349 case RES_PARATR_ADJUST:
5350 ParaAdjust( static_cast< const SvxAdjustItem& >( rHt ) );
5351 break;
5352 case RES_PARATR_SPLIT:
5353 ParaSplit( static_cast< const SvxFormatSplitItem& >( rHt ) );
5354 break;
5355 case RES_PARATR_WIDOWS:
5356 ParaWidows( static_cast< const SvxWidowsItem& >( rHt ) );
5357 break;
5358 case RES_PARATR_TABSTOP:
5359 ParaTabStop( static_cast< const SvxTabStopItem& >( rHt ) );
5360 break;
5361 case RES_PARATR_HYPHENZONE:
5362 ParaHyphenZone( static_cast< const SvxHyphenZoneItem& >( rHt ) );
5363 break;
5364 case RES_PARATR_NUMRULE:
5365 ParaNumRule( static_cast< const SwNumRuleItem& >( rHt ) );
5366 break;
5367 case RES_PARATR_SCRIPTSPACE:
5368 ParaScriptSpace( static_cast< const SfxBoolItem& >( rHt ) );
5369 break;
5370 case RES_PARATR_HANGINGPUNCTUATION:
5371 ParaHangingPunctuation( static_cast< const SfxBoolItem& >( rHt ) );
5372 break;
5373 case RES_PARATR_FORBIDDEN_RULES:
5374 ParaForbiddenRules( static_cast< const SfxBoolItem& >( rHt ) );
5375 break;
5376 case RES_PARATR_VERTALIGN:
5377 ParaVerticalAlign( static_cast< const SvxParaVertAlignItem& >( rHt ) );
5378 break;
5379 case RES_PARATR_SNAPTOGRID:
5380 ParaSnapToGrid( static_cast< const SvxParaGridItem& >( rHt ) );
5381 break;
5383 case RES_FRM_SIZE:
5384 FormatFrameSize( static_cast< const SwFormatFrameSize& >( rHt ) );
5385 break;
5386 case RES_PAPER_BIN:
5387 FormatPaperBin( static_cast< const SvxPaperBinItem& >( rHt ) );
5388 break;
5389 case RES_LR_SPACE:
5390 FormatLRSpace( static_cast< const SvxLRSpaceItem& >( rHt ) );
5391 break;
5392 case RES_UL_SPACE:
5393 FormatULSpace( static_cast< const SvxULSpaceItem& >( rHt ) );
5394 break;
5395 case RES_PAGEDESC:
5396 FormatPageDescription( static_cast< const SwFormatPageDesc& >( rHt ) );
5397 break;
5398 case RES_BREAK:
5399 FormatBreak( static_cast< const SvxFormatBreakItem& >( rHt ) );
5400 break;
5401 case RES_SURROUND:
5402 FormatSurround( static_cast< const SwFormatSurround& >( rHt ) );
5403 break;
5404 case RES_VERT_ORIENT:
5405 FormatVertOrientation( static_cast< const SwFormatVertOrient& >( rHt ) );
5406 break;
5407 case RES_HORI_ORIENT:
5408 FormatHorizOrientation( static_cast< const SwFormatHoriOrient& >( rHt ) );
5409 break;
5410 case RES_ANCHOR:
5411 FormatAnchor( static_cast< const SwFormatAnchor& >( rHt ) );
5412 break;
5413 case RES_BACKGROUND:
5414 FormatBackground( static_cast< const SvxBrushItem& >( rHt ) );
5415 break;
5416 case XATTR_FILLSTYLE:
5417 FormatFillStyle( static_cast< const XFillStyleItem& >( rHt ) );
5418 break;
5419 case XATTR_FILLGRADIENT:
5420 FormatFillGradient( static_cast< const XFillGradientItem& >( rHt ) );
5421 break;
5422 case RES_BOX:
5423 FormatBox( static_cast< const SvxBoxItem& >( rHt ) );
5424 break;
5425 case RES_COL:
5426 FormatColumns( static_cast< const SwFormatCol& >( rHt ) );
5427 break;
5428 case RES_KEEP:
5429 FormatKeep( static_cast< const SvxFormatKeepItem& >( rHt ) );
5430 break;
5431 case RES_TEXTGRID:
5432 FormatTextGrid( static_cast< const SwTextGridItem& >( rHt ) );
5433 break;
5434 case RES_LINENUMBER:
5435 FormatLineNumbering( static_cast< const SwFormatLineNumber& >( rHt ) );
5436 break;
5437 case RES_FRAMEDIR:
5438 FormatFrameDirection( static_cast< const SvxFrameDirectionItem& >( rHt ) );
5439 break;
5440 case RES_PARATR_GRABBAG:
5441 ParaGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
5442 break;
5443 case RES_PARATR_OUTLINELEVEL:
5444 ParaOutlineLevel(static_cast<const SfxUInt16Item&>(rHt));
5445 break;
5446 case RES_CHRATR_GRABBAG:
5447 CharGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
5448 break;
5450 default:
5451 SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() );
5452 break;
5456 void AttributeOutputBase::OutputStyleItemSet( const SfxItemSet& rSet, bool bTestForDefault )
5458 // based on OutputItemSet() from wrt_fn.cxx
5460 const SfxItemPool& rPool = *rSet.GetPool();
5461 const SfxItemSet* pSet = &rSet;
5462 if ( !pSet->Count() )
5464 while ( nullptr != ( pSet = pSet->GetParent() ) && !pSet->Count() )
5467 if ( !pSet )
5468 return;
5471 const SfxPoolItem* pItem;
5472 if ( !pSet->GetParent() )
5474 assert(rSet.Count() && "Was already handled or?");
5475 SfxItemIter aIter( *pSet );
5476 pItem = aIter.GetCurItem();
5477 do {
5478 OutputItem( *pItem );
5479 } while ((pItem = aIter.NextItem()));
5481 else
5483 SfxWhichIter aIter( *pSet );
5484 sal_uInt16 nWhich = aIter.FirstWhich();
5485 while ( nWhich )
5487 if ( SfxItemState::SET == pSet->GetItemState( nWhich, true/*bDeep*/, &pItem ) &&
5488 ( !bTestForDefault ||
5489 nWhich == RES_UL_SPACE ||
5490 nWhich == RES_LR_SPACE ||
5491 *pItem != rPool.GetDefaultItem( nWhich ) ||
5492 ( pSet->GetParent() && *pItem != pSet->GetParent()->Get( nWhich ) ) ) )
5494 OutputItem( *pItem );
5496 nWhich = aIter.NextWhich();
5501 void AttributeOutputBase::FormatCharBorder( const SvxBoxItem& rBox )
5503 // Get one of the borders (if there is any border then in docx also will be)
5504 const SvxBorderLine* pBorderLine = nullptr;
5505 sal_uInt16 nDist = 0;
5506 if( rBox.GetTop() )
5508 pBorderLine = rBox.GetTop();
5509 nDist = rBox.GetDistance( SvxBoxItemLine::TOP );
5511 else if( rBox.GetLeft() )
5513 pBorderLine = rBox.GetLeft();
5514 nDist = rBox.GetDistance( SvxBoxItemLine::LEFT );
5516 else if( rBox.GetBottom() )
5518 pBorderLine = rBox.GetBottom();
5519 nDist = rBox.GetDistance( SvxBoxItemLine::BOTTOM );
5521 else if( rBox.GetRight() )
5523 pBorderLine = rBox.GetRight();
5524 nDist = rBox.GetDistance( SvxBoxItemLine::RIGHT );
5527 // RTF: avoid regressions since RTF doesn't know how to export a border_NONE style-override
5528 if( pBorderLine || GetExport().GetExportFormat() != MSWordExportBase::ExportFormat::RTF )
5530 const SfxPoolItem* pItem = GetExport().HasItem( RES_CHRATR_SHADOW );
5531 const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
5532 const bool bShadow = pBorderLine &&
5533 pShadowItem && pShadowItem->GetLocation() != SvxShadowLocation::NONE &&
5534 pShadowItem->GetWidth() > 0;
5536 CharBorder( pBorderLine, nDist, bShadow );
5541 * This function is used to check if the current SwTextNode (paragraph) has a redline object
5542 * that is attached to the paragraph marker.
5543 * This is done by checking if the range (SwPaM) of the redline is :
5544 * - Start = the last character of the current paragraph
5545 * - End = the first character of the next paragraph
5547 const SwRedlineData* AttributeOutputBase::GetParagraphMarkerRedline( const SwTextNode& rNode, RedlineType aRedlineType)
5549 // ToDo : this is not the most ideal ... should start maybe from 'nCurRedlinePos'
5550 for(SwRangeRedline* pRedl : GetExport().m_rDoc.getIDocumentRedlineAccess().GetRedlineTable())
5552 // Only check redlines that are of type 'Delete'
5553 if ( pRedl->GetRedlineData().GetType() != aRedlineType )
5554 continue;
5556 sal_uLong uStartNodeIndex = pRedl->Start()->nNode.GetIndex();
5557 sal_uLong uEndNodeIndex = pRedl->End()->nNode.GetIndex();
5558 sal_uLong uNodeIndex = rNode.GetIndex();
5560 if( uStartNodeIndex <= uNodeIndex && uNodeIndex < uEndNodeIndex )
5561 return &( pRedl->GetRedlineData() );
5563 return nullptr;
5566 void AttributeOutputBase::CharBackgroundBase( const SvxBrushItem& rBrush )
5568 bool bConvertToShading = SvtFilterOptions::Get().IsCharBackground2Shading();
5569 bool bHasShadingMarker = false;
5571 // Check shading marker
5572 const SfxPoolItem* pItem = GetExport().HasItem(RES_CHRATR_GRABBAG);
5573 if( pItem )
5575 const SfxGrabBagItem aGrabBag = static_cast< const SfxGrabBagItem& >(*pItem);
5576 const std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
5577 auto aIterator = rMap.find("CharShadingMarker");
5578 if( aIterator != rMap.end() )
5580 aIterator->second >>= bHasShadingMarker;
5584 if( bConvertToShading || bHasShadingMarker )
5586 CharBackground(rBrush);
5588 else
5590 CharHighlight(rBrush);
5594 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */