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