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