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