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