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