LibreOffice Module sw (master)  1
docxattributeoutput.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 #include <memory>
21 #include "docxattributeoutput.hxx"
22 #include "docxhelper.hxx"
23 #include "docxsdrexport.hxx"
24 #include "docxexportfilter.hxx"
25 #include "docxfootnotes.hxx"
26 #include "writerwordglue.hxx"
27 #include "ww8par.hxx"
28 #include <fmtcntnt.hxx>
29 #include <fmtftn.hxx>
30 #include <fchrfmt.hxx>
31 #include <tgrditem.hxx>
32 #include <fmtruby.hxx>
33 #include <fmtanchr.hxx>
34 #include <breakit.hxx>
35 #include <redline.hxx>
36 #include <unocoll.hxx>
37 #include <unoframe.hxx>
38 #include <textboxhelper.hxx>
39 #include <rdfhelper.hxx>
40 #include "wrtww8.hxx"
41 
43 #include <comphelper/random.hxx>
44 #include <comphelper/string.hxx>
45 #include <comphelper/flagguard.hxx>
46 #include <comphelper/sequence.hxx>
47 #include <oox/token/namespaces.hxx>
48 #include <oox/token/tokens.hxx>
49 #include <oox/export/utils.hxx>
50 #include <oox/mathml/export.hxx>
53 #include <oox/export/vmlexport.hxx>
54 #include <oox/ole/olehelper.hxx>
55 
56 #include <editeng/autokernitem.hxx>
57 #include <editeng/unoprnms.hxx>
58 #include <editeng/fontitem.hxx>
59 #include <editeng/tstpitem.hxx>
60 #include <editeng/spltitem.hxx>
61 #include <editeng/widwitem.hxx>
62 #include <editeng/shaditem.hxx>
63 #include <editeng/brushitem.hxx>
64 #include <editeng/postitem.hxx>
65 #include <editeng/wghtitem.hxx>
66 #include <editeng/kernitem.hxx>
68 #include <editeng/cmapitem.hxx>
69 #include <editeng/udlnitem.hxx>
70 #include <editeng/langitem.hxx>
71 #include <editeng/lspcitem.hxx>
73 #include <editeng/fhgtitem.hxx>
74 #include <editeng/colritem.hxx>
76 #include <editeng/ulspitem.hxx>
77 #include <editeng/contouritem.hxx>
78 #include <editeng/shdditem.hxx>
80 #include <editeng/twolinesitem.hxx>
85 #include <editeng/pgrditem.hxx>
86 #include <editeng/frmdiritem.hxx>
87 #include <editeng/blinkitem.hxx>
89 #include <editeng/editobj.hxx>
90 #include <editeng/keepitem.hxx>
91 #include <editeng/borderline.hxx>
92 #include <svx/xdef.hxx>
93 #include <svx/xfillit0.hxx>
94 #include <svx/xflclit.hxx>
95 #include <svx/xflgrit.hxx>
96 #include <svx/svdouno.hxx>
98 #include <svl/grabbagitem.hxx>
99 #include <sfx2/sfxbasemodel.hxx>
100 #include <tools/datetimeutils.hxx>
101 #include <tools/UnitConversion.hxx>
102 #include <svl/whiter.hxx>
103 #include <rtl/tencinfo.h>
104 #include <sal/log.hxx>
105 #include <sot/exchange.hxx>
106 
107 #include <docufld.hxx>
108 #include <authfld.hxx>
109 #include <flddropdown.hxx>
110 #include <fmtclds.hxx>
111 #include <fmtinfmt.hxx>
112 #include <fmtrowsplt.hxx>
113 #include <fmtline.hxx>
114 #include <ftninfo.hxx>
115 #include <htmltbl.hxx>
116 #include <lineinfo.hxx>
117 #include <ndgrf.hxx>
118 #include <ndole.hxx>
119 #include <ndtxt.hxx>
120 #include <pagedesc.hxx>
121 #include <paratr.hxx>
122 #include <swmodule.hxx>
123 #include <swtable.hxx>
124 #include <txtftn.hxx>
125 #include <fmtautofmt.hxx>
126 #include <docsh.hxx>
127 #include <docary.hxx>
128 #include <fmtclbl.hxx>
131 #include <grfatr.hxx>
132 #include <frmatr.hxx>
133 #include <txtatr.hxx>
134 #include <frameformats.hxx>
135 
136 #include <osl/file.hxx>
137 #include <utility>
139 
140 #include <com/sun/star/i18n/ScriptType.hpp>
141 #include <com/sun/star/i18n/XBreakIterator.hpp>
142 #include <com/sun/star/chart2/XChartDocument.hpp>
143 #include <com/sun/star/drawing/ShadingPattern.hpp>
144 #include <com/sun/star/text/GraphicCrop.hpp>
145 #include <com/sun/star/embed/EmbedStates.hpp>
146 #include <com/sun/star/embed/Aspects.hpp>
147 
148 #include <algorithm>
149 #include <stdarg.h>
150 #include <string_view>
151 
153 #include <unicode/regex.h>
154 
155 using ::editeng::SvxBorderLine;
156 
157 using namespace oox;
158 using namespace docx;
159 using namespace sax_fastparser;
160 using namespace nsSwDocInfoSubType;
161 using namespace sw::util;
162 using namespace ::com::sun::star;
163 using namespace ::com::sun::star::drawing;
164 
165 const sal_Int32 Tag_StartParagraph_1 = 1;
166 const sal_Int32 Tag_StartParagraph_2 = 2;
167 const sal_Int32 Tag_WriteSdtBlock = 3;
168 const sal_Int32 Tag_StartParagraphProperties = 4;
170 const sal_Int32 Tag_StartRun_1 = 6;
171 const sal_Int32 Tag_StartRun_2 = 7;
172 const sal_Int32 Tag_StartRun_3 = 8;
173 const sal_Int32 Tag_EndRun_1 = 9;
174 const sal_Int32 Tag_EndRun_2 = 10;
175 const sal_Int32 Tag_StartRunProperties = 11;
176 const sal_Int32 Tag_InitCollectedRunProperties = 12;
177 //static const sal_Int32 Tag_Redline_1 = 13;
178 const sal_Int32 Tag_Redline_2 = 14;
179 const sal_Int32 Tag_TableDefinition = 15;
180 const sal_Int32 Tag_OutputFlyFrame = 16;
181 const sal_Int32 Tag_StartSection = 17;
182 
183 namespace {
184 
185 class FFDataWriterHelper
186 {
187  ::sax_fastparser::FSHelperPtr m_pSerializer;
188  void writeCommonStart( const OUString& rName,
189  const OUString& rEntryMacro,
190  const OUString& rExitMacro,
191  const OUString& rHelp,
192  const OUString& rHint )
193  {
194  m_pSerializer->startElementNS(XML_w, XML_ffData);
195  m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName);
196  m_pSerializer->singleElementNS(XML_w, XML_enabled);
197  m_pSerializer->singleElementNS(XML_w, XML_calcOnExit, FSNS(XML_w, XML_val), "0");
198 
199  if ( !rEntryMacro.isEmpty() )
200  m_pSerializer->singleElementNS( XML_w, XML_entryMacro,
201  FSNS(XML_w, XML_val), rEntryMacro );
202 
203  if ( !rExitMacro.isEmpty() )
204  m_pSerializer->singleElementNS(XML_w, XML_exitMacro, FSNS(XML_w, XML_val), rExitMacro);
205 
206  if ( !rHelp.isEmpty() )
207  m_pSerializer->singleElementNS( XML_w, XML_helpText,
208  FSNS(XML_w, XML_type), "text",
209  FSNS(XML_w, XML_val), rHelp );
210 
211  if ( !rHint.isEmpty() )
212  m_pSerializer->singleElementNS( XML_w, XML_statusText,
213  FSNS(XML_w, XML_type), "text",
214  FSNS(XML_w, XML_val), rHint );
215 
216  }
217  void writeFinish()
218  {
219  m_pSerializer->endElementNS( XML_w, XML_ffData );
220  }
221 public:
222  explicit FFDataWriterHelper( ::sax_fastparser::FSHelperPtr rSerializer ) : m_pSerializer(std::move( rSerializer )){}
223  void WriteFormCheckbox( const OUString& rName,
224  const OUString& rEntryMacro,
225  const OUString& rExitMacro,
226  const OUString& rHelp,
227  const OUString& rHint,
228  bool bChecked )
229  {
230  writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint );
231  // Checkbox specific bits
232  m_pSerializer->startElementNS(XML_w, XML_checkBox);
233  // currently hardcoding autosize
234  // #TODO check if this defaulted
235  m_pSerializer->startElementNS(XML_w, XML_sizeAuto);
236  m_pSerializer->endElementNS( XML_w, XML_sizeAuto );
237  if ( bChecked )
238  m_pSerializer->singleElementNS(XML_w, XML_checked);
239  m_pSerializer->endElementNS( XML_w, XML_checkBox );
240  writeFinish();
241  }
242 
243  void WriteFormText( const OUString& rName,
244  const OUString& rEntryMacro,
245  const OUString& rExitMacro,
246  const OUString& rHelp,
247  const OUString& rHint,
248  const OUString& rType,
249  const OUString& rDefaultText,
250  sal_uInt16 nMaxLength,
251  const OUString& rFormat )
252  {
253  writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint );
254 
255  m_pSerializer->startElementNS(XML_w, XML_textInput);
256  if ( !rType.isEmpty() )
257  m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), rType);
258  if ( !rDefaultText.isEmpty() )
259  m_pSerializer->singleElementNS(XML_w, XML_default, FSNS(XML_w, XML_val), rDefaultText);
260  if ( nMaxLength )
261  m_pSerializer->singleElementNS( XML_w, XML_maxLength,
262  FSNS(XML_w, XML_val), OString::number(nMaxLength) );
263  if ( !rFormat.isEmpty() )
264  m_pSerializer->singleElementNS(XML_w, XML_format, FSNS(XML_w, XML_val), rFormat);
265  m_pSerializer->endElementNS( XML_w, XML_textInput );
266 
267  writeFinish();
268  }
269 };
270 
271 class FieldMarkParamsHelper
272 {
273  const sw::mark::IFieldmark& mrFieldmark;
274  public:
275  explicit FieldMarkParamsHelper( const sw::mark::IFieldmark& rFieldmark ) : mrFieldmark( rFieldmark ) {}
276  OUString const & getName() const { return mrFieldmark.GetName(); }
277  template < typename T >
278  bool extractParam( const OUString& rKey, T& rResult )
279  {
280  bool bResult = false;
281  if ( mrFieldmark.GetParameters() )
282  {
283  sw::mark::IFieldmark::parameter_map_t::const_iterator it = mrFieldmark.GetParameters()->find( rKey );
284  if ( it != mrFieldmark.GetParameters()->end() )
285  bResult = ( it->second >>= rResult );
286  }
287  return bResult;
288  }
289 };
290 
291 }
292 
293 void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ )
294 {
295  if (bIsRTL)
296  m_pSerializer->singleElementNS(XML_w, XML_rtl, FSNS(XML_w, XML_val), "true");
297 }
298 
300 static bool lcl_isOnelinerSdt(std::u16string_view rName)
301 {
302  return rName == u"Title" || rName == u"Subtitle" || rName == u"Company";
303 }
304 
305 // write a floating table directly to docx without the surrounding frame
307 {
308  const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
309  m_aFloatingTablesOfParagraph.insert(&rFrameFormat);
310  const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
311 
312  sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0;
313  sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0;
314 
315  //Save data here and restore when out of scope
316  ExportDataSaveRestore aDataGuard(GetExport(), nStt, nEnd, pParentFrame);
317 
318  // set a floatingTableFrame AND unset parent frame,
319  // otherwise exporter thinks we are still in a frame
320  m_rExport.SetFloatingTableFrame(pParentFrame);
321  m_rExport.m_pParentFrame = nullptr;
322 
323  GetExport().WriteText();
324 
325  m_rExport.SetFloatingTableFrame(nullptr);
326 }
327 
328 static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput)
329 {
330  const auto& rExport = rDocxAttributeOutput.GetExport();
331  // iterate though all SpzFrameFormats and check whether they are anchored to the current text node
332  for( sal_uInt16 nCnt = rExport.m_rDoc.GetSpzFrameFormats()->size(); nCnt; )
333  {
334  const SwFrameFormat* pFrameFormat = (*rExport.m_rDoc.GetSpzFrameFormats())[ --nCnt ];
335  const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
336  const SwPosition* pPosition = rAnchor.GetContentAnchor();
337 
338  if (!pPosition || ! rExport.m_pCurPam->GetNode().GetTextNode())
339  continue;
340 
341  if (pPosition->nNode != rExport.m_pCurPam->GetNode().GetTextNode()->GetIndex())
342  continue;
343 
344  const SwNodeIndex* pStartNode = pFrameFormat->GetContent().GetContentIdx();
345  if (!pStartNode)
346  continue;
347 
348  SwNodeIndex aStartNode = *pStartNode;
349 
350  // go to the next node (actual content)
351  ++aStartNode;
352 
353  // this has to be a table
354  if (!aStartNode.GetNode().IsTableNode())
355  continue;
356 
357  // go to the end of the table
358  sal_uLong aEndIndex = aStartNode.GetNode().EndOfSectionIndex();
359  // go one deeper
360  aEndIndex++;
361  // this has to be the end of the content
362  if (aEndIndex != pFrameFormat->GetContent().GetContentIdx()->GetNode().EndOfSectionIndex())
363  continue;
364 
365  // check for a grabBag and "TablePosition" attribute -> then we can export the table directly
366  SwTableNode* pTableNode = aStartNode.GetNode().GetTableNode();
367  SwTable& rTable = pTableNode->GetTable();
368  SwFrameFormat* pTableFormat = rTable.GetFrameFormat();
369  const SfxGrabBagItem* pTableGrabBag = pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG);
370  std::map<OUString, css::uno::Any> aTableGrabBag = pTableGrabBag->GetGrabBag();
371  // no grabbag?
372  if (aTableGrabBag.find("TablePosition") == aTableGrabBag.end())
373  continue;
374 
375  // write table to docx
376  ww8::Frame aFrame(*pFrameFormat,*pPosition);
377  rDocxAttributeOutput.WriteFloatingTable(&aFrame);
378  }
379 }
380 
382 {
383  // Paragraphs (in headers/footers/comments/frames etc) can start before another finishes.
384  // So a stack is needed to keep track of each paragraph's status separately.
385  // Complication: Word can't handle nested text boxes, so those need to be collected together.
386  if ( !m_aFramesOfParagraph.size() || !m_nTextFrameLevel )
387  m_aFramesOfParagraph.push(std::vector<ww8::Frame>());
388 
389  // look ahead for floating tables that were put into a frame during import
390  // floating tables in shapes are not supported: exclude this case
391  if (!pTextNodeInfo && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
392  {
394  }
395 
396  if ( m_nColBreakStatus == COLBRK_POSTPONE )
397  m_nColBreakStatus = COLBRK_WRITE;
398 
399  // Output table/table row/table cell starts if needed
400  if ( pTextNodeInfo )
401  {
402  // New cell/row?
403  if ( m_tableReference->m_nTableDepth > 0 && !m_tableReference->m_bTableCellOpen )
404  {
405  ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( pTextNodeInfo->getInnerForDepth( m_tableReference->m_nTableDepth ) );
406  if ( pDeepInner->getCell() == 0 )
407  StartTableRow( pDeepInner );
408 
409  const sal_uInt32 nCell = pDeepInner->getCell();
410  const sal_uInt32 nRow = pDeepInner->getRow();
411 
412  SyncNodelessCells(pDeepInner, nCell, nRow);
413  StartTableCell(pDeepInner, nCell, nRow);
414  }
415 
416  sal_uInt32 nRow = pTextNodeInfo->getRow();
417  sal_uInt32 nCell = pTextNodeInfo->getCell();
418  if (nCell == 0)
419  {
420  // Do we have to start the table?
421  // [If we are at the right depth already, it means that we
422  // continue the table cell]
423  sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
424 
425  if ( nCurrentDepth > m_tableReference->m_nTableDepth )
426  {
427  // Start all the tables that begin here
428  for ( sal_uInt32 nDepth = m_tableReference->m_nTableDepth + 1; nDepth <= nCurrentDepth; ++nDepth )
429  {
430  ww8::WW8TableNodeInfoInner::Pointer_t pInner( pTextNodeInfo->getInnerForDepth( nDepth ) );
431 
432  StartTable( pInner );
433  StartTableRow( pInner );
434 
435  StartTableCell(pInner, 0, nDepth == nCurrentDepth ? nRow : 0);
436  }
437 
438  m_tableReference->m_nTableDepth = nCurrentDepth;
439  }
440  }
441  }
442 
443  // Look up the "sdt end before this paragraph" property early, when it
444  // would normally arrive, it would be too late (would be after the
445  // paragraph start has been written).
446  bool bEndParaSdt = false;
447  if (m_bStartedParaSdt)
448  {
449  SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode();
450  if (pTextNode && pTextNode->GetpSwAttrSet())
451  {
452  const SfxItemSet* pSet = pTextNode->GetpSwAttrSet();
453  if (const SfxPoolItem* pItem = pSet->GetItem(RES_PARATR_GRABBAG))
454  {
455  const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem);
456  const std::map<OUString, css::uno::Any>& rMap = rParaGrabBag.GetGrabBag();
457  bEndParaSdt = m_bStartedParaSdt && rMap.find("ParaSdtEndBefore") != rMap.end();
458  }
459  }
460  }
461  // TODO also avoid multiline paragraphs in those SDT types for shape text
462  bool bOneliner = m_bStartedParaSdt && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias);
463  if (bEndParaSdt || (m_bStartedParaSdt && m_bHadSectPr) || bOneliner)
464  {
465  // This is the common case: "close sdt before the current paragraph" was requested by the next paragraph.
466  EndSdtBlock();
467  m_bStartedParaSdt = false;
468  m_aStartedParagraphSdtPrAlias.clear();
469  }
470  m_bHadSectPr = false;
471 
472  // this mark is used to be able to enclose the paragraph inside a sdr tag.
473  // We will only know if we have to do that later.
474  m_pSerializer->mark(Tag_StartParagraph_1);
475 
476  m_pSerializer->startElementNS(XML_w, XML_p);
477 
478  // postpone the output of the run (we get it before the paragraph
479  // properties, but must write it after them)
480  m_pSerializer->mark(Tag_StartParagraph_2);
481 
482  // no section break in this paragraph yet; can be set in SectionBreak()
483  m_pSectionInfo.reset();
484 
485  m_bParagraphOpened = true;
486  m_bIsFirstParagraph = false;
487 }
488 
489 static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
490 {
491  switch( nOrient )
492  {
493  case text::VertOrientation::CENTER:
494  case text::VertOrientation::LINE_CENTER:
495  return "center";
496  case text::VertOrientation::BOTTOM:
497  return "bottom";
498  case text::VertOrientation::LINE_BOTTOM:
499  return "outside";
500  case text::VertOrientation::TOP:
501  return "top";
502  case text::VertOrientation::LINE_TOP:
503  return "inside";
504  default:
505  return OString();
506  }
507 }
508 
509 static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle)
510 {
511  switch( nOrient )
512  {
513  case text::HoriOrientation::LEFT:
514  return bIsPosToggle ? "inside" : "left";
515  case text::HoriOrientation::INSIDE:
516  return "inside";
517  case text::HoriOrientation::RIGHT:
518  return bIsPosToggle ? "outside" : "right";
519  case text::HoriOrientation::OUTSIDE:
520  return "outside";
521  case text::HoriOrientation::CENTER:
522  case text::HoriOrientation::FULL:
523  return "center";
524  default:
525  return OString();
526  }
527 }
528 
529 static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
530 {
531  switch (nOrientRel)
532  {
533  case text::RelOrientation::PAGE_PRINT_AREA:
534  return "margin";
535  case text::RelOrientation::PAGE_FRAME:
536  return "page";
537  case text::RelOrientation::FRAME:
538  case text::RelOrientation::TEXT_LINE:
539  default:
540  return "text";
541  }
542 }
543 
544 static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)
545 {
546  switch (nOrientRel)
547  {
548  case text::RelOrientation::PAGE_PRINT_AREA:
549  return "margin";
550  case text::RelOrientation::PAGE_FRAME:
551  return "page";
552  case text::RelOrientation::CHAR:
553  case text::RelOrientation::PAGE_RIGHT:
554  case text::RelOrientation::FRAME:
555  default:
556  return "text";
557  }
558 }
559 
560 static void lcl_deleteAndResetTheLists( rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs, OUString& rSdtPrAlias)
561 {
562  if( pSdtPrTokenChildren.is() )
563  pSdtPrTokenChildren.clear();
564  if( pSdtPrDataBindingAttrs.is() )
565  pSdtPrDataBindingAttrs.clear();
566  if (!rSdtPrAlias.isEmpty())
567  rSdtPrAlias.clear();
568 }
569 
570 void DocxAttributeOutput::PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize)
571 {
572  sax_fastparser::FastAttributeList* attrList = FastSerializerHelper::createAttrList();
573 
574  awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(), pFrameFormat->GetVertOrient().GetPos());
575 
576  attrList->add( FSNS( XML_w, XML_w), OString::number(rSize.Width()));
577  attrList->add( FSNS( XML_w, XML_h), OString::number(rSize.Height()));
578 
579  attrList->add( FSNS( XML_w, XML_x), OString::number(aPos.X));
580  attrList->add( FSNS( XML_w, XML_y), OString::number(aPos.Y));
581 
582  sal_Int16 nLeft = pFrameFormat->GetLRSpace().GetLeft();
583  sal_Int16 nRight = pFrameFormat->GetLRSpace().GetRight();
584  sal_Int16 nUpper = pFrameFormat->GetULSpace().GetUpper();
585  sal_Int16 nLower = pFrameFormat->GetULSpace().GetLower();
586 
587  attrList->add(FSNS(XML_w, XML_hSpace), OString::number((nLeft + nRight) / 2));
588  attrList->add(FSNS(XML_w, XML_vSpace), OString::number((nUpper + nLower) / 2));
589 
590  OString relativeFromH = convertToOOXMLHoriOrientRel( pFrameFormat->GetHoriOrient().GetRelationOrient() );
591  OString relativeFromV = convertToOOXMLVertOrientRel( pFrameFormat->GetVertOrient().GetRelationOrient() );
592 
593  switch (pFrameFormat->GetSurround().GetValue())
594  {
595  case css::text::WrapTextMode_NONE:
596  attrList->add( FSNS( XML_w, XML_wrap), "notBeside");
597  break;
598  case css::text::WrapTextMode_DYNAMIC:
599  attrList->add(FSNS(XML_w, XML_wrap), "auto");
600  break;
601  case css::text::WrapTextMode_PARALLEL:
602  default:
603  attrList->add(FSNS(XML_w, XML_wrap), "around");
604  break;
605  }
606  attrList->add( FSNS( XML_w, XML_vAnchor), relativeFromV );
607  attrList->add( FSNS( XML_w, XML_hAnchor), relativeFromH );
608  attrList->add( FSNS( XML_w, XML_hRule), "exact");
609 
610  sax_fastparser::XFastAttributeListRef xAttrList(attrList);
611  m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList );
612 }
613 
615 {
616  uno::Reference< drawing::XShape > xShape;
617  const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
618  if (pSdrObj)
619  xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
620  uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY);
621  uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
622  if (xPropertySet.is())
623  xPropSetInfo = xPropertySet->getPropertySetInfo();
624  uno::Any aFrameProperties ;
625  if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
626  {
627  uno::Sequence< beans::PropertyValue > propList;
628  xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
629  auto pProp = std::find_if(propList.begin(), propList.end(),
630  [](const beans::PropertyValue& rProp) { return rProp.Name == "ParaFrameProperties"; });
631  if (pProp != propList.end())
632  aFrameProperties = pProp->Value;
633  }
634  bool bFrameProperties = false;
635  aFrameProperties >>= bFrameProperties;
636  return bFrameProperties;
637 }
638 
640 {
641  // write the paragraph properties + the run, already in the correct order
642  m_pSerializer->mergeTopMarks(Tag_StartParagraph_2);
643  std::vector< std::shared_ptr <ww8::Frame> > aFramePrTextbox;
644  // Write the anchored frame if any
645  // Word can't handle nested text boxes, so write them on the same level.
646  ++m_nTextFrameLevel;
647  if( m_nTextFrameLevel == 1 && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
648  {
649  comphelper::FlagRestorationGuard aStartedParaSdtGuard(m_bStartedParaSdt, false);
650 
651  assert(!m_pPostponedCustomShape);
652  m_pPostponedCustomShape.reset(new std::vector<PostponedDrawing>);
653 
654  // The for loop can change the size of m_aFramesOfParagraph, so the max size cannot be set in stone before the loop.
655  size_t nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
656  for (size_t nIndex = 0; nIndex < nFrames; ++nIndex)
657  {
658  m_bParagraphFrameOpen = true;
659  ww8::Frame aFrame = m_aFramesOfParagraph.top()[nIndex];
660  const SwFrameFormat& rFrameFormat = aFrame.GetFrameFormat();
661 
662  if (!TextBoxIsFramePr(rFrameFormat) || m_bWritingHeaderFooter)
663  {
664  if (m_bStartedCharSdt)
665  {
666  // Run-level SDT still open? Close it before AlternateContent.
667  EndSdtBlock();
668  m_bStartedCharSdt = false;
669  }
670  m_pSerializer->startElementNS(XML_w, XML_r);
671  m_pSerializer->startElementNS(XML_mc, XML_AlternateContent);
672  m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "wps");
678  SetAlternateContentChoiceOpen( true );
684  ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.m_pTableInfo;
685  //Reset the table infos after saving.
686  m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
687 
693  {
694  DocxTableExportContext aDMLTableExportContext(*this);
695  m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++);
696  }
697  m_pSerializer->endElementNS(XML_mc, XML_Choice);
698  SetAlternateContentChoiceOpen( false );
699 
700  // Reset table infos, otherwise the depth of the cells will be incorrect,
701  // in case the text frame had table(s) and we try to export the
702  // same table second time.
703  m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
704  //reset the tableReference.
705 
706  m_pSerializer->startElementNS(XML_mc, XML_Fallback);
707  {
708  DocxTableExportContext aVMLTableExportContext(*this);
709  m_rExport.SdrExporter().writeVMLTextFrame(&aFrame);
710  }
711  m_rExport.m_pTableInfo = pOldTableInfo;
712 
713  m_pSerializer->endElementNS(XML_mc, XML_Fallback);
714  m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);
715  m_pSerializer->endElementNS( XML_w, XML_r );
716  m_bParagraphFrameOpen = false;
717  }
718  else
719  {
720  std::shared_ptr<ww8::Frame> pFramePr = std::make_shared<ww8::Frame>(aFrame);
721  aFramePrTextbox.push_back(pFramePr);
722  }
723 
724  nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
725  }
726  if (!m_pPostponedCustomShape->empty())
727  {
728  m_pSerializer->startElementNS(XML_w, XML_r);
729  WritePostponedCustomShape();
730  m_pSerializer->endElementNS( XML_w, XML_r );
731  }
732  m_pPostponedCustomShape.reset();
733 
734  if ( m_aFramesOfParagraph.size() )
735  m_aFramesOfParagraph.top().clear();
736 
737  if (!pTextNodeInfoInner)
738  {
739  // Ending a non-table paragraph, clear floating tables before paragraph.
740  m_aFloatingTablesOfParagraph.clear();
741  }
742  }
743 
744  --m_nTextFrameLevel;
745  if ( m_aFramesOfParagraph.size() && !m_nTextFrameLevel )
746  m_aFramesOfParagraph.pop();
747 
748  /* If m_nHyperLinkCount > 0 that means hyperlink tag is not yet closed.
749  * This is due to nested hyperlink tags. So close it before end of paragraph.
750  */
751  if(m_nHyperLinkCount > 0)
752  {
753  for(sal_Int32 nHyperLinkToClose = 0; nHyperLinkToClose < m_nHyperLinkCount; ++nHyperLinkToClose)
754  m_pSerializer->endElementNS( XML_w, XML_hyperlink );
755  m_nHyperLinkCount = 0;
756  }
757 
758  if (m_bStartedCharSdt)
759  {
760  // Run-level SDT still open? Close it now.
761  EndSdtBlock();
762  m_bStartedCharSdt = false;
763  }
764 
765  if (m_bPageBreakAfter)
766  {
767  // tdf#128889 Trailing page break
768  SectionBreak(msword::PageBreak, false);
769  m_bPageBreakAfter = false;
770  }
771 
772  m_pSerializer->endElementNS( XML_w, XML_p );
773  // on export sdt blocks are never nested ATM
774  if( !m_bAnchorLinkedToNode && !m_bStartedParaSdt )
775  WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrTokenAttributes, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias, /*bPara=*/true );
776  else
777  {
778  //These should be written out to the actual Node and not to the anchor.
779  //Clear them as they will be repopulated when the node is processed.
780  m_nParagraphSdtPrToken = 0;
781  m_bParagraphSdtHasId = false;
782  lcl_deleteAndResetTheLists( m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias );
783  }
784 
785  m_pSerializer->mark(Tag_StartParagraph_2);
786 
787  // Write framePr
788  for ( const auto & pFrame : aFramePrTextbox )
789  {
790  DocxTableExportContext aTableExportContext(*this);
791  m_pCurrentFrame = pFrame.get();
792  m_rExport.SdrExporter().writeOnlyTextOfFrame(pFrame.get());
793  m_pCurrentFrame = nullptr;
794  }
795 
796  m_pSerializer->mergeTopMarks(Tag_StartParagraph_2, sax_fastparser::MergeMarks::PREPEND);
797 
798  //sdtcontent is written so Set m_bParagraphHasDrawing to false
799  m_rExport.SdrExporter().setParagraphHasDrawing(false);
800  m_bRunTextIsOn = false;
801  m_pSerializer->mergeTopMarks(Tag_StartParagraph_1);
802 
803  aFramePrTextbox.clear();
804  // Check for end of cell, rows, tables here
805  FinishTableRowCell( pTextNodeInfoInner );
806 
807  if( !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
808  m_bParagraphOpened = false;
809 
810  // Clear bookmarks of the current paragraph
811  m_aBookmarksOfParagraphStart.clear();
812  m_aBookmarksOfParagraphEnd.clear();
813 }
814 
815 void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken,
819  OUString& rSdtPrAlias,
820  bool bPara )
821 {
822  if( nSdtPrToken <= 0 && !pSdtPrDataBindingAttrs.is() )
823  return;
824 
825  // sdt start mark
826  m_pSerializer->mark(Tag_WriteSdtBlock);
827 
828  m_pSerializer->startElementNS(XML_w, XML_sdt);
829 
830  // output sdt properties
831  m_pSerializer->startElementNS(XML_w, XML_sdtPr);
832 
833  if( nSdtPrToken > 0 && pSdtPrTokenChildren.is() )
834  {
835  if (!pSdtPrTokenAttributes.is())
836  m_pSerializer->startElement(nSdtPrToken);
837  else
838  {
839  XFastAttributeListRef xAttrList(pSdtPrTokenAttributes.get());
840  pSdtPrTokenAttributes.clear();
841  m_pSerializer->startElement(nSdtPrToken, xAttrList);
842  }
843 
844  if (nSdtPrToken == FSNS( XML_w, XML_date ) || nSdtPrToken == FSNS( XML_w, XML_docPartObj ) || nSdtPrToken == FSNS( XML_w, XML_docPartList ) || nSdtPrToken == FSNS( XML_w14, XML_checkbox )) {
845  const uno::Sequence<xml::FastAttribute> aChildren = pSdtPrTokenChildren->getFastAttributes();
846  for( const auto& rChild : aChildren )
847  m_pSerializer->singleElement(rChild.Token, FSNS(XML_w, XML_val), rChild.Value);
848  }
849 
850  m_pSerializer->endElement( nSdtPrToken );
851  }
852  else if( (nSdtPrToken > 0) && nSdtPrToken != FSNS( XML_w, XML_id ) && !(m_bRunTextIsOn && m_rExport.SdrExporter().IsParagraphHasDrawing()))
853  {
854  if (!pSdtPrTokenAttributes.is())
855  m_pSerializer->singleElement(nSdtPrToken);
856  else
857  {
858  XFastAttributeListRef xAttrList(pSdtPrTokenAttributes.get());
859  pSdtPrTokenAttributes.clear();
860  m_pSerializer->singleElement(nSdtPrToken, xAttrList);
861  }
862  }
863 
864  if( nSdtPrToken == FSNS( XML_w, XML_id ) || ( bPara && m_bParagraphSdtHasId ) )
865  //Word won't open a document with an empty id tag, we fill it with a random number
866  m_pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val),
867  OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())));
868 
869  if( pSdtPrDataBindingAttrs.is() && !m_rExport.SdrExporter().IsParagraphHasDrawing())
870  {
871  XFastAttributeListRef xAttrList( pSdtPrDataBindingAttrs.get() );
872  pSdtPrDataBindingAttrs.clear();
873  m_pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList);
874  }
875 
876  if (!rSdtPrAlias.isEmpty())
877  m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), rSdtPrAlias);
878 
879  m_pSerializer->endElementNS( XML_w, XML_sdtPr );
880 
881  // sdt contents start tag
882  m_pSerializer->startElementNS(XML_w, XML_sdtContent);
883 
884  // prepend the tags since the sdt start mark before the paragraph
885  m_pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND);
886 
887  // write the ending tags after the paragraph
888  if (bPara)
889  {
890  m_bStartedParaSdt = true;
891  if (m_tableReference->m_bTableCellOpen)
892  m_tableReference->m_bTableCellParaSdtOpen = true;
893  if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
894  m_rExport.SdrExporter().setParagraphSdtOpen(true);
895  }
896  else
897  // Support multiple runs inside a run-level SDT: don't close the SDT block yet.
898  m_bStartedCharSdt = true;
899 
900  // clear sdt status
901  nSdtPrToken = 0;
902  pSdtPrTokenChildren.clear();
903  pSdtPrDataBindingAttrs.clear();
904  rSdtPrAlias.clear();
905 
906 }
907 
909 {
910  m_pSerializer->endElementNS( XML_w, XML_sdtContent );
911  m_pSerializer->endElementNS( XML_w, XML_sdt );
912 }
913 
914 #define MAX_CELL_IN_WORD 62
915 
916 void DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow)
917 {
918  sal_Int32 nOpenCell = lastOpenCell.back();
919  if (nOpenCell != -1 && nOpenCell != nCell && nOpenCell < MAX_CELL_IN_WORD)
920  EndTableCell(nOpenCell);
921 
922  sal_Int32 nClosedCell = lastClosedCell.back();
923  for (sal_Int32 i = nClosedCell+1; i < nCell; ++i)
924  {
925  if (i >= MAX_CELL_IN_WORD)
926  break;
927 
928  if (i == 0)
929  StartTableRow(pInner);
930 
931  StartTableCell(pInner, i, nRow);
932  m_pSerializer->singleElementNS(XML_w, XML_p);
933  EndTableCell(i);
934  }
935 }
936 
938 {
939  if ( !pInner )
940  return;
941 
942  // Where are we in the table
943  sal_uInt32 nRow = pInner->getRow();
944  sal_Int32 nCell = pInner->getCell();
945 
946  InitTableHelper( pInner );
947 
948  // HACK
949  // msoffice seems to have an internal limitation of 63 columns for tables
950  // and refuses to load .docx with more, even though the spec seems to allow that;
951  // so simply if there are more columns, don't close the last one msoffice will handle
952  // and merge the contents of the remaining ones into it (since we don't close the cell
953  // here, following ones will not be opened)
954  const bool limitWorkaround = (nCell >= MAX_CELL_IN_WORD && !pInner->isEndOfLine());
955  const bool bEndCell = pInner->isEndOfCell() && !limitWorkaround;
956  const bool bEndRow = pInner->isEndOfLine();
957 
958  if (bEndCell)
959  {
960  while (pInner->getDepth() < m_tableReference->m_nTableDepth)
961  {
962  //we expect that the higher depth row was closed, and
963  //we are just missing the table close
964  assert(lastOpenCell.back() == -1 && lastClosedCell.back() == -1);
965  EndTable();
966  }
967 
968  SyncNodelessCells(pInner, nCell, nRow);
969 
970  sal_Int32 nClosedCell = lastClosedCell.back();
971  if (nCell == nClosedCell)
972  {
973  //Start missing trailing cell(s)
974  ++nCell;
975  StartTableCell(pInner, nCell, nRow);
976 
977  //Continue on missing next trailing cell(s)
978  ww8::RowSpansPtr xRowSpans = pInner->getRowSpansOfRow();
979  sal_Int32 nRemainingCells = xRowSpans->size() - nCell;
980  for (sal_Int32 i = 1; i < nRemainingCells; ++i)
981  {
982  if (bForceEmptyParagraph)
983  {
984  m_pSerializer->singleElementNS(XML_w, XML_p);
985  }
986 
987  EndTableCell(nCell);
988 
989  StartTableCell(pInner, nCell, nRow);
990  }
991  }
992 
993  if (bForceEmptyParagraph)
994  {
995  m_pSerializer->singleElementNS(XML_w, XML_p);
996  }
997 
998  EndTableCell(nCell);
999  }
1000 
1001  // This is a line end
1002  if (bEndRow)
1003  EndTableRow();
1004 
1005  // This is the end of the table
1006  if (pInner->isFinalEndOfLine())
1007  EndTable();
1008 }
1009 
1011 {
1012  m_pSerializer->singleElementNS(XML_w, XML_p);
1013 }
1014 
1016 {
1017  // output page/section breaks
1018  // Writer can have them at the beginning of a paragraph, or at the end, but
1019  // in docx, we have to output them in the paragraph properties of the last
1020  // paragraph in a section. To get it right, we have to switch to the next
1021  // paragraph, and detect the section breaks there.
1022  SwNodeIndex aNextIndex( rNode, 1 );
1023 
1024  if (rNode.IsTextNode() || rNode.IsSectionNode())
1025  {
1026  if (aNextIndex.GetNode().IsTextNode())
1027  {
1028  const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
1029  m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen);
1030  }
1031  else if (aNextIndex.GetNode().IsTableNode())
1032  {
1033  const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
1034  const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
1035  m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
1036  }
1037  }
1038  else if (rNode.IsEndNode())
1039  {
1040  if (aNextIndex.GetNode().IsTextNode())
1041  {
1042  // Handle section break between a table and a text node following it.
1043  // Also handle section endings
1044  const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
1045  if (rNode.StartOfSectionNode()->IsTableNode() || rNode.StartOfSectionNode()->IsSectionNode())
1046  m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen);
1047  }
1048  else if (aNextIndex.GetNode().IsTableNode())
1049  {
1050  // Handle section break between tables.
1051  const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
1052  const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
1053  m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
1054  }
1055  }
1056 }
1057 
1059 {
1060  m_pSerializer->mark(Tag_StartParagraphProperties);
1061 
1062  m_pSerializer->startElementNS(XML_w, XML_pPr);
1063 
1064  // and output the section break now (if it appeared)
1065  if (m_pSectionInfo && m_rExport.m_nTextTyp == TXT_MAINTEXT)
1066  {
1067  m_rExport.SectionProperties( *m_pSectionInfo );
1068  m_pSectionInfo.reset();
1069  }
1070 
1071  InitCollectedParagraphProperties();
1072 }
1073 
1075 {
1076  m_pParagraphSpacingAttrList.clear();
1077 
1078  // Write the elements in the spec order
1079  static const sal_Int32 aOrder[] =
1080  {
1081  FSNS( XML_w, XML_pStyle ),
1082  FSNS( XML_w, XML_keepNext ),
1083  FSNS( XML_w, XML_keepLines ),
1084  FSNS( XML_w, XML_pageBreakBefore ),
1085  FSNS( XML_w, XML_framePr ),
1086  FSNS( XML_w, XML_widowControl ),
1087  FSNS( XML_w, XML_numPr ),
1088  FSNS( XML_w, XML_suppressLineNumbers ),
1089  FSNS( XML_w, XML_pBdr ),
1090  FSNS( XML_w, XML_shd ),
1091  FSNS( XML_w, XML_tabs ),
1092  FSNS( XML_w, XML_suppressAutoHyphens ),
1093  FSNS( XML_w, XML_kinsoku ),
1094  FSNS( XML_w, XML_wordWrap ),
1095  FSNS( XML_w, XML_overflowPunct ),
1096  FSNS( XML_w, XML_topLinePunct ),
1097  FSNS( XML_w, XML_autoSpaceDE ),
1098  FSNS( XML_w, XML_autoSpaceDN ),
1099  FSNS( XML_w, XML_bidi ),
1100  FSNS( XML_w, XML_adjustRightInd ),
1101  FSNS( XML_w, XML_snapToGrid ),
1102  FSNS( XML_w, XML_spacing ),
1103  FSNS( XML_w, XML_ind ),
1104  FSNS( XML_w, XML_contextualSpacing ),
1105  FSNS( XML_w, XML_mirrorIndents ),
1106  FSNS( XML_w, XML_suppressOverlap ),
1107  FSNS( XML_w, XML_jc ),
1108  FSNS( XML_w, XML_textDirection ),
1109  FSNS( XML_w, XML_textAlignment ),
1110  FSNS( XML_w, XML_textboxTightWrap ),
1111  FSNS( XML_w, XML_outlineLvl ),
1112  FSNS( XML_w, XML_divId ),
1113  FSNS( XML_w, XML_cnfStyle ),
1114  FSNS( XML_w, XML_rPr ),
1115  FSNS( XML_w, XML_sectPr ),
1116  FSNS( XML_w, XML_pPrChange )
1117  };
1118 
1119  // postpone the output so that we can later [in EndParagraphProperties()]
1120  // prepend the properties before the run
1122 }
1123 
1125 {
1126  if ( m_rExport.SdrExporter().getFlyAttrList().is() )
1127  {
1128  XFastAttributeListRef xAttrList( m_rExport.SdrExporter().getFlyAttrList().get() );
1129  m_rExport.SdrExporter().getFlyAttrList().clear();
1130 
1131  m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList );
1132  }
1133 
1134  if ( m_pParagraphSpacingAttrList.is() )
1135  {
1136  XFastAttributeListRef xAttrList( m_pParagraphSpacingAttrList.get() );
1137  m_pParagraphSpacingAttrList.clear();
1138 
1139  m_pSerializer->singleElementNS( XML_w, XML_spacing, xAttrList );
1140  }
1141 
1142  if ( m_pBackgroundAttrList.is() )
1143  {
1144  XFastAttributeListRef xAttrList( m_pBackgroundAttrList.get() );
1145  m_pBackgroundAttrList.clear();
1146 
1147  m_pSerializer->singleElementNS( XML_w, XML_shd, xAttrList );
1148  }
1149 }
1150 
1151 namespace
1152 {
1153 
1155 void lcl_writeParagraphMarkerProperties(DocxAttributeOutput& rAttributeOutput, const SfxItemSet& rParagraphMarkerProperties)
1156 {
1157  SfxWhichIter aIter(rParagraphMarkerProperties);
1158  sal_uInt16 nWhichId = aIter.FirstWhich();
1159  const SfxPoolItem* pItem = nullptr;
1160  // Did we already produce a <w:sz> element?
1161  bool bFontSizeWritten = false;
1162  while (nWhichId)
1163  {
1164  if (rParagraphMarkerProperties.GetItemState(nWhichId, true, &pItem) == SfxItemState::SET)
1165  {
1166  if (isCHRATR(nWhichId) || nWhichId == RES_TXTATR_CHARFMT)
1167  {
1168  // Will this item produce a <w:sz> element?
1169  bool bFontSizeItem = nWhichId == RES_CHRATR_FONTSIZE || nWhichId == RES_CHRATR_CJK_FONTSIZE;
1170  if (!bFontSizeWritten || !bFontSizeItem)
1171  rAttributeOutput.OutputItem(*pItem);
1172  if (bFontSizeItem)
1173  bFontSizeWritten = true;
1174  }
1175  else if (nWhichId == RES_TXTATR_AUTOFMT)
1176  {
1177  const SwFormatAutoFormat* pAutoFormat = static_cast<const SwFormatAutoFormat*>(pItem);
1178  lcl_writeParagraphMarkerProperties(rAttributeOutput, *pAutoFormat->GetStyleHandle());
1179  }
1180  }
1181  nWhichId = aIter.NextWhich();
1182  }
1183 }
1184 
1185 const char *RubyAlignValues[] =
1186 {
1187  "center",
1188  "distributeLetter",
1189  "distributeSpace",
1190  "left",
1191  "right",
1192  "rightVertical"
1193 };
1194 
1195 
1196 const char *lclConvertWW8JCToOOXMLRubyAlign(sal_Int32 nJC)
1197 {
1198  const sal_Int32 nElements = SAL_N_ELEMENTS(RubyAlignValues);
1199  if ( nJC >=0 && nJC < nElements )
1200  return RubyAlignValues[nJC];
1201  return RubyAlignValues[0];
1202 }
1203 
1204 }
1205 
1206 void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted)
1207 {
1208  // Call the 'Redline' function. This will add redline (change-tracking) information that regards to paragraph properties.
1209  // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
1210 
1211  // If there is RedlineData present, call WriteCollectedParagraphProperties() for writing pPr before calling Redline().
1212  // As there will be another pPr for redline and LO might mix both.
1213  if(pRedlineData)
1214  WriteCollectedParagraphProperties();
1215  Redline( pRedlineData );
1216 
1217  WriteCollectedParagraphProperties();
1218 
1219  // Merge the marks for the ordered elements
1220  m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties);
1221 
1222  // Write 'Paragraph Mark' properties
1223  m_pSerializer->startElementNS(XML_w, XML_rPr);
1224  // mark() before paragraph mark properties child elements.
1225  InitCollectedRunProperties();
1226 
1227  // The 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' are used to hold information
1228  // that should be collected by different properties in the core, and are all flushed together
1229  // to the DOCX when the function 'WriteCollectedRunProperties' gets called.
1230  // So we need to store the current status of these lists, so that we can revert back to them when
1231  // we are done exporting the redline attributes.
1232  rtl::Reference<sax_fastparser::FastAttributeList> pFontsAttrList_Original(m_pFontsAttrList);
1233  m_pFontsAttrList.clear();
1234  rtl::Reference<sax_fastparser::FastAttributeList> pEastAsianLayoutAttrList_Original(m_pEastAsianLayoutAttrList);
1235  m_pEastAsianLayoutAttrList.clear();
1236  rtl::Reference<sax_fastparser::FastAttributeList> pCharLangAttrList_Original(m_pCharLangAttrList);
1237  m_pCharLangAttrList.clear();
1238 
1239  lcl_writeParagraphMarkerProperties(*this, rParagraphMarkerProperties);
1240 
1241  // Write the collected run properties that are stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
1242  WriteCollectedRunProperties();
1243 
1244  // Revert back the original values that were stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
1245  m_pFontsAttrList = pFontsAttrList_Original.get();
1246  m_pEastAsianLayoutAttrList = pEastAsianLayoutAttrList_Original.get();
1247  m_pCharLangAttrList = pCharLangAttrList_Original.get();
1248 
1249  if ( pRedlineParagraphMarkerDeleted )
1250  {
1251  StartRedline( pRedlineParagraphMarkerDeleted );
1252  EndRedline( pRedlineParagraphMarkerDeleted );
1253  }
1254  if ( pRedlineParagraphMarkerInserted )
1255  {
1256  StartRedline( pRedlineParagraphMarkerInserted );
1257  EndRedline( pRedlineParagraphMarkerInserted );
1258  }
1259 
1260  // mergeTopMarks() after paragraph mark properties child elements.
1261  m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
1262  m_pSerializer->endElementNS( XML_w, XML_rPr );
1263 
1264  if (!m_bWritingHeaderFooter && m_pCurrentFrame)
1265  {
1266  const SwFrameFormat& rFrameFormat = m_pCurrentFrame->GetFrameFormat();
1267  const SvxBoxItem& rBox = rFrameFormat.GetBox();
1268  if (TextBoxIsFramePr(rFrameFormat))
1269  {
1270  const Size aSize = m_pCurrentFrame->GetSize();
1271  PopulateFrameProperties(&rFrameFormat, aSize);
1272  FormatBox(rBox);
1273  }
1274  }
1275 
1276  m_pSerializer->endElementNS( XML_w, XML_pPr );
1277 
1278  // RDF metadata for this text node.
1279  SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode();
1280  std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", *pTextNode);
1281  if (!aStatements.empty())
1282  {
1283  m_pSerializer->startElementNS(XML_w, XML_smartTag,
1284  FSNS(XML_w, XML_uri), "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
1285  FSNS(XML_w, XML_element), "RDF");
1286  m_pSerializer->startElementNS(XML_w, XML_smartTagPr);
1287  for (const auto& rStatement : aStatements)
1288  m_pSerializer->singleElementNS(XML_w, XML_attr,
1289  FSNS(XML_w, XML_name), rStatement.first,
1290  FSNS(XML_w, XML_val), rStatement.second);
1291  m_pSerializer->endElementNS(XML_w, XML_smartTagPr);
1292  m_pSerializer->endElementNS(XML_w, XML_smartTag);
1293  }
1294 
1295  if ( m_nColBreakStatus == COLBRK_WRITE || m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE )
1296  {
1297  m_pSerializer->startElementNS(XML_w, XML_r);
1298  m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "column");
1299  m_pSerializer->endElementNS( XML_w, XML_r );
1300 
1301  if ( m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE )
1302  m_nColBreakStatus = COLBRK_POSTPONE;
1303  else
1304  m_nColBreakStatus = COLBRK_NONE;
1305  }
1306 
1307  if ( m_bPostponedPageBreak && !m_bWritingHeaderFooter )
1308  {
1309  m_pSerializer->startElementNS(XML_w, XML_r);
1310  m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
1311  m_pSerializer->endElementNS( XML_w, XML_r );
1312 
1313  m_bPostponedPageBreak = false;
1314  }
1315 
1316  // merge the properties _before_ the run (strictly speaking, just
1317  // after the start of the paragraph)
1319 }
1320 
1322 {
1323  m_nStateOfFlyFrame = nStateOfFlyFrame;
1324 }
1325 
1326 void DocxAttributeOutput::SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode )
1327 {
1328  m_bAnchorLinkedToNode = bAnchorLinkedToNode ;
1329 }
1330 
1332 {
1333  m_bPostponedProcessingFly = false ;
1334 }
1335 
1337 {
1338  return m_bPostponedProcessingFly;
1339 }
1340 
1341 void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, bool /*bSingleEmptyRun*/ )
1342 {
1343  // Don't start redline data here, possibly there is a hyperlink later, and
1344  // that has to be started first.
1345  m_pRedlineData = pRedlineData;
1346 
1347  // this mark is used to be able to enclose the run inside a sdr tag.
1348  m_pSerializer->mark(Tag_StartRun_1);
1349 
1350  // postpone the output of the start of a run (there are elements that need
1351  // to be written before the start of the run, but we learn which they are
1352  // _inside_ of the run)
1353  m_pSerializer->mark(Tag_StartRun_2); // let's call it "postponed run start"
1354 
1355  // postpone the output of the text (we get it before the run properties,
1356  // but must write it after them)
1357  m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text"
1358 }
1359 
1360 void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool /*bLastRun*/)
1361 {
1362  int nFieldsInPrevHyperlink = m_nFieldsInHyperlink;
1363  // Reset m_nFieldsInHyperlink if a new hyperlink is about to start
1364  if ( m_pHyperlinkAttrList.is() )
1365  {
1366  m_nFieldsInHyperlink = 0;
1367  }
1368 
1369  // Write field starts
1370  for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin() + nFieldsInPrevHyperlink; pIt != m_Fields.end(); )
1371  {
1372  // Add the fields starts for all but hyperlinks and TOCs
1373  if (pIt->bOpen && pIt->pField && pIt->eType != ww::eFORMDROPDOWN)
1374  {
1375  StartField_Impl( pNode, nPos, *pIt );
1376 
1377  // Remove the field from the stack if only the start has to be written
1378  // Unknown fields should be removed too
1379  if ( !pIt->bClose || ( pIt->eType == ww::eUNKNOWN ) )
1380  {
1381  pIt = m_Fields.erase( pIt );
1382  continue;
1383  }
1384 
1385  if (m_startedHyperlink || m_pHyperlinkAttrList.is())
1386  {
1387  ++m_nFieldsInHyperlink;
1388  }
1389  }
1390  ++pIt;
1391  }
1392 
1393  // write the run properties + the text, already in the correct order
1394  m_pSerializer->mergeTopMarks(Tag_StartRun_3); // merges with "postponed text", see above
1395 
1396  // level down, to be able to prepend the actual run start attribute (just
1397  // before "postponed run start")
1398  m_pSerializer->mark(Tag_EndRun_1); // let's call it "actual run start"
1399  bool bCloseEarlierSDT = false;
1400 
1401  if (m_bEndCharSdt)
1402  {
1403  // This is the common case: "close sdt before the current run" was requested by the next run.
1404 
1405  // if another sdt starts in this run, then wait
1406  // as closing the sdt now, might cause nesting of sdts
1407  if (m_nRunSdtPrToken > 0)
1408  bCloseEarlierSDT = true;
1409  else
1410  EndSdtBlock();
1411  m_bEndCharSdt = false;
1412  m_bStartedCharSdt = false;
1413  }
1414 
1415  if ( m_closeHyperlinkInPreviousRun )
1416  {
1417  if ( m_startedHyperlink )
1418  {
1419  for ( int i = 0; i < nFieldsInPrevHyperlink; i++ )
1420  {
1421  // If fields begin before hyperlink then
1422  // it should end before hyperlink close
1423  EndField_Impl( pNode, nPos, m_Fields.back( ) );
1424  m_Fields.pop_back();
1425  }
1426  m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1427  m_startedHyperlink = false;
1428  m_endPageRef = false;
1429  m_nHyperLinkCount--;
1430  }
1431  m_closeHyperlinkInPreviousRun = false;
1432  }
1433 
1434  // Write the hyperlink and toc fields starts
1435  for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); )
1436  {
1437  // Add the fields starts for hyperlinks, TOCs and index marks
1438  if (pIt->bOpen && (!pIt->pField || pIt->eType == ww::eFORMDROPDOWN))
1439  {
1440  StartRedline( m_pRedlineData );
1441  StartField_Impl( pNode, nPos, *pIt, true );
1442  EndRedline( m_pRedlineData );
1443 
1444  if (m_startedHyperlink)
1445  ++m_nFieldsInHyperlink;
1446 
1447  // Remove the field if no end needs to be written
1448  if (!pIt->bSep)
1449  {
1450  pIt = m_Fields.erase( pIt );
1451  continue;
1452  }
1453  }
1454  if (pIt->bSep && !pIt->pField)
1455  {
1456  CmdEndField_Impl(pNode, nPos, true);
1457  // Remove the field if no end needs to be written
1458  if (!pIt->bClose)
1459  {
1460  pIt = m_Fields.erase( pIt );
1461  continue;
1462  }
1463  }
1464  ++pIt;
1465  }
1466 
1467  // Start the hyperlink after the fields separators or we would generate invalid file
1468  bool newStartedHyperlink(false);
1469  if ( m_pHyperlinkAttrList.is() )
1470  {
1471  // if we are ending a hyperlink and there's another one starting here,
1472  // don't do this, so that the fields are closed further down when
1473  // the end hyperlink is handled, which is more likely to put the end in
1474  // the right place, as far as i can tell (not very far in this muck)
1475  if (!m_closeHyperlinkInThisRun)
1476  {
1477  // end ToX fields that want to end _before_ starting the hyperlink
1478  for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); )
1479  {
1480  if (it->bClose && !it->pField)
1481  {
1482  EndField_Impl( pNode, nPos, *it );
1483  it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1));
1484  }
1485  else
1486  {
1487  ++it;
1488  }
1489  }
1490  }
1491  newStartedHyperlink = true;
1492 
1493  XFastAttributeListRef xAttrList ( m_pHyperlinkAttrList.get() );
1494  m_pHyperlinkAttrList.clear();
1495 
1496  m_pSerializer->startElementNS( XML_w, XML_hyperlink, xAttrList );
1497  m_startedHyperlink = true;
1498  m_nHyperLinkCount++;
1499  }
1500 
1501  // if there is some redlining in the document, output it
1502  StartRedline( m_pRedlineData );
1503 
1504  // XML_r node should be surrounded with bookmark-begin and bookmark-end nodes if it has bookmarks.
1505  // The same is applied for permission ranges.
1506  // But due to unit test "testFdo85542" let's output bookmark-begin with bookmark-end.
1507  DoWriteBookmarksStart(m_rBookmarksStart);
1508  DoWriteBookmarksEnd(m_rBookmarksEnd);
1509  DoWritePermissionsStart();
1510  DoWriteAnnotationMarks();
1511 
1512  if( m_closeHyperlinkInThisRun && m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc"))
1513  {
1514  OUString sToken;
1515  m_pSerializer->startElementNS(XML_w, XML_r);
1516  m_pSerializer->startElementNS(XML_w, XML_rPr);
1517  m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1518  m_pSerializer->endElementNS( XML_w, XML_rPr );
1519  m_pSerializer->startElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "begin");
1520  m_pSerializer->endElementNS( XML_w, XML_fldChar );
1521  m_pSerializer->endElementNS( XML_w, XML_r );
1522 
1523 
1524  m_pSerializer->startElementNS(XML_w, XML_r);
1525  m_pSerializer->startElementNS(XML_w, XML_rPr);
1526  m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1527  m_pSerializer->endElementNS( XML_w, XML_rPr );
1528  sToken = "PAGEREF " + m_hyperLinkAnchor + " \\h"; // '\h' Creates a hyperlink to the bookmarked paragraph.
1529  DoWriteCmd( sToken );
1530  m_pSerializer->endElementNS( XML_w, XML_r );
1531 
1532  // Write the Field separator
1533  m_pSerializer->startElementNS(XML_w, XML_r);
1534  m_pSerializer->startElementNS(XML_w, XML_rPr);
1535  m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1536  m_pSerializer->endElementNS( XML_w, XML_rPr );
1537  m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1538  FSNS( XML_w, XML_fldCharType ), "separate" );
1539  m_pSerializer->endElementNS( XML_w, XML_r );
1540  // At start of every "PAGEREF" field m_endPageRef value should be true.
1541  m_endPageRef = true;
1542  }
1543 
1544  DoWriteBookmarkStartIfExist(nPos);
1545 
1546  m_pSerializer->startElementNS(XML_w, XML_r);
1547  if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is())
1548  {
1549  RunText("\t") ;
1550  }
1551  m_pSerializer->mergeTopMarks(Tag_EndRun_1, sax_fastparser::MergeMarks::PREPEND); // merges with "postponed run start", see above
1552 
1553  if ( !m_sRawText.isEmpty() )
1554  {
1555  RunText( m_sRawText );
1556  m_sRawText.clear();
1557  }
1558 
1559  // write the run start + the run content
1560  m_pSerializer->mergeTopMarks(Tag_StartRun_2); // merges the "actual run start"
1561  // append the actual run end
1562  m_pSerializer->endElementNS( XML_w, XML_r );
1563 
1564  // if there is some redlining in the document, output it
1565  // (except in the case of fields with multiple runs)
1566  EndRedline( m_pRedlineData );
1567 
1568  // enclose in a sdt block, if necessary: if one is already started, then don't do it for now
1569  // (so on export sdt blocks are never nested ATM)
1570  if ( !m_bAnchorLinkedToNode && !m_bStartedCharSdt )
1571  {
1572  rtl::Reference<sax_fastparser::FastAttributeList> pRunSdtPrTokenAttributes;
1573  WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, pRunSdtPrTokenAttributes, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias, /*bPara=*/false );
1574  }
1575  else
1576  {
1577  //These should be written out to the actual Node and not to the anchor.
1578  //Clear them as they will be repopulated when the node is processed.
1579  m_nRunSdtPrToken = 0;
1580  lcl_deleteAndResetTheLists( m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias );
1581  }
1582 
1583  if (bCloseEarlierSDT)
1584  {
1585  m_pSerializer->mark(Tag_EndRun_2);
1586  EndSdtBlock();
1587  m_pSerializer->mergeTopMarks(Tag_EndRun_2, sax_fastparser::MergeMarks::PREPEND);
1588  }
1589 
1590  m_pSerializer->mergeTopMarks(Tag_StartRun_1);
1591 
1592  // XML_r node should be surrounded with permission-begin and permission-end nodes if it has permission.
1593  DoWritePermissionsEnd();
1594 
1595  for (const auto& rpMath : m_aPostponedMaths)
1596  WritePostponedMath(rpMath.pMathObject, rpMath.nMathObjAlignment);
1597  m_aPostponedMaths.clear();
1598 
1599  for (const auto& rpControl : m_aPostponedFormControls)
1600  WritePostponedFormControl(rpControl);
1601  m_aPostponedFormControls.clear();
1602 
1603  WritePostponedActiveXControl(false);
1604 
1605  WritePendingPlaceholder();
1606 
1607  if ( !m_bWritingField )
1608  {
1609  m_pRedlineData = nullptr;
1610  }
1611 
1612  if ( m_closeHyperlinkInThisRun )
1613  {
1614  if ( m_startedHyperlink )
1615  {
1616  if( m_endPageRef )
1617  {
1618  // Hyperlink is started and fldchar "end" needs to be written for PAGEREF
1619  m_pSerializer->startElementNS(XML_w, XML_r);
1620  m_pSerializer->startElementNS(XML_w, XML_rPr);
1621  m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1622  m_pSerializer->endElementNS( XML_w, XML_rPr );
1623  m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1624  FSNS( XML_w, XML_fldCharType ), "end" );
1625  m_pSerializer->endElementNS( XML_w, XML_r );
1626  m_endPageRef = false;
1627  m_hyperLinkAnchor.clear();
1628  }
1629  for ( int i = 0; i < m_nFieldsInHyperlink; i++ )
1630  {
1631  // If fields begin after hyperlink start then
1632  // it should end before hyperlink close
1633  EndField_Impl( pNode, nPos, m_Fields.back( ) );
1634  m_Fields.pop_back();
1635  }
1636  m_nFieldsInHyperlink = 0;
1637 
1638  m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1639  m_startedHyperlink = false;
1640  m_nHyperLinkCount--;
1641  }
1642  m_closeHyperlinkInThisRun = false;
1643  }
1644 
1645  if (!newStartedHyperlink)
1646  {
1647  while ( m_Fields.begin() != m_Fields.end() )
1648  {
1649  EndField_Impl( pNode, nPos, m_Fields.front( ) );
1650  m_Fields.erase( m_Fields.begin( ) );
1651  }
1652  m_nFieldsInHyperlink = 0;
1653  }
1654 
1655  // end ToX fields
1656  for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); )
1657  {
1658  if (it->bClose && !it->pField)
1659  {
1660  EndField_Impl( pNode, nPos, *it );
1661  it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1));
1662  }
1663  else
1664  {
1665  ++it;
1666  }
1667  }
1668 
1669  if ( m_pRedlineData )
1670  {
1671  EndRedline( m_pRedlineData );
1672  m_pRedlineData = nullptr;
1673  }
1674 
1675  DoWriteBookmarksStart(m_rFinalBookmarksStart);
1676  DoWriteBookmarksEnd(m_rFinalBookmarksEnd);
1677  DoWriteBookmarkEndIfExist(nPos);
1678 }
1679 
1680 void DocxAttributeOutput::DoWriteBookmarkTagStart(const OUString & bookmarkName)
1681 {
1682  m_pSerializer->singleElementNS(XML_w, XML_bookmarkStart,
1683  FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId),
1684  FSNS(XML_w, XML_name), BookmarkToWord(bookmarkName));
1685 }
1686 
1687 void DocxAttributeOutput::DoWriteBookmarkTagEnd(const OUString & bookmarkName)
1688 {
1689  const auto nameToIdIter = m_rOpenedBookmarksIds.find(bookmarkName);
1690  if (nameToIdIter != m_rOpenedBookmarksIds.end())
1691  {
1692  const sal_Int32 nId = nameToIdIter->second;
1693 
1694  m_pSerializer->singleElementNS(XML_w, XML_bookmarkEnd,
1695  FSNS(XML_w, XML_id), OString::number(nId));
1696  }
1697 }
1698 
1700 {
1701  auto aRange = m_aBookmarksOfParagraphStart.equal_range(nRunPos);
1702  for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1703  {
1704  DoWriteBookmarkTagStart(aIter->second);
1705  m_rOpenedBookmarksIds[aIter->second] = m_nNextBookmarkId;
1706  m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(aIter->second), RTL_TEXTENCODING_UTF8);
1707  m_nNextBookmarkId++;
1708  }
1709 }
1710 
1712 {
1713  auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nRunPos);
1714  for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1715  {
1716  // Get the id of the bookmark
1717  auto pPos = m_rOpenedBookmarksIds.find(aIter->second);
1718  if (pPos != m_rOpenedBookmarksIds.end())
1719  {
1720  // Output the bookmark
1721  DoWriteBookmarkTagEnd(aIter->second);
1722  m_rOpenedBookmarksIds.erase(aIter->second);
1723  }
1724  }
1725 }
1726 
1728 void DocxAttributeOutput::DoWriteBookmarksStart(std::vector<OUString>& rStarts)
1729 {
1730  for (const OUString & bookmarkName : rStarts)
1731  {
1732  // Output the bookmark
1733  DoWriteBookmarkTagStart(bookmarkName);
1734 
1735  m_rOpenedBookmarksIds[bookmarkName] = m_nNextBookmarkId;
1736  m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(bookmarkName), RTL_TEXTENCODING_UTF8);
1737  m_nNextBookmarkId++;
1738  }
1739  rStarts.clear();
1740 }
1741 
1743 void DocxAttributeOutput::DoWriteBookmarksEnd(std::vector<OUString>& rEnds)
1744 {
1745  for (const OUString & bookmarkName : rEnds)
1746  {
1747  // Get the id of the bookmark
1748  auto pPos = m_rOpenedBookmarksIds.find(bookmarkName);
1749  if (pPos != m_rOpenedBookmarksIds.end())
1750  {
1751  // Output the bookmark
1752  DoWriteBookmarkTagEnd(bookmarkName);
1753 
1754  m_rOpenedBookmarksIds.erase(bookmarkName);
1755  }
1756  }
1757  rEnds.clear();
1758 }
1759 
1760 // For construction of the special bookmark name template for permissions:
1761 // see, PermInsertPosition::createBookmarkName()
1762 //
1763 // Syntax:
1764 // - "permission-for-user:<permission-id>:<permission-user-name>"
1765 // - "permission-for-group:<permission-id>:<permission-group-name>"
1766 //
1767 void DocxAttributeOutput::DoWritePermissionTagStart(const OUString & permission)
1768 {
1769  OUString permissionIdAndName;
1770 
1771  if (permission.startsWith("permission-for-group:", &permissionIdAndName))
1772  {
1773  const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':');
1774  const OUString permissionId = permissionIdAndName.copy(0, sparatorIndex);
1775  const OUString permissionName = permissionIdAndName.copy(sparatorIndex + 1);
1776 
1777  m_pSerializer->singleElementNS(XML_w, XML_permStart,
1778  FSNS(XML_w, XML_id), BookmarkToWord(permissionId),
1779  FSNS(XML_w, XML_edGrp), BookmarkToWord(permissionName));
1780  }
1781  else // if (permission.startsWith("permission-for-user:", &permissionIdAndName))
1782  {
1783  const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':');
1784  const OUString permissionId = permissionIdAndName.copy(0, sparatorIndex);
1785  const OUString permissionName = permissionIdAndName.copy(sparatorIndex + 1);
1786 
1787  m_pSerializer->singleElementNS(XML_w, XML_permStart,
1788  FSNS(XML_w, XML_id), BookmarkToWord(permissionId),
1789  FSNS(XML_w, XML_ed), BookmarkToWord(permissionName));
1790  }
1791 }
1792 
1793 
1794 // For construction of the special bookmark name template for permissions:
1795 // see, PermInsertPosition::createBookmarkName()
1796 //
1797 // Syntax:
1798 // - "permission-for-user:<permission-id>:<permission-user-name>"
1799 // - "permission-for-group:<permission-id>:<permission-group-name>"
1800 //
1801 void DocxAttributeOutput::DoWritePermissionTagEnd(const OUString & permission)
1802 {
1803  OUString permissionIdAndName;
1804 
1805  if (permission.startsWith("permission-for-group:", &permissionIdAndName) ||
1806  permission.startsWith("permission-for-user:", &permissionIdAndName))
1807  {
1808  const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':');
1809  const OUString permissionId = permissionIdAndName.copy(0, sparatorIndex);
1810 
1811  m_pSerializer->singleElementNS(XML_w, XML_permEnd,
1812  FSNS(XML_w, XML_id), BookmarkToWord(permissionId));
1813  }
1814 }
1815 
1818 {
1819  for (const OUString & permission : m_rPermissionsStart)
1820  {
1821  DoWritePermissionTagStart(permission);
1822  }
1823  m_rPermissionsStart.clear();
1824 }
1825 
1828 {
1829  for (const OUString & permission : m_rPermissionsEnd)
1830  {
1831  DoWritePermissionTagEnd(permission);
1832  }
1833  m_rPermissionsEnd.clear();
1834 }
1835 
1837 {
1838  // Write the start annotation marks
1839  for ( const auto & rName : m_rAnnotationMarksStart )
1840  {
1841  // Output the annotation mark
1842  /* Ensure that the existing Annotation Marks are not overwritten
1843  as it causes discrepancy when DocxAttributeOutput::PostitField
1844  refers to this map & while mapping comment id's in document.xml &
1845  comment.xml.
1846  */
1847  if ( m_rOpenedAnnotationMarksIds.end() == m_rOpenedAnnotationMarksIds.find( rName ) )
1848  {
1849  const sal_Int32 nId = m_nNextAnnotationMarkId++;
1850  m_rOpenedAnnotationMarksIds[rName] = nId;
1851  m_pSerializer->singleElementNS( XML_w, XML_commentRangeStart,
1852  FSNS( XML_w, XML_id ), OString::number(nId) );
1853  m_sLastOpenedAnnotationMark = rName;
1854  }
1855  }
1856  m_rAnnotationMarksStart.clear();
1857 
1858  // export the end annotation marks
1859  for ( const auto & rName : m_rAnnotationMarksEnd )
1860  {
1861  // Get the id of the annotation mark
1862  std::map< OString, sal_Int32 >::iterator pPos = m_rOpenedAnnotationMarksIds.find( rName );
1863  if ( pPos != m_rOpenedAnnotationMarksIds.end( ) )
1864  {
1865  const sal_Int32 nId = ( *pPos ).second;
1866  m_pSerializer->singleElementNS( XML_w, XML_commentRangeEnd,
1867  FSNS( XML_w, XML_id ), OString::number(nId) );
1868  m_rOpenedAnnotationMarksIds.erase( rName );
1869 
1870  m_pSerializer->startElementNS(XML_w, XML_r);
1871  m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ),
1872  OString::number(nId) );
1873  m_pSerializer->endElementNS(XML_w, XML_r);
1874  }
1875  }
1876  m_rAnnotationMarksEnd.clear();
1877 }
1878 
1880 {
1881  const ::sw::mark::IFieldmark& rFieldmark = *rInfos.pFieldmark;
1882  FieldMarkParamsHelper params( rFieldmark );
1883 
1884  OUString sEntryMacro;
1885  params.extractParam("EntryMacro", sEntryMacro);
1886  OUString sExitMacro;
1887  params.extractParam("ExitMacro", sExitMacro);
1888  OUString sHelp;
1889  params.extractParam("Help", sHelp);
1890  OUString sHint;
1891  params.extractParam("Hint", sHint); // .docx StatusText
1892  if ( sHint.isEmpty() )
1893  params.extractParam("Description", sHint); // .doc StatusText
1894 
1895  if ( rInfos.eType == ww::eFORMDROPDOWN )
1896  {
1897  uno::Sequence< OUString> vListEntries;
1898  OUString sName, sSelected;
1899 
1900  params.extractParam( ODF_FORMDROPDOWN_LISTENTRY, vListEntries );
1901  if (vListEntries.getLength() > ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT)
1902  vListEntries = uno::Sequence< OUString>(vListEntries.getArray(), ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT);
1903 
1904  sName = params.getName();
1905  sal_Int32 nSelectedIndex = 0;
1906 
1907  if ( params.extractParam( ODF_FORMDROPDOWN_RESULT, nSelectedIndex ) )
1908  {
1909  if (nSelectedIndex < vListEntries.getLength() )
1910  sSelected = vListEntries[ nSelectedIndex ];
1911  }
1912 
1913  GetExport().DoComboBox( sName, OUString(), OUString(), sSelected, vListEntries );
1914  }
1915  else if ( rInfos.eType == ww::eFORMCHECKBOX )
1916  {
1917  OUString sName;
1918  bool bChecked = false;
1919 
1920  params.extractParam( ODF_FORMCHECKBOX_NAME, sName );
1921 
1922  const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark);
1923  if ( pCheckboxFm && pCheckboxFm->IsChecked() )
1924  bChecked = true;
1925 
1926  FFDataWriterHelper ffdataOut( m_pSerializer );
1927  ffdataOut.WriteFormCheckbox( sName, sEntryMacro, sExitMacro, sHelp, sHint, bChecked );
1928  }
1929  else if ( rInfos.eType == ww::eFORMTEXT )
1930  {
1931  OUString sType;
1932  params.extractParam("Type", sType);
1933  OUString sDefaultText;
1934  params.extractParam("Content", sDefaultText);
1935  sal_uInt16 nMaxLength = 0;
1936  params.extractParam("MaxLength", nMaxLength);
1937  OUString sFormat;
1938  params.extractParam("Format", sFormat);
1939  FFDataWriterHelper ffdataOut( m_pSerializer );
1940  ffdataOut.WriteFormText( params.getName(), sEntryMacro, sExitMacro, sHelp, sHint,
1941  sType, sDefaultText, nMaxLength, sFormat );
1942  }
1943 }
1944 
1945 void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang)
1946 {
1947  m_pSerializer->startElementNS(XML_w, XML_sdt);
1948  m_pSerializer->startElementNS(XML_w, XML_sdtPr);
1949 
1950  if(!sFullDate.isEmpty())
1951  m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sFullDate);
1952  else
1953  m_pSerializer->startElementNS(XML_w, XML_date);
1954 
1955  // Replace quotation mark used for marking static strings in date format
1956  OUString sDateFormat1 = sDateFormat.replaceAll("\"", "'");
1957  m_pSerializer->singleElementNS(XML_w, XML_dateFormat,
1958  FSNS(XML_w, XML_val), sDateFormat1);
1959  m_pSerializer->singleElementNS(XML_w, XML_lid,
1960  FSNS(XML_w, XML_val), sLang);
1961  m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs,
1962  FSNS(XML_w, XML_val), "dateTime");
1963  m_pSerializer->singleElementNS(XML_w, XML_calendar,
1964  FSNS(XML_w, XML_val), "gregorian");
1965 
1966  m_pSerializer->endElementNS(XML_w, XML_date);
1967  m_pSerializer->endElementNS(XML_w, XML_sdtPr);
1968 
1969  m_pSerializer->startElementNS(XML_w, XML_sdtContent);
1970 }
1971 
1973 {
1974  m_pSerializer->endElementNS(XML_w, XML_sdtContent);
1975  m_pSerializer->endElementNS(XML_w, XML_sdt);
1976 }
1977 
1979  std::u16string_view rName,
1980  OUString const& rSelected,
1981  uno::Sequence<OUString> const& rListItems)
1982 {
1983  m_pSerializer->startElementNS(XML_w, XML_sdt);
1984  m_pSerializer->startElementNS(XML_w, XML_sdtPr);
1985 
1986  m_pSerializer->singleElementNS(XML_w, XML_alias,
1987  FSNS(XML_w, XML_val), OUStringToOString(rName, RTL_TEXTENCODING_UTF8));
1988 
1989  sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
1990  if (nId == -1)
1991  {
1992  nId = 0;
1993  }
1994 
1995  m_pSerializer->startElementNS(XML_w, XML_dropDownList,
1996  FSNS(XML_w, XML_lastValue), OString::number(nId));
1997 
1998  for (auto const& rItem : rListItems)
1999  {
2000  auto const item(OUStringToOString(rItem, RTL_TEXTENCODING_UTF8));
2001  m_pSerializer->singleElementNS(XML_w, XML_listItem,
2002  FSNS(XML_w, XML_value), item,
2003  FSNS(XML_w, XML_displayText), item);
2004  }
2005 
2006  m_pSerializer->endElementNS(XML_w, XML_dropDownList);
2007  m_pSerializer->endElementNS(XML_w, XML_sdtPr);
2008 
2009  m_pSerializer->startElementNS(XML_w, XML_sdtContent);
2010 }
2011 
2012 void DocxAttributeOutput::WriteSdtDropDownEnd(OUString const& rSelected,
2013  uno::Sequence<OUString> const& rListItems)
2014 {
2015  // note: rSelected might be empty?
2016  sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
2017  if (nId == -1)
2018  {
2019  nId = 0;
2020  }
2021 
2022  // the lastValue only identifies the entry in the list, also export
2023  // currently selected item's displayText as run content (if one exists)
2024  if (rListItems.size())
2025  {
2026  m_pSerializer->startElementNS(XML_w, XML_r);
2027  m_pSerializer->startElementNS(XML_w, XML_t);
2028  m_pSerializer->writeEscaped(rListItems[nId]);
2029  m_pSerializer->endElementNS(XML_w, XML_t);
2030  m_pSerializer->endElementNS(XML_w, XML_r);
2031  }
2032 
2033  WriteSdtEnd();
2034 }
2035 
2036 void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun )
2037 {
2038  if ( rInfos.pField && rInfos.eType == ww::eUNKNOWN )
2039  {
2040  // Expand unsupported fields
2041  RunText( rInfos.pField->GetFieldName() );
2042  }
2043  else if ( rInfos.eType == ww::eFORMDATE )
2044  {
2045  const sw::mark::IDateFieldmark& rFieldmark = dynamic_cast<const sw::mark::IDateFieldmark&>(*rInfos.pFieldmark);
2046  FieldMarkParamsHelper params(rFieldmark);
2047 
2048  OUString sFullDate;
2049  OUString sCurrentDate;
2050  params.extractParam( ODF_FORMDATE_CURRENTDATE, sCurrentDate );
2051  if(!sCurrentDate.isEmpty())
2052  {
2053  sFullDate = sCurrentDate + "T00:00:00Z";
2054  }
2055  else
2056  {
2057  std::pair<bool, double> aResult = rFieldmark.GetCurrentDate();
2058  if(aResult.first)
2059  {
2060  sFullDate = rFieldmark.GetDateInStandardDateFormat(aResult.second) + "T00:00:00Z";
2061  }
2062  }
2063 
2064  OUString sDateFormat;
2065  params.extractParam( ODF_FORMDATE_DATEFORMAT, sDateFormat );
2066  OUString sLang;
2067  params.extractParam( ODF_FORMDATE_DATEFORMAT_LANGUAGE, sLang );
2068 
2069  WriteFormDateStart( sFullDate, sDateFormat, sLang );
2070  }
2071  else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField)
2072  {
2073  assert(!rInfos.pFieldmark);
2074  SwDropDownField const& rField2(*static_cast<SwDropDownField const*>(rInfos.pField.get()));
2075  WriteSdtDropDownStart(rField2.GetName(),
2076  rField2.GetSelectedItem(),
2077  rField2.GetItemSequence());
2078  }
2079  else if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands
2080  {
2081  if ( bWriteRun )
2082  m_pSerializer->startElementNS(XML_w, XML_r);
2083 
2084  if ( rInfos.eType == ww::eFORMDROPDOWN )
2085  {
2086  m_pSerializer->startElementNS( XML_w, XML_fldChar,
2087  FSNS( XML_w, XML_fldCharType ), "begin" );
2088  assert( rInfos.pFieldmark && !rInfos.pField );
2089  WriteFFData(rInfos);
2090  m_pSerializer->endElementNS( XML_w, XML_fldChar );
2091 
2092  if ( bWriteRun )
2093  m_pSerializer->endElementNS( XML_w, XML_r );
2094 
2095  CmdField_Impl( pNode, nPos, rInfos, bWriteRun );
2096  }
2097  else
2098  {
2099  // Write the field start
2100  if ( rInfos.pField && (rInfos.pField->Which() == SwFieldIds::DateTime) && rInfos.pField->GetSubType() & FIXEDFLD )
2101  {
2102  m_pSerializer->startElementNS( XML_w, XML_fldChar,
2103  FSNS( XML_w, XML_fldCharType ), "begin",
2104  FSNS( XML_w, XML_fldLock ), "true" );
2105  }
2106  else
2107  {
2108  m_pSerializer->startElementNS( XML_w, XML_fldChar,
2109  FSNS( XML_w, XML_fldCharType ), "begin" );
2110  }
2111 
2112  if ( rInfos.pFieldmark )
2113  WriteFFData( rInfos );
2114 
2115  m_pSerializer->endElementNS( XML_w, XML_fldChar );
2116 
2117  if ( bWriteRun )
2118  m_pSerializer->endElementNS( XML_w, XML_r );
2119 
2120  // The hyperlinks fields can't be expanded: the value is
2121  // normally in the text run
2122  if ( !rInfos.pField )
2123  CmdField_Impl( pNode, nPos, rInfos, bWriteRun );
2124  else
2125  m_bWritingField = true;
2126  }
2127  }
2128 }
2129 
2130 void DocxAttributeOutput::DoWriteCmd( const OUString& rCmd )
2131 {
2132  OUString sCmd = rCmd.trim();
2133  if (sCmd.startsWith("SEQ"))
2134  {
2135  OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\').trim();
2136  m_aSeqBookmarksNames[sSeqName].push_back(m_sLastOpenedBookmark);
2137  }
2138  // Write the Field command
2139  sal_Int32 nTextToken = XML_instrText;
2140  if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete )
2141  nTextToken = XML_delInstrText;
2142 
2143  m_pSerializer->startElementNS(XML_w, nTextToken);
2144  m_pSerializer->writeEscaped( rCmd );
2145  m_pSerializer->endElementNS( XML_w, nTextToken );
2146 
2147 }
2148 
2149 void DocxAttributeOutput::CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun )
2150 {
2151  // Write the Field instruction
2152  if ( bWriteRun )
2153  {
2154  bool bWriteCombChars(false);
2155  m_pSerializer->startElementNS(XML_w, XML_r);
2156 
2157  if (rInfos.eType == ww::eEQ)
2158  bWriteCombChars = true;
2159 
2160  DoWriteFieldRunProperties( pNode, nPos, bWriteCombChars );
2161  }
2162 
2163  sal_Int32 nIdx { rInfos.sCmd.isEmpty() ? -1 : 0 };
2164  while ( nIdx >= 0 )
2165  {
2166  OUString sToken = rInfos.sCmd.getToken( 0, '\t', nIdx );
2167  if ( rInfos.eType == ww::eCREATEDATE
2168  || rInfos.eType == ww::eSAVEDATE
2169  || rInfos.eType == ww::ePRINTDATE
2170  || rInfos.eType == ww::eDATE
2171  || rInfos.eType == ww::eTIME )
2172  {
2173  sToken = sToken.replaceAll("NNNN", "dddd");
2174  sToken = sToken.replaceAll("NN", "ddd");
2175  }
2176  else if ( rInfos.eType == ww::eEquals )
2177  {
2178  // Use original OOXML formula, if it exists and its conversion hasn't been changed
2179  bool bIsChanged = true;
2180  if ( pNode->GetTableBox() )
2181  {
2183  {
2184  OUString sActualFormula = sToken.trim();
2185  const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
2186  std::map<OUString, uno::Any>::const_iterator aStoredFormula = rGrabBag.find("CellFormulaConverted");
2187  if ( aStoredFormula != rGrabBag.end() && sActualFormula.indexOf('=') == 0 &&
2188  sActualFormula.copy(1).trim() == aStoredFormula->second.get<OUString>().trim() )
2189  {
2190  aStoredFormula = rGrabBag.find("CellFormula");
2191  if ( aStoredFormula != rGrabBag.end() )
2192  {
2193  sToken = " =" + aStoredFormula->second.get<OUString>();
2194  bIsChanged = false;
2195  }
2196  }
2197  }
2198  }
2199 
2200  if ( bIsChanged )
2201  {
2202  UErrorCode nErr(U_ZERO_ERROR);
2203  icu::UnicodeString sInput(sToken.getStr());
2204  // remove < and > around cell references, e.g. <A1> to A1, <A1:B2> to A1:B2
2205  icu::RegexMatcher aMatcher("<([A-Z]{1,3}[0-9]+(:[A-Z]{1,3}[0-9]+)?)>", sInput, 0, nErr);
2206  sInput = aMatcher.replaceAll(icu::UnicodeString("$1"), nErr);
2207  // convert MEAN to AVERAGE
2208  icu::RegexMatcher aMatcher2("\\bMEAN\\b", sInput, UREGEX_CASE_INSENSITIVE, nErr);
2209  sToken = aMatcher2.replaceAll(icu::UnicodeString("AVERAGE"), nErr).getTerminatedBuffer();
2210  }
2211  }
2212 
2213  // Write the Field command
2214  DoWriteCmd( sToken );
2215 
2216  // Replace tabs by </instrText><tab/><instrText>
2217  if ( nIdx > 0 ) // Is another token expected?
2218  RunText( "\t" );
2219  }
2220 
2221  if ( bWriteRun )
2222  {
2223  m_pSerializer->endElementNS( XML_w, XML_r );
2224  }
2225 }
2226 
2228  sal_Int32 const nPos, bool const bWriteRun)
2229 {
2230  // Write the Field separator
2231  if ( bWriteRun )
2232  {
2233  m_pSerializer->startElementNS(XML_w, XML_r);
2234  DoWriteFieldRunProperties( pNode, nPos );
2235  }
2236 
2237  m_pSerializer->singleElementNS( XML_w, XML_fldChar,
2238  FSNS( XML_w, XML_fldCharType ), "separate" );
2239 
2240  if ( bWriteRun )
2241  {
2242  m_pSerializer->endElementNS( XML_w, XML_r );
2243  }
2244 }
2245 
2279 void DocxAttributeOutput::DoWriteFieldRunProperties( const SwTextNode * pNode, sal_Int32 nPos, bool bWriteCombChars)
2280 {
2281  if (! pNode)
2282  {
2283  // nothing to do
2284  return;
2285  }
2286 
2287  m_bPreventDoubleFieldsHandling = true;
2288 
2289  {
2290  m_pSerializer->startElementNS(XML_w, XML_rPr);
2291 
2292  // 1. output webHidden flag
2293  if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
2294  {
2295  m_pSerializer->singleElementNS(XML_w, XML_webHidden);
2296  }
2297 
2298  // 2. find all active character properties
2299  SwWW8AttrIter aAttrIt( m_rExport, *pNode );
2300  aAttrIt.OutAttr( nPos, bWriteCombChars );
2301 
2302  // 3. write the character properties
2303  WriteCollectedRunProperties();
2304 
2305  m_pSerializer->endElementNS( XML_w, XML_rPr );
2306  }
2307 
2308  m_bPreventDoubleFieldsHandling = false;
2309 }
2310 
2311 void DocxAttributeOutput::EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos )
2312 {
2313  if (rInfos.eType == ww::eFORMDATE)
2314  {
2315  WriteSdtEnd();
2316  return;
2317  }
2318  if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField)
2319  {
2320  // write selected item from End not Start to ensure that any bookmarks
2321  // precede it
2322  SwDropDownField const& rField(*static_cast<SwDropDownField const*>(rInfos.pField.get()));
2323  WriteSdtDropDownEnd(rField.GetSelectedItem(), rField.GetItemSequence());
2324  return;
2325  }
2326 
2327  // The command has to be written before for the hyperlinks
2328  if ( rInfos.pField )
2329  {
2330  CmdField_Impl( pNode, nPos, rInfos, true );
2331  CmdEndField_Impl( pNode, nPos, true );
2332  }
2333 
2334  // Write the bookmark start if any
2335  if ( !m_sFieldBkm.isEmpty() )
2336  {
2337  DoWriteBookmarkTagStart(m_sFieldBkm);
2338  }
2339 
2340  if (rInfos.pField ) // For hyperlinks and TOX
2341  {
2342  // Write the Field latest value
2343  m_pSerializer->startElementNS(XML_w, XML_r);
2344  DoWriteFieldRunProperties( pNode, nPos );
2345 
2346  OUString sExpand;
2347  if(rInfos.eType == ww::eCITATION)
2348  {
2349  sExpand = static_cast<SwAuthorityField const*>(rInfos.pField.get())
2350  ->ExpandCitation(AUTH_FIELD_TITLE, nullptr);
2351  }
2352  else if(rInfos.eType != ww::eFORMDROPDOWN)
2353  {
2354  sExpand = rInfos.pField->ExpandField(true, nullptr);
2355  }
2356  // newlines embedded in fields are 0x0B in MSO and 0x0A for us
2357  RunText(sExpand.replace(0x0A, 0x0B));
2358 
2359  m_pSerializer->endElementNS( XML_w, XML_r );
2360  }
2361 
2362  // Write the bookmark end if any
2363  if ( !m_sFieldBkm.isEmpty() )
2364  {
2365  DoWriteBookmarkTagEnd(m_sFieldBkm);
2366 
2367  m_nNextBookmarkId++;
2368  }
2369 
2370  // Write the Field end
2371  if ( rInfos.bClose )
2372  {
2373  m_bWritingField = false;
2374  m_pSerializer->startElementNS(XML_w, XML_r);
2375  DoWriteFieldRunProperties( pNode, nPos );
2376  m_pSerializer->singleElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "end");
2377  m_pSerializer->endElementNS( XML_w, XML_r );
2378  }
2379  // Write the ref field if a bookmark had to be set and the field
2380  // should be visible
2381  if ( !rInfos.pField )
2382  return;
2383 
2384  sal_uInt16 nSubType = rInfos.pField->GetSubType( );
2385  bool bIsSetField = rInfos.pField->GetTyp( )->Which( ) == SwFieldIds::SetExp;
2386  bool bShowRef = bIsSetField && ( nSubType & nsSwExtendedSubType::SUB_INVISIBLE ) == 0;
2387 
2388  if ( m_sFieldBkm.isEmpty() || !bShowRef )
2389  return;
2390 
2391  // Write the field beginning
2392  m_pSerializer->startElementNS(XML_w, XML_r);
2393  m_pSerializer->singleElementNS( XML_w, XML_fldChar,
2394  FSNS( XML_w, XML_fldCharType ), "begin" );
2395  m_pSerializer->endElementNS( XML_w, XML_r );
2396 
2397  rInfos.sCmd = FieldString( ww::eREF );
2398  rInfos.sCmd += "\"";
2399  rInfos.sCmd += m_sFieldBkm;
2400  rInfos.sCmd += "\" ";
2401 
2402  // Clean the field bookmark data to avoid infinite loop
2403  m_sFieldBkm = OUString( );
2404 
2405  // Write the end of the field
2406  EndField_Impl( pNode, nPos, rInfos );
2407 }
2408 
2410 {
2411  // postpone the output so that we can later [in EndRunProperties()]
2412  // prepend the properties before the text
2413  m_pSerializer->mark(Tag_StartRunProperties);
2414 
2415  m_pSerializer->startElementNS(XML_w, XML_rPr);
2416 
2417  if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
2418  {
2419  m_pSerializer->singleElementNS(XML_w, XML_webHidden);
2420  }
2421  InitCollectedRunProperties();
2422 
2423  assert( !m_pPostponedGraphic );
2424  m_pPostponedGraphic.reset(new std::vector<PostponedGraphic>);
2425 
2426  assert( !m_pPostponedDiagrams );
2427  m_pPostponedDiagrams.reset(new std::vector<PostponedDiagram>);
2428 
2429  assert(!m_pPostponedDMLDrawings);
2430  m_pPostponedDMLDrawings.reset(new std::vector<PostponedDrawing>);
2431 
2432  assert( !m_pPostponedOLEs );
2433  m_pPostponedOLEs.reset(new std::vector<PostponedOLE>);
2434 }
2435 
2437 {
2438  m_pFontsAttrList = nullptr;
2439  m_pEastAsianLayoutAttrList = nullptr;
2440  m_pCharLangAttrList = nullptr;
2441 
2442  // Write the elements in the spec order
2443  static const sal_Int32 aOrder[] =
2444  {
2445  FSNS( XML_w, XML_rStyle ),
2446  FSNS( XML_w, XML_rFonts ),
2447  FSNS( XML_w, XML_b ),
2448  FSNS( XML_w, XML_bCs ),
2449  FSNS( XML_w, XML_i ),
2450  FSNS( XML_w, XML_iCs ),
2451  FSNS( XML_w, XML_caps ),
2452  FSNS( XML_w, XML_smallCaps ),
2453  FSNS( XML_w, XML_strike ),
2454  FSNS( XML_w, XML_dstrike ),
2455  FSNS( XML_w, XML_outline ),
2456  FSNS( XML_w, XML_shadow ),
2457  FSNS( XML_w, XML_emboss ),
2458  FSNS( XML_w, XML_imprint ),
2459  FSNS( XML_w, XML_noProof ),
2460  FSNS( XML_w, XML_snapToGrid ),
2461  FSNS( XML_w, XML_vanish ),
2462  FSNS( XML_w, XML_webHidden ),
2463  FSNS( XML_w, XML_color ),
2464  FSNS( XML_w, XML_spacing ),
2465  FSNS( XML_w, XML_w ),
2466  FSNS( XML_w, XML_kern ),
2467  FSNS( XML_w, XML_position ),
2468  FSNS( XML_w, XML_sz ),
2469  FSNS( XML_w, XML_szCs ),
2470  FSNS( XML_w, XML_highlight ),
2471  FSNS( XML_w, XML_u ),
2472  FSNS( XML_w, XML_effect ),
2473  FSNS( XML_w, XML_bdr ),
2474  FSNS( XML_w, XML_shd ),
2475  FSNS( XML_w, XML_fitText ),
2476  FSNS( XML_w, XML_vertAlign ),
2477  FSNS( XML_w, XML_rtl ),
2478  FSNS( XML_w, XML_cs ),
2479  FSNS( XML_w, XML_em ),
2480  FSNS( XML_w, XML_lang ),
2481  FSNS( XML_w, XML_eastAsianLayout ),
2482  FSNS( XML_w, XML_specVanish ),
2483  FSNS( XML_w, XML_oMath ),
2484  FSNS( XML_w, XML_rPrChange ),
2485  FSNS( XML_w, XML_del ),
2486  FSNS( XML_w14, XML_glow ),
2487  FSNS( XML_w14, XML_shadow ),
2488  FSNS( XML_w14, XML_reflection ),
2489  FSNS( XML_w14, XML_textOutline ),
2490  FSNS( XML_w14, XML_textFill ),
2491  FSNS( XML_w14, XML_scene3d ),
2492  FSNS( XML_w14, XML_props3d ),
2493  FSNS( XML_w14, XML_ligatures ),
2494  FSNS( XML_w14, XML_numForm ),
2495  FSNS( XML_w14, XML_numSpacing ),
2496  FSNS( XML_w14, XML_stylisticSets ),
2497  FSNS( XML_w14, XML_cntxtAlts ),
2498  };
2499 
2500  // postpone the output so that we can later [in EndParagraphProperties()]
2501  // prepend the properties before the run
2503 }
2504 
2505 namespace
2506 {
2507 
2508 struct NameToId
2509 {
2510  OUString maName;
2511  sal_Int32 maId;
2512 };
2513 
2514 const NameToId constNameToIdMapping[] =
2515 {
2516  { OUString("glow"), FSNS( XML_w14, XML_glow ) },
2517  { OUString("shadow"), FSNS( XML_w14, XML_shadow ) },
2518  { OUString("reflection"), FSNS( XML_w14, XML_reflection ) },
2519  { OUString("textOutline"), FSNS( XML_w14, XML_textOutline ) },
2520  { OUString("textFill"), FSNS( XML_w14, XML_textFill ) },
2521  { OUString("scene3d"), FSNS( XML_w14, XML_scene3d ) },
2522  { OUString("props3d"), FSNS( XML_w14, XML_props3d ) },
2523  { OUString("ligatures"), FSNS( XML_w14, XML_ligatures ) },
2524  { OUString("numForm"), FSNS( XML_w14, XML_numForm ) },
2525  { OUString("numSpacing"), FSNS( XML_w14, XML_numSpacing ) },
2526  { OUString("stylisticSets"),FSNS( XML_w14, XML_stylisticSets ) },
2527  { OUString("cntxtAlts"), FSNS( XML_w14, XML_cntxtAlts ) },
2528 
2529  { OUString("val"), FSNS( XML_w14, XML_val ) },
2530  { OUString("rad"), FSNS( XML_w14, XML_rad ) },
2531  { OUString("blurRad"), FSNS( XML_w14, XML_blurRad ) },
2532  { OUString("stA"), FSNS( XML_w14, XML_stA ) },
2533  { OUString("stPos"), FSNS( XML_w14, XML_stPos ) },
2534  { OUString("endA"), FSNS( XML_w14, XML_endA ) },
2535  { OUString("endPos"), FSNS( XML_w14, XML_endPos ) },
2536  { OUString("dist"), FSNS( XML_w14, XML_dist ) },
2537  { OUString("dir"), FSNS( XML_w14, XML_dir ) },
2538  { OUString("fadeDir"), FSNS( XML_w14, XML_fadeDir ) },
2539  { OUString("sx"), FSNS( XML_w14, XML_sx ) },
2540  { OUString("sy"), FSNS( XML_w14, XML_sy ) },
2541  { OUString("kx"), FSNS( XML_w14, XML_kx ) },
2542  { OUString("ky"), FSNS( XML_w14, XML_ky ) },
2543  { OUString("algn"), FSNS( XML_w14, XML_algn ) },
2544  { OUString("w"), FSNS( XML_w14, XML_w ) },
2545  { OUString("cap"), FSNS( XML_w14, XML_cap ) },
2546  { OUString("cmpd"), FSNS( XML_w14, XML_cmpd ) },
2547  { OUString("pos"), FSNS( XML_w14, XML_pos ) },
2548  { OUString("ang"), FSNS( XML_w14, XML_ang ) },
2549  { OUString("scaled"), FSNS( XML_w14, XML_scaled ) },
2550  { OUString("path"), FSNS( XML_w14, XML_path ) },
2551  { OUString("l"), FSNS( XML_w14, XML_l ) },
2552  { OUString("t"), FSNS( XML_w14, XML_t ) },
2553  { OUString("r"), FSNS( XML_w14, XML_r ) },
2554  { OUString("b"), FSNS( XML_w14, XML_b ) },
2555  { OUString("lim"), FSNS( XML_w14, XML_lim ) },
2556  { OUString("prst"), FSNS( XML_w14, XML_prst ) },
2557  { OUString("rig"), FSNS( XML_w14, XML_rig ) },
2558  { OUString("lat"), FSNS( XML_w14, XML_lat ) },
2559  { OUString("lon"), FSNS( XML_w14, XML_lon ) },
2560  { OUString("rev"), FSNS( XML_w14, XML_rev ) },
2561  { OUString("h"), FSNS( XML_w14, XML_h ) },
2562  { OUString("extrusionH"), FSNS( XML_w14, XML_extrusionH ) },
2563  { OUString("contourW"), FSNS( XML_w14, XML_contourW ) },
2564  { OUString("prstMaterial"), FSNS( XML_w14, XML_prstMaterial ) },
2565  { OUString("id"), FSNS( XML_w14, XML_id ) },
2566 
2567  { OUString("schemeClr"), FSNS( XML_w14, XML_schemeClr ) },
2568  { OUString("srgbClr"), FSNS( XML_w14, XML_srgbClr ) },
2569  { OUString("tint"), FSNS( XML_w14, XML_tint ) },
2570  { OUString("shade"), FSNS( XML_w14, XML_shade ) },
2571  { OUString("alpha"), FSNS( XML_w14, XML_alpha ) },
2572  { OUString("hueMod"), FSNS( XML_w14, XML_hueMod ) },
2573  { OUString("sat"), FSNS( XML_w14, XML_sat ) },
2574  { OUString("satOff"), FSNS( XML_w14, XML_satOff ) },
2575  { OUString("satMod"), FSNS( XML_w14, XML_satMod ) },
2576  { OUString("lum"), FSNS( XML_w14, XML_lum ) },
2577  { OUString("lumOff"), FSNS( XML_w14, XML_lumOff ) },
2578  { OUString("lumMod"), FSNS( XML_w14, XML_lumMod ) },
2579  { OUString("noFill"), FSNS( XML_w14, XML_noFill ) },
2580  { OUString("solidFill"), FSNS( XML_w14, XML_solidFill ) },
2581  { OUString("gradFill"), FSNS( XML_w14, XML_gradFill ) },
2582  { OUString("gsLst"), FSNS( XML_w14, XML_gsLst ) },
2583  { OUString("gs"), FSNS( XML_w14, XML_gs ) },
2584  { OUString("pos"), FSNS( XML_w14, XML_pos ) },
2585  { OUString("lin"), FSNS( XML_w14, XML_lin ) },
2586  { OUString("path"), FSNS( XML_w14, XML_path ) },
2587  { OUString("fillToRect"), FSNS( XML_w14, XML_fillToRect ) },
2588  { OUString("prstDash"), FSNS( XML_w14, XML_prstDash ) },
2589  { OUString("round"), FSNS( XML_w14, XML_round ) },
2590  { OUString("bevel"), FSNS( XML_w14, XML_bevel ) },
2591  { OUString("miter"), FSNS( XML_w14, XML_miter ) },
2592  { OUString("camera"), FSNS( XML_w14, XML_camera ) },
2593  { OUString("lightRig"), FSNS( XML_w14, XML_lightRig ) },
2594  { OUString("rot"), FSNS( XML_w14, XML_rot ) },
2595  { OUString("bevelT"), FSNS( XML_w14, XML_bevelT ) },
2596  { OUString("bevelB"), FSNS( XML_w14, XML_bevelB ) },
2597  { OUString("extrusionClr"), FSNS( XML_w14, XML_extrusionClr ) },
2598  { OUString("contourClr"), FSNS( XML_w14, XML_contourClr ) },
2599  { OUString("styleSet"), FSNS( XML_w14, XML_styleSet ) },
2600 };
2601 
2602 std::optional<sal_Int32> lclGetElementIdForName(std::u16string_view rName)
2603 {
2604  for (auto const & i : constNameToIdMapping)
2605  {
2606  if (rName == i.maName)
2607  {
2608  return i.maId;
2609  }
2610  }
2611  return std::optional<sal_Int32>();
2612 }
2613 
2614 void lclProcessRecursiveGrabBag(sal_Int32 aElementId, const css::uno::Sequence<css::beans::PropertyValue>& rElements, sax_fastparser::FSHelperPtr const & pSerializer)
2615 {
2616  css::uno::Sequence<css::beans::PropertyValue> aAttributes;
2617  FastAttributeList* pAttributes = FastSerializerHelper::createAttrList();
2618 
2619  for (const auto& rElement : rElements)
2620  {
2621  if (rElement.Name == "attributes")
2622  {
2623  rElement.Value >>= aAttributes;
2624  }
2625  }
2626 
2627  for (const auto& rAttribute : std::as_const(aAttributes))
2628  {
2629  uno::Any aAny = rAttribute.Value;
2630  OString aValue;
2631 
2632  if(aAny.getValueType() == cppu::UnoType<sal_Int32>::get())
2633  {
2634  aValue = OString::number(aAny.get<sal_Int32>());
2635  }
2636  else if(aAny.getValueType() == cppu::UnoType<OUString>::get())
2637  {
2638  aValue = OUStringToOString(aAny.get<OUString>(), RTL_TEXTENCODING_ASCII_US);
2639  }
2640 
2641  std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rAttribute.Name);
2642  if(aSubElementId)
2643  pAttributes->add(*aSubElementId, aValue.getStr());
2644  }
2645 
2646  XFastAttributeListRef xAttributesList( pAttributes );
2647 
2648  pSerializer->startElement(aElementId, xAttributesList);
2649 
2650  for (const auto& rElement : rElements)
2651  {
2652  css::uno::Sequence<css::beans::PropertyValue> aSumElements;
2653 
2654  std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rElement.Name);
2655  if(aSubElementId)
2656  {
2657  rElement.Value >>= aSumElements;
2658  lclProcessRecursiveGrabBag(*aSubElementId, aSumElements, pSerializer);
2659  }
2660  }
2661 
2662  pSerializer->endElement(aElementId);
2663 }
2664 
2665 }
2666 
2668 {
2669  // Write all differed properties
2670  if ( m_pFontsAttrList.is() )
2671  {
2672  XFastAttributeListRef xAttrList( m_pFontsAttrList.get() );
2673  m_pFontsAttrList.clear();
2674  m_pSerializer->singleElementNS( XML_w, XML_rFonts, xAttrList );
2675  }
2676 
2677  if ( m_pColorAttrList.is() )
2678  {
2679  XFastAttributeListRef xAttrList( m_pColorAttrList.get() );
2680 
2681  m_pSerializer->singleElementNS( XML_w, XML_color, xAttrList );
2682  }
2683 
2684  if ( m_pEastAsianLayoutAttrList.is() )
2685  {
2686  XFastAttributeListRef xAttrList( m_pEastAsianLayoutAttrList.get() );
2687  m_pEastAsianLayoutAttrList.clear();
2688  m_pSerializer->singleElementNS( XML_w, XML_eastAsianLayout, xAttrList );
2689  }
2690 
2691  if ( m_pCharLangAttrList.is() )
2692  {
2693  XFastAttributeListRef xAttrList( m_pCharLangAttrList.get() );
2694  m_pCharLangAttrList.clear();
2695  m_pSerializer->singleElementNS( XML_w, XML_lang, xAttrList );
2696  }
2697 
2698  if (m_nCharTransparence != 0 && m_pColorAttrList && m_aTextEffectsGrabBag.empty())
2699  {
2700  const char* pVal = nullptr;
2701  m_pColorAttrList->getAsChar(FSNS(XML_w, XML_val), pVal);
2702  if (OString("auto") != pVal)
2703  {
2704  m_pSerializer->startElementNS(XML_w14, XML_textFill);
2705  m_pSerializer->startElementNS(XML_w14, XML_solidFill);
2706  m_pSerializer->startElementNS(XML_w14, XML_srgbClr, FSNS(XML_w14, XML_val), pVal);
2707  sal_Int32 nTransparence = m_nCharTransparence * oox::drawingml::MAX_PERCENT / 255.0;
2708  m_pSerializer->singleElementNS(XML_w14, XML_alpha, FSNS(XML_w14, XML_val), OString::number(nTransparence));
2709  m_pSerializer->endElementNS(XML_w14, XML_srgbClr);
2710  m_pSerializer->endElementNS(XML_w14, XML_solidFill);
2711  m_pSerializer->endElementNS(XML_w14, XML_textFill);
2712  m_nCharTransparence = 0;
2713  }
2714  }
2715  m_pColorAttrList.clear();
2716  for (const beans::PropertyValue & i : m_aTextEffectsGrabBag)
2717  {
2718  std::optional<sal_Int32> aElementId = lclGetElementIdForName(i.Name);
2719  if(aElementId)
2720  {
2721  uno::Sequence<beans::PropertyValue> aGrabBagSeq;
2722  i.Value >>= aGrabBagSeq;
2723  lclProcessRecursiveGrabBag(*aElementId, aGrabBagSeq, m_pSerializer);
2724  }
2725  }
2726  m_aTextEffectsGrabBag.clear();
2727 }
2728 
2730 {
2731  // Call the 'Redline' function. This will add redline (change-tracking) information that regards to run properties.
2732  // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
2733  Redline( pRedlineData );
2734 
2735  WriteCollectedRunProperties();
2736 
2737  // Merge the marks for the ordered elements
2738  m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
2739 
2740  m_pSerializer->endElementNS( XML_w, XML_rPr );
2741 
2742  // write footnotes/endnotes if we have any
2743  FootnoteEndnoteReference();
2744 
2745  // merge the properties _before_ the run text (strictly speaking, just
2746  // after the start of the run)
2747  m_pSerializer->mergeTopMarks(Tag_StartRunProperties, sax_fastparser::MergeMarks::PREPEND);
2748 
2749  WritePostponedGraphic();
2750 
2751  WritePostponedDiagram();
2752  //We need to write w:drawing tag after the w:rPr.
2753  WritePostponedChart();
2754 
2755  //We need to write w:pict tag after the w:rPr.
2756  WritePostponedDMLDrawing();
2757 
2758  WritePostponedOLE();
2759 
2760  WritePostponedActiveXControl(true);
2761 }
2762 
2764 {
2765  if (!pSdrObj)
2766  return;
2767 
2768  uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY_THROW);
2769  uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
2770  if( !xPropSet.is() )
2771  return;
2772 
2773  uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
2774  uno::Sequence< beans::PropertyValue > aGrabBag;
2775  if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
2776  {
2777  xPropSet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
2778  }
2779  else if(xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
2780  {
2781  xPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
2782  }
2783 
2784  auto pProp = std::find_if(aGrabBag.begin(), aGrabBag.end(),
2785  [this](const beans::PropertyValue& rProp) {
2786  return "SdtEndBefore" == rProp.Name && m_bStartedCharSdt && !m_bEndCharSdt; });
2787  if (pProp != aGrabBag.end())
2788  pProp->Value >>= m_bEndCharSdt;
2789 }
2790 
2792 {
2793  for (const auto & rPostponedDiagram : *m_pPostponedGraphic)
2794  FlyFrameGraphic(rPostponedDiagram.grfNode, rPostponedDiagram.size,
2795  nullptr, nullptr,
2796  rPostponedDiagram.pSdrObj);
2797  m_pPostponedGraphic.reset();
2798 }
2799 
2801 {
2802  for( const auto & rPostponedDiagram : *m_pPostponedDiagrams )
2803  m_rExport.SdrExporter().writeDiagram(rPostponedDiagram.object,
2804  *rPostponedDiagram.frame, m_anchorId++);
2805  m_pPostponedDiagrams.reset();
2806 }
2807 
2809 {
2810  if( m_footnoteEndnoteRefTag == 0 )
2811  return false;
2812 
2813  // output the character style for MS Word's benefit
2814  const SwEndNoteInfo& rInfo = m_footnoteEndnoteRefTag == XML_footnoteRef ?
2815  m_rExport.m_rDoc.GetFootnoteInfo() : m_rExport.m_rDoc.GetEndNoteInfo();
2816  const SwCharFormat* pCharFormat = rInfo.GetCharFormat( m_rExport.m_rDoc );
2817  if ( pCharFormat )
2818  {
2819  const OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat)));
2820  m_pSerializer->startElementNS(XML_w, XML_rPr);
2821  m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
2822  m_pSerializer->endElementNS( XML_w, XML_rPr );
2823  }
2824 
2825  if (m_footnoteCustomLabel.isEmpty())
2826  m_pSerializer->singleElementNS(XML_w, m_footnoteEndnoteRefTag);
2827  else
2828  RunText(m_footnoteCustomLabel);
2829  m_footnoteEndnoteRefTag = 0;
2830  return true;
2831 }
2832 
2839 static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextToken,
2840  const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true )
2841 {
2842  const sal_Unicode *pBegin = rBegin;
2843 
2844  // skip one character after the end
2845  if ( bMove )
2846  rBegin = pEnd + 1;
2847 
2848  if ( pBegin >= pEnd )
2849  return false; // we want to write at least one character
2850 
2851  // we have to add 'preserve' when starting/ending with space
2852  if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' )
2853  {
2854  pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve");
2855  }
2856  else
2857  pSerializer->startElementNS(XML_w, nTextToken);
2858 
2859  pSerializer->writeEscaped( std::u16string_view( pBegin, pEnd - pBegin ) );
2860 
2861  pSerializer->endElementNS( XML_w, nTextToken );
2862 
2863  return true;
2864 }
2865 
2866 void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/ )
2867 {
2868  if( m_closeHyperlinkInThisRun )
2869  {
2870  m_closeHyperlinkInPreviousRun = true;
2871  }
2872  m_bRunTextIsOn = true;
2873  // one text can be split into more <w:t>blah</w:t>'s by line breaks etc.
2874  const sal_Unicode *pBegin = rText.getStr();
2875  const sal_Unicode *pEnd = pBegin + rText.getLength();
2876 
2877  // the text run is usually XML_t, with the exception of the deleted text
2878  sal_Int32 nTextToken = XML_t;
2879  if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete )
2880  nTextToken = XML_delText;
2881 
2882  sal_Unicode prevUnicode = *pBegin;
2883 
2884  for ( const sal_Unicode *pIt = pBegin; pIt < pEnd; ++pIt )
2885  {
2886  switch ( *pIt )
2887  {
2888  case 0x09: // tab
2889  impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2890  m_pSerializer->singleElementNS(XML_w, XML_tab);
2891  prevUnicode = *pIt;
2892  break;
2893  case 0x0b: // line break
2894  {
2895  if (impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ) || prevUnicode < 0x0020)
2896  {
2897  m_pSerializer->singleElementNS(XML_w, XML_br);
2898  prevUnicode = *pIt;
2899  }
2900  }
2901  break;
2902  case 0x1E: //non-breaking hyphen
2903  impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2904  m_pSerializer->singleElementNS(XML_w, XML_noBreakHyphen);
2905  prevUnicode = *pIt;
2906  break;
2907  case 0x1F: //soft (on demand) hyphen
2908  impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2909  m_pSerializer->singleElementNS(XML_w, XML_softHyphen);
2910  prevUnicode = *pIt;
2911  break;
2912  default:
2913  if ( *pIt < 0x0020 ) // filter out the control codes
2914  {
2915  impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2916  SAL_INFO("sw.ww8", "Ignored control code in a text run: " << unsigned(*pIt) );
2917  }
2918  prevUnicode = *pIt;
2919  break;
2920  }
2921  }
2922 
2923  impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false );
2924 }
2925 
2926 void DocxAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding /*eCharSet*/)
2927 {
2928  m_sRawText = rText;
2929 }
2930 
2931 void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby )
2932 {
2933  WW8Ruby aWW8Ruby( rNode, rRuby, GetExport() );
2934  SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRuby( const SwTextNode& rNode, const SwFormatRuby& rRuby )" );
2935  EndRun( &rNode, nPos ); // end run before starting ruby to avoid nested runs, and overlap
2936  assert(!m_closeHyperlinkInThisRun); // check that no hyperlink overlaps ruby
2937  assert(!m_closeHyperlinkInPreviousRun);
2938  m_pSerializer->startElementNS(XML_w, XML_r);
2939  m_pSerializer->startElementNS(XML_w, XML_ruby);
2940  m_pSerializer->startElementNS(XML_w, XML_rubyPr);
2941 
2942  m_pSerializer->singleElementNS( XML_w, XML_rubyAlign,
2943  FSNS( XML_w, XML_val ), lclConvertWW8JCToOOXMLRubyAlign(aWW8Ruby.GetJC()) );
2944  sal_uInt32 nHps = (aWW8Ruby.GetRubyHeight() + 5) / 10;
2945  sal_uInt32 nHpsBaseText = (aWW8Ruby.GetBaseHeight() + 5) / 10;
2946  m_pSerializer->singleElementNS(XML_w, XML_hps, FSNS(XML_w, XML_val), OString::number(nHps));
2947 
2948  m_pSerializer->singleElementNS( XML_w, XML_hpsRaise,
2949  FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) );
2950 
2951  m_pSerializer->singleElementNS( XML_w, XML_hpsBaseText,
2952  FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) );
2953 
2954  lang::Locale aLocale( SwBreakIt::Get()->GetLocale(
2955  rNode.GetLang( nPos ) ) );
2956  OUString sLang( LanguageTag::convertToBcp47( aLocale) );
2957  m_pSerializer->singleElementNS(XML_w, XML_lid, FSNS(XML_w, XML_val), sLang);
2958 
2959  m_pSerializer->endElementNS( XML_w, XML_rubyPr );
2960 
2961  m_pSerializer->startElementNS(XML_w, XML_rt);
2962  StartRun( nullptr, nPos );
2963  StartRunProperties( );
2964 
2965  if (rRuby.GetTextRuby() && rRuby.GetTextRuby()->GetCharFormat())
2966  {
2967  const SwCharFormat* pFormat = rRuby.GetTextRuby()->GetCharFormat();
2968  sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(rRuby.GetText(), 0);
2969  sal_uInt16 nWhichFont = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONT : RES_CHRATR_CJK_FONT;
2970  sal_uInt16 nWhichFontSize = (nScript == i18n::ScriptType::LATIN) ? RES_CHRATR_FONTSIZE : RES_CHRATR_CJK_FONTSIZE;
2971 
2972  CharFont(ItemGet<SvxFontItem>(*pFormat, nWhichFont));
2973  CharFontSize(ItemGet<SvxFontHeightItem>(*pFormat, nWhichFontSize));
2974  CharFontSize(ItemGet<SvxFontHeightItem>(*pFormat, RES_CHRATR_CTL_FONTSIZE));
2975  }
2976 
2977  EndRunProperties( nullptr );
2978  RunText( rRuby.GetText( ) );
2979  EndRun( &rNode, nPos );
2980  m_pSerializer->endElementNS( XML_w, XML_rt );
2981 
2982  m_pSerializer->startElementNS(XML_w, XML_rubyBase);
2983  StartRun( nullptr, nPos );
2984 }
2985 
2986 void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos)
2987 {
2988  SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRuby()" );
2989  EndRun( &rNode, nPos );
2990  m_pSerializer->endElementNS( XML_w, XML_rubyBase );
2991  m_pSerializer->endElementNS( XML_w, XML_ruby );
2992  m_pSerializer->endElementNS( XML_w, XML_r );
2993  StartRun(nullptr, nPos); // open Run again so OutputTextNode loop can close it
2994 }
2995 
2996 bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark )
2997 {
2998  bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark );
2999 
3000  if ( !pMark->isEmpty() )
3001  {
3002  OUString sURL = *pLinkURL;
3003 
3004  if ( bBookMarkOnly )
3005  sURL = FieldString( ww::eHYPERLINK );
3006  else
3007  sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\"";
3008 
3009  sURL += " \\l \"" + *pMark + "\"";
3010 
3011  if ( !rTarget.isEmpty() )
3012  sURL += " \\n " + rTarget;
3013 
3014  *pLinkURL = sURL;
3015  }
3016 
3017  return bBookMarkOnly;
3018 }
3019 
3020 void DocxAttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos )
3021 {
3022  m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName));
3023  m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName));
3024 }
3025 
3026 bool DocxAttributeOutput::StartURL( const OUString& rUrl, const OUString& rTarget )
3027 {
3028  OUString sMark;
3029  OUString sUrl;
3030 
3031  bool bBookmarkOnly = AnalyzeURL( rUrl, rTarget, &sUrl, &sMark );
3032 
3033  m_hyperLinkAnchor = sMark;
3034 
3035  if ( !sMark.isEmpty() && !bBookmarkOnly )
3036  {
3037  m_rExport.OutputField( nullptr, ww::eHYPERLINK, sUrl );
3038  }
3039  else
3040  {
3041  // Output a hyperlink XML element
3042  m_pHyperlinkAttrList = FastSerializerHelper::createAttrList();
3043 
3044  if ( !bBookmarkOnly )
3045  {
3046  OString sId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
3048  sUrl, true ), RTL_TEXTENCODING_UTF8 );
3049 
3050  m_pHyperlinkAttrList->add( FSNS( XML_r, XML_id), sId.getStr());
3051  }
3052  else
3053  {
3054  // Is this a link to a sequence? Then try to replace that with a
3055  // normal bookmark, as Word won't understand our special
3056  // <seqname>!<index>|sequence syntax.
3057  if (sMark.endsWith("|sequence"))
3058  {
3059  sal_Int32 nPos = sMark.indexOf('!');
3060  if (nPos != -1)
3061  {
3062  // Extract <seqname>, the field instruction text has the name quoted.
3063  OUString aSequenceName = sMark.copy(0, nPos);
3064  // Extract <index>.
3065  sal_uInt32 nIndex = sMark.copy(nPos + 1, sMark.getLength() - nPos - sizeof("|sequence")).toUInt32();
3066  std::map<OUString, std::vector<OString> >::iterator it = m_aSeqBookmarksNames.find(aSequenceName);
3067  if (it != m_aSeqBookmarksNames.end())
3068  {
3069  std::vector<OString>& rNames = it->second;
3070  if (rNames.size() > nIndex)
3071  // We know the bookmark name for this sequence and this index, do the replacement.
3072  sMark = OStringToOUString(rNames[nIndex], RTL_TEXTENCODING_UTF8);
3073  }
3074  }
3075  }
3076  // Spaces are prohibited in bookmark name.
3077  sMark = sMark.replace(' ', '_');
3078  m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ),
3079  OUStringToOString( sMark, RTL_TEXTENCODING_UTF8 ).getStr( ) );
3080  }
3081 
3082  if ( !rTarget.isEmpty() )
3083  {
3084  OString soTarget = OUStringToOString( rTarget, RTL_TEXTENCODING_UTF8 );
3085  m_pHyperlinkAttrList->add(FSNS( XML_w, XML_tgtFrame ), soTarget.getStr());
3086  }
3087  }
3088 
3089  return true;
3090 }
3091 
3093 {
3094  m_closeHyperlinkInThisRun = true;
3095  if(m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc"))
3096  {
3097  m_endPageRef = true;
3098  }
3099  return true;
3100 }
3101 
3102 void DocxAttributeOutput::FieldVanish( const OUString& rText, ww::eField eType )
3103 {
3104  WriteField_Impl( nullptr, eType, rText, FieldFlags::All );
3105 }
3106 
3107 // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
3108 // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
3109 // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
3111 {
3112  if ( !pRedlineData )
3113  return;
3114 
3115  OString aId( OString::number( pRedlineData->GetSeqNo() ) );
3116  const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
3117  OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) );
3118 
3119  switch( pRedlineData->GetType() )
3120  {
3121  case RedlineType::Insert:
3122  break;
3123 
3124  case RedlineType::Delete:
3125  break;
3126 
3127  case RedlineType::Format:
3128  m_pSerializer->startElementNS( XML_w, XML_rPrChange,
3129  FSNS( XML_w, XML_id ), aId,
3130  FSNS( XML_w, XML_author ), rAuthor,
3131  FSNS( XML_w, XML_date ), aDate );
3132 
3133  m_pSerializer->endElementNS( XML_w, XML_rPrChange );
3134  break;
3135 
3136  case RedlineType::ParagraphFormat:
3137  m_pSerializer->startElementNS( XML_w, XML_pPrChange,
3138  FSNS( XML_w, XML_id ), aId,
3139  FSNS( XML_w, XML_author ), rAuthor,
3140  FSNS( XML_w, XML_date ), aDate );
3141 
3142  // Check if there is any extra data stored in the redline object
3143  if (pRedlineData->GetExtraData())
3144  {
3145  const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData();
3146  const SwRedlineExtraData_FormatColl* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
3147 
3148  // Check if the extra data is of type 'formatting changes'
3149  if (pFormattingChanges)
3150  {
3151  // Get the item set that holds all the changes properties
3152  const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
3153  const OUString & sParaStyleName = pFormattingChanges->GetFormatName();
3154  if (pChangesSet || !sParaStyleName.isEmpty())
3155  {
3156  m_pSerializer->mark(Tag_Redline_2);
3157 
3158  m_pSerializer->startElementNS(XML_w, XML_pPr);
3159 
3160  OString sStyleName = MSWordStyles::CreateStyleId( sParaStyleName );
3161  if ( !sStyleName.isEmpty() )
3162  m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), sStyleName);
3163 
3164  // The 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' are used to hold information
3165  // that should be collected by different properties in the core, and are all flushed together
3166  // to the DOCX when the function 'WriteCollectedParagraphProperties' gets called.
3167  // So we need to store the current status of these lists, so that we can revert back to them when
3168  // we are done exporting the redline attributes.
3169  rtl::Reference<sax_fastparser::FastAttributeList> pFlyAttrList_Original(m_rExport.SdrExporter().getFlyAttrList());
3170  m_rExport.SdrExporter().getFlyAttrList().clear();
3171  rtl::Reference<sax_fastparser::FastAttributeList> pParagraphSpacingAttrList_Original(m_pParagraphSpacingAttrList);
3172  m_pParagraphSpacingAttrList.clear();
3173 
3174  // Output the redline item set
3175  if (pChangesSet)
3176  m_rExport.OutputItemSet( *pChangesSet, true, false, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
3177 
3178  // Write the collected paragraph properties that are stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
3179  WriteCollectedParagraphProperties();
3180 
3181  // Revert back the original values that were stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
3182  m_rExport.SdrExporter().getFlyAttrList() = pFlyAttrList_Original;
3183  m_pParagraphSpacingAttrList = pParagraphSpacingAttrList_Original;
3184 
3185  m_pSerializer->endElementNS( XML_w, XML_pPr );
3186 
3187  m_pSerializer->mergeTopMarks(Tag_Redline_2, sax_fastparser::MergeMarks::PREPEND);
3188  }
3189  }
3190  }
3191  m_pSerializer->endElementNS( XML_w, XML_pPrChange );
3192  break;
3193 
3194  default:
3195  SAL_WARN("sw.ww8", "Unhandled redline type for export " << SwRedlineTypeToOUString(pRedlineData->GetType()));
3196  break;
3197  }
3198 }
3199 
3200 // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
3201 // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
3202 // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
3204 {
3205  if ( !pRedlineData )
3206  return;
3207 
3208  // FIXME check if it's necessary to travel over the Next()'s in pRedlineData
3209 
3210  OString aId( OString::number( m_nRedlineId++ ) );
3211 
3212  const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
3213  OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
3214 
3215  OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) );
3216 
3217  switch ( pRedlineData->GetType() )
3218  {
3219  case RedlineType::Insert:
3220  m_pSerializer->startElementNS( XML_w, XML_ins,
3221  FSNS( XML_w, XML_id ), aId,
3222  FSNS( XML_w, XML_author ), aAuthor,
3223  FSNS( XML_w, XML_date ), aDate );
3224  break;
3225 
3226  case RedlineType::Delete:
3227  m_pSerializer->startElementNS( XML_w, XML_del,
3228  FSNS( XML_w, XML_id ), aId,
3229  FSNS( XML_w, XML_author ), aAuthor,
3230  FSNS( XML_w, XML_date ), aDate );
3231  break;
3232 
3233  case RedlineType::Format:
3234  SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRedline()" );
3235  break;
3236  default:
3237  break;
3238  }
3239 }
3240 
3242 {
3243  if ( !pRedlineData || m_bWritingField )
3244  return;
3245 
3246  switch ( pRedlineData->GetType() )
3247  {
3248  case RedlineType::Insert:
3249  m_pSerializer->endElementNS( XML_w, XML_ins );
3250  break;
3251 
3252  case RedlineType::Delete:
3253  m_pSerializer->endElementNS( XML_w, XML_del );
3254  break;
3255 
3256  case RedlineType::Format:
3257  SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRedline()" );
3258  break;
3259  default:
3260  break;
3261  }
3262 }
3263 
3264 void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t )
3265 {
3266  SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle )" );
3267 }
3268 
3269 void DocxAttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
3270 {
3271  OString aStyleId(m_rExport.m_pStyles->GetStyleId(nStyle));
3272 
3273  m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), aStyleId);
3274 }
3275 
3276 static void impl_borderLine( FSHelperPtr const & pSerializer, sal_Int32 elementToken, const SvxBorderLine* pBorderLine, sal_uInt16 nDist,
3277  bool bWriteShadow, const table::BorderLine2* rStyleProps = nullptr )
3278 {
3279  // Compute val attribute value
3280  // Can be one of:
3281  // single, double,
3282  // basicWideOutline, basicWideInline
3283  // OOXml also supports those types of borders, but we'll try to play with the first ones.
3284  // thickThinMediumGap, thickThinLargeGap, thickThinSmallGap
3285  // thinThickLargeGap, thinThickMediumGap, thinThickSmallGap
3286  const char* pVal = "nil";
3287  if ( pBorderLine && !pBorderLine->isEmpty( ) )
3288  {
3289  switch (pBorderLine->GetBorderLineStyle())
3290  {
3291  case SvxBorderLineStyle::SOLID:
3292  pVal = "single";
3293  break;
3294  case SvxBorderLineStyle::DOTTED:
3295  pVal = "dotted";
3296  break;
3297  case SvxBorderLineStyle::DASHED:
3298  pVal = "dashed";
3299  break;
3300  case SvxBorderLineStyle::DOUBLE:
3301  case SvxBorderLineStyle::DOUBLE_THIN:
3302  pVal = "double";
3303  break;
3304  case SvxBorderLineStyle::THINTHICK_SMALLGAP:
3305  pVal = "thinThickSmallGap";
3306  break;
3307  case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
3308  pVal = "thinThickMediumGap";
3309  break;
3310  case SvxBorderLineStyle::THINTHICK_LARGEGAP:
3311  pVal = "thinThickLargeGap";
3312  break;
3313  case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
3314  pVal = "thickThinSmallGap";
3315  break;
3316  case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
3317  pVal = "thickThinMediumGap";
3318  break;
3319  case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
3320  pVal = "thickThinLargeGap";
3321  break;
3322  case SvxBorderLineStyle::EMBOSSED:
3323  pVal = "threeDEmboss";
3324  break;
3325  case SvxBorderLineStyle::ENGRAVED:
3326  pVal = "threeDEngrave";
3327  break;
3328  case SvxBorderLineStyle::OUTSET:
3329  pVal = "outset";
3330  break;
3331  case SvxBorderLineStyle::INSET:
3332  pVal = "inset";
3333  break;
3334  case SvxBorderLineStyle::FINE_DASHED:
3335  pVal = "dashSmallGap";
3336  break;
3337  case SvxBorderLineStyle::DASH_DOT:
3338  pVal = "dotDash";
3339  break;
3340  case SvxBorderLineStyle::DASH_DOT_DOT:
3341  pVal = "dotDotDash";
3342  break;
3343  case SvxBorderLineStyle::NONE:
3344  default:
3345  break;
3346  }
3347  }
3348  else if ( !rStyleProps || !rStyleProps->LineWidth )
3349  // no line, and no line set by the style either:
3350  // there is no need to write the property
3351  return;
3352 
3353  // compare the properties with the theme properties before writing them:
3354  // if they are equal, it means that they were style-defined and there is
3355  // no need to write them.
3356  if( rStyleProps != nullptr && pBorderLine && !pBorderLine->isEmpty() &&
3357  pBorderLine->GetBorderLineStyle() == static_cast<SvxBorderLineStyle>(rStyleProps->LineStyle) &&
3358  pBorderLine->GetColor() == Color(rStyleProps->Color) &&
3359  pBorderLine->GetWidth() == convertMm100ToTwip( rStyleProps->LineWidth ) )
3360  return;
3361 
3362  FastAttributeList* pAttr = FastSerializerHelper::createAttrList();
3363  pAttr->add( FSNS( XML_w, XML_val ), OString( pVal ) );
3364 
3365  if ( pBorderLine && !pBorderLine->isEmpty() )
3366  {
3367  // Compute the sz attribute
3368 
3369  double const fConverted( ::editeng::ConvertBorderWidthToWord(
3370  pBorderLine->GetBorderLineStyle(), pBorderLine->GetWidth()));
3371  // The unit is the 8th of point
3372  sal_Int32 nWidth = sal_Int32( fConverted / 2.5 );
3373  const sal_Int32 nMinWidth = 2;
3374  const sal_Int32 nMaxWidth = 96;
3375 
3376  if ( nWidth > nMaxWidth )
3377  nWidth = nMaxWidth;
3378  else if ( nWidth < nMinWidth )
3379  nWidth = nMinWidth;
3380 
3381  pAttr->add( FSNS( XML_w, XML_sz ), OString::number( nWidth ) );
3382 
3383  // Get the distance (in pt)
3384  pAttr->add(FSNS(XML_w, XML_space), OString::number(rtl::math::round(nDist / 20.0)));
3385 
3386  // Get the color code as an RRGGBB hex value
3387  OString sColor( msfilter::util::ConvertColor( pBorderLine->GetColor( ) ) );
3388  pAttr->add( FSNS( XML_w, XML_color ), sColor );
3389  }
3390 
3391  if (bWriteShadow)
3392  {
3393  // Set the shadow value
3394  pAttr->add( FSNS( XML_w, XML_shadow ), "1" );
3395  }
3396 
3397  XFastAttributeListRef xAttrs( pAttr );
3398  pSerializer->singleElementNS( XML_w, elementToken, xAttrs );
3399 }
3400 
3402 {
3403  OutputBorderOptions rOptions;
3404 
3405  rOptions.tag = XML_tcBorders;
3406  rOptions.bUseStartEnd = !bEcma;
3407  rOptions.bWriteTag = true;
3408  rOptions.bWriteDistance = false;
3409 
3410  return rOptions;
3411 }
3412 
3414 {
3415  OutputBorderOptions rOptions;
3416 
3417  rOptions.tag = XML_pBdr;
3418  rOptions.bUseStartEnd = false;
3419  rOptions.bWriteTag = false;
3420  rOptions.bWriteDistance = true;
3421 
3422  return rOptions;
3423 }
3424 
3425 static void impl_borders( FSHelperPtr const & pSerializer,
3426  const SvxBoxItem& rBox,
3427  const OutputBorderOptions& rOptions,
3428  std::map<SvxBoxItemLine,
3429  css::table::BorderLine2> &rTableStyleConf )
3430 {
3431  static const SvxBoxItemLine aBorders[] =
3432  {
3433  SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
3434  };
3435 
3436  const sal_Int32 aXmlElements[] =
3437  {
3438  XML_top,
3439  rOptions.bUseStartEnd ? XML_start : XML_left,
3440  XML_bottom,
3441  rOptions.bUseStartEnd ? XML_end : XML_right
3442  };
3443  bool tagWritten = false;
3444  const SvxBoxItemLine* pBrd = aBorders;
3445 
3446  for( int i = 0; i < 4; ++i, ++pBrd )
3447  {
3448  const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
3449  const table::BorderLine2 *aStyleProps = nullptr;
3450  if( rTableStyleConf.find( *pBrd ) != rTableStyleConf.end() )
3451  aStyleProps = &rTableStyleConf[ *pBrd ];
3452 
3453  if (!tagWritten && rOptions.bWriteTag)
3454  {
3455  pSerializer->startElementNS(XML_w, rOptions.tag);
3456  tagWritten = true;
3457  }
3458 
3459  bool bWriteShadow = false;
3460  if (rOptions.aShadowLocation == SvxShadowLocation::NONE)
3461  {
3462  // The border has no shadow
3463  }
3464  else if (rOptions.aShadowLocation == SvxShadowLocation::BottomRight)
3465  {
3466  // Special case of 'Bottom-Right' shadow:
3467  // If the shadow location is 'Bottom-Right' - then turn on the shadow
3468  // for ALL the sides. This is because in Word - if you select a shadow
3469  // for a border - it turn on the shadow for ALL the sides (but shows only
3470  // the bottom-right one).
3471  // This is so that no information will be lost if passed through LibreOffice
3472  bWriteShadow = true;
3473  }
3474  else
3475  {
3476  // If there is a shadow, and it's not the regular 'Bottom-Right',
3477  // then write only the 'shadowed' sides of the border
3478  if (
3479  ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::TOP ) ||
3480  ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft || rOptions.aShadowLocation == SvxShadowLocation::BottomLeft) && *pBrd == SvxBoxItemLine::LEFT ) ||
3481  ((rOptions.aShadowLocation == SvxShadowLocation::BottomLeft ) && *pBrd == SvxBoxItemLine::BOTTOM) ||
3482  ((rOptions.aShadowLocation == SvxShadowLocation::TopRight ) && *pBrd == SvxBoxItemLine::RIGHT )
3483  )
3484  {
3485  bWriteShadow = true;
3486  }
3487  }
3488 
3489  sal_uInt16 nDist = 0;
3490  if (rOptions.bWriteDistance)
3491  {
3492  if (rOptions.pDistances)
3493  {
3494  if ( *pBrd == SvxBoxItemLine::TOP)
3495  nDist = rOptions.pDistances->nTop;
3496  else if ( *pBrd == SvxBoxItemLine::LEFT)
3497  nDist = rOptions.pDistances->nLeft;
3498  else if ( *pBrd == SvxBoxItemLine::BOTTOM)
3499  nDist = rOptions.pDistances->nBottom;
3500  else if ( *pBrd == SvxBoxItemLine::RIGHT)
3501  nDist = rOptions.pDistances->nRight;
3502  }
3503  else
3504  {
3505  nDist = rBox.GetDistance(*pBrd);
3506  }
3507  }
3508 
3509  impl_borderLine( pSerializer, aXmlElements[i], pLn, nDist, bWriteShadow, aStyleProps );
3510  }
3511  if (tagWritten && rOptions.bWriteTag) {
3512  pSerializer->endElementNS( XML_w, rOptions.tag );
3513  }
3514 }
3515 
3516 static void impl_cellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr)
3517 {
3518  static const SvxBoxItemLine aBorders[] =
3519  {
3520  SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
3521  };
3522 
3523  const sal_Int32 aXmlElements[] =
3524  {
3525  XML_top,
3526  bUseStartEnd ? XML_start : XML_left,
3527  XML_bottom,
3528  bUseStartEnd ? XML_end : XML_right
3529  };
3530  bool tagWritten = false;
3531  const SvxBoxItemLine* pBrd = aBorders;
3532  for( int i = 0; i < 4; ++i, ++pBrd )
3533  {
3534  sal_Int32 nDist = sal_Int32( rBox.GetDistance( *pBrd ) );
3535 
3536  if (pDefaultMargins)
3537  {
3538  // Skip output if cell margin == table default margin
3539  if (sal_Int32( pDefaultMargins->GetDistance( *pBrd ) ) == nDist)
3540  continue;
3541  }
3542 
3543  if (!tagWritten) {
3544  pSerializer->startElementNS(XML_w, tag);
3545  tagWritten = true;
3546  }
3547  pSerializer->singleElementNS( XML_w, aXmlElements[i],
3548  FSNS( XML_w, XML_w ), OString::number(nDist),
3549  FSNS( XML_w, XML_type ), "dxa" );
3550  }
3551  if (tagWritten) {
3552  pSerializer->endElementNS( XML_w, tag );
3553  }
3554 }
3555 
3556 void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
3557 {
3558  m_pSerializer->startElementNS(XML_w, XML_tcPr);
3559 
3560  const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
3561 
3562  bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
3563 
3564  // Output any table cell redlines if there are any attached to this specific cell
3565  TableCellRedline( pTableTextNodeInfoInner );
3566 
3567  // Cell preferred width
3568  SwTwips nWidth = GetGridCols( pTableTextNodeInfoInner )->at( nCell );
3569  if ( nCell )
3570  nWidth = nWidth - GetGridCols( pTableTextNodeInfoInner )->at( nCell - 1 );
3571  m_pSerializer->singleElementNS( XML_w, XML_tcW,
3572  FSNS( XML_w, XML_w ), OString::number(nWidth),
3573  FSNS( XML_w, XML_type ), "dxa" );
3574 
3575  // Horizontal spans
3576  const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
3577  SwWriteTableRow *pRow = rRows[ nRow ].get();
3578  const SwWriteTableCells& rTableCells = pRow->GetCells();
3579  if (nCell < rTableCells.size() )
3580  {
3581  const SwWriteTableCell& rCell = *rTableCells[nCell];
3582  const sal_uInt16 nColSpan = rCell.GetColSpan();
3583  if ( nColSpan > 1 )
3584  m_pSerializer->singleElementNS( XML_w, XML_gridSpan,
3585  FSNS( XML_w, XML_val ), OString::number(nColSpan) );
3586  }
3587 
3588  // Vertical merges
3589  ww8::RowSpansPtr xRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
3590  sal_Int32 vSpan = (*xRowSpans)[nCell];
3591  if ( vSpan > 1 )
3592  {
3593  m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "restart");
3594  }
3595  else if ( vSpan < 0 )
3596  {
3597  m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "continue");
3598  }
3599 
3601  {
3602  const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
3603  std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("CellCnfStyle");
3604  if (it != rGrabBag.end())
3605  {
3606  uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
3607  m_pTableStyleExport->CnfStyle(aAttributes);
3608  }
3609  }
3610 
3611 
3612  const SvxBoxItem& rBox = pTableBox->GetFrameFormat( )->GetBox( );
3613  const SvxBoxItem& rDefaultBox = (*tableFirstCells.rbegin())->getTableBox( )->GetFrameFormat( )->GetBox( );
3614  {
3615  // The cell borders
3616  impl_borders(m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma),
3617  m_aTableStyleConfs.back());
3618  }
3619 
3620  TableBackgrounds( pTableTextNodeInfoInner );
3621 
3622  {
3623  // Cell margins
3624  impl_cellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox );
3625  }
3626 
3627  TableVerticalCell( pTableTextNodeInfoInner );
3628 
3629  m_pSerializer->endElementNS( XML_w, XML_tcPr );
3630 }
3631 
3633 {
3634  const SwTable* pTable = pTableTextNodeInfoInner->getTable();
3635  if (m_xTableWrt && pTable == m_xTableWrt->GetTable())
3636  return;
3637 
3638  tools::Long nPageSize = 0;
3639  bool bRelBoxSize = false;
3640 
3641  // Create the SwWriteTable instance to use col spans (and maybe other infos)
3642  GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
3643 
3644  const SwFrameFormat *pFormat = pTable->GetFrameFormat( );
3645  const sal_uInt32 nTableSz = static_cast<sal_uInt32>(pFormat->GetFrameSize( ).GetWidth( ));
3646 
3647  const SwHTMLTableLayout *pLayout = pTable->GetHTMLTableLayout();
3648  if( pLayout && pLayout->IsExportable() )
3649  m_xTableWrt.reset(new SwWriteTable(pTable, pLayout));
3650  else
3651  m_xTableWrt.reset(new SwWriteTable(pTable, pTable->GetTabLines(), nPageSize, nTableSz, false));
3652 }
3653 
3655 {
3656  m_aTableStyleConfs.push_back({});
3657 
3658  // In case any paragraph SDT's are open, close them here.
3659  EndParaSdtBlock();
3660 
3661  m_pSerializer->startElementNS(XML_w, XML_tbl);
3662 
3663  tableFirstCells.push_back(pTableTextNodeInfoInner);
3664  lastOpenCell.push_back(-1);
3665  lastClosedCell.push_back(-1);
3666 
3667  InitTableHelper( pTableTextNodeInfoInner );
3668  TableDefinition( pTableTextNodeInfoInner );
3669 }
3670 
3672 {
3673  m_pSerializer->endElementNS( XML_w, XML_tbl );
3674 
3675  if ( m_tableReference->m_nTableDepth > 0 )
3676  --m_tableReference->m_nTableDepth;
3677 
3678  lastClosedCell.pop_back();
3679  lastOpenCell.pop_back();
3680  tableFirstCells.pop_back();
3681 
3682  // We closed the table; if it is a nested table, the cell that contains it
3683  // still continues
3684  // set to true only if we were in a nested table, not otherwise.
3685  if( !tableFirstCells.empty() )
3686  m_tableReference->m_bTableCellOpen = true;
3687 
3688  // Cleans the table helper
3689  m_xTableWrt.reset();
3690 
3691  m_aTableStyleConfs.pop_back();
3692 }
3693 
3695 {
3696  m_pSerializer->startElementNS(XML_w, XML_tr);
3697 
3698  // Output the row properties
3699  m_pSerializer->startElementNS(XML_w, XML_trPr);
3700 
3701  // Header row: tblHeader
3702  const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
3703  if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) )
3704  m_pSerializer->singleElementNS(XML_w, XML_tblHeader, FSNS(XML_w, XML_val), "true"); // TODO to overwrite table style may need explicit false
3705 
3706  TableRowRedline( pTableTextNodeInfoInner );
3707  TableHeight( pTableTextNodeInfoInner );
3708  TableCanSplit( pTableTextNodeInfoInner );
3709 
3710  const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox();
3711  const SwTableLine* pTableLine = pTableBox->GetUpper();
3712  if (const SfxGrabBagItem* pItem = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG))
3713  {
3714  const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
3715  std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("RowCnfStyle");
3716  if (it != rGrabBag.end())
3717  {
3718  uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
3719  m_pTableStyleExport->CnfStyle(aAttributes);
3720  }
3721  }
3722 
3723 
3724  m_pSerializer->endElementNS( XML_w, XML_trPr );
3725 }
3726 
3728 {
3729  m_pSerializer->endElementNS( XML_w, XML_tr );
3730  lastOpenCell.back() = -1;
3731  lastClosedCell.back() = -1;
3732 }
3733 
3734 void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
3735 {
3736  lastOpenCell.back() = nCell;
3737 
3738  InitTableHelper( pTableTextNodeInfoInner );
3739 
3740  m_pSerializer->startElementNS(XML_w, XML_tc);
3741 
3742  // Write the cell properties here
3743  TableCellProperties( pTableTextNodeInfoInner, nCell, nRow );
3744 
3745  m_tableReference->m_bTableCellOpen = true;
3746 }
3747 
3749 {
3750  lastClosedCell.back() = nCell;
3751  lastOpenCell.back() = -1;
3752 
3753  if (m_tableReference->m_bTableCellParaSdtOpen)
3754  EndParaSdtBlock();
3755 
3756  m_pSerializer->endElementNS( XML_w, XML_tc );
3757 
3758  m_tableReference->m_bTableCellOpen = false;
3759  m_tableReference->m_bTableCellParaSdtOpen = false;
3760 }
3761 
3763 {
3764 }
3765 
3767 {
3768 }
3769 
3770 namespace
3771 {
3772 
3774 OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, char cFill)
3775 {
3776  if (nLen > aString.getLength())
3777  {
3778  sal_Int32 nDiff = nLen - aString.getLength();
3779  OStringBuffer aBuffer;
3780  comphelper::string::padToLength(aBuffer, nDiff, cFill);
3781  aBuffer.append(aString);
3782  return aBuffer.makeStringAndClear();
3783  }
3784  else
3785  return aString;
3786 }
3787 
3788 //Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
3789 //Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
3790 //To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
3791 sal_Int32 lcl_getWordCompatibilityMode( const SwDoc& rDoc )
3792 {
3793  uno::Reference< beans::XPropertySet > xPropSet( rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
3794  uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
3795 
3796  sal_Int32 nWordCompatibilityMode = -1;
3797  if ( xPropSetInfo->hasPropertyByName( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) )
3798  {
3799  uno::Sequence< beans::PropertyValue > propList;
3800  xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= propList;
3801 
3802  for ( const auto& rProp : std::as_const(propList) )
3803  {
3804  if ( rProp.Name == "CompatSettings" )
3805  {
3806  css::uno::Sequence< css::beans::PropertyValue > aCurrentCompatSettings;
3807  rProp.Value >>= aCurrentCompatSettings;
3808 
3809  for ( const auto& rCurrentCompatSetting : std::as_const(aCurrentCompatSettings) )
3810  {
3811  uno::Sequence< beans::PropertyValue > aCompatSetting;
3812  rCurrentCompatSetting.Value >>= aCompatSetting;
3813 
3814  OUString sName;
3815  OUString sUri;
3816  OUString sVal;
3817 
3818  for ( const auto& rPropVal : std::as_const(aCompatSetting) )
3819  {
3820  if ( rPropVal.Name == "name" ) rPropVal.Value >>= sName;
3821  if ( rPropVal.Name == "uri" ) rPropVal.Value >>= sUri;
3822  if ( rPropVal.Name == "val" ) rPropVal.Value >>= sVal;
3823  }
3824 
3825  if ( sName == "compatibilityMode" && sUri == "http://schemas.microsoft.com/office/word" )
3826  {
3827  const sal_Int32 nValidMode = sVal.toInt32();
3828  // if repeated, highest mode wins in MS Word. 11 is the first valid mode.
3829  if ( nValidMode > 10 && nValidMode > nWordCompatibilityMode )
3830  nWordCompatibilityMode = nValidMode;
3831 
3832  }
3833  }
3834  }
3835  }
3836  }
3837 
3838  // TODO: this is duplicated, better store it in DocxExport member?
3840  {
3841  if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
3842  {
3843  nWordCompatibilityMode = 14;
3844  }
3845  }
3846 
3847  return nWordCompatibilityMode;
3848 }
3849 
3850 }
3851 
3853 {
3854  bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
3855 
3856  // Write the table properties
3857  m_pSerializer->startElementNS(XML_w, XML_tblPr);
3858 
3859  static const sal_Int32 aOrder[] =
3860  {
3861  FSNS( XML_w, XML_tblStyle ),
3862  FSNS( XML_w, XML_tblpPr ),
3863  FSNS( XML_w, XML_tblOverlap ),
3864  FSNS( XML_w, XML_bidiVisual ),
3865  FSNS( XML_w, XML_tblStyleRowBandSize ),
3866  FSNS( XML_w, XML_tblStyleColBandSize ),
3867  FSNS( XML_w, XML_tblW ),
3868  FSNS( XML_w, XML_jc ),
3869  FSNS( XML_w, XML_tblCellSpacing ),
3870  FSNS( XML_w, XML_tblInd ),
3871  FSNS( XML_w, XML_tblBorders ),
3872  FSNS( XML_w, XML_shd ),
3873  FSNS( XML_w, XML_tblLayout ),
3874  FSNS( XML_w, XML_tblCellMar ),
3875  FSNS( XML_w, XML_tblLook ),
3876  FSNS( XML_w, XML_tblPrChange )
3877  };
3878 
3879  // postpone the output so that we can later []
3880  // prepend the properties before the run
3881  m_pSerializer->mark(Tag_TableDefinition, comphelper::containerToSequence(aOrder));
3882 
3883  tools::Long nPageSize = 0;
3884  const char* widthType = "dxa";
3885 
3886  // If actual width of table is relative it should export is as "pct".`
3887  const SwTable *pTable = pTableTextNodeInfoInner->getTable();
3888  SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
3889  const SwFormatFrameSize &rSize = pTableFormat->GetFrameSize();
3890  int nWidthPercent = rSize.GetWidthPercent();
3891  // If we export a floating table: we use the widthPercent of the surrounding frame
3892  const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame();
3893  if (pFloatingTableFrame)
3894  {
3895  const SwFormatFrameSize &rFrameSize = pFloatingTableFrame->GetFrameFormat().GetFrameSize();
3896  nWidthPercent = rFrameSize.GetWidthPercent();
3897  }
3898  uno::Reference<beans::XPropertySet> xPropertySet(SwXTextTables::GetObject(*pTable->GetFrameFormat( )),uno::UNO_QUERY);
3899  bool isWidthRelative = false;
3900  xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
3901 
3902  if(isWidthRelative)
3903  {
3915  nPageSize = nWidthPercent * 50 ;
3916  widthType = "pct" ;
3917  }
3918  else
3919  {
3920  bool bRelBoxSize = false;
3921  // Create the SwWriteTable instance to use col spans (and maybe other infos)
3922  GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
3923  if(nPageSize == 0)
3924  widthType = "auto";
3925  }
3926 
3927  // Output the table preferred width
3928  m_pSerializer->singleElementNS( XML_w, XML_tblW,
3929  FSNS( XML_w, XML_w ), OString::number(nPageSize),
3930  FSNS( XML_w, XML_type ), widthType );
3931 
3932  // Disable layout autofit, as it does not exist in LibreOffice yet
3933  m_pSerializer->singleElementNS( XML_w, XML_tblLayout,
3934  FSNS( XML_w, XML_type ), "fixed" );
3935 
3936  // Look for the table style property in the table grab bag
3937  std::map<OUString, css::uno::Any> aGrabBag =
3938  pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
3939 
3940  // We should clear the TableStyle map. In case of Table inside multiple tables it contains the
3941  // table border style of the previous table.
3942  std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = m_aTableStyleConfs.back();
3943  rTableStyleConf.clear();
3944 
3945  // Extract properties from grab bag
3946  for( const auto & rGrabBagElement : aGrabBag )
3947  {
3948  if( rGrabBagElement.first == "TableStyleName")
3949  {
3950  OString sStyleName = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
3951  m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, XML_val), sStyleName);
3952  }
3953  else if( rGrabBagElement.first == "TableStyleTopBorder" )
3954  rTableStyleConf[SvxBoxItemLine::TOP] = rGrabBagElement.second.get<table::BorderLine2>();
3955  else if( rGrabBagElement.first == "TableStyleBottomBorder" )
3956  rTableStyleConf[SvxBoxItemLine::BOTTOM]
3957  = rGrabBagElement.second.get<table::BorderLine2>();
3958  else if( rGrabBagElement.first == "TableStyleLeftBorder" )
3959  rTableStyleConf[SvxBoxItemLine::LEFT]
3960  = rGrabBagElement.second.get<table::BorderLine2>();
3961  else if( rGrabBagElement.first == "TableStyleRightBorder" )
3962  rTableStyleConf[SvxBoxItemLine::RIGHT]
3963  = rGrabBagElement.second.get<table::BorderLine2>();
3964  else if (rGrabBagElement.first == "TableStyleLook")
3965  {
3966  FastAttributeList* pAttributeList = FastSerializerHelper::createAttrList();
3967  const uno::Sequence<beans::PropertyValue> aAttributeList = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
3968 
3969  for (const auto& rAttribute : aAttributeList)
3970  {
3971  if (rAttribute.Name == "val")
3972  pAttributeList->add(FSNS(XML_w, XML_val), lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16), 4, '0'));
3973  else
3974  {
3975  static DocxStringTokenMap const aTokens[] =
3976  {
3977  {"firstRow", XML_firstRow},
3978  {"lastRow", XML_lastRow},
3979  {"firstColumn", XML_firstColumn},
3980  {"lastColumn", XML_lastColumn},
3981  {"noHBand", XML_noHBand},
3982  {"noVBand", XML_noVBand},
3983  {nullptr, 0}
3984  };
3985 
3986  if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name))
3987  pAttributeList->add(FSNS(XML_w, nToken), (rAttribute.Value.get<sal_Int32>() ? "1" : "0"));
3988  }
3989  }
3990 
3991  XFastAttributeListRef xAttributeList(pAttributeList);
3992  m_pSerializer->singleElementNS(XML_w, XML_tblLook, xAttributeList);
3993  }
3994  else if (rGrabBagElement.first == "TablePosition" )
3995  {
3996  FastAttributeList *attrListTablePos = FastSerializerHelper::createAttrList( );
3997  const uno::Sequence<beans::PropertyValue> aTablePosition = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >();
3998  // look for a surrounding frame and take it's position values
3999  const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
4000  if( pFrame )
4001  {
4002  // we export the values of the surrounding Frame
4003  OString sOrientation;
4004  sal_Int32 nValue;
4005 
4006  // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY!
4007  OString sTblpXSpec = convertToOOXMLHoriOrient( pFrame->GetFrameFormat().GetHoriOrient().GetHoriOrient(), pFrame->GetFrameFormat().GetHoriOrient().IsPosToggle() );
4008  OString sTblpYSpec = convertToOOXMLVertOrient( pFrame->GetFrameFormat().GetVertOrient().GetVertOrient() );
4009 
4010  sOrientation = convertToOOXMLVertOrientRel( pFrame->GetFrameFormat().GetVertOrient().GetRelationOrient() );
4011  attrListTablePos->add( FSNS( XML_w, XML_vertAnchor ), sOrientation.getStr() );
4012 
4013  if( !sTblpYSpec.isEmpty() )
4014  attrListTablePos->add( FSNS( XML_w, XML_tblpYSpec ), sTblpYSpec.getStr() );
4015 
4016  sOrientation = convertToOOXMLHoriOrientRel( pFrame->GetFrameFormat().GetHoriOrient().GetRelationOrient() );
4017  attrListTablePos->add( FSNS( XML_w, XML_horzAnchor ), sOrientation.getStr() );
4018 
4019  if( !sTblpXSpec.isEmpty() )
4020  attrListTablePos->add( FSNS( XML_w, XML_tblpXSpec ), sTblpXSpec.getStr() );
4021 
4022  nValue = pFrame->GetFrameFormat().GetULSpace().GetLower();
4023  if( nValue != 0 )
4024  attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) );
4025 
4026  nValue = pFrame->GetFrameFormat().GetLRSpace().GetLeft();
4027  if( nValue != 0 )
4028  attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) );
4029 
4030  nValue = pFrame->GetFrameFormat().GetLRSpace().GetRight();
4031  if( nValue != 0 )
4032  attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) );
4033 
4034  nValue = pFrame->GetFrameFormat().GetULSpace().GetUpper();
4035  if( nValue != 0 )
4036  attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) );
4037 
4038  if( sTblpXSpec.isEmpty() ) // do not write tblpX if tblpXSpec is present
4039  {
4040  nValue = pFrame->GetFrameFormat().GetHoriOrient().GetPos();
4041  // we need to revert the additional shift introduced by
4042  // lcl_DecrementHoriOrientPosition() in writerfilter
4043  // 1st: left distance of the table
4044  const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4045  const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
4046  const SvxBoxItem& rBox = pFrameFormat->GetBox( );
4047  sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
4048  nValue += nLeftDistance;
4049 
4050  // 2nd: if a left border is given, revert the shift by half the width
4051  // from lcl_DecrementHoriOrientPosition() in writerfilter
4052  if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft())
4053  {
4054  tools::Long nWidth = pLeftBorder->GetWidth();
4055  nValue += (nWidth / 2);
4056  }
4057 
4058  attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) );
4059  }
4060 
4061  if( sTblpYSpec.isEmpty() ) // do not write tblpY if tblpYSpec is present
4062  {
4063  nValue = pFrame->GetFrameFormat().GetVertOrient().GetPos();
4064  attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) );
4065  }
4066  }
4067  else // ( pFrame = 0 )
4068  {
4069  // we export the values from the grabBag
4070  for (const auto& rProp : aTablePosition)
4071  {
4072  if (rProp.Name == "vertAnchor" && !rProp.Value.get<OUString>().isEmpty())
4073  {
4074  OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4075  attrListTablePos->add( FSNS( XML_w, XML_vertAnchor ), sOrientation.getStr() );
4076  }
4077  else if (rProp.Name == "tblpYSpec" && !rProp.Value.get<OUString>().isEmpty())
4078  {
4079  OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4080  attrListTablePos->add( FSNS( XML_w, XML_tblpYSpec ), sOrientation.getStr() );
4081  }
4082  else if (rProp.Name == "horzAnchor" && !rProp.Value.get<OUString>().isEmpty())
4083  {
4084  OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4085  attrListTablePos->add( FSNS( XML_w, XML_horzAnchor ), sOrientation.getStr() );
4086  }
4087  else if (rProp.Name == "tblpXSpec" && !rProp.Value.get<OUString>().isEmpty())
4088  {
4089  OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4090  attrListTablePos->add( FSNS( XML_w, XML_tblpXSpec ), sOrientation.getStr() );
4091  }
4092  else if (rProp.Name == "bottomFromText")
4093  {
4094  sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4095  attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) );
4096  }
4097  else if (rProp.Name == "leftFromText")
4098  {
4099  sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4100  attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) );
4101  }
4102  else if (rProp.Name == "rightFromText")
4103  {
4104  sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4105  attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) );
4106  }
4107  else if (rProp.Name == "topFromText")
4108  {
4109  sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4110  attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) );
4111  }
4112  else if (rProp.Name == "tblpX")
4113  {
4114  sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4115  attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) );
4116  }
4117  else if (rProp.Name == "tblpY")
4118  {
4119  sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4120  attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) );
4121  }
4122  }
4123  }
4124 
4125  XFastAttributeListRef xAttrListTablePosRef( attrListTablePos );
4126 
4127  m_pSerializer->singleElementNS( XML_w, XML_tblpPr, xAttrListTablePosRef);
4128  attrListTablePos = nullptr;
4129  }
4130  else
4131  SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: " << rGrabBagElement.first);
4132  }
4133 
4134  // Output the table alignment
4135  const char* pJcVal;
4136  sal_Int32 nIndent = 0;
4137  switch ( pTableFormat->GetHoriOrient( ).GetHoriOrient( ) )
4138  {
4139  case text::HoriOrientation::CENTER:
4140  pJcVal = "center";
4141  break;
4142  case text::HoriOrientation::RIGHT:
4143  if ( bEcma )
4144  pJcVal = "right";
4145  else
4146  pJcVal = "end";
4147  break;
4148  default:
4150  case text::HoriOrientation::LEFT_AND_WIDTH:
4151  {
4152  if ( bEcma )
4153  pJcVal = "left";
4154  else
4155  pJcVal = "start";
4156  nIndent = sal_Int32( pTableFormat->GetLRSpace().GetLeft() );
4157 
4158  // Table indentation has different meaning in Word, depending if the table is nested or not.
4159  // If nested, tblInd is added to parent table's left spacing and defines left edge position
4160  // If not nested, text position of left-most cell must be at absolute X = tblInd
4161  // so, table_spacing + table_spacing_to_content = tblInd
4162 
4163  // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
4164  // if no compatibilityMode is defined (which now should only happen on a new export to .docx),
4165  // LO uses a higher compatibility than 2010's 14.
4166  sal_Int32 nMode = lcl_getWordCompatibilityMode( m_rExport.m_rDoc );
4167 
4168  const SwFrameFormat* pFrameFormat = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
4169  if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth == 0)
4170  nIndent += pFrameFormat->GetBox().GetDistance( SvxBoxItemLine::LEFT );
4171  else
4172  {
4173  // adjust for SW considering table to start mid-border instead of nested/2013's left-side-of-border.
4174  nIndent -= pFrameFormat->GetBox().CalcLineWidth( SvxBoxItemLine::LEFT ) / 2;
4175  }
4176 
4177  break;
4178  }
4179  }
4180  m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pJcVal);
4181 
4182  // Output the table background color (although cell value still needs to be specified)
4183  const SvxBrushItem *pColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4184  Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
4185  if ( aColor != COL_AUTO )
4186  {
4187  OString sColor = msfilter::util::ConvertColor( aColor );
4188  m_pSerializer->singleElementNS( XML_w, XML_shd,
4189  FSNS( XML_w, XML_fill ), sColor,
4190  FSNS( XML_w, XML_val ), "clear" );
4191  }
4192 
4193  // Output the table borders
4194  TableDefaultBorders( pTableTextNodeInfoInner );
4195 
4196  // Output the default cell margins
4197  TableDefaultCellMargins( pTableTextNodeInfoInner );
4198 
4199  TableBidi( pTableTextNodeInfoInner );
4200 
4201  // Table indent (need to get written even if == 0)
4202  m_pSerializer->singleElementNS( XML_w, XML_tblInd,
4203  FSNS( XML_w, XML_w ), OString::number(nIndent),
4204  FSNS( XML_w, XML_type ), "dxa" );
4205 
4206  // Merge the marks for the ordered elements
4207  m_pSerializer->mergeTopMarks(Tag_TableDefinition);
4208 
4209  m_pSerializer->endElementNS( XML_w, XML_tblPr );
4210 
4211  // Write the table grid infos
4212  m_pSerializer->startElementNS(XML_w, XML_tblGrid);
4213  sal_Int32 nPrv = 0;
4214  ww8::WidthsPtr pColumnWidths = GetColumnWidths( pTableTextNodeInfoInner );
4215  for ( auto aColumnWidth : *pColumnWidths )
4216  {
4217  sal_Int32 nWidth = sal_Int32( aColumnWidth ) - nPrv;
4218  m_pSerializer->singleElementNS( XML_w, XML_gridCol,
4219  FSNS( XML_w, XML_w ), OString::number(nWidth) );
4220  nPrv = sal_Int32( aColumnWidth );
4221  }
4222 
4223  m_pSerializer->endElementNS( XML_w, XML_tblGrid );
4224 }
4225 
4227 {
4228  // Table defaults should only be created IF m_aTableStyleConf contents haven't come from a table style.
4229  // Previously this function wrote out Cell A1 as the table default, causing problems with no benefit.
4230 }
4231 
4233 {
4234  const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4235  const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
4236  const SvxBoxItem& rBox = pFrameFormat->GetBox( );
4237  const bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
4238 
4239  impl_cellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma);
4240 }
4241 
4243 {
4244  const SwTable *pTable = pTableTextNodeInfoInner->getTable();
4245  const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
4246  const SwTableLine *pTableRow = pTableBox->GetUpper();
4247  const SwFrameFormat *pFormat = pTableBox->GetFrameFormat( );
4248 
4249  const SvxBrushItem *pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4250  Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
4251 
4252  const SwFrameFormat *pRowFormat = pTableRow->GetFrameFormat( );
4253  const SvxBrushItem *pRowColorProp = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4254  if ( pRowColorProp && aColor == COL_AUTO)
4255  aColor = pRowColorProp->GetColor();
4256 
4257  const SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
4258  const SvxBrushItem *pTableColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4259  if ( pTableColorProp && aColor == COL_AUTO )
4260  aColor = pTableColorProp->GetColor();
4261 
4262  const OString sColor = msfilter::util::ConvertColor( aColor );
4263 
4264  std::map<OUString, css::uno::Any> aGrabBag =
4265  pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
4266 
4267  OString sOriginalColor;
4268  std::map<OUString, css::uno::Any>::iterator aGrabBagElement = aGrabBag.find("originalColor");
4269  if( aGrabBagElement != aGrabBag.end() )
4270  sOriginalColor = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
4271 
4272  if ( sOriginalColor != sColor )
4273  {
4274  // color changed by the user, or no grab bag: write sColor
4275  if ( sColor != "auto" )
4276  {
4277  m_pSerializer->singleElementNS( XML_w, XML_shd,
4278  FSNS( XML_w, XML_fill ), sColor,
4279  FSNS( XML_w, XML_val ), "clear" );
4280  }
4281  }
4282  else
4283  {
4285 
4286  for( const auto & rGrabBagElement : aGrabBag )
4287  {
4288  if (!rGrabBagElement.second.has<OUString>())
4289  continue;
4290 
4291  OString sValue = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
4292  if( rGrabBagElement.first == "themeFill")
4293  AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFill ), sValue.getStr() );
4294  else if( rGrabBagElement.first == "themeFillTint")
4295  AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillTint ), sValue.getStr() );
4296  else if( rGrabBagElement.first == "themeFillShade")
4297  AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillShade ), sValue.getStr() );
4298  else if( rGrabBagElement.first == "fill" )
4299  AddToAttrList( pAttrList, FSNS( XML_w, XML_fill ), sValue.getStr() );
4300  else if( rGrabBagElement.first == "themeColor")
4301  AddToAttrList( pAttrList, FSNS( XML_w, XML_themeColor ), sValue.getStr() );
4302  else if( rGrabBagElement.first == "themeTint")
4303  AddToAttrList( pAttrList, FSNS( XML_w, XML_themeTint ), sValue.getStr() );
4304  else if( rGrabBagElement.first == "themeShade")
4305  AddToAttrList( pAttrList, FSNS( XML_w, XML_themeShade ), sValue.getStr() );
4306  else if( rGrabBagElement.first == "color")
4307  AddToAttrList( pAttrList, FSNS( XML_w, XML_color ), sValue.getStr() );
4308  else if( rGrabBagElement.first == "val")
4309  AddToAttrList( pAttrList, FSNS( XML_w, XML_val ), sValue.getStr() );
4310  }
4311  m_pSerializer->singleElementNS( XML_w, XML_shd, pAttrList.get() );
4312  }
4313 }
4314 
4316 {
4317  const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4318  const SwTableLine * pTabLine = pTabBox->GetUpper();
4319 
4320  // search next Redline
4321  const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
4322  for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
4323  {
4324  SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
4325  const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
4326  if (pTableRowRedline && &pTableRowRedline->GetTableLine() == pTabLine)
4327  {
4328  // Redline for this table row
4329  const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
4330  RedlineType nRedlineType = aRedlineData.GetType();
4331  switch (nRedlineType)
4332  {
4333  case RedlineType::TableRowInsert:
4334  case RedlineType::TableRowDelete:
4335  {
4336  OString aId( OString::number( m_nRedlineId++ ) );
4337  const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
4338  OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
4339 
4340  OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
4341 
4342  if (nRedlineType == RedlineType::TableRowInsert)
4343  m_pSerializer->singleElementNS( XML_w, XML_ins,
4344  FSNS( XML_w, XML_id ), aId,
4345  FSNS( XML_w, XML_author ), aAuthor,
4346  FSNS( XML_w, XML_date ), aDate );
4347  else if (nRedlineType == RedlineType::TableRowDelete)
4348  m_pSerializer->singleElementNS( XML_w, XML_del,
4349  FSNS( XML_w, XML_id ), aId,
4350  FSNS( XML_w, XML_author ), aAuthor,
4351  FSNS( XML_w, XML_date ), aDate );
4352  }
4353  break;
4354  default: break;
4355  }
4356  }
4357  }
4358 }
4359 
4361 {
4362  const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4363 
4364  // search next Redline
4365  const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
4366  for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
4367  {
4368  SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
4369  const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
4370  if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox)
4371  {
4372  // Redline for this table cell
4373  const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
4374  RedlineType nRedlineType = aRedlineData.GetType();
4375  switch (nRedlineType)
4376  {
4377  case RedlineType::TableCellInsert:
4378  case RedlineType::TableCellDelete:
4379  {
4380  OString aId( OString::number( m_nRedlineId++ ) );
4381  const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
4382  OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
4383 
4384  OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
4385 
4386  if (nRedlineType == RedlineType::TableCellInsert)
4387  m_pSerializer->singleElementNS( XML_w, XML_cellIns,
4388  FSNS( XML_w, XML_id ), aId,
4389  FSNS( XML_w, XML_author ), aAuthor,
4390  FSNS( XML_w, XML_date ), aDate );
4391  else if (nRedlineType == RedlineType::TableCellDelete)
4392  m_pSerializer->singleElementNS( XML_w, XML_cellDel,
4393  FSNS( XML_w, XML_id ), aId,
4394  FSNS( XML_w, XML_author ), aAuthor,
4395  FSNS( XML_w, XML_date ), aDate );
4396  }
4397  break;
4398  default: break;
4399  }
4400  }
4401  }
4402 }
4403 
4405 {
4406  const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4407  const SwTableLine * pTabLine = pTabBox->GetUpper();
4408  const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
4409 
4410  const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
4411  if ( !(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()) )
4412  return;
4413 
4414  sal_Int32 nHeight = rLSz.GetHeight();
4415  const char *pRule = nullptr;
4416 
4417  switch ( rLSz.GetHeightSizeType() )
4418  {
4419  case SwFrameSize::Fixed: pRule = "exact"; break;
4420  case SwFrameSize::Minimum: pRule = "atLeast"; break;
4421  default: break;
4422  }
4423 
4424  if ( pRule )
4425  m_pSerializer->singleElementNS( XML_w, XML_trHeight,
4426  FSNS( XML_w, XML_val ), OString::number(nHeight),
4427  FSNS( XML_w, XML_hRule ), pRule );
4428 }
4429 
4431 {
4432  const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4433  const SwTableLine * pTabLine = pTabBox->GetUpper();
4434  const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
4435 
4436  const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit( );
4437  // if rSplittable is true then no need to write <w:cantSplit w:val="false"/>
4438  // as default row prop is allow row to break across page.
4439  if( !rSplittable.GetValue( ) )
4440  m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, XML_val), "true");
4441 }
4442 
4444 {
4445  const SwTable * pTable = pTableTextNodeInfoInner->getTable();
4446  const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
4447 
4448  if ( m_rExport.TrueFrameDirection( *pFrameFormat ) == SvxFrameDirection::Horizontal_RL_TB )
4449  {
4450  m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, XML_val), "true");
4451  }
4452 }
4453 
4455 {
4456  const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4457  const SwFrameFormat *pFrameFormat = pTabBox->GetFrameFormat( );
4458 
4459  if ( SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection( *pFrameFormat ) )
4460  m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "tbRl");
4461  else if ( SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection( *pFrameFormat ) )
4462  {
4463  m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "btLr");
4464  }
4465 
4466  const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
4467  SwWriteTableRow *pRow = rRows[ pTableTextNodeInfoInner->getRow( ) ].get();
4468  sal_uInt32 nCell = pTableTextNodeInfoInner->getCell();
4469  const SwWriteTableCells& rTableCells = pRow->GetCells();
4470  if (nCell >= rTableCells.size() )
4471  return;
4472 
4473  const SwWriteTableCell *const pCell = pRow->GetCells()[ nCell ].get();
4474  switch( pCell->GetVertOri())
4475  {
4476  case text::VertOrientation::TOP:
4477  break;
4478  case text::VertOrientation::CENTER:
4479  m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center");
4480  break;
4481  case text::VertOrientation::BOTTOM:
4482  m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom");
4483  break;
4484  }
4485 }
4486 
4488 {
4489  // This is called when the nested table ends in a cell, and there's no
4490  // paragraph behind that; so we must check for the ends of cell, rows,
4491  // tables
4492  // ['true' to write an empty paragraph, MS Word insists on that]
4493  FinishTableRowCell( pNodeInfoInner, true );
4494 }
4495 
4497 {
4498  SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
4499 }
4500 
4502 {
4503  SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
4504 }
4505 
4506 void DocxAttributeOutput::TableRowEnd( sal_uInt32 /*nDepth*/ )
4507 {
4508  SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )" );
4509 }
4510 
4512 {
4513  m_pSerializer->startElementNS( XML_w, XML_styles,
4514  FSNS( XML_xmlns, XML_w ), GetExport().GetFilter().getNamespaceURL(OOX_NS(doc)),
4515  FSNS( XML_xmlns, XML_w14 ), GetExport().GetFilter().getNamespaceURL(OOX_NS(w14)),
4516  FSNS( XML_xmlns, XML_mc ), GetExport().GetFilter().getNamespaceURL(OOX_NS(mce)),
4517  FSNS( XML_mc, XML_Ignorable ), "w14" );
4518 
4519  DocDefaults();
4520  LatentStyles();
4521 }
4522 
4523 sal_Int32 DocxStringGetToken(DocxStringTokenMap const * pMap, std::u16string_view rName)
4524 {
4525  OString sName = OUStringToOString(rName, RTL_TEXTENCODING_UTF8);
4526  while (pMap->pToken)
4527  {
4528  if (sName == pMap->pToken)
4529  return pMap->nToken;
4530  ++pMap;
4531  }
4532  return 0;
4533 }
4534 
4535 namespace
4536 {
4537 
4538 DocxStringTokenMap const aDefaultTokens[] = {
4539  {"defQFormat", XML_defQFormat},
4540  {"defUnhideWhenUsed", XML_defUnhideWhenUsed},
4541  {"defSemiHidden", XML_defSemiHidden},
4542  {"count", XML_count},
4543  {"defUIPriority", XML_defUIPriority},
4544  {"defLockedState", XML_defLockedState},
4545  {nullptr, 0}
4546 };
4547 
4548 DocxStringTokenMap const aExceptionTokens[] = {
4549  {"name", XML_name},
4550  {"locked", XML_locked},
4551  {"uiPriority", XML_uiPriority},
4552  {"semiHidden", XML_semiHidden},
4553  {"unhideWhenUsed", XML_unhideWhenUsed},
4554  {"qFormat", XML_qFormat},
4555  {nullptr, 0}
4556 };
4557 
4558 }
4559 
4561 {
4562  // Do we have latent styles available?
4563  uno::Reference<beans::XPropertySet> xPropertySet(m_rExport.m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
4564  uno::Sequence<beans::PropertyValue> aInteropGrabBag;
4565  xPropertySet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
4566  uno::Sequence<beans::PropertyValue> aLatentStyles;
4567  auto pProp = std::find_if(aInteropGrabBag.begin(), aInteropGrabBag.end(),
4568  [](const beans::PropertyValue& rProp) { return rProp.Name == "latentStyles"; });
4569  if (pProp != aInteropGrabBag.end())
4570  pProp->Value >>= aLatentStyles;
4571  if (!aLatentStyles.hasElements())
4572  return;
4573 
4574  // Extract default attributes first.
4575  sax_fastparser::FastAttributeList* pAttributeList = FastSerializerHelper::createAttrList();
4576  uno::Sequence<beans::PropertyValue> aLsdExceptions;
4577  for (const auto& rLatentStyle : std::as_const(aLatentStyles))
4578  {
4579  if (sal_Int32 nToken = DocxStringGetToken(aDefaultTokens, rLatentStyle.Name))
4580  pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(rLatentStyle.Value.get<OUString>(), RTL_TEXTENCODING_UTF8));
4581  else if (rLatentStyle.Name == "lsdExceptions")
4582  rLatentStyle.Value >>= aLsdExceptions;
4583  }
4584 
4585  XFastAttributeListRef xAttributeList(pAttributeList);
4586  m_pSerializer->startElementNS(XML_w, XML_latentStyles, xAttributeList);
4587  pAttributeList = nullptr;
4588 
4589  // Then handle the exceptions.
4590  for (const auto& rLsdException : std::as_const(aLsdExceptions))
4591  {
4592  pAttributeList = FastSerializerHelper::createAttrList();
4593 
4594  uno::Sequence<beans::PropertyValue> aAttributes;
4595  rLsdException.Value >>= aAttributes;
4596  for (const auto& rAttribute : std::as_const(aAttributes))
4597  if (sal_Int32 nToken = DocxStringGetToken(aExceptionTokens, rAttribute.Name))
4598  pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(rAttribute.Value.get<OUString>(), RTL_TEXTENCODING_UTF8));
4599 
4600  xAttributeList = pAttributeList;
4601  m_pSerializer->singleElementNS(XML_w, XML_lsdException, xAttributeList);
4602  pAttributeList = nullptr;
4603  }
4604 
4605  m_pSerializer->endElementNS(XML_w, XML_latentStyles);
4606 }
4607 
4609 {
4610  bool bMustWrite = true;
4611  switch (rHt.Which())
4612  {
4613  case RES_CHRATR_CASEMAP:
4614  bMustWrite = static_cast< const SvxCaseMapItem& >(rHt).GetCaseMap() != SvxCaseMap::NotMapped;
4615  break;
4616  case RES_CHRATR_COLOR:
4617  bMustWrite = static_cast< const SvxColorItem& >(rHt).GetValue() != COL_AUTO;
4618  break;
4619  case RES_CHRATR_CONTOUR:
4620  bMustWrite = static_cast< const SvxContourItem& >(rHt).GetValue();
4621  break;
4622  case RES_CHRATR_CROSSEDOUT:
4623  bMustWrite = static_cast< const SvxCrossedOutItem& >(rHt).GetStrikeout() != STRIKEOUT_NONE;
4624  break;
4625  case RES_CHRATR_ESCAPEMENT:
4626  bMustWrite = static_cast< const SvxEscapementItem& >(rHt).GetEscapement() != SvxEscapement::Off;
4627  break;
4628  case RES_CHRATR_FONT:
4629  bMustWrite = true;
4630  break;
4631  case RES_CHRATR_FONTSIZE:
4632  bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default
4633  break;
4634  case RES_CHRATR_KERNING:
4635  bMustWrite = static_cast< const SvxKerningItem& >(rHt).GetValue() != 0;
4636  break;
4637  case RES_CHRATR_LANGUAGE:
4638  bMustWrite = true;
4639  break;
4640  case RES_CHRATR_POSTURE:
4641  bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE;
4642  break;
4643  case RES_CHRATR_SHADOWED:
4644  bMustWrite = static_cast< const SvxShadowedItem& >(rHt).GetValue();
4645  break;
4646  case RES_CHRATR_UNDERLINE:
4647  bMustWrite = static_cast< const SvxUnderlineItem& >(rHt).GetLineStyle() != LINESTYLE_NONE;
4648  break;
4649  case RES_CHRATR_WEIGHT:
4650  bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL;
4651  break;
4652  case RES_CHRATR_AUTOKERN:
4653  bMustWrite = static_cast< const SvxAutoKernItem& >(rHt).GetValue();
4654  break;
4655  case RES_CHRATR_BLINK:
4656  bMustWrite = static_cast< const SvxBlinkItem& >(rHt).GetValue();
4657  break;
4658  case RES_CHRATR_BACKGROUND:
4659  {
4660  const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt);
4661  bMustWrite = (rBrushItem.GetColor() != COL_AUTO ||
4662  rBrushItem.GetShadingValue() != ShadingPattern::CLEAR ||
4663  rBrushItem.GetGraphic() != nullptr ||
4664  rBrushItem.GetGraphicObject() != nullptr);
4665  }
4666  break;
4667 
4668  case RES_CHRATR_CJK_FONT:
4669  bMustWrite = true;
4670  break;
4672  bMustWrite = false; // we have written it already as RES_CHRATR_FONTSIZE
4673  break;
4675  bMustWrite = true;
4676  break;
4678  bMustWrite = false; // we have written it already as RES_CHRATR_POSTURE
4679  break;
4680  case RES_CHRATR_CJK_WEIGHT:
4681  bMustWrite = false; // we have written it already as RES_CHRATR_WEIGHT
4682  break;
4683 
4684  case RES_CHRATR_CTL_FONT:
4685  bMustWrite = true;
4686  break;
4688  bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default
4689  break;
4691  bMustWrite = true;
4692  break;
4694  bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE;
4695  break;
4696  case RES_CHRATR_CTL_WEIGHT:
4697  bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL;
4698  break;
4699 
4700  case RES_CHRATR_ROTATE:
4701  bMustWrite = static_cast< const SvxCharRotateItem& >(rHt).GetValue() != 0_deg10;
4702  break;
4704  bMustWrite = static_cast< const SvxEmphasisMarkItem& >(rHt).GetEmphasisMark() != FontEmphasisMark::NONE;
4705  break;
4706  case RES_CHRATR_TWO_LINES:
4707  bMustWrite = static_cast< const SvxTwoLinesItem& >(rHt).GetValue();
4708  break;
4709  case RES_CHRATR_SCALEW:
4710  bMustWrite = static_cast< const SvxCharScaleWidthItem& >(rHt).GetValue() != 100;
4711  break;
4712  case RES_CHRATR_RELIEF:
4713  bMustWrite = static_cast< const SvxCharReliefItem& >(rHt).GetValue() != FontRelief::NONE;
4714  break;
4715  case RES_CHRATR_HIDDEN:
4716  bMustWrite = static_cast< const SvxCharHiddenItem& >(rHt).GetValue();
4717  break;
4718  case RES_CHRATR_BOX:
4719  {
4720  const SvxBoxItem& rBoxItem = static_cast< const SvxBoxItem& >(rHt);
4721  bMustWrite = rBoxItem.GetTop() || rBoxItem.GetLeft() ||
4722  rBoxItem.GetBottom() || rBoxItem.GetRight() ||
4723  rBoxItem.GetSmallestDistance();
4724  }
4725  break;
4726  case RES_CHRATR_HIGHLIGHT:
4727  {
4728  const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt);
4729  bMustWrite = (rBrushItem.GetColor() != COL_AUTO ||
4730  rBrushItem.GetShadingValue() != ShadingPattern::CLEAR ||
4731  rBrushItem.GetGraphic() != nullptr ||
4732  rBrushItem.GetGraphicObject() != nullptr);
4733  }
4734  break;
4735 
4737  bMustWrite = static_cast< const SvxLineSpacingItem& >(rHt).GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off;
4738  break;
4739  case RES_PARATR_ADJUST:
4740  bMustWrite = static_cast< const SvxAdjustItem& >(rHt).GetAdjust() != SvxAdjust::Left;
4741  break;
4742  case RES_PARATR_SPLIT:
4743  bMustWrite = !static_cast< const SvxFormatSplitItem& >(rHt).GetValue();
4744  break;
4745  case RES_PARATR_WIDOWS:
4746  bMustWrite = static_cast< const SvxWidowsItem& >(rHt).GetValue();
4747  break;
4748  case RES_PARATR_TABSTOP:
4749  bMustWrite = static_cast< const SvxTabStopItem& >(rHt).Count() != 0;
4750  break;
4751  case RES_PARATR_HYPHENZONE:
4752  bMustWrite = true;
4753  break;
4754  case RES_PARATR_NUMRULE:
4755  bMustWrite = !static_cast< const SwNumRuleItem& >(rHt).GetValue().isEmpty();
4756  break;
4758  bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4759  break;
4761  bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4762  break;
4764  bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4765  break;
4766  case RES_PARATR_VERTALIGN:
4767  bMustWrite = static_cast< const SvxParaVertAlignItem& >(rHt).GetValue() != SvxParaVertAlignItem::Align::Automatic;
4768  break;
4769  case RES_PARATR_SNAPTOGRID:
4770  bMustWrite = !static_cast< const SvxParaGridItem& >(rHt).GetValue();
4771  break;
4772  case RES_CHRATR_GRABBAG:
4773  bMustWrite = true;
4774  break;
4775 
4776  default:
4777  SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() );
4778  break;
4779  }
4780 
4781  if (bMustWrite)
4782  OutputItem(rHt);
4783 }
4784 
4786 {
4787  // Write the '<w:docDefaults>' section here
4788  m_pSerializer->startElementNS(XML_w, XML_docDefaults);
4789 
4790  // Output the default run properties
4791  m_pSerializer->startElementNS(XML_w, XML_rPrDefault);
4792 
4793  StartStyleProperties(false, 0);
4794 
4795  for (int i = int(RES_CHRATR_BEGIN); i < int(RES_CHRATR_END); ++i)
4796  OutputDefaultItem(m_rExport.m_rDoc.GetDefault(i));
4797 
4798  EndStyleProperties(false);
4799 
4800  m_pSerializer->endElementNS(XML_w, XML_rPrDefault);
4801 
4802  // Output the default paragraph properties
4803  m_pSerializer->startElementNS(XML_w, XML_pPrDefault);
4804 
4805  StartStyleProperties(true, 0);
4806 
4807  for (int i = int(RES_PARATR_BEGIN); i < int(RES_PARATR_END); ++i)
4808  OutputDefaultItem(m_rExport.m_rDoc.GetDefault(i));
4809 
4810  EndStyleProperties(true);
4811 
4812  m_pSerializer->endElementNS(XML_w, XML_pPrDefault);
4813 
4814  m_pSerializer->endElementNS(XML_w, XML_docDefaults);
4815 }
4816 
4817 void DocxAttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles )
4818 {
4819  // HACK
4820  // Ms Office seems to have an internal limitation of 4091 styles
4821  // and refuses to load .docx with more, even though the spec seems to allow that;
4822  // so simply if there are more styles, don't export those
4823  const sal_Int32 nCountStylesToWrite = MSWORD_MAX_STYLES_LIMIT - nNumberOfStyles;
4824  m_pTableStyleExport->TableStyles(nCountStylesToWrite);
4825  m_pSerializer->endElementNS( XML_w, XML_styles );
4826 }
4827 
4829 {
4830  // are these the values of enum ww::sti (see ../inc/wwstyles.hxx)?
4831  SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::DefaultStyle()");
4832 }
4833 
4834 /* Writes <a:srcRect> tag back to document.xml if a file contains a cropped image.
4835 * NOTE : Tested on images of type JPEG,EMF/WMF,BMP, PNG and GIF.
4836 */
4838  const css::uno::Reference<css::beans::XPropertySet>& xShapePropSet,
4839  const SwFrameFormat* pFrameFormat)
4840 {
4841  uno::Reference<graphic::XGraphic> xGraphic;
4842  xShapePropSet->getPropertyValue("Graphic") >>= xGraphic;
4843  const Graphic aGraphic(xGraphic);
4844 
4845  Size aOriginalSize(aGraphic.GetPrefSize());
4846 
4847  const MapMode aMap100mm( MapUnit::Map100thMM );
4848  const MapMode& rMapMode = aGraphic.GetPrefMapMode();
4849  if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
4850  {
4851  aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, aMap100mm);
4852  }
4853 
4854  css::text::GraphicCrop aGraphicCropStruct;
4855  xShapePropSet->getPropertyValue("GraphicCrop") >>= aGraphicCropStruct;
4856  sal_Int32 nCropL = aGraphicCropStruct.Left;
4857  sal_Int32 nCropR = aGraphicCropStruct.Right;
4858  sal_Int32 nCropT = aGraphicCropStruct.Top;
4859  sal_Int32 nCropB = aGraphicCropStruct.Bottom;
4860 
4861  // simulate border padding as a negative crop.
4862  const SfxPoolItem* pItem;
4863  if (pFrameFormat && SfxItemState::SET == pFrameFormat->GetItemState(RES_BOX, false, &pItem))
4864  {
4865  const SvxBoxItem& rBox = *static_cast<const SvxBoxItem*>(pItem);
4866  nCropL -= rBox.GetDistance( SvxBoxItemLine::LEFT );
4867  nCropR -= rBox.GetDistance( SvxBoxItemLine::RIGHT );
4868  nCropT -= rBox.GetDistance( SvxBoxItemLine::TOP );
4869  nCropB -= rBox.GetDistance( SvxBoxItemLine::BOTTOM );
4870  }
4871 
4872  if ( !((0 != nCropL) || (0 != nCropT) || (0 != nCropR) || (0 != nCropB)) )
4873  return;
4874 
4875  double widthMultiplier = 100000.0/aOriginalSize.Width();
4876  double heightMultiplier = 100000.0/aOriginalSize.Height();
4877 
4878  sal_Int32 left = static_cast<sal_Int32>(rtl::math::round(nCropL * widthMultiplier));
4879  sal_Int32 right = static_cast<sal_Int32>(rtl::math::round(nCropR * widthMultiplier));
4880  sal_Int32 top = static_cast<sal_Int32>(rtl::math::round(nCropT * heightMultiplier));
4881  sal_Int32 bottom = static_cast<sal_Int32>(rtl::math::round(nCropB * heightMultiplier));
4882 
4883  m_pSerializer->singleElementNS( XML_a, XML_srcRect,
4884  XML_l, OString::number(left),
4885  XML_t, OString::number(top),
4886  XML_r, OString::number(right),
4887  XML_b, OString::number(bottom) );
4888 }
4889 
4891 {
4892  if (!m_aRelIdCache.empty())
4893  m_aRelIdCache.pop();
4894  if (!m_aSdrRelIdCache.empty())
4895  m_aSdrRelIdCache.pop();
4896 }
4897 
4899 {
4900  m_aRelIdCache.emplace();
4901  m_aSdrRelIdCache.emplace();
4902 }
4903 
4905 {
4906  OUString aRet;
4907 
4908  if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end())
4909  aRet = m_aSdrRelIdCache.top()[nChecksum];
4910 
4911  return aRet;
4912 }
4913 
4914 void DocxAttributeOutput::CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId)
4915 {
4916  if (!m_aSdrRelIdCache.empty())
4917  m_aSdrRelIdCache.top()[nChecksum] = rRelId;
4918 }
4919 
4920 uno::Reference<css::text::XTextFrame> DocxAttributeOutput::GetUnoTextFrame(
4921  css::uno::Reference<css::drawing::XShape> xShape)
4922 {
4923  return SwTextBoxHelper::getUnoTextFrame(xShape);
4924 }
4925 
4927 {
4928  OString aResult;
4929 
4930  if (m_aRelIdCache.empty())
4931  return aResult;
4932 
4933  auto pIterator = m_aRelIdCache.top().find(nChecksum);
4934 
4935  if (pIterator != m_aRelIdCache.top().end())
4936  {
4937  aResult = pIterator->second;
4938  }
4939 
4940  return aResult;
4941 }
4942 
4943 void DocxAttributeOutput::cacheGraphicRelId(BitmapChecksum nChecksum, OString const & rRelId)
4944 {
4945  if (!m_aRelIdCache.empty())
4946  m_aRelIdCache.top().emplace(nChecksum, rRelId);
4947 }
4948 
4949 void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj )
4950 {
4951  SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj ) - some stuff still missing" );
4952 
4953  GetSdtEndBefore(pSdrObj);
4954 
4955  // detect mis-use of the API
4956  assert(pGrfNode || (pOLEFrameFormat && pOLENode));
4957  const SwFrameFormat* pFrameFormat = pGrfNode ? pGrfNode->GetFlyFormat() : pOLEFrameFormat;
4958  // create the relation ID
4959  OString aRelId;
4960  sal_Int32 nImageType;
4961  if ( pGrfNode && pGrfNode->IsLinkedFile() )
4962  {
4963  // linked image, just create the relation
4964  OUString aFileName;
4965  pGrfNode->GetFileFilterNms( &aFileName, nullptr );
4966 
4967  sal_Int32 const nFragment(aFileName.indexOf('#'));
4968  sal_Int32 const nForbiddenU(aFileName.indexOf("%5C"));
4969  sal_Int32 const nForbiddenL(aFileName.indexOf("%5c"));
4970  if ( (nForbiddenU != -1 && (nFragment == -1 || nForbiddenU < nFragment))
4971  || (nForbiddenL != -1 && (nFragment == -1 || nForbiddenL < nFragment)))
4972  {
4973  SAL_WARN("sw.ww8", "DocxAttributeOutput::FlyFrameGraphic: ignoring image with invalid link URL");
4974  return;
4975  }
4976 
4977  // TODO Convert the file name to relative for better interoperability
4978 
4979  aRelId = m_rExport.AddRelation(
4981  aFileName );
4982 
4983  nImageType = XML_link;
4984  }
4985  else
4986  {
4987  // inline, we also have to write the image itself
4988  Graphic aGraphic;
4989  if (pGrfNode)
4990  aGraphic = pGrfNode->GetGrf();
4991  else
4992  aGraphic = *pOLENode->GetGraphic();
4993 
4994  BitmapChecksum aChecksum = aGraphic.GetChecksum();
4995  aRelId = getExistingGraphicRelId(aChecksum);
4996 
4997  if (aRelId.isEmpty())
4998  {
4999  // Not in cache, then need to write it.
5000  m_rDrawingML.SetFS( m_pSerializer ); // to be sure that we write to the right stream
5001 
5002  OUString aImageId = m_rDrawingML.WriteImage(aGraphic);
5003 
5004  aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 );
5005  cacheGraphicRelId(aChecksum, aRelId);
5006  }
5007 
5008  nImageType = XML_embed;
5009  }
5010 
5011  // In case there are any grab-bag items on the graphic frame, emit them now.
5012  // These are always character grab-bags, as graphics are at-char or as-char in Word.
5013  const SfxPoolItem* pItem = nullptr;
5014  if (pFrameFormat->GetAttrSet().HasItem(RES_FRMATR_GRABBAG, &pItem))
5015  {
5016  const SfxGrabBagItem* pGrabBag = static_cast<const SfxGrabBagItem*>(pItem);
5017  CharGrabBag(*pGrabBag);
5018  }
5019 
5021  FastSerializerHelper::createAttrList());
5022  if (pGrfNode)
5023  {
5024  const SwAttrSet& rSet = pGrfNode->GetSwAttrSet();
5025  MirrorGraph eMirror = rSet.Get(RES_GRFATR_MIRRORGRF).GetValue();
5026  if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
5027  // Mirror on the vertical axis is a horizontal flip.
5028  xFrameAttributes->add(XML_flipH, "1");
5029  // RES_GRFATR_ROTATION is sal_uInt16; use sal_uInt32 for multiplication later
5030  if (Degree10 nRot = rSet.Get(RES_GRFATR_ROTATION).GetValue())
5031  {
5032  // RES_GRFATR_ROTATION is in 10ths of degree; convert to 100ths for macro
5033  sal_uInt32 mOOXMLRot = oox::drawingml::ExportRotateClockwisify(toDegree100(nRot));
5034  xFrameAttributes->add(XML_rot, OString::number(mOOXMLRot));
5035  }
5036  }
5037 
5038  css::uno::Reference<css::beans::XPropertySet> xShapePropSet;
5039  if (pSdrObj)
5040  {
5041  css::uno::Reference<css::drawing::XShape> xShape(
5042  const_cast<SdrObject*>(pSdrObj)->getUnoShape(), css::uno::UNO_QUERY);
5043  xShapePropSet.set(xShape, css::uno::UNO_QUERY);
5044  assert(xShapePropSet);
5045  }
5046 
5047  Size aSize = rSize;
5048  // We need the original (cropped, but unrotated) size of object. So prefer the object data,
5049  // and only use passed frame size as fallback.
5050  if (xShapePropSet)
5051  {
5052  if (css::awt::Size val; xShapePropSet->getPropertyValue("Size") >>= val)
5053  aSize = Size(convertMm100ToTwip(val.Width), convertMm100ToTwip(val.Height));
5054  }
5055 
5056  m_rExport.SdrExporter().startDMLAnchorInline(pFrameFormat, aSize);
5057 
5058  // picture description (used for pic:cNvPr later too)
5059  ::sax_fastparser::FastAttributeList* docPrattrList = FastSerializerHelper::createAttrList();
5060  docPrattrList->add( XML_id, OString::number( m_anchorId++).getStr());
5061  docPrattrList->add( XML_name, OUStringToOString( pFrameFormat->GetName(), RTL_TEXTENCODING_UTF8 ) );
5062  docPrattrList->add( XML_descr, OUStringToOString( pGrfNode ? pGrfNode->GetDescription() : pOLEFrameFormat->GetObjDescription(), RTL_TEXTENCODING_UTF8 ).getStr());
5063  if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT )
5064  docPrattrList->add( XML_title, OUStringToOString( pGrfNode ? pGrfNode->GetTitle() : pOLEFrameFormat->GetObjTitle(), RTL_TEXTENCODING_UTF8 ).getStr());
5065  XFastAttributeListRef docPrAttrListRef( docPrattrList );
5066  m_pSerializer->startElementNS( XML_wp, XML_docPr, docPrAttrListRef );
5067 
5068  OUString sURL, sRelId;
5069  if (xShapePropSet)
5070  {
5071  xShapePropSet->getPropertyValue("HyperLinkURL") >>= sURL;
5072  if(!sURL.isEmpty())
5073  {
5074  if (sURL.startsWith("#") && sURL.indexOf(' ') != -1 && !sURL.endsWith("|outline") && !sURL.endsWith("|table") &&
5075  !sURL.endsWith("|frame") && !sURL.endsWith("|graphic") && !sURL.endsWith("|ole") && !sURL.endsWith("|region"))
5076  {
5077  // Spaces are prohibited in bookmark name.
5078  sURL = sURL.replace(' ', '_');
5079  }
5080  sRelId = GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
5082  sURL, !sURL.startsWith("#") );
5083  m_pSerializer->singleElementNS( XML_a, XML_hlinkClick,
5084  FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
5085  FSNS( XML_r, XML_id ), sRelId);
5086  }
5087  }
5088 
5089  m_pSerializer->endElementNS( XML_wp, XML_docPr );
5090 
5091  m_pSerializer->startElementNS(XML_wp, XML_cNvGraphicFramePr);
5092  // TODO change aspect?
5093  m_pSerializer->singleElementNS( XML_a, XML_graphicFrameLocks,
5094  FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)),
5095  XML_noChangeAspect, "1" );
5096  m_pSerializer->endElementNS( XML_wp, XML_cNvGraphicFramePr );
5097 
5098  m_pSerializer->startElementNS( XML_a, XML_graphic,
5099  FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)) );
5100  m_pSerializer->startElementNS( XML_a, XML_graphicData,
5101  XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/picture" );
5102 
5103  m_pSerializer->startElementNS( XML_pic, XML_pic,
5104  FSNS( XML_xmlns, XML_pic ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlPicture)) );
5105 
5106  m_pSerializer->startElementNS(XML_pic, XML_nvPicPr);
5107  // It seems pic:cNvpr and wp:docPr are pretty much the same thing with the same attributes
5108  m_pSerializer->startElementNS(XML_pic, XML_cNvPr, docPrAttrListRef);
5109 
5110  if(!sURL.isEmpty())
5111  m_pSerializer->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
5112 
5113  m_pSerializer->endElementNS( XML_pic, XML_cNvPr );
5114 
5115  m_pSerializer->startElementNS(XML_pic, XML_cNvPicPr);
5116  // TODO change aspect?
5117  m_pSerializer->singleElementNS( XML_a, XML_picLocks,
5118  XML_noChangeAspect, "1", XML_noChangeArrowheads, "1" );
5119  m_pSerializer->endElementNS( XML_pic, XML_cNvPicPr );
5120  m_pSerializer->endElementNS( XML_pic, XML_nvPicPr );
5121 
5122  // the actual picture
5123  m_pSerializer->startElementNS(XML_pic, XML_blipFill);
5124 
5125 /* At this point we are certain that, WriteImage returns empty RelId
5126  for unhandled graphic type. Therefore we write the picture description
5127  and not the relation( coz there ain't any), so that the user knows
5128  there is an image/graphic in the doc but it is broken instead of
5129  completely discarding it.
5130 */
5131  if ( aRelId.isEmpty() )
5132  m_pSerializer->startElementNS(XML_a, XML_blip);
5133  else
5134  m_pSerializer->startElementNS(XML_a, XML_blip, FSNS(XML_r, nImageType), aRelId);
5135 
5136  pItem = nullptr;
5137 
5138  if ( pGrfNode && SfxItemState::SET == pGrfNode->GetSwAttrSet().GetItemState(RES_GRFATR_DRAWMODE, true, &pItem))
5139  {
5140  GraphicDrawMode nMode = static_cast<GraphicDrawMode>(static_cast<const SfxEnumItemInterface*>(pItem)->GetEnumValue());
5141  if (nMode == GraphicDrawMode::Greys)
5142  m_pSerializer->singleElementNS (XML_a, XML_grayscl);
5143  else if (nMode == GraphicDrawMode::Mono) //black/white has a 0,5 threshold in LibreOffice
5144  m_pSerializer->singleElementNS (XML_a, XML_biLevel, XML_thresh, OString::number(50000));
5145  else if (nMode == GraphicDrawMode::Watermark) //watermark has a brightness/luminance of 0,5 and contrast of -0.7 in LibreOffice
5146  m_pSerializer->singleElementNS( XML_a, XML_lum, XML_bright, OString::number(70000), XML_contrast, OString::number(-70000) );
5147  }
5148  m_pSerializer->endElementNS( XML_a, XML_blip );
5149 
5150  if (xShapePropSet)
5151  WriteSrcRect(xShapePropSet, pFrameFormat);
5152 
5153  m_pSerializer->startElementNS(XML_a, XML_stretch);
5154  m_pSerializer->singleElementNS(XML_a, XML_fillRect);
5155  m_pSerializer->endElementNS( XML_a, XML_stretch );
5156  m_pSerializer->endElementNS( XML_pic, XML_blipFill );
5157 
5158  // TODO setup the right values below
5159  m_pSerializer->startElementNS(XML_pic, XML_spPr, XML_bwMode, "auto");
5160 
5161  m_pSerializer->startElementNS(
5162  XML_a, XML_xfrm, uno::Reference<xml::sax::XFastAttributeList>(xFrameAttributes.get()));
5163 
5164  m_pSerializer->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
5165  OString aWidth( OString::number( TwipsToEMU( aSize.Width() ) ) );
5166  OString aHeight( OString::number( TwipsToEMU( aSize.Height() ) ) );
5167  m_pSerializer->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
5168  m_pSerializer->endElementNS( XML_a, XML_xfrm );
5169  m_pSerializer->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect");
5170  m_pSerializer->singleElementNS(XML_a, XML_avLst);
5171  m_pSerializer->endElementNS( XML_a, XML_prstGeom );
5172 
5173  const SvxBoxItem& rBoxItem = pFrameFormat->GetBox();
5174  const SvxBorderLine* pLeft = rBoxItem.GetLine(SvxBoxItemLine::LEFT);
5175  const SvxBorderLine* pRight = rBoxItem.GetLine(SvxBoxItemLine::RIGHT);
5176  const SvxBorderLine* pTop = rBoxItem.GetLine(SvxBoxItemLine::TOP);
5177  const SvxBorderLine* pBottom = rBoxItem.GetLine(SvxBoxItemLine::BOTTOM);
5178  if (pLeft || pRight || pTop || pBottom)
5179  m_rExport.SdrExporter().writeBoxItemLine(rBoxItem);
5180 
5181  m_rExport.SdrExporter().writeDMLEffectLst(*pFrameFormat);
5182 
5183  m_pSerializer->endElementNS( XML_pic, XML_spPr );
5184 
5185  m_pSerializer->endElementNS( XML_pic, XML_pic );
5186 
5187  m_pSerializer->endElementNS( XML_a, XML_graphicData );
5188  m_pSerializer->endElementNS( XML_a, XML_graphic );
5189  m_rExport.SdrExporter().endDMLAnchorInline(pFrameFormat);
5190 }
5191 
5192 void DocxAttributeOutput::WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rOLENode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat, const sal_Int8 nFormulaAlignment )
5193 {
5194  if( WriteOLEChart( pSdrObj, rSize, pFlyFrameFormat ))
5195  return;
5196  if( WriteOLEMath( rOLENode , nFormulaAlignment))
5197  return;
5198  PostponeOLE( rOLENode, rSize, pFlyFrameFormat );
5199 }
5200 
5201 bool DocxAttributeOutput::WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
5202 {
5203  uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY );
5204  if (!xShape.is())
5205  return false;
5206 
5207  uno::Reference<beans::XPropertySet> const xPropSet(xShape, uno::UNO_QUERY);
5208  if (!xPropSet.is())
5209  return false;
5210 
5211  OUString clsid; // why is the property of type string, not sequence<byte>?
5212  xPropSet->getPropertyValue("CLSID") >>= clsid;
5213  assert(!clsid.isEmpty());
5215  bool const isValid(aClassID.MakeId(clsid));
5216  assert(isValid); (void)isValid;
5217 
5218  if (!SotExchange::IsChart(aClassID))
5219  return false;
5220 
5221  m_aPostponedCharts.push_back(PostponedChart(pSdrObj, rSize, pFlyFrameFormat));
5222  return true;
5223 }
5224 
5225 /*
5226  * Write chart hierarchy in w:drawing after end element of w:rPr tag.
5227  */
5229 {
5230  if (m_aPostponedCharts.empty())
5231  return;
5232 
5233  for (const PostponedChart& rChart : m_aPostponedCharts)
5234  {
5235  uno::Reference< chart2::XChartDocument > xChartDoc;
5236  uno::Reference< drawing::XShape > xShape(const_cast<SdrObject*>(rChart.object)->getUnoShape(), uno::UNO_QUERY );
5237  if( xShape.is() )
5238  {
5239  uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
5240  if( xPropSet.is() )
5241  xChartDoc.set( xPropSet->getPropertyValue( "Model" ), uno::UNO_QUERY );
5242  }
5243 
5244  if( xChartDoc.is() )
5245  {
5246  SAL_INFO("sw.ww8", "DocxAttributeOutput::WriteOLE2Obj: export chart ");
5247 
5248  m_rExport.SdrExporter().startDMLAnchorInline(rChart.frame, rChart.size);
5249 
5250  OUString sName("Object 1");
5251  uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY );
5252  if( xNamed.is() )
5253  sName = xNamed->getName();
5254 
5255  /* If there is a scenario where a chart is followed by a shape
5256  which is being exported as an alternate content then, the
5257  docPr Id is being repeated, ECMA 20.4.2.5 says that the
5258  docPr Id should be unique, ensuring the same here.
5259  */
5260  m_pSerializer->singleElementNS( XML_wp, XML_docPr,
5261  XML_id, OString::number(m_anchorId++),
5262  XML_name, sName );
5263 
5264  m_pSerializer->singleElementNS(XML_wp, XML_cNvGraphicFramePr);
5265 
5266  m_pSerializer->startElementNS( XML_a, XML_graphic,
5267  FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)) );
5268 
5269  m_pSerializer->startElementNS( XML_a, XML_graphicData,
5270  XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" );
5271 
5272  OString aRelId;
5273  m_nChartCount++;
5274  aRelId = m_rExport.OutputChart( xChartDoc, m_nChartCount, m_pSerializer );
5275 
5276  m_pSerializer->singleElementNS( XML_c, XML_chart,
5277  FSNS( XML_xmlns, XML_c ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlChart)),
5278  FSNS( XML_xmlns, XML_r ), GetExport().GetFilter().getNamespaceURL(OOX_NS(officeRel)),
5279  FSNS( XML_r, XML_id ), aRelId );
5280 
5281  m_pSerializer->endElementNS( XML_a, XML_graphicData );
5282  m_pSerializer->endElementNS( XML_a, XML_graphic );
5283 
5284  m_rExport.SdrExporter().endDMLAnchorInline(rChart.frame);
5285  }
5286  }
5287 
5288  m_aPostponedCharts.clear();
5289 }
5290 
5291 bool DocxAttributeOutput::WriteOLEMath( const SwOLENode& rOLENode ,const sal_Int8 nAlign)
5292 {
5293  uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef());
5294  SvGlobalName aObjName(xObj->getClassID());
5295 
5296  if( !SotExchange::IsMath(aObjName) )
5297  return false;
5298 
5299  try
5300  {
5301  PostponedMathObjects aPostponedMathObject;
5302  aPostponedMathObject.pMathObject = const_cast<SwOLENode*>( &rOLENode);
5303  aPostponedMathObject.nMathObjAlignment = nAlign;
5304  m_aPostponedMaths.push_back(aPostponedMathObject);
5305  }
5306  catch (const uno::Exception&)
5307  {
5308  }
5309  return true;
5310 }
5311 
5313 {
5314  uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode*>(pPostponedMath)->GetOLEObj().GetOleRef());
5315  if (embed::EmbedStates::LOADED == xObj->getCurrentState())
5316  {
5317  // must be running so there is a Component
5318  try
5319  {
5320  xObj->changeState(embed::EmbedStates::RUNNING);
5321  }
5322  catch (const uno::Exception&)
5323  {
5324  }
5325  }
5326  uno::Reference< uno::XInterface > xInterface( xObj->getComponent(), uno::UNO_QUERY );
5327  if (!xInterface.is())
5328  {
5329  SAL_WARN("sw.ww8", "Broken math object");
5330  return;
5331  }
5332 // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class,
5333 // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated
5334 // to RTLD_GLOBAL, so most probably a gcc bug.
5335  oox::FormulaExportBase* formulaexport = dynamic_cast<oox::FormulaExportBase*>(dynamic_cast<SfxBaseModel*>(xInterface.get()));
5336  assert( formulaexport != nullptr );
5337  if (formulaexport)
5338  formulaexport->writeFormulaOoxml( m_pSerializer, GetExport().GetFilter().getVersion(),
5340 }
5341 
5343 {
5344  if (!pObject || pObject->GetObjInventor() != SdrInventor::FmForm)
5345  return;
5346 
5347  SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
5348  if (!pFormObj)
5349  return;
5350 
5351  uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
5352  uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
5353  if (!xInfo.is())
5354  return;
5355 
5356  if (xInfo->supportsService("com.sun.star.form.component.DateField"))
5357  {
5358  // gather component properties
5359 
5360  OUString sDateFormat;
5361  uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
5362 
5363  OString sDate;
5364  OUString aContentText;
5365  bool bHasDate = false;
5366  css::util::Date aUNODate;
5367  if (xPropertySet->getPropertyValue("Date") >>= aUNODate)
5368  {
5369  bHasDate = true;
5370  Date aDate(aUNODate.Day, aUNODate.Month, aUNODate.Year);
5371  sDate = DateToOString(aDate);
5372  aContentText = OUString::createFromAscii(DateToDDMMYYYYOString(aDate).getStr());
5373  sDateFormat = "dd/MM/yyyy";
5374  }
5375  else
5376  {
5377  aContentText = xPropertySet->getPropertyValue("HelpText").get<OUString>();
5378  if(sDateFormat.isEmpty())
5379  sDateFormat = "dd/MM/yyyy"; // Need to set date format even if there is no date set
5380  }
5381 
5382  // output component
5383 
5384  m_pSerializer->startElementNS(XML_w, XML_sdt);
5385  m_pSerializer->startElementNS(XML_w, XML_sdtPr);
5386 
5387  if (bHasDate)
5388  m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sDate);
5389  else
5390  m_pSerializer->startElementNS(XML_w, XML_date);
5391 
5392  m_pSerializer->singleElementNS(XML_w, XML_dateFormat, FSNS(XML_w, XML_val), sDateFormat);
5393  m_pSerializer->singleElementNS(XML_w, XML_lid,
5394  FSNS(XML_w, XML_val), "en-US");
5395  m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs,
5396  FSNS(XML_w, XML_val), "dateTime");
5397  m_pSerializer->singleElementNS(XML_w, XML_calendar,
5398  FSNS(XML_w, XML_val), "gregorian");
5399 
5400  m_pSerializer->endElementNS(XML_w, XML_date);
5401  m_pSerializer->endElementNS(XML_w, XML_sdtPr);
5402 
5403  m_pSerializer->startElementNS(XML_w, XML_sdtContent);
5404  m_pSerializer->startElementNS(XML_w, XML_r);
5405 
5406  RunText(aContentText);
5407  m_pSerializer->endElementNS(XML_w, XML_r);
5408  m_pSerializer->endElementNS(XML_w, XML_sdtContent);
5409 
5410  m_pSerializer->endElementNS(XML_w, XML_sdt);
5411  }
5412  else if (xInfo->supportsService("com.sun.star.form.component.ComboBox"))
5413  {
5414  // gather component properties
5415 
5416  uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
5417  OUString sText = xPropertySet->getPropertyValue("Text").get<OUString>();
5418  const uno::Sequence<OUString> aItems = xPropertySet->getPropertyValue("StringItemList").get< uno::Sequence<OUString> >();
5419 
5420  // output component
5421 
5422  m_pSerializer->startElementNS(XML_w, XML_sdt);
5423  m_pSerializer->startElementNS(XML_w, XML_sdtPr);
5424 
5425  m_pSerializer->startElementNS(XML_w, XML_dropDownList);
5426 
5427  for (const auto& rItem : aItems)
5428  {
5429  m_pSerializer->singleElementNS(XML_w, XML_listItem,
5430  FSNS(XML_w, XML_displayText), rItem,
5431  FSNS(XML_w, XML_value), rItem);
5432  }
5433 
5434  m_pSerializer->endElementNS(XML_w, XML_dropDownList);
5435  m_pSerializer->endElementNS(XML_w, XML_sdtPr);
5436 
5437  m_pSerializer->startElementNS(XML_w, XML_sdtContent);
5438  m_pSerializer->startElementNS(X