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