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