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