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