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