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