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