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