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