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