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