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