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 
43 #include <comphelper/processfactory.hxx>
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>
52 #include <oox/drawingml/drawingmltypes.hxx>
53 #include <oox/token/relationship.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>
68 #include <editeng/crossedoutitem.hxx>
69 #include <editeng/cmapitem.hxx>
70 #include <editeng/udlnitem.hxx>
71 #include <editeng/langitem.hxx>
72 #include <editeng/lspcitem.hxx>
73 #include <editeng/escapementitem.hxx>
74 #include <editeng/fhgtitem.hxx>
75 #include <editeng/colritem.hxx>
76 #include <editeng/hyphenzoneitem.hxx>
77 #include <editeng/ulspitem.hxx>
78 #include <editeng/contouritem.hxx>
79 #include <editeng/shdditem.hxx>
80 #include <editeng/emphasismarkitem.hxx>
81 #include <editeng/twolinesitem.hxx>
82 #include <editeng/charscaleitem.hxx>
83 #include <editeng/charrotateitem.hxx>
84 #include <editeng/charreliefitem.hxx>
85 #include <editeng/paravertalignitem.hxx>
86 #include <editeng/pgrditem.hxx>
87 #include <editeng/frmdiritem.hxx>
88 #include <editeng/blinkitem.hxx>
89 #include <editeng/charhiddenitem.hxx>
90 #include <editeng/editobj.hxx>
91 #include <editeng/keepitem.hxx>
92 #include <editeng/borderline.hxx>
93 #include <editeng/prntitem.hxx>
94 #include <sax/tools/converter.hxx>
95 #include <svx/xdef.hxx>
96 #include <svx/xfillit0.hxx>
97 #include <svx/xflclit.hxx>
98 #include <svx/xflgrit.hxx>
99 #include <svx/svdouno.hxx>
100 #include <svx/unobrushitemhelper.hxx>
101 #include <svl/grabbagitem.hxx>
102 #include <sfx2/sfxbasemodel.hxx>
103 #include <tools/datetimeutils.hxx>
104 #include <tools/UnitConversion.hxx>
105 #include <svl/whiter.hxx>
106 #include <rtl/tencinfo.h>
107 #include <sal/log.hxx>
108 #include <sot/exchange.hxx>
109 
110 #include <docufld.hxx>
111 #include <authfld.hxx>
112 #include <flddropdown.hxx>
113 #include <fmtclds.hxx>
114 #include <fmtinfmt.hxx>
115 #include <fmtrowsplt.hxx>
116 #include <fmtline.hxx>
117 #include <ftninfo.hxx>
118 #include <htmltbl.hxx>
119 #include <lineinfo.hxx>
120 #include <ndgrf.hxx>
121 #include <ndole.hxx>
122 #include <ndtxt.hxx>
123 #include <pagedesc.hxx>
124 #include <paratr.hxx>
125 #include <swmodule.hxx>
126 #include <swtable.hxx>
127 #include <txtftn.hxx>
128 #include <fmtautofmt.hxx>
129 #include <docsh.hxx>
130 #include <docary.hxx>
131 #include <fmtclbl.hxx>
132 #include <IDocumentSettingAccess.hxx>
133 #include <IDocumentRedlineAccess.hxx>
134 #include <grfatr.hxx>
135 #include <frmatr.hxx>
136 #include <txtatr.hxx>
137 #include <frameformats.hxx>
138 
139 #include <o3tl/unit_conversion.hxx>
140 #include <osl/file.hxx>
141 #include <utility>
142 #include <vcl/embeddedfontshelper.hxx>
143 
144 #include <com/sun/star/i18n/ScriptType.hpp>
145 #include <com/sun/star/i18n/XBreakIterator.hpp>
146 #include <com/sun/star/chart2/XChartDocument.hpp>
147 #include <com/sun/star/drawing/ShadingPattern.hpp>
148 #include <com/sun/star/text/GraphicCrop.hpp>
149 #include <com/sun/star/embed/EmbedStates.hpp>
150 #include <com/sun/star/embed/Aspects.hpp>
151 
152 #include <algorithm>
153 #include <stdarg.h>
154 #include <string_view>
155 
156 #include <toolkit/helper/vclunohelper.hxx>
157 #include <unicode/regex.h>
158 
159 using ::editeng::SvxBorderLine;
160 
161 using namespace oox;
162 using namespace docx;
163 using namespace sax_fastparser;
164 using namespace nsSwDocInfoSubType;
165 using namespace sw::util;
166 using namespace ::com::sun::star;
167 using namespace ::com::sun::star::drawing;
168 
169 const sal_Int32 Tag_StartParagraph_1 = 1;
170 const sal_Int32 Tag_StartParagraph_2 = 2;
171 const sal_Int32 Tag_WriteSdtBlock = 3;
172 const sal_Int32 Tag_StartParagraphProperties = 4;
173 const sal_Int32 Tag_InitCollectedParagraphProperties = 5;
174 const sal_Int32 Tag_StartRun_1 = 6;
175 const sal_Int32 Tag_StartRun_2 = 7;
176 const sal_Int32 Tag_StartRun_3 = 8;
177 const sal_Int32 Tag_EndRun_1 = 9;
178 const sal_Int32 Tag_EndRun_2 = 10;
179 const sal_Int32 Tag_StartRunProperties = 11;
180 const sal_Int32 Tag_InitCollectedRunProperties = 12;
181 //static const sal_Int32 Tag_Redline_1 = 13;
182 const sal_Int32 Tag_Redline_2 = 14;
183 const sal_Int32 Tag_TableDefinition = 15;
184 const sal_Int32 Tag_OutputFlyFrame = 16;
185 const sal_Int32 Tag_StartSection = 17;
186 
187 namespace {
188 
189 class FFDataWriterHelper
190 {
191     ::sax_fastparser::FSHelperPtr m_pSerializer;
writeCommonStart(const OUString & rName,const OUString & rEntryMacro,const OUString & rExitMacro,const OUString & rHelp,const OUString & rHint)192     void writeCommonStart( const OUString& rName,
193                            const OUString& rEntryMacro,
194                            const OUString& rExitMacro,
195                            const OUString& rHelp,
196                            const OUString& rHint )
197     {
198         m_pSerializer->startElementNS(XML_w, XML_ffData);
199         m_pSerializer->singleElementNS(XML_w, XML_name, FSNS(XML_w, XML_val), rName);
200         m_pSerializer->singleElementNS(XML_w, XML_enabled);
201         m_pSerializer->singleElementNS(XML_w, XML_calcOnExit, FSNS(XML_w, XML_val), "0");
202 
203         if ( !rEntryMacro.isEmpty() )
204             m_pSerializer->singleElementNS( XML_w, XML_entryMacro,
205                 FSNS(XML_w, XML_val), rEntryMacro );
206 
207         if ( !rExitMacro.isEmpty() )
208             m_pSerializer->singleElementNS(XML_w, XML_exitMacro, FSNS(XML_w, XML_val), rExitMacro);
209 
210         if ( !rHelp.isEmpty() )
211             m_pSerializer->singleElementNS( XML_w, XML_helpText,
212                 FSNS(XML_w, XML_type), "text",
213                 FSNS(XML_w, XML_val), rHelp );
214 
215         if ( !rHint.isEmpty() )
216             m_pSerializer->singleElementNS( XML_w, XML_statusText,
217                 FSNS(XML_w, XML_type), "text",
218                 FSNS(XML_w, XML_val), rHint );
219 
220     }
writeFinish()221     void writeFinish()
222     {
223         m_pSerializer->endElementNS( XML_w, XML_ffData );
224     }
225 public:
FFDataWriterHelper(::sax_fastparser::FSHelperPtr rSerializer)226     explicit FFDataWriterHelper( ::sax_fastparser::FSHelperPtr  rSerializer ) : m_pSerializer(std::move( rSerializer )){}
WriteFormCheckbox(const OUString & rName,const OUString & rEntryMacro,const OUString & rExitMacro,const OUString & rHelp,const OUString & rHint,bool bChecked)227     void WriteFormCheckbox( const OUString& rName,
228                             const OUString& rEntryMacro,
229                             const OUString& rExitMacro,
230                             const OUString& rHelp,
231                             const OUString& rHint,
232                             bool bChecked )
233     {
234         writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint );
235         // Checkbox specific bits
236         m_pSerializer->startElementNS(XML_w, XML_checkBox);
237         // currently hardcoding autosize
238         // #TODO check if this defaulted
239         m_pSerializer->startElementNS(XML_w, XML_sizeAuto);
240         m_pSerializer->endElementNS( XML_w, XML_sizeAuto );
241         if ( bChecked )
242             m_pSerializer->singleElementNS(XML_w, XML_checked);
243         m_pSerializer->endElementNS( XML_w, XML_checkBox );
244         writeFinish();
245     }
246 
WriteFormText(const OUString & rName,const OUString & rEntryMacro,const OUString & rExitMacro,const OUString & rHelp,const OUString & rHint,const OUString & rType,const OUString & rDefaultText,sal_uInt16 nMaxLength,const OUString & rFormat)247     void WriteFormText(  const OUString& rName,
248                          const OUString& rEntryMacro,
249                          const OUString& rExitMacro,
250                          const OUString& rHelp,
251                          const OUString& rHint,
252                          const OUString& rType,
253                          const OUString& rDefaultText,
254                          sal_uInt16 nMaxLength,
255                          const OUString& rFormat )
256     {
257         writeCommonStart( rName, rEntryMacro, rExitMacro, rHelp, rHint );
258 
259         m_pSerializer->startElementNS(XML_w, XML_textInput);
260         if ( !rType.isEmpty() )
261             m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), rType);
262         if ( !rDefaultText.isEmpty() )
263             m_pSerializer->singleElementNS(XML_w, XML_default, FSNS(XML_w, XML_val), rDefaultText);
264         if ( nMaxLength )
265             m_pSerializer->singleElementNS( XML_w, XML_maxLength,
266                 FSNS(XML_w, XML_val), OString::number(nMaxLength) );
267         if ( !rFormat.isEmpty() )
268             m_pSerializer->singleElementNS(XML_w, XML_format, FSNS(XML_w, XML_val), rFormat);
269         m_pSerializer->endElementNS( XML_w, XML_textInput );
270 
271         writeFinish();
272     }
273 };
274 
275 class FieldMarkParamsHelper
276 {
277     const sw::mark::IFieldmark& mrFieldmark;
278     public:
FieldMarkParamsHelper(const sw::mark::IFieldmark & rFieldmark)279     explicit FieldMarkParamsHelper( const sw::mark::IFieldmark& rFieldmark ) : mrFieldmark( rFieldmark ) {}
getName() const280     OUString const & getName() const { return mrFieldmark.GetName(); }
281     template < typename T >
extractParam(const OUString & rKey,T & rResult)282     bool extractParam( const OUString& rKey, T& rResult )
283     {
284         bool bResult = false;
285         if ( mrFieldmark.GetParameters() )
286         {
287             sw::mark::IFieldmark::parameter_map_t::const_iterator it = mrFieldmark.GetParameters()->find( rKey );
288             if ( it != mrFieldmark.GetParameters()->end() )
289                 bResult = ( it->second >>= rResult );
290         }
291         return bResult;
292     }
293 };
294 
295 // [ISO/IEC29500-1:2016] 17.18.50 ST_LongHexNumber (Eight Digit Hexadecimal Value)
NumberToHexBinary(sal_Int32 n)296 OUString NumberToHexBinary(sal_Int32 n)
297 {
298     OUStringBuffer aBuf;
299     sax::Converter::convertNumberToHexBinary(aBuf, n);
300     return aBuf.makeStringAndClear();
301 }
302 
303 }
304 
RTLAndCJKState(bool bIsRTL,sal_uInt16)305 void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ )
306 {
307     if (bIsRTL)
308         m_pSerializer->singleElementNS(XML_w, XML_rtl, FSNS(XML_w, XML_val), "true");
309 }
310 
311 /// Are multiple paragraphs disallowed inside this type of SDT?
lcl_isOnelinerSdt(std::u16string_view rName)312 static bool lcl_isOnelinerSdt(std::u16string_view rName)
313 {
314     return rName == u"Title" || rName == u"Subtitle" || rName == u"Company";
315 }
316 
317 // write a floating table directly to docx without the surrounding frame
WriteFloatingTable(ww8::Frame const * pParentFrame)318 void DocxAttributeOutput::WriteFloatingTable(ww8::Frame const* pParentFrame)
319 {
320     const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
321     m_aFloatingTablesOfParagraph.insert(&rFrameFormat);
322     const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
323 
324     sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0;
325     sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0;
326 
327     //Save data here and restore when out of scope
328     ExportDataSaveRestore aDataGuard(GetExport(), nStt, nEnd, pParentFrame);
329 
330     // set a floatingTableFrame AND unset parent frame,
331     // otherwise exporter thinks we are still in a frame
332     m_rExport.SetFloatingTableFrame(pParentFrame);
333     m_rExport.m_pParentFrame = nullptr;
334 
335     GetExport().WriteText();
336 
337     m_rExport.SetFloatingTableFrame(nullptr);
338 }
339 
checkAndWriteFloatingTables(DocxAttributeOutput & rDocxAttributeOutput)340 static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutput)
341 {
342     const auto& rExport = rDocxAttributeOutput.GetExport();
343     // iterate though all SpzFrameFormats and check whether they are anchored to the current text node
344     for( sal_uInt16 nCnt = rExport.m_rDoc.GetSpzFrameFormats()->size(); nCnt; )
345     {
346         const SwFrameFormat* pFrameFormat = (*rExport.m_rDoc.GetSpzFrameFormats())[ --nCnt ];
347         const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
348         const SwPosition* pPosition = rAnchor.GetContentAnchor();
349 
350         if (!pPosition || ! rExport.m_pCurPam->GetNode().GetTextNode())
351             continue;
352 
353         if (pPosition->nNode != rExport.m_pCurPam->GetNode().GetTextNode()->GetIndex())
354             continue;
355 
356         const SwNodeIndex* pStartNode = pFrameFormat->GetContent().GetContentIdx();
357         if (!pStartNode)
358             continue;
359 
360         SwNodeIndex aStartNode = *pStartNode;
361 
362         // go to the next node (actual content)
363         ++aStartNode;
364 
365         // this has to be a table
366         if (!aStartNode.GetNode().IsTableNode())
367             continue;
368 
369         // go to the end of the table
370         sal_uLong aEndIndex = aStartNode.GetNode().EndOfSectionIndex();
371         // go one deeper
372         aEndIndex++;
373         // this has to be the end of the content
374         if (aEndIndex != pFrameFormat->GetContent().GetContentIdx()->GetNode().EndOfSectionIndex())
375             continue;
376 
377         // check for a grabBag and "TablePosition" attribute -> then we can export the table directly
378         SwTableNode* pTableNode = aStartNode.GetNode().GetTableNode();
379         SwTable& rTable = pTableNode->GetTable();
380         SwFrameFormat* pTableFormat = rTable.GetFrameFormat();
381         const SfxGrabBagItem* pTableGrabBag = pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG);
382         std::map<OUString, css::uno::Any> aTableGrabBag = pTableGrabBag->GetGrabBag();
383         // no grabbag?
384         if (aTableGrabBag.find("TablePosition") == aTableGrabBag.end())
385             continue;
386 
387         // write table to docx
388         ww8::Frame aFrame(*pFrameFormat,*pPosition);
389         rDocxAttributeOutput.WriteFloatingTable(&aFrame);
390     }
391 }
392 
StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,bool bGenerateParaId)393 sal_Int32 DocxAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
394                                               bool bGenerateParaId)
395 {
396     // Paragraphs (in headers/footers/comments/frames etc) can start before another finishes.
397     // So a stack is needed to keep track of each paragraph's status separately.
398     // Complication: Word can't handle nested text boxes, so those need to be collected together.
399     if ( !m_aFramesOfParagraph.size() || !m_nTextFrameLevel )
400         m_aFramesOfParagraph.push(std::vector<ww8::Frame>());
401 
402     // look ahead for floating tables that were put into a frame during import
403     // floating tables in shapes are not supported: exclude this case
404     if (!pTextNodeInfo && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
405     {
406         checkAndWriteFloatingTables(*this);
407     }
408 
409     if ( m_nColBreakStatus == COLBRK_POSTPONE )
410         m_nColBreakStatus = COLBRK_WRITE;
411 
412     // Output table/table row/table cell starts if needed
413     if ( pTextNodeInfo )
414     {
415         // New cell/row?
416         if ( m_tableReference->m_nTableDepth > 0 && !m_tableReference->m_bTableCellOpen )
417         {
418             ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( pTextNodeInfo->getInnerForDepth( m_tableReference->m_nTableDepth ) );
419             if ( pDeepInner->getCell() == 0 )
420                 StartTableRow( pDeepInner );
421 
422             const sal_uInt32 nCell = pDeepInner->getCell();
423             const sal_uInt32 nRow = pDeepInner->getRow();
424 
425             SyncNodelessCells(pDeepInner, nCell, nRow);
426             StartTableCell(pDeepInner, nCell, nRow);
427         }
428 
429         sal_uInt32 nRow = pTextNodeInfo->getRow();
430         sal_uInt32 nCell = pTextNodeInfo->getCell();
431         if (nCell == 0)
432         {
433             // Do we have to start the table?
434             // [If we are at the right depth already, it means that we
435             // continue the table cell]
436             sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
437 
438             if ( nCurrentDepth > m_tableReference->m_nTableDepth )
439             {
440                 // Start all the tables that begin here
441                 for ( sal_uInt32 nDepth = m_tableReference->m_nTableDepth + 1; nDepth <= nCurrentDepth; ++nDepth )
442                 {
443                     ww8::WW8TableNodeInfoInner::Pointer_t pInner( pTextNodeInfo->getInnerForDepth( nDepth ) );
444 
445                     StartTable( pInner );
446                     StartTableRow( pInner );
447 
448                     StartTableCell(pInner, 0, nDepth == nCurrentDepth ? nRow : 0);
449                 }
450 
451                 m_tableReference->m_nTableDepth = nCurrentDepth;
452             }
453         }
454     }
455 
456     // Look up the "sdt end before this paragraph" property early, when it
457     // would normally arrive, it would be too late (would be after the
458     // paragraph start has been written).
459     bool bEndParaSdt = false;
460     if (m_bStartedParaSdt)
461     {
462         SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode();
463         if (pTextNode && pTextNode->GetpSwAttrSet())
464         {
465             const SfxItemSet* pSet = pTextNode->GetpSwAttrSet();
466             if (const SfxPoolItem* pItem = pSet->GetItem(RES_PARATR_GRABBAG))
467             {
468                 const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem);
469                 const std::map<OUString, css::uno::Any>& rMap = rParaGrabBag.GetGrabBag();
470                 bEndParaSdt = m_bStartedParaSdt && rMap.find("ParaSdtEndBefore") != rMap.end();
471             }
472         }
473     }
474     // TODO also avoid multiline paragraphs in those SDT types for shape text
475     bool bOneliner = m_bStartedParaSdt && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias);
476     if (bEndParaSdt || (m_bStartedParaSdt && m_bHadSectPr) || bOneliner)
477     {
478         // This is the common case: "close sdt before the current paragraph" was requested by the next paragraph.
479         EndSdtBlock();
480         m_bStartedParaSdt = false;
481         m_aStartedParagraphSdtPrAlias.clear();
482     }
483     m_bHadSectPr = false;
484 
485     // this mark is used to be able to enclose the paragraph inside a sdr tag.
486     // We will only know if we have to do that later.
487     m_pSerializer->mark(Tag_StartParagraph_1);
488 
489     std::optional<OUString> aParaId;
490     sal_Int32 nParaId = 0;
491     if (bGenerateParaId)
492     {
493         nParaId = m_nNextParaId++;
494         aParaId = NumberToHexBinary(nParaId);
495     }
496     m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId);
497 
498     // postpone the output of the run (we get it before the paragraph
499     // properties, but must write it after them)
500     m_pSerializer->mark(Tag_StartParagraph_2);
501 
502     // no section break in this paragraph yet; can be set in SectionBreak()
503     m_pSectionInfo.reset();
504 
505     m_bParagraphOpened = true;
506     m_bIsFirstParagraph = false;
507 
508     return nParaId;
509 }
510 
convertToOOXMLVertOrient(sal_Int16 nOrient)511 static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
512 {
513     switch( nOrient )
514     {
515         case text::VertOrientation::CENTER:
516         case text::VertOrientation::LINE_CENTER:
517             return "center";
518         case text::VertOrientation::BOTTOM:
519             return "bottom";
520         case text::VertOrientation::LINE_BOTTOM:
521             return "outside";
522         case text::VertOrientation::TOP:
523             return "top";
524         case text::VertOrientation::LINE_TOP:
525             return "inside";
526         default:
527             return OString();
528     }
529 }
530 
convertToOOXMLHoriOrient(sal_Int16 nOrient,bool bIsPosToggle)531 static OString convertToOOXMLHoriOrient(sal_Int16 nOrient, bool bIsPosToggle)
532 {
533     switch( nOrient )
534     {
535         case text::HoriOrientation::LEFT:
536             return bIsPosToggle ? "inside" : "left";
537         case text::HoriOrientation::INSIDE:
538             return "inside";
539         case text::HoriOrientation::RIGHT:
540             return bIsPosToggle ? "outside" : "right";
541         case text::HoriOrientation::OUTSIDE:
542             return "outside";
543         case text::HoriOrientation::CENTER:
544         case text::HoriOrientation::FULL:
545             return "center";
546         default:
547             return OString();
548     }
549 }
550 
convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)551 static OString convertToOOXMLVertOrientRel(sal_Int16 nOrientRel)
552 {
553     switch (nOrientRel)
554     {
555         case text::RelOrientation::PAGE_PRINT_AREA:
556             return "margin";
557         case text::RelOrientation::PAGE_FRAME:
558             return "page";
559         case text::RelOrientation::FRAME:
560         case text::RelOrientation::TEXT_LINE:
561         default:
562             return "text";
563     }
564 }
565 
convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)566 static OString convertToOOXMLHoriOrientRel(sal_Int16 nOrientRel)
567 {
568     switch (nOrientRel)
569     {
570         case text::RelOrientation::PAGE_PRINT_AREA:
571             return "margin";
572         case text::RelOrientation::PAGE_FRAME:
573             return "page";
574         case text::RelOrientation::CHAR:
575         case text::RelOrientation::PAGE_RIGHT:
576         case text::RelOrientation::FRAME:
577         default:
578             return "text";
579     }
580 }
581 
lcl_deleteAndResetTheLists(rtl::Reference<sax_fastparser::FastAttributeList> & pSdtPrTokenChildren,rtl::Reference<sax_fastparser::FastAttributeList> & pSdtPrDataBindingAttrs,rtl::Reference<sax_fastparser::FastAttributeList> & pSdtPrTextAttrs,OUString & rSdtPrAlias)582 static void lcl_deleteAndResetTheLists(
583     rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren,
584     rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs,
585     rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTextAttrs,
586     OUString& rSdtPrAlias)
587 {
588     if( pSdtPrTokenChildren.is() )
589         pSdtPrTokenChildren.clear();
590     if( pSdtPrDataBindingAttrs.is() )
591         pSdtPrDataBindingAttrs.clear();
592     if (pSdtPrTextAttrs.is())
593         pSdtPrTextAttrs.clear();
594     if (!rSdtPrAlias.isEmpty())
595         rSdtPrAlias.clear();
596 }
597 
PopulateFrameProperties(const SwFrameFormat * pFrameFormat,const Size & rSize)598 void DocxAttributeOutput::PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize)
599 {
600     rtl::Reference<sax_fastparser::FastAttributeList> attrList = FastSerializerHelper::createAttrList();
601 
602     awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(), pFrameFormat->GetVertOrient().GetPos());
603 
604     attrList->add( FSNS( XML_w, XML_w), OString::number(rSize.Width()));
605     attrList->add( FSNS( XML_w, XML_h), OString::number(rSize.Height()));
606 
607     attrList->add( FSNS( XML_w, XML_x), OString::number(aPos.X));
608     attrList->add( FSNS( XML_w, XML_y), OString::number(aPos.Y));
609 
610     sal_Int16 nLeft = pFrameFormat->GetLRSpace().GetLeft();
611     sal_Int16 nRight = pFrameFormat->GetLRSpace().GetRight();
612     sal_Int16 nUpper = pFrameFormat->GetULSpace().GetUpper();
613     sal_Int16 nLower = pFrameFormat->GetULSpace().GetLower();
614 
615     attrList->add(FSNS(XML_w, XML_hSpace), OString::number((nLeft + nRight) / 2));
616     attrList->add(FSNS(XML_w, XML_vSpace), OString::number((nUpper + nLower) / 2));
617 
618     OString relativeFromH = convertToOOXMLHoriOrientRel( pFrameFormat->GetHoriOrient().GetRelationOrient() );
619     OString relativeFromV = convertToOOXMLVertOrientRel( pFrameFormat->GetVertOrient().GetRelationOrient() );
620 
621     switch (pFrameFormat->GetSurround().GetValue())
622     {
623     case css::text::WrapTextMode_NONE:
624         attrList->add( FSNS( XML_w, XML_wrap), "notBeside");
625         break;
626     case css::text::WrapTextMode_DYNAMIC:
627         attrList->add(FSNS(XML_w, XML_wrap), "auto");
628         break;
629     case css::text::WrapTextMode_PARALLEL:
630     default:
631         attrList->add(FSNS(XML_w, XML_wrap), "around");
632         break;
633     }
634     attrList->add( FSNS( XML_w, XML_vAnchor), relativeFromV );
635     attrList->add( FSNS( XML_w, XML_hAnchor), relativeFromH );
636     attrList->add( FSNS( XML_w, XML_hRule), "exact");
637 
638     m_pSerializer->singleElementNS( XML_w, XML_framePr, attrList );
639 }
640 
TextBoxIsFramePr(const SwFrameFormat & rFrameFormat)641 bool DocxAttributeOutput::TextBoxIsFramePr(const SwFrameFormat& rFrameFormat)
642 {
643     uno::Reference< drawing::XShape > xShape;
644     const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
645     if (pSdrObj)
646         xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
647     uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY);
648     uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
649     if (xPropertySet.is())
650         xPropSetInfo = xPropertySet->getPropertySetInfo();
651     uno::Any aFrameProperties ;
652     if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
653     {
654         uno::Sequence< beans::PropertyValue > propList;
655         xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
656         auto pProp = std::find_if(propList.begin(), propList.end(),
657             [](const beans::PropertyValue& rProp) { return rProp.Name == "ParaFrameProperties"; });
658         if (pProp != propList.end())
659             aFrameProperties = pProp->Value;
660     }
661     bool bFrameProperties = false;
662     aFrameProperties >>= bFrameProperties;
663     return bFrameProperties;
664 }
665 
EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner)666 void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
667 {
668     // write the paragraph properties + the run, already in the correct order
669     m_pSerializer->mergeTopMarks(Tag_StartParagraph_2);
670     std::vector<  std::shared_ptr <ww8::Frame> > aFramePrTextbox;
671     // Write the anchored frame if any
672     // Word can't handle nested text boxes, so write them on the same level.
673     ++m_nTextFrameLevel;
674     if( m_nTextFrameLevel == 1 && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
675     {
676         comphelper::FlagRestorationGuard aStartedParaSdtGuard(m_bStartedParaSdt, false);
677 
678         assert(!m_pPostponedCustomShape);
679         m_pPostponedCustomShape.reset(new std::vector<PostponedDrawing>);
680 
681         // The for loop can change the size of m_aFramesOfParagraph, so the max size cannot be set in stone before the loop.
682         size_t nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
683         for (size_t nIndex = 0; nIndex < nFrames; ++nIndex)
684         {
685             m_bParagraphFrameOpen = true;
686             ww8::Frame aFrame = m_aFramesOfParagraph.top()[nIndex];
687             const SwFrameFormat& rFrameFormat = aFrame.GetFrameFormat();
688 
689             if (!TextBoxIsFramePr(rFrameFormat) || m_bWritingHeaderFooter)
690             {
691                 if (m_bStartedCharSdt)
692                 {
693                     // Run-level SDT still open? Close it before AlternateContent.
694                     EndSdtBlock();
695                     m_bStartedCharSdt = false;
696                 }
697                 m_pSerializer->startElementNS(XML_w, XML_r);
698                 m_pSerializer->startElementNS(XML_mc, XML_AlternateContent);
699                 m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "wps");
700                 /**
701                     This is to avoid AlternateContent within another AlternateContent.
702                        So when Choice is Open, only write the DML Drawing instead of both DML
703                        and VML Drawing in another AlternateContent.
704                  **/
705                 SetAlternateContentChoiceOpen( true );
706                 /** Save the table info's before writing the shape
707                         as there might be a new table that might get
708                         spawned from within the VML & DML block and alter
709                         the contents.
710                 */
711                 ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.m_pTableInfo;
712                 //Reset the table infos after saving.
713                 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
714 
715                 /** FDO#71834 :
716                        Save the table reference attributes before calling WriteDMLTextFrame,
717                        otherwise the StartParagraph function will use the previous existing
718                        table reference attributes since the variable is being shared.
719                 */
720                 {
721                     DocxTableExportContext aDMLTableExportContext(*this);
722                     m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++);
723                 }
724                 m_pSerializer->endElementNS(XML_mc, XML_Choice);
725                 SetAlternateContentChoiceOpen( false );
726 
727                 // Reset table infos, otherwise the depth of the cells will be incorrect,
728                 // in case the text frame had table(s) and we try to export the
729                 // same table second time.
730                 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
731                 //reset the tableReference.
732 
733                 m_pSerializer->startElementNS(XML_mc, XML_Fallback);
734                 {
735                     DocxTableExportContext aVMLTableExportContext(*this);
736                     m_rExport.SdrExporter().writeVMLTextFrame(&aFrame);
737                 }
738                 m_rExport.m_pTableInfo = pOldTableInfo;
739 
740                 m_pSerializer->endElementNS(XML_mc, XML_Fallback);
741                 m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);
742                 m_pSerializer->endElementNS( XML_w, XML_r );
743                 m_bParagraphFrameOpen = false;
744             }
745             else
746             {
747                 std::shared_ptr<ww8::Frame> pFramePr = std::make_shared<ww8::Frame>(aFrame);
748                 aFramePrTextbox.push_back(pFramePr);
749             }
750 
751             nFrames = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
752         }
753         if (!m_pPostponedCustomShape->empty())
754         {
755             m_pSerializer->startElementNS(XML_w, XML_r);
756             WritePostponedCustomShape();
757             m_pSerializer->endElementNS( XML_w, XML_r );
758         }
759         m_pPostponedCustomShape.reset();
760 
761         if ( m_aFramesOfParagraph.size() )
762             m_aFramesOfParagraph.top().clear();
763 
764         if (!pTextNodeInfoInner)
765         {
766             // Ending a non-table paragraph, clear floating tables before paragraph.
767             m_aFloatingTablesOfParagraph.clear();
768         }
769     }
770 
771     --m_nTextFrameLevel;
772     if ( m_aFramesOfParagraph.size() && !m_nTextFrameLevel )
773         m_aFramesOfParagraph.pop();
774 
775     /* If m_nHyperLinkCount > 0 that means hyperlink tag is not yet closed.
776      * This is due to nested hyperlink tags. So close it before end of paragraph.
777      */
778     if(m_nHyperLinkCount > 0)
779     {
780         for(sal_Int32 nHyperLinkToClose = 0; nHyperLinkToClose < m_nHyperLinkCount; ++nHyperLinkToClose)
781             m_pSerializer->endElementNS( XML_w, XML_hyperlink );
782         m_nHyperLinkCount = 0;
783     }
784 
785     if (m_bStartedCharSdt)
786     {
787         // Run-level SDT still open? Close it now.
788         EndSdtBlock();
789         m_bStartedCharSdt = false;
790     }
791 
792     if (m_bPageBreakAfter)
793     {
794         // tdf#128889 Trailing page break
795         SectionBreak(msword::PageBreak, false);
796         m_bPageBreakAfter = false;
797     }
798 
799     m_pSerializer->endElementNS( XML_w, XML_p );
800     // on export sdt blocks are never nested ATM
801     if( !m_bAnchorLinkedToNode && !m_bStartedParaSdt )
802         WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrTokenAttributes, m_pParagraphSdtPrDataBindingAttrs, m_pParagraphSdtPrTextAttrs, m_aParagraphSdtPrAlias, /*bPara=*/true );
803     else
804     {
805         //These should be written out to the actual Node and not to the anchor.
806         //Clear them as they will be repopulated when the node is processed.
807         m_nParagraphSdtPrToken = 0;
808         m_bParagraphSdtHasId = false;
809         lcl_deleteAndResetTheLists( m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs, m_pParagraphSdtPrTextAttrs, m_aParagraphSdtPrAlias );
810     }
811 
812     m_pSerializer->mark(Tag_StartParagraph_2);
813 
814     // Write framePr
815     for ( const auto & pFrame : aFramePrTextbox )
816     {
817         DocxTableExportContext aTableExportContext(*this);
818         m_pCurrentFrame = pFrame.get();
819         m_rExport.SdrExporter().writeOnlyTextOfFrame(pFrame.get());
820         m_pCurrentFrame = nullptr;
821     }
822 
823     m_pSerializer->mergeTopMarks(Tag_StartParagraph_2, sax_fastparser::MergeMarks::PREPEND);
824 
825     //sdtcontent is written so Set m_bParagraphHasDrawing to false
826     m_rExport.SdrExporter().setParagraphHasDrawing(false);
827     m_bRunTextIsOn = false;
828     m_pSerializer->mergeTopMarks(Tag_StartParagraph_1);
829 
830     aFramePrTextbox.clear();
831     // Check for end of cell, rows, tables here
832     FinishTableRowCell( pTextNodeInfoInner );
833 
834     if( !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
835         m_bParagraphOpened = false;
836 
837     // Clear bookmarks of the current paragraph
838     m_aBookmarksOfParagraphStart.clear();
839     m_aBookmarksOfParagraphEnd.clear();
840 }
841 
WriteSdtBlock(sal_Int32 & nSdtPrToken,rtl::Reference<sax_fastparser::FastAttributeList> & pSdtPrTokenChildren,rtl::Reference<sax_fastparser::FastAttributeList> & pSdtPrTokenAttributes,rtl::Reference<sax_fastparser::FastAttributeList> & pSdtPrDataBindingAttrs,rtl::Reference<sax_fastparser::FastAttributeList> & pSdtPrTextAttrs,OUString & rSdtPrAlias,bool bPara)842 void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken,
843                                          rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren,
844                                          rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenAttributes,
845                                          rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs,
846                                          rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTextAttrs,
847                                          OUString& rSdtPrAlias,
848                                          bool bPara )
849 {
850     if( nSdtPrToken <= 0 && !pSdtPrDataBindingAttrs.is() )
851         return;
852 
853     // sdt start mark
854     m_pSerializer->mark(Tag_WriteSdtBlock);
855 
856     m_pSerializer->startElementNS(XML_w, XML_sdt);
857 
858     // output sdt properties
859     m_pSerializer->startElementNS(XML_w, XML_sdtPr);
860 
861     if( nSdtPrToken > 0 && pSdtPrTokenChildren.is() )
862     {
863         if (!pSdtPrTokenAttributes.is())
864             m_pSerializer->startElement(nSdtPrToken);
865         else
866         {
867             rtl::Reference<FastAttributeList> xAttrList = std::move(pSdtPrTokenAttributes);
868             m_pSerializer->startElement(nSdtPrToken, xAttrList);
869         }
870 
871         if (nSdtPrToken ==  FSNS( XML_w, XML_date ) || nSdtPrToken ==  FSNS( XML_w, XML_docPartObj ) || nSdtPrToken ==  FSNS( XML_w, XML_docPartList ) || nSdtPrToken ==  FSNS( XML_w14, XML_checkbox )) {
872             const uno::Sequence<xml::FastAttribute> aChildren = pSdtPrTokenChildren->getFastAttributes();
873             for( const auto& rChild : aChildren )
874                 m_pSerializer->singleElement(rChild.Token, FSNS(XML_w, XML_val), rChild.Value);
875         }
876 
877         m_pSerializer->endElement( nSdtPrToken );
878     }
879     else if( (nSdtPrToken > 0) && nSdtPrToken != FSNS( XML_w, XML_id ) && !(m_bRunTextIsOn && m_rExport.SdrExporter().IsParagraphHasDrawing()))
880     {
881         if (!pSdtPrTokenAttributes.is())
882             m_pSerializer->singleElement(nSdtPrToken);
883         else
884         {
885             rtl::Reference<FastAttributeList> xAttrList = std::move(pSdtPrTokenAttributes);
886             m_pSerializer->singleElement(nSdtPrToken, xAttrList);
887         }
888     }
889 
890     if( nSdtPrToken == FSNS( XML_w, XML_id ) || ( bPara && m_bParagraphSdtHasId ) )
891         //Word won't open a document with an empty id tag, we fill it with a random number
892         m_pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val),
893                                       OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())));
894 
895     if( pSdtPrDataBindingAttrs.is() && !m_rExport.SdrExporter().IsParagraphHasDrawing())
896     {
897         rtl::Reference<FastAttributeList> xAttrList = std::move( pSdtPrDataBindingAttrs );
898         m_pSerializer->singleElementNS(XML_w, XML_dataBinding, xAttrList);
899     }
900 
901     if (pSdtPrTextAttrs.is())
902     {
903         rtl::Reference<FastAttributeList> xAttrList = std::move(pSdtPrTextAttrs);
904         m_pSerializer->singleElementNS(XML_w, XML_text, xAttrList);
905     }
906 
907     if (!rSdtPrAlias.isEmpty())
908         m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), rSdtPrAlias);
909 
910     m_pSerializer->endElementNS( XML_w, XML_sdtPr );
911 
912     // sdt contents start tag
913     m_pSerializer->startElementNS(XML_w, XML_sdtContent);
914 
915     // prepend the tags since the sdt start mark before the paragraph
916     m_pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND);
917 
918     // write the ending tags after the paragraph
919     if (bPara)
920     {
921         m_bStartedParaSdt = true;
922         if (m_tableReference->m_bTableCellOpen)
923             m_tableReference->m_bTableCellParaSdtOpen = true;
924         if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
925             m_rExport.SdrExporter().setParagraphSdtOpen(true);
926     }
927     else
928         // Support multiple runs inside a run-level SDT: don't close the SDT block yet.
929         m_bStartedCharSdt = true;
930 
931     // clear sdt status
932     nSdtPrToken = 0;
933     pSdtPrTokenChildren.clear();
934     pSdtPrDataBindingAttrs.clear();
935     pSdtPrTextAttrs.clear();
936     rSdtPrAlias.clear();
937 
938 }
939 
EndSdtBlock()940 void DocxAttributeOutput::EndSdtBlock()
941 {
942     m_pSerializer->endElementNS( XML_w, XML_sdtContent );
943     m_pSerializer->endElementNS( XML_w, XML_sdt );
944 }
945 
946 #define MAX_CELL_IN_WORD 62
947 
SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner,sal_Int32 nCell,sal_uInt32 nRow)948 void DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow)
949 {
950     sal_Int32 nOpenCell = lastOpenCell.back();
951     if (nOpenCell != -1 && nOpenCell != nCell && nOpenCell < MAX_CELL_IN_WORD)
952         EndTableCell(nOpenCell);
953 
954     sal_Int32 nClosedCell = lastClosedCell.back();
955     for (sal_Int32 i = nClosedCell+1; i < nCell; ++i)
956     {
957         if (i >= MAX_CELL_IN_WORD)
958             break;
959 
960         if (i == 0)
961             StartTableRow(pInner);
962 
963         StartTableCell(pInner, i, nRow);
964         m_pSerializer->singleElementNS(XML_w, XML_p);
965         EndTableCell(i);
966     }
967 }
968 
FinishTableRowCell(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner,bool bForceEmptyParagraph)969 void DocxAttributeOutput::FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, bool bForceEmptyParagraph )
970 {
971     if ( !pInner )
972         return;
973 
974     // Where are we in the table
975     sal_uInt32 nRow = pInner->getRow();
976     sal_Int32 nCell = pInner->getCell();
977 
978     InitTableHelper( pInner );
979 
980     // HACK
981     // msoffice seems to have an internal limitation of 63 columns for tables
982     // and refuses to load .docx with more, even though the spec seems to allow that;
983     // so simply if there are more columns, don't close the last one msoffice will handle
984     // and merge the contents of the remaining ones into it (since we don't close the cell
985     // here, following ones will not be opened)
986     const bool limitWorkaround = (nCell >= MAX_CELL_IN_WORD && !pInner->isEndOfLine());
987     const bool bEndCell = pInner->isEndOfCell() && !limitWorkaround;
988     const bool bEndRow = pInner->isEndOfLine();
989 
990     if (bEndCell)
991     {
992         while (pInner->getDepth() < m_tableReference->m_nTableDepth)
993         {
994             //we expect that the higher depth row was closed, and
995             //we are just missing the table close
996             assert(lastOpenCell.back() == -1 && lastClosedCell.back() == -1);
997             EndTable();
998         }
999 
1000         SyncNodelessCells(pInner, nCell, nRow);
1001 
1002         sal_Int32 nClosedCell = lastClosedCell.back();
1003         if (nCell == nClosedCell)
1004         {
1005             //Start missing trailing cell(s)
1006             ++nCell;
1007             StartTableCell(pInner, nCell, nRow);
1008 
1009             //Continue on missing next trailing cell(s)
1010             ww8::RowSpansPtr xRowSpans = pInner->getRowSpansOfRow();
1011             sal_Int32 nRemainingCells = xRowSpans->size() - nCell;
1012             for (sal_Int32 i = 1; i < nRemainingCells; ++i)
1013             {
1014                 if (bForceEmptyParagraph)
1015                 {
1016                     m_pSerializer->singleElementNS(XML_w, XML_p);
1017                 }
1018 
1019                 EndTableCell(nCell);
1020 
1021                 StartTableCell(pInner, nCell, nRow);
1022             }
1023         }
1024 
1025         if (bForceEmptyParagraph)
1026         {
1027             m_pSerializer->singleElementNS(XML_w, XML_p);
1028         }
1029 
1030         EndTableCell(nCell);
1031     }
1032 
1033     // This is a line end
1034     if (bEndRow)
1035         EndTableRow();
1036 
1037     // This is the end of the table
1038     if (pInner->isFinalEndOfLine())
1039         EndTable();
1040 }
1041 
EmptyParagraph()1042 void DocxAttributeOutput::EmptyParagraph()
1043 {
1044     m_pSerializer->singleElementNS(XML_w, XML_p);
1045 }
1046 
SectionBreaks(const SwNode & rNode)1047 void DocxAttributeOutput::SectionBreaks(const SwNode& rNode)
1048 {
1049     // output page/section breaks
1050     // Writer can have them at the beginning of a paragraph, or at the end, but
1051     // in docx, we have to output them in the paragraph properties of the last
1052     // paragraph in a section.  To get it right, we have to switch to the next
1053     // paragraph, and detect the section breaks there.
1054     SwNodeIndex aNextIndex( rNode, 1 );
1055 
1056     if (rNode.IsTextNode() || rNode.IsSectionNode())
1057     {
1058         if (aNextIndex.GetNode().IsTextNode())
1059         {
1060             const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
1061             m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen);
1062         }
1063         else if (aNextIndex.GetNode().IsTableNode())
1064         {
1065             const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
1066             const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
1067             m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
1068         }
1069     }
1070     else if (rNode.IsEndNode())
1071     {
1072         if (aNextIndex.GetNode().IsTextNode())
1073         {
1074             // Handle section break between a table and a text node following it.
1075             // Also handle section endings
1076             const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
1077             if (rNode.StartOfSectionNode()->IsTableNode() || rNode.StartOfSectionNode()->IsSectionNode())
1078                 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen);
1079         }
1080         else if (aNextIndex.GetNode().IsTableNode())
1081         {
1082             // Handle section break between tables.
1083             const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
1084             const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
1085             m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
1086         }
1087     }
1088 }
1089 
StartParagraphProperties()1090 void DocxAttributeOutput::StartParagraphProperties()
1091 {
1092     m_pSerializer->mark(Tag_StartParagraphProperties);
1093 
1094     m_pSerializer->startElementNS(XML_w, XML_pPr);
1095 
1096     // and output the section break now (if it appeared)
1097     if (m_pSectionInfo && m_rExport.m_nTextTyp == TXT_MAINTEXT)
1098     {
1099         m_rExport.SectionProperties( *m_pSectionInfo );
1100         m_pSectionInfo.reset();
1101     }
1102 
1103     InitCollectedParagraphProperties();
1104 }
1105 
InitCollectedParagraphProperties()1106 void DocxAttributeOutput::InitCollectedParagraphProperties()
1107 {
1108     m_pParagraphSpacingAttrList.clear();
1109 
1110     // Write the elements in the spec order
1111     static const sal_Int32 aOrder[] =
1112     {
1113         FSNS( XML_w, XML_pStyle ),
1114         FSNS( XML_w, XML_keepNext ),
1115         FSNS( XML_w, XML_keepLines ),
1116         FSNS( XML_w, XML_pageBreakBefore ),
1117         FSNS( XML_w, XML_framePr ),
1118         FSNS( XML_w, XML_widowControl ),
1119         FSNS( XML_w, XML_numPr ),
1120         FSNS( XML_w, XML_suppressLineNumbers ),
1121         FSNS( XML_w, XML_pBdr ),
1122         FSNS( XML_w, XML_shd ),
1123         FSNS( XML_w, XML_tabs ),
1124         FSNS( XML_w, XML_suppressAutoHyphens ),
1125         FSNS( XML_w, XML_kinsoku ),
1126         FSNS( XML_w, XML_wordWrap ),
1127         FSNS( XML_w, XML_overflowPunct ),
1128         FSNS( XML_w, XML_topLinePunct ),
1129         FSNS( XML_w, XML_autoSpaceDE ),
1130         FSNS( XML_w, XML_autoSpaceDN ),
1131         FSNS( XML_w, XML_bidi ),
1132         FSNS( XML_w, XML_adjustRightInd ),
1133         FSNS( XML_w, XML_snapToGrid ),
1134         FSNS( XML_w, XML_spacing ),
1135         FSNS( XML_w, XML_ind ),
1136         FSNS( XML_w, XML_contextualSpacing ),
1137         FSNS( XML_w, XML_mirrorIndents ),
1138         FSNS( XML_w, XML_suppressOverlap ),
1139         FSNS( XML_w, XML_jc ),
1140         FSNS( XML_w, XML_textDirection ),
1141         FSNS( XML_w, XML_textAlignment ),
1142         FSNS( XML_w, XML_textboxTightWrap ),
1143         FSNS( XML_w, XML_outlineLvl ),
1144         FSNS( XML_w, XML_divId ),
1145         FSNS( XML_w, XML_cnfStyle ),
1146         FSNS( XML_w, XML_rPr ),
1147         FSNS( XML_w, XML_sectPr ),
1148         FSNS( XML_w, XML_pPrChange )
1149     };
1150 
1151     // postpone the output so that we can later [in EndParagraphProperties()]
1152     // prepend the properties before the run
1153     m_pSerializer->mark(Tag_InitCollectedParagraphProperties, comphelper::containerToSequence(aOrder));
1154 }
1155 
WriteCollectedParagraphProperties()1156 void DocxAttributeOutput::WriteCollectedParagraphProperties()
1157 {
1158     if ( m_rExport.SdrExporter().getFlyAttrList().is() )
1159     {
1160         rtl::Reference<FastAttributeList> xAttrList( m_rExport.SdrExporter().getFlyAttrList() );
1161         m_rExport.SdrExporter().getFlyAttrList().clear();
1162 
1163         m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList );
1164     }
1165 
1166     if ( m_pParagraphSpacingAttrList.is() )
1167     {
1168         rtl::Reference<FastAttributeList> xAttrList = std::move( m_pParagraphSpacingAttrList );
1169         m_pSerializer->singleElementNS( XML_w, XML_spacing, xAttrList );
1170     }
1171 
1172     if ( m_pBackgroundAttrList.is() )
1173     {
1174         rtl::Reference<FastAttributeList> xAttrList = std::move( m_pBackgroundAttrList );
1175         m_pSerializer->singleElementNS( XML_w, XML_shd, xAttrList );
1176     }
1177 }
1178 
1179 namespace
1180 {
1181 
1182 /// Outputs an item set, that contains the formatting of the paragraph marker.
lcl_writeParagraphMarkerProperties(DocxAttributeOutput & rAttributeOutput,const SfxItemSet & rParagraphMarkerProperties)1183 void lcl_writeParagraphMarkerProperties(DocxAttributeOutput& rAttributeOutput, const SfxItemSet& rParagraphMarkerProperties)
1184 {
1185     const SfxItemSet* pOldI = rAttributeOutput.GetExport().GetCurItemSet();
1186     rAttributeOutput.GetExport().SetCurItemSet(&rParagraphMarkerProperties);
1187 
1188     SfxWhichIter aIter(rParagraphMarkerProperties);
1189     sal_uInt16 nWhichId = aIter.FirstWhich();
1190     const SfxPoolItem* pItem = nullptr;
1191     // Did we already produce a <w:sz> element?
1192     bool bFontSizeWritten = false;
1193     while (nWhichId)
1194     {
1195         if (rParagraphMarkerProperties.GetItemState(nWhichId, true, &pItem) == SfxItemState::SET)
1196         {
1197             if (isCHRATR(nWhichId) || nWhichId == RES_TXTATR_CHARFMT)
1198             {
1199                 // Will this item produce a <w:sz> element?
1200                 bool bFontSizeItem = nWhichId == RES_CHRATR_FONTSIZE || nWhichId == RES_CHRATR_CJK_FONTSIZE;
1201                 if (!bFontSizeWritten || !bFontSizeItem)
1202                     rAttributeOutput.OutputItem(*pItem);
1203                 if (bFontSizeItem)
1204                     bFontSizeWritten = true;
1205             }
1206             else if (nWhichId == RES_TXTATR_AUTOFMT)
1207             {
1208                 const SwFormatAutoFormat* pAutoFormat = static_cast<const SwFormatAutoFormat*>(pItem);
1209                 lcl_writeParagraphMarkerProperties(rAttributeOutput, *pAutoFormat->GetStyleHandle());
1210             }
1211         }
1212         nWhichId = aIter.NextWhich();
1213     }
1214     rAttributeOutput.GetExport().SetCurItemSet(pOldI);
1215 }
1216 
1217 const char *RubyAlignValues[] =
1218 {
1219     "center",
1220     "distributeLetter",
1221     "distributeSpace",
1222     "left",
1223     "right",
1224     "rightVertical"
1225 };
1226 
1227 
lclConvertWW8JCToOOXMLRubyAlign(sal_Int32 nJC)1228 const char *lclConvertWW8JCToOOXMLRubyAlign(sal_Int32 nJC)
1229 {
1230     const sal_Int32 nElements = SAL_N_ELEMENTS(RubyAlignValues);
1231     if ( nJC >=0 && nJC < nElements )
1232         return RubyAlignValues[nJC];
1233     return RubyAlignValues[0];
1234 }
1235 
1236 }
1237 
EndParagraphProperties(const SfxItemSet & rParagraphMarkerProperties,const SwRedlineData * pRedlineData,const SwRedlineData * pRedlineParagraphMarkerDeleted,const SwRedlineData * pRedlineParagraphMarkerInserted)1238 void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted)
1239 {
1240     // Call the 'Redline' function. This will add redline (change-tracking) information that regards to paragraph properties.
1241     // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
1242 
1243     // If there is RedlineData present, call WriteCollectedParagraphProperties() for writing pPr before calling Redline().
1244     // As there will be another pPr for redline and LO might mix both.
1245     if(pRedlineData)
1246         WriteCollectedParagraphProperties();
1247     Redline( pRedlineData );
1248 
1249     WriteCollectedParagraphProperties();
1250 
1251     // Merge the marks for the ordered elements
1252     m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties);
1253 
1254     // Write 'Paragraph Mark' properties
1255     m_pSerializer->startElementNS(XML_w, XML_rPr);
1256     // mark() before paragraph mark properties child elements.
1257     InitCollectedRunProperties();
1258 
1259     // The 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' are used to hold information
1260     // that should be collected by different properties in the core, and are all flushed together
1261     // to the DOCX when the function 'WriteCollectedRunProperties' gets called.
1262     // So we need to store the current status of these lists, so that we can revert back to them when
1263     // we are done exporting the redline attributes.
1264     rtl::Reference<sax_fastparser::FastAttributeList> pFontsAttrList_Original(m_pFontsAttrList);
1265     m_pFontsAttrList.clear();
1266     rtl::Reference<sax_fastparser::FastAttributeList> pEastAsianLayoutAttrList_Original(m_pEastAsianLayoutAttrList);
1267     m_pEastAsianLayoutAttrList.clear();
1268     rtl::Reference<sax_fastparser::FastAttributeList> pCharLangAttrList_Original(m_pCharLangAttrList);
1269     m_pCharLangAttrList.clear();
1270 
1271     lcl_writeParagraphMarkerProperties(*this, rParagraphMarkerProperties);
1272 
1273     // Write the collected run properties that are stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
1274     WriteCollectedRunProperties();
1275 
1276     // Revert back the original values that were stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
1277     m_pFontsAttrList = pFontsAttrList_Original.get();
1278     m_pEastAsianLayoutAttrList = pEastAsianLayoutAttrList_Original.get();
1279     m_pCharLangAttrList = pCharLangAttrList_Original.get();
1280 
1281     if ( pRedlineParagraphMarkerDeleted )
1282     {
1283         StartRedline( pRedlineParagraphMarkerDeleted );
1284         EndRedline( pRedlineParagraphMarkerDeleted );
1285     }
1286     if ( pRedlineParagraphMarkerInserted )
1287     {
1288         StartRedline( pRedlineParagraphMarkerInserted );
1289         EndRedline( pRedlineParagraphMarkerInserted );
1290     }
1291 
1292     // mergeTopMarks() after paragraph mark properties child elements.
1293     m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
1294     m_pSerializer->endElementNS( XML_w, XML_rPr );
1295 
1296     if (!m_bWritingHeaderFooter && m_pCurrentFrame)
1297     {
1298         const SwFrameFormat& rFrameFormat = m_pCurrentFrame->GetFrameFormat();
1299         const SvxBoxItem& rBox = rFrameFormat.GetBox();
1300         if (TextBoxIsFramePr(rFrameFormat))
1301         {
1302             const Size aSize = m_pCurrentFrame->GetSize();
1303             PopulateFrameProperties(&rFrameFormat, aSize);
1304             FormatBox(rBox);
1305         }
1306     }
1307 
1308     m_pSerializer->endElementNS( XML_w, XML_pPr );
1309 
1310     // RDF metadata for this text node.
1311     SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode();
1312     std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", *pTextNode);
1313     if (!aStatements.empty())
1314     {
1315         m_pSerializer->startElementNS(XML_w, XML_smartTag,
1316                                       FSNS(XML_w, XML_uri), "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
1317                                       FSNS(XML_w, XML_element), "RDF");
1318         m_pSerializer->startElementNS(XML_w, XML_smartTagPr);
1319         for (const auto& rStatement : aStatements)
1320             m_pSerializer->singleElementNS(XML_w, XML_attr,
1321                                            FSNS(XML_w, XML_name), rStatement.first,
1322                                            FSNS(XML_w, XML_val), rStatement.second);
1323         m_pSerializer->endElementNS(XML_w, XML_smartTagPr);
1324         m_pSerializer->endElementNS(XML_w, XML_smartTag);
1325     }
1326 
1327     if ( m_nColBreakStatus == COLBRK_WRITE || m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE )
1328     {
1329         m_pSerializer->startElementNS(XML_w, XML_r);
1330         m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "column");
1331         m_pSerializer->endElementNS( XML_w, XML_r );
1332 
1333         if ( m_nColBreakStatus == COLBRK_WRITEANDPOSTPONE )
1334             m_nColBreakStatus = COLBRK_POSTPONE;
1335         else
1336             m_nColBreakStatus = COLBRK_NONE;
1337     }
1338 
1339     if ( m_bPostponedPageBreak && !m_bWritingHeaderFooter )
1340     {
1341         m_pSerializer->startElementNS(XML_w, XML_r);
1342         m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
1343         m_pSerializer->endElementNS( XML_w, XML_r );
1344 
1345         m_bPostponedPageBreak = false;
1346     }
1347 
1348     // merge the properties _before_ the run (strictly speaking, just
1349     // after the start of the paragraph)
1350     m_pSerializer->mergeTopMarks(Tag_StartParagraphProperties, sax_fastparser::MergeMarks::PREPEND);
1351 }
1352 
SetStateOfFlyFrame(FlyProcessingState nStateOfFlyFrame)1353 void DocxAttributeOutput::SetStateOfFlyFrame( FlyProcessingState nStateOfFlyFrame )
1354 {
1355     m_nStateOfFlyFrame = nStateOfFlyFrame;
1356 }
1357 
SetAnchorIsLinkedToNode(bool bAnchorLinkedToNode)1358 void DocxAttributeOutput::SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode )
1359 {
1360     m_bAnchorLinkedToNode = bAnchorLinkedToNode ;
1361 }
1362 
ResetFlyProcessingFlag()1363 void DocxAttributeOutput::ResetFlyProcessingFlag()
1364 {
1365     m_bPostponedProcessingFly = false ;
1366 }
1367 
IsFlyProcessingPostponed()1368 bool DocxAttributeOutput::IsFlyProcessingPostponed()
1369 {
1370     return m_bPostponedProcessingFly;
1371 }
1372 
StartRun(const SwRedlineData * pRedlineData,sal_Int32,bool)1373 void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 /*nPos*/, bool /*bSingleEmptyRun*/ )
1374 {
1375     // Don't start redline data here, possibly there is a hyperlink later, and
1376     // that has to be started first.
1377     m_pRedlineData = pRedlineData;
1378 
1379     // this mark is used to be able to enclose the run inside a sdr tag.
1380     m_pSerializer->mark(Tag_StartRun_1);
1381 
1382     // postpone the output of the start of a run (there are elements that need
1383     // to be written before the start of the run, but we learn which they are
1384     // _inside_ of the run)
1385     m_pSerializer->mark(Tag_StartRun_2); // let's call it "postponed run start"
1386 
1387     // postpone the output of the text (we get it before the run properties,
1388     // but must write it after them)
1389     m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text"
1390 }
1391 
EndRun(const SwTextNode * pNode,sal_Int32 nPos,bool)1392 void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool /*bLastRun*/)
1393 {
1394     int nFieldsInPrevHyperlink = m_nFieldsInHyperlink;
1395     // Reset m_nFieldsInHyperlink if a new hyperlink is about to start
1396     if ( m_pHyperlinkAttrList.is() )
1397     {
1398         m_nFieldsInHyperlink = 0;
1399     }
1400 
1401     // Write field starts
1402     for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin() + nFieldsInPrevHyperlink; pIt != m_Fields.end(); )
1403     {
1404         // Add the fields starts for all but hyperlinks and TOCs
1405         if (pIt->bOpen && pIt->pField && pIt->eType != ww::eFORMDROPDOWN)
1406         {
1407             StartField_Impl( pNode, nPos, *pIt );
1408 
1409             // Remove the field from the stack if only the start has to be written
1410             // Unknown fields should be removed too
1411             if ( !pIt->bClose || ( pIt->eType == ww::eUNKNOWN ) )
1412             {
1413                 pIt = m_Fields.erase( pIt );
1414                 continue;
1415             }
1416 
1417             if (m_startedHyperlink || m_pHyperlinkAttrList.is())
1418             {
1419                 ++m_nFieldsInHyperlink;
1420             }
1421         }
1422         ++pIt;
1423     }
1424 
1425     // write the run properties + the text, already in the correct order
1426     m_pSerializer->mergeTopMarks(Tag_StartRun_3); // merges with "postponed text", see above
1427 
1428     // level down, to be able to prepend the actual run start attribute (just
1429     // before "postponed run start")
1430     m_pSerializer->mark(Tag_EndRun_1); // let's call it "actual run start"
1431     bool bCloseEarlierSDT = false;
1432 
1433     if (m_bEndCharSdt)
1434     {
1435         // This is the common case: "close sdt before the current run" was requested by the next run.
1436 
1437         // if another sdt starts in this run, then wait
1438         // as closing the sdt now, might cause nesting of sdts
1439         if (m_nRunSdtPrToken > 0)
1440             bCloseEarlierSDT = true;
1441         else
1442             EndSdtBlock();
1443         m_bEndCharSdt = false;
1444         m_bStartedCharSdt = false;
1445     }
1446 
1447     if ( m_closeHyperlinkInPreviousRun )
1448     {
1449         if ( m_startedHyperlink )
1450         {
1451             for ( int i = 0; i < nFieldsInPrevHyperlink; i++ )
1452             {
1453                 // If fields begin before hyperlink then
1454                 // it should end before hyperlink close
1455                 EndField_Impl( pNode, nPos, m_Fields.back( ) );
1456                 m_Fields.pop_back();
1457             }
1458             m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1459             m_startedHyperlink = false;
1460             m_endPageRef = false;
1461             m_nHyperLinkCount--;
1462         }
1463         m_closeHyperlinkInPreviousRun = false;
1464     }
1465 
1466     // Write the hyperlink and toc fields starts
1467     for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); )
1468     {
1469         // Add the fields starts for hyperlinks, TOCs and index marks
1470         if (pIt->bOpen && (!pIt->pField || pIt->eType == ww::eFORMDROPDOWN))
1471         {
1472             StartRedline( m_pRedlineData );
1473             StartField_Impl( pNode, nPos, *pIt, true );
1474             EndRedline( m_pRedlineData );
1475 
1476             if (m_startedHyperlink)
1477                 ++m_nFieldsInHyperlink;
1478 
1479             // Remove the field if no end needs to be written
1480             if (!pIt->bSep)
1481             {
1482                 pIt = m_Fields.erase( pIt );
1483                 continue;
1484             }
1485         }
1486         if (pIt->bSep && !pIt->pField)
1487         {
1488             // for TOXMark:
1489             // Word ignores bookmarks in field result that is empty;
1490             // work around this by writing bookmark into field command.
1491             if (!m_sFieldBkm.isEmpty())
1492             {
1493                 DoWriteBookmarkTagStart(m_sFieldBkm);
1494                 DoWriteBookmarkTagEnd(m_nNextBookmarkId);
1495                 m_nNextBookmarkId++;
1496                 m_sFieldBkm.clear();
1497             }
1498             CmdEndField_Impl(pNode, nPos, true);
1499             // Remove the field if no end needs to be written
1500             if (!pIt->bClose)
1501             {
1502                 pIt = m_Fields.erase( pIt );
1503                 continue;
1504             }
1505         }
1506         ++pIt;
1507     }
1508 
1509     // Start the hyperlink after the fields separators or we would generate invalid file
1510     bool newStartedHyperlink(false);
1511     if ( m_pHyperlinkAttrList.is() )
1512     {
1513         // if we are ending a hyperlink and there's another one starting here,
1514         // don't do this, so that the fields are closed further down when
1515         // the end hyperlink is handled, which is more likely to put the end in
1516         // the right place, as far as i can tell (not very far in this muck)
1517         if (!m_closeHyperlinkInThisRun)
1518         {
1519             // end ToX fields that want to end _before_ starting the hyperlink
1520             for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); )
1521             {
1522                 if (it->bClose && !it->pField)
1523                 {
1524                     EndField_Impl( pNode, nPos, *it );
1525                     it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1));
1526                 }
1527                 else
1528                 {
1529                     ++it;
1530                 }
1531             }
1532         }
1533         newStartedHyperlink = true;
1534 
1535         rtl::Reference<FastAttributeList> xAttrList = std::move( m_pHyperlinkAttrList );
1536 
1537         m_pSerializer->startElementNS( XML_w, XML_hyperlink, xAttrList );
1538         m_startedHyperlink = true;
1539         m_nHyperLinkCount++;
1540     }
1541 
1542     // if there is some redlining in the document, output it
1543     StartRedline( m_pRedlineData );
1544 
1545     // XML_r node should be surrounded with bookmark-begin and bookmark-end nodes if it has bookmarks.
1546     // The same is applied for permission ranges.
1547     // But due to unit test "testFdo85542" let's output bookmark-begin with bookmark-end.
1548     DoWriteBookmarksStart(m_rBookmarksStart);
1549     DoWriteBookmarksEnd(m_rBookmarksEnd);
1550     DoWritePermissionsStart();
1551     DoWriteAnnotationMarks();
1552 
1553     if( m_closeHyperlinkInThisRun && m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc"))
1554     {
1555         OUString sToken;
1556         m_pSerializer->startElementNS(XML_w, XML_r);
1557         m_pSerializer->startElementNS(XML_w, XML_rPr);
1558         m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1559         m_pSerializer->endElementNS( XML_w, XML_rPr );
1560         m_pSerializer->startElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "begin");
1561         m_pSerializer->endElementNS( XML_w, XML_fldChar );
1562         m_pSerializer->endElementNS( XML_w, XML_r );
1563 
1564 
1565         m_pSerializer->startElementNS(XML_w, XML_r);
1566         m_pSerializer->startElementNS(XML_w, XML_rPr);
1567         m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1568         m_pSerializer->endElementNS( XML_w, XML_rPr );
1569         sToken = "PAGEREF " + m_hyperLinkAnchor + " \\h"; // '\h' Creates a hyperlink to the bookmarked paragraph.
1570         DoWriteCmd( sToken );
1571         m_pSerializer->endElementNS( XML_w, XML_r );
1572 
1573         // Write the Field separator
1574         m_pSerializer->startElementNS(XML_w, XML_r);
1575         m_pSerializer->startElementNS(XML_w, XML_rPr);
1576         m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1577         m_pSerializer->endElementNS( XML_w, XML_rPr );
1578         m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1579                 FSNS( XML_w, XML_fldCharType ), "separate" );
1580         m_pSerializer->endElementNS( XML_w, XML_r );
1581         // At start of every "PAGEREF" field m_endPageRef value should be true.
1582         m_endPageRef = true;
1583     }
1584 
1585     DoWriteBookmarkStartIfExist(nPos);
1586 
1587     m_pSerializer->startElementNS(XML_w, XML_r);
1588     if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is())
1589     {
1590         RunText("\t") ;
1591     }
1592     m_pSerializer->mergeTopMarks(Tag_EndRun_1, sax_fastparser::MergeMarks::PREPEND); // merges with "postponed run start", see above
1593 
1594     if ( !m_sRawText.isEmpty() )
1595     {
1596         RunText( m_sRawText );
1597         m_sRawText.clear();
1598     }
1599 
1600     // write the run start + the run content
1601     m_pSerializer->mergeTopMarks(Tag_StartRun_2); // merges the "actual run start"
1602     // append the actual run end
1603     m_pSerializer->endElementNS( XML_w, XML_r );
1604 
1605     // if there is some redlining in the document, output it
1606     // (except in the case of fields with multiple runs)
1607     EndRedline( m_pRedlineData );
1608 
1609     // enclose in a sdt block, if necessary: if one is already started, then don't do it for now
1610     // (so on export sdt blocks are never nested ATM)
1611     if ( !m_bAnchorLinkedToNode && !m_bStartedCharSdt )
1612     {
1613         rtl::Reference<sax_fastparser::FastAttributeList> pRunSdtPrTokenAttributes;
1614         WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, pRunSdtPrTokenAttributes, m_pRunSdtPrDataBindingAttrs, m_pRunSdtPrTextAttrs, m_aRunSdtPrAlias, /*bPara=*/false );
1615     }
1616     else
1617     {
1618         //These should be written out to the actual Node and not to the anchor.
1619         //Clear them as they will be repopulated when the node is processed.
1620         m_nRunSdtPrToken = 0;
1621         lcl_deleteAndResetTheLists( m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs, m_pRunSdtPrTextAttrs, m_aRunSdtPrAlias );
1622     }
1623 
1624     if (bCloseEarlierSDT)
1625     {
1626         m_pSerializer->mark(Tag_EndRun_2);
1627         EndSdtBlock();
1628         m_pSerializer->mergeTopMarks(Tag_EndRun_2, sax_fastparser::MergeMarks::PREPEND);
1629     }
1630 
1631     m_pSerializer->mergeTopMarks(Tag_StartRun_1);
1632 
1633     // XML_r node should be surrounded with permission-begin and permission-end nodes if it has permission.
1634     DoWritePermissionsEnd();
1635 
1636     for (const auto& rpMath : m_aPostponedMaths)
1637         WritePostponedMath(rpMath.pMathObject, rpMath.nMathObjAlignment);
1638     m_aPostponedMaths.clear();
1639 
1640     for (const auto& rpControl : m_aPostponedFormControls)
1641         WritePostponedFormControl(rpControl);
1642     m_aPostponedFormControls.clear();
1643 
1644     WritePostponedActiveXControl(false);
1645 
1646     WritePendingPlaceholder();
1647 
1648     if ( !m_bWritingField )
1649     {
1650         m_pRedlineData = nullptr;
1651     }
1652 
1653     if ( m_closeHyperlinkInThisRun )
1654     {
1655         if ( m_startedHyperlink )
1656         {
1657             if( m_endPageRef )
1658             {
1659                 // Hyperlink is started and fldchar "end" needs to be written for PAGEREF
1660                 m_pSerializer->startElementNS(XML_w, XML_r);
1661                 m_pSerializer->startElementNS(XML_w, XML_rPr);
1662                 m_pSerializer->singleElementNS(XML_w, XML_webHidden);
1663                 m_pSerializer->endElementNS( XML_w, XML_rPr );
1664                 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1665                         FSNS( XML_w, XML_fldCharType ), "end" );
1666                 m_pSerializer->endElementNS( XML_w, XML_r );
1667                 m_endPageRef = false;
1668                 m_hyperLinkAnchor.clear();
1669             }
1670             for ( int i = 0; i < m_nFieldsInHyperlink; i++ )
1671             {
1672                 // If fields begin after hyperlink start then
1673                 // it should end before hyperlink close
1674                 EndField_Impl( pNode, nPos, m_Fields.back( ) );
1675                 m_Fields.pop_back();
1676             }
1677             m_nFieldsInHyperlink = 0;
1678 
1679             m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1680             m_startedHyperlink = false;
1681             m_nHyperLinkCount--;
1682         }
1683         m_closeHyperlinkInThisRun = false;
1684     }
1685 
1686     if (!newStartedHyperlink)
1687     {
1688         while ( m_Fields.begin() != m_Fields.end() )
1689         {
1690             EndField_Impl( pNode, nPos, m_Fields.front( ) );
1691             m_Fields.erase( m_Fields.begin( ) );
1692         }
1693         m_nFieldsInHyperlink = 0;
1694     }
1695 
1696     // end ToX fields
1697     for (auto it = m_Fields.rbegin(); it != m_Fields.rend(); )
1698     {
1699         if (it->bClose && !it->pField)
1700         {
1701             EndField_Impl( pNode, nPos, *it );
1702             it = decltype(m_Fields)::reverse_iterator(m_Fields.erase(it.base() - 1));
1703         }
1704         else
1705         {
1706             ++it;
1707         }
1708     }
1709 
1710     if ( m_pRedlineData )
1711     {
1712         EndRedline( m_pRedlineData );
1713         m_pRedlineData = nullptr;
1714     }
1715 
1716     DoWriteBookmarksStart(m_rFinalBookmarksStart);
1717     DoWriteBookmarksEnd(m_rFinalBookmarksEnd);
1718     DoWriteBookmarkEndIfExist(nPos);
1719 }
1720 
DoWriteBookmarkTagStart(const OUString & bookmarkName)1721 void DocxAttributeOutput::DoWriteBookmarkTagStart(const OUString & bookmarkName)
1722 {
1723     m_pSerializer->singleElementNS(XML_w, XML_bookmarkStart,
1724         FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId),
1725         FSNS(XML_w, XML_name), BookmarkToWord(bookmarkName));
1726 }
1727 
DoWriteBookmarkTagEnd(sal_Int32 const nId)1728 void DocxAttributeOutput::DoWriteBookmarkTagEnd(sal_Int32 const nId)
1729 {
1730     m_pSerializer->singleElementNS(XML_w, XML_bookmarkEnd,
1731         FSNS(XML_w, XML_id), OString::number(nId));
1732 }
1733 
DoWriteBookmarkStartIfExist(sal_Int32 nRunPos)1734 void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nRunPos)
1735 {
1736     auto aRange = m_aBookmarksOfParagraphStart.equal_range(nRunPos);
1737     for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1738     {
1739         DoWriteBookmarkTagStart(aIter->second);
1740         m_rOpenedBookmarksIds[aIter->second] = m_nNextBookmarkId;
1741         m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(aIter->second), RTL_TEXTENCODING_UTF8);
1742         m_nNextBookmarkId++;
1743     }
1744 }
1745 
DoWriteBookmarkEndIfExist(sal_Int32 nRunPos)1746 void DocxAttributeOutput::DoWriteBookmarkEndIfExist(sal_Int32 nRunPos)
1747 {
1748     auto aRange = m_aBookmarksOfParagraphEnd.equal_range(nRunPos);
1749     for( auto aIter = aRange.first; aIter != aRange.second; ++aIter)
1750     {
1751         // Get the id of the bookmark
1752         auto pPos = m_rOpenedBookmarksIds.find(aIter->second);
1753         if (pPos != m_rOpenedBookmarksIds.end())
1754         {
1755             // Output the bookmark
1756             DoWriteBookmarkTagEnd(pPos->second);
1757             m_rOpenedBookmarksIds.erase(aIter->second);
1758         }
1759     }
1760 }
1761 
1762 /// Write the start bookmarks
DoWriteBookmarksStart(std::vector<OUString> & rStarts)1763 void DocxAttributeOutput::DoWriteBookmarksStart(std::vector<OUString>& rStarts)
1764 {
1765     for (const OUString & bookmarkName : rStarts)
1766     {
1767         // Output the bookmark
1768         DoWriteBookmarkTagStart(bookmarkName);
1769 
1770         m_rOpenedBookmarksIds[bookmarkName] = m_nNextBookmarkId;
1771         m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(bookmarkName), RTL_TEXTENCODING_UTF8);
1772         m_nNextBookmarkId++;
1773     }
1774     rStarts.clear();
1775 }
1776 
1777 /// export the end bookmarks
DoWriteBookmarksEnd(std::vector<OUString> & rEnds)1778 void DocxAttributeOutput::DoWriteBookmarksEnd(std::vector<OUString>& rEnds)
1779 {
1780     for (const OUString & bookmarkName : rEnds)
1781     {
1782         // Get the id of the bookmark
1783         auto pPos = m_rOpenedBookmarksIds.find(bookmarkName);
1784         if (pPos != m_rOpenedBookmarksIds.end())
1785         {
1786             // Output the bookmark
1787             DoWriteBookmarkTagEnd(pPos->second);
1788 
1789             m_rOpenedBookmarksIds.erase(bookmarkName);
1790         }
1791     }
1792     rEnds.clear();
1793 }
1794 
1795 // For construction of the special bookmark name template for permissions:
1796 // see, PermInsertPosition::createBookmarkName()
1797 //
1798 // Syntax:
1799 // - "permission-for-user:<permission-id>:<permission-user-name>"
1800 // - "permission-for-group:<permission-id>:<permission-group-name>"
1801 //
DoWritePermissionTagStart(const OUString & permission)1802 void DocxAttributeOutput::DoWritePermissionTagStart(const OUString & permission)
1803 {
1804     OUString permissionIdAndName;
1805 
1806     if (permission.startsWith("permission-for-group:", &permissionIdAndName))
1807     {
1808         const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':');
1809         const OUString permissionId   = permissionIdAndName.copy(0, sparatorIndex);
1810         const OUString permissionName = permissionIdAndName.copy(sparatorIndex + 1);
1811 
1812         m_pSerializer->singleElementNS(XML_w, XML_permStart,
1813             FSNS(XML_w, XML_id), BookmarkToWord(permissionId),
1814             FSNS(XML_w, XML_edGrp), BookmarkToWord(permissionName));
1815     }
1816     else // if (permission.startsWith("permission-for-user:", &permissionIdAndName))
1817     {
1818         const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':');
1819         const OUString permissionId   = permissionIdAndName.copy(0, sparatorIndex);
1820         const OUString permissionName = permissionIdAndName.copy(sparatorIndex + 1);
1821 
1822         m_pSerializer->singleElementNS(XML_w, XML_permStart,
1823             FSNS(XML_w, XML_id), BookmarkToWord(permissionId),
1824             FSNS(XML_w, XML_ed), BookmarkToWord(permissionName));
1825     }
1826 }
1827 
1828 
1829 // For construction of the special bookmark name template for permissions:
1830 // see, PermInsertPosition::createBookmarkName()
1831 //
1832 // Syntax:
1833 // - "permission-for-user:<permission-id>:<permission-user-name>"
1834 // - "permission-for-group:<permission-id>:<permission-group-name>"
1835 //
DoWritePermissionTagEnd(const OUString & permission)1836 void DocxAttributeOutput::DoWritePermissionTagEnd(const OUString & permission)
1837 {
1838     OUString permissionIdAndName;
1839 
1840     if (permission.startsWith("permission-for-group:", &permissionIdAndName) ||
1841         permission.startsWith("permission-for-user:", &permissionIdAndName))
1842     {
1843         const sal_Int32 sparatorIndex = permissionIdAndName.indexOf(':');
1844         const OUString permissionId   = permissionIdAndName.copy(0, sparatorIndex);
1845 
1846         m_pSerializer->singleElementNS(XML_w, XML_permEnd,
1847             FSNS(XML_w, XML_id), BookmarkToWord(permissionId));
1848     }
1849 }
1850 
1851 /// Write the start permissions
DoWritePermissionsStart()1852 void DocxAttributeOutput::DoWritePermissionsStart()
1853 {
1854     for (const OUString & permission : m_rPermissionsStart)
1855     {
1856         DoWritePermissionTagStart(permission);
1857     }
1858     m_rPermissionsStart.clear();
1859 }
1860 
1861 /// export the end permissions
DoWritePermissionsEnd()1862 void DocxAttributeOutput::DoWritePermissionsEnd()
1863 {
1864     for (const OUString & permission : m_rPermissionsEnd)
1865     {
1866         DoWritePermissionTagEnd(permission);
1867     }
1868     m_rPermissionsEnd.clear();
1869 }
1870 
DoWriteAnnotationMarks()1871 void DocxAttributeOutput::DoWriteAnnotationMarks()
1872 {
1873     // Write the start annotation marks
1874     for ( const auto & rName : m_rAnnotationMarksStart )
1875     {
1876         // Output the annotation mark
1877         /* Ensure that the existing Annotation Marks are not overwritten
1878            as it causes discrepancy when DocxAttributeOutput::PostitField
1879            refers to this map & while mapping comment id's in document.xml &
1880            comment.xml.
1881         */
1882         if ( m_rOpenedAnnotationMarksIds.end() == m_rOpenedAnnotationMarksIds.find( rName ) )
1883         {
1884             const sal_Int32 nId = m_nNextAnnotationMarkId++;
1885             m_rOpenedAnnotationMarksIds[rName] = nId;
1886             m_pSerializer->singleElementNS( XML_w, XML_commentRangeStart,
1887                 FSNS( XML_w, XML_id ), OString::number(nId) );
1888             m_sLastOpenedAnnotationMark = rName;
1889         }
1890     }
1891     m_rAnnotationMarksStart.clear();
1892 
1893     // export the end annotation marks
1894     for ( const auto & rName : m_rAnnotationMarksEnd )
1895     {
1896         // Get the id of the annotation mark
1897         std::map< OString, sal_Int32 >::iterator pPos = m_rOpenedAnnotationMarksIds.find( rName );
1898         if ( pPos != m_rOpenedAnnotationMarksIds.end(  ) )
1899         {
1900             const sal_Int32 nId = ( *pPos ).second;
1901             m_pSerializer->singleElementNS( XML_w, XML_commentRangeEnd,
1902                 FSNS( XML_w, XML_id ), OString::number(nId) );
1903             m_rOpenedAnnotationMarksIds.erase( rName );
1904 
1905             m_pSerializer->startElementNS(XML_w, XML_r);
1906             m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ),
1907                                             OString::number(nId) );
1908             m_pSerializer->endElementNS(XML_w, XML_r);
1909         }
1910     }
1911     m_rAnnotationMarksEnd.clear();
1912 }
1913 
WriteFFData(const FieldInfos & rInfos)1914 void DocxAttributeOutput::WriteFFData(  const FieldInfos& rInfos )
1915 {
1916     const ::sw::mark::IFieldmark& rFieldmark = *rInfos.pFieldmark;
1917     FieldMarkParamsHelper params( rFieldmark );
1918 
1919     OUString sEntryMacro;
1920     params.extractParam("EntryMacro", sEntryMacro);
1921     OUString sExitMacro;
1922     params.extractParam("ExitMacro", sExitMacro);
1923     OUString sHelp;
1924     params.extractParam("Help", sHelp);
1925     OUString sHint;
1926     params.extractParam("Hint", sHint); // .docx StatusText
1927     if ( sHint.isEmpty() )
1928         params.extractParam("Description", sHint); // .doc StatusText
1929 
1930     if ( rInfos.eType == ww::eFORMDROPDOWN )
1931     {
1932         uno::Sequence< OUString> vListEntries;
1933         OUString sName, sSelected;
1934 
1935         params.extractParam( ODF_FORMDROPDOWN_LISTENTRY, vListEntries );
1936         if (vListEntries.getLength() > ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT)
1937             vListEntries = uno::Sequence< OUString>(vListEntries.getArray(), ODF_FORMDROPDOWN_ENTRY_COUNT_LIMIT);
1938 
1939         sName = params.getName();
1940         sal_Int32 nSelectedIndex = 0;
1941 
1942         if ( params.extractParam( ODF_FORMDROPDOWN_RESULT, nSelectedIndex ) )
1943         {
1944             if (nSelectedIndex < vListEntries.getLength() )
1945                 sSelected = vListEntries[ nSelectedIndex ];
1946         }
1947 
1948         GetExport().DoComboBox( sName, OUString(), OUString(), sSelected, vListEntries );
1949     }
1950     else if ( rInfos.eType == ww::eFORMCHECKBOX )
1951     {
1952         OUString sName;
1953         bool bChecked = false;
1954 
1955         params.extractParam( ODF_FORMCHECKBOX_NAME, sName );
1956 
1957         const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark);
1958         if ( pCheckboxFm && pCheckboxFm->IsChecked() )
1959             bChecked = true;
1960 
1961         FFDataWriterHelper ffdataOut( m_pSerializer );
1962         ffdataOut.WriteFormCheckbox( sName, sEntryMacro, sExitMacro, sHelp, sHint, bChecked );
1963     }
1964     else if ( rInfos.eType == ww::eFORMTEXT )
1965     {
1966         OUString sType;
1967         params.extractParam("Type", sType);
1968         OUString sDefaultText;
1969         params.extractParam("Content", sDefaultText);
1970         sal_uInt16 nMaxLength = 0;
1971         params.extractParam("MaxLength", nMaxLength);
1972         OUString sFormat;
1973         params.extractParam("Format", sFormat);
1974         FFDataWriterHelper ffdataOut( m_pSerializer );
1975         ffdataOut.WriteFormText( params.getName(), sEntryMacro, sExitMacro, sHelp, sHint,
1976                                  sType, sDefaultText, nMaxLength, sFormat );
1977     }
1978 }
1979 
WriteFormDateStart(const OUString & sFullDate,const OUString & sDateFormat,const OUString & sLang)1980 void DocxAttributeOutput::WriteFormDateStart(const OUString& sFullDate, const OUString& sDateFormat, const OUString& sLang)
1981 {
1982     m_pSerializer->startElementNS(XML_w, XML_sdt);
1983     m_pSerializer->startElementNS(XML_w, XML_sdtPr);
1984 
1985     if(!sFullDate.isEmpty())
1986         m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sFullDate);
1987     else
1988         m_pSerializer->startElementNS(XML_w, XML_date);
1989 
1990     // Replace quotation mark used for marking static strings in date format
1991     OUString sDateFormat1 = sDateFormat.replaceAll("\"", "'");
1992     m_pSerializer->singleElementNS(XML_w, XML_dateFormat,
1993                                    FSNS(XML_w, XML_val), sDateFormat1);
1994     m_pSerializer->singleElementNS(XML_w, XML_lid,
1995                                    FSNS(XML_w, XML_val), sLang);
1996     m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs,
1997                                    FSNS(XML_w, XML_val), "dateTime");
1998     m_pSerializer->singleElementNS(XML_w, XML_calendar,
1999                                    FSNS(XML_w, XML_val), "gregorian");
2000 
2001     m_pSerializer->endElementNS(XML_w, XML_date);
2002     m_pSerializer->endElementNS(XML_w, XML_sdtPr);
2003 
2004     m_pSerializer->startElementNS(XML_w, XML_sdtContent);
2005 }
2006 
WriteSdtEnd()2007 void DocxAttributeOutput::WriteSdtEnd()
2008 {
2009     m_pSerializer->endElementNS(XML_w, XML_sdtContent);
2010     m_pSerializer->endElementNS(XML_w, XML_sdt);
2011 }
2012 
WriteSdtDropDownStart(std::u16string_view rName,OUString const & rSelected,uno::Sequence<OUString> const & rListItems)2013 void DocxAttributeOutput::WriteSdtDropDownStart(
2014         std::u16string_view rName,
2015         OUString const& rSelected,
2016         uno::Sequence<OUString> const& rListItems)
2017 {
2018     m_pSerializer->startElementNS(XML_w, XML_sdt);
2019     m_pSerializer->startElementNS(XML_w, XML_sdtPr);
2020 
2021     m_pSerializer->singleElementNS(XML_w, XML_alias,
2022         FSNS(XML_w, XML_val), OUStringToOString(rName, RTL_TEXTENCODING_UTF8));
2023 
2024     sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
2025     if (nId == -1)
2026     {
2027         nId = 0;
2028     }
2029 
2030     m_pSerializer->startElementNS(XML_w, XML_dropDownList,
2031             FSNS(XML_w, XML_lastValue), OString::number(nId));
2032 
2033     for (auto const& rItem : rListItems)
2034     {
2035         auto const item(OUStringToOString(rItem, RTL_TEXTENCODING_UTF8));
2036         m_pSerializer->singleElementNS(XML_w, XML_listItem,
2037                 FSNS(XML_w, XML_value), item,
2038                 FSNS(XML_w, XML_displayText), item);
2039     }
2040 
2041     m_pSerializer->endElementNS(XML_w, XML_dropDownList);
2042     m_pSerializer->endElementNS(XML_w, XML_sdtPr);
2043 
2044     m_pSerializer->startElementNS(XML_w, XML_sdtContent);
2045 }
2046 
WriteSdtDropDownEnd(OUString const & rSelected,uno::Sequence<OUString> const & rListItems)2047 void DocxAttributeOutput::WriteSdtDropDownEnd(OUString const& rSelected,
2048         uno::Sequence<OUString> const& rListItems)
2049 {
2050     // note: rSelected might be empty?
2051     sal_Int32 nId = comphelper::findValue(rListItems, rSelected);
2052     if (nId == -1)
2053     {
2054         nId = 0;
2055     }
2056 
2057     // the lastValue only identifies the entry in the list, also export
2058     // currently selected item's displayText as run content (if one exists)
2059     if (rListItems.size())
2060     {
2061         m_pSerializer->startElementNS(XML_w, XML_r);
2062         m_pSerializer->startElementNS(XML_w, XML_t);
2063         m_pSerializer->writeEscaped(rListItems[nId]);
2064         m_pSerializer->endElementNS(XML_w, XML_t);
2065         m_pSerializer->endElementNS(XML_w, XML_r);
2066     }
2067 
2068     WriteSdtEnd();
2069 }
2070 
StartField_Impl(const SwTextNode * pNode,sal_Int32 nPos,FieldInfos const & rInfos,bool bWriteRun)2071 void DocxAttributeOutput::StartField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun )
2072 {
2073     if ( rInfos.pField && rInfos.eType == ww::eUNKNOWN )
2074     {
2075         // Expand unsupported fields
2076         RunText( rInfos.pField->GetFieldName() );
2077     }
2078     else if ( rInfos.eType == ww::eFORMDATE )
2079     {
2080         const sw::mark::IDateFieldmark& rFieldmark = dynamic_cast<const sw::mark::IDateFieldmark&>(*rInfos.pFieldmark);
2081         FieldMarkParamsHelper params(rFieldmark);
2082 
2083         OUString sFullDate;
2084         OUString sCurrentDate;
2085         params.extractParam( ODF_FORMDATE_CURRENTDATE, sCurrentDate );
2086         if(!sCurrentDate.isEmpty())
2087         {
2088             sFullDate = sCurrentDate + "T00:00:00Z";
2089         }
2090         else
2091         {
2092             std::pair<bool, double> aResult = rFieldmark.GetCurrentDate();
2093             if(aResult.first)
2094             {
2095                 sFullDate = rFieldmark.GetDateInStandardDateFormat(aResult.second) + "T00:00:00Z";
2096             }
2097         }
2098 
2099         OUString sDateFormat;
2100         params.extractParam( ODF_FORMDATE_DATEFORMAT, sDateFormat );
2101         OUString sLang;
2102         params.extractParam( ODF_FORMDATE_DATEFORMAT_LANGUAGE, sLang );
2103 
2104         WriteFormDateStart( sFullDate, sDateFormat, sLang );
2105     }
2106     else if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField)
2107     {
2108         assert(!rInfos.pFieldmark);
2109         SwDropDownField const& rField2(*static_cast<SwDropDownField const*>(rInfos.pField.get()));
2110         WriteSdtDropDownStart(rField2.GetName(),
2111                 rField2.GetSelectedItem(),
2112                 rField2.GetItemSequence());
2113     }
2114     else if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands
2115     {
2116         if ( bWriteRun )
2117             m_pSerializer->startElementNS(XML_w, XML_r);
2118 
2119         if ( rInfos.eType == ww::eFORMDROPDOWN )
2120         {
2121             m_pSerializer->startElementNS( XML_w, XML_fldChar,
2122                 FSNS( XML_w, XML_fldCharType ), "begin" );
2123             assert( rInfos.pFieldmark && !rInfos.pField );
2124             WriteFFData(rInfos);
2125             m_pSerializer->endElementNS( XML_w, XML_fldChar );
2126 
2127             if ( bWriteRun )
2128                 m_pSerializer->endElementNS( XML_w, XML_r );
2129 
2130             CmdField_Impl( pNode, nPos, rInfos, bWriteRun );
2131         }
2132         else
2133         {
2134             // Write the field start
2135             if ( rInfos.pField && (rInfos.pField->Which() == SwFieldIds::DateTime) && rInfos.pField->GetSubType() & FIXEDFLD )
2136             {
2137                 m_pSerializer->startElementNS( XML_w, XML_fldChar,
2138                     FSNS( XML_w, XML_fldCharType ), "begin",
2139                     FSNS( XML_w, XML_fldLock ), "true" );
2140             }
2141             else
2142             {
2143                 m_pSerializer->startElementNS( XML_w, XML_fldChar,
2144                     FSNS( XML_w, XML_fldCharType ), "begin" );
2145             }
2146 
2147             if ( rInfos.pFieldmark )
2148                 WriteFFData(  rInfos );
2149 
2150             m_pSerializer->endElementNS( XML_w, XML_fldChar );
2151 
2152             if ( bWriteRun )
2153                 m_pSerializer->endElementNS( XML_w, XML_r );
2154 
2155             // The hyperlinks fields can't be expanded: the value is
2156             // normally in the text run
2157             if ( !rInfos.pField )
2158                 CmdField_Impl( pNode, nPos, rInfos, bWriteRun );
2159             else
2160                 m_bWritingField = true;
2161         }
2162     }
2163 }
2164 
DoWriteCmd(const OUString & rCmd)2165 void DocxAttributeOutput::DoWriteCmd( const OUString& rCmd )
2166 {
2167     OUString sCmd = rCmd.trim();
2168     if (sCmd.startsWith("SEQ"))
2169     {
2170         OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\').trim();
2171         m_aSeqBookmarksNames[sSeqName].push_back(m_sLastOpenedBookmark);
2172     }
2173     // Write the Field command
2174     sal_Int32 nTextToken = XML_instrText;
2175     if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete )
2176         nTextToken = XML_delInstrText;
2177 
2178     m_pSerializer->startElementNS(XML_w, nTextToken);
2179     m_pSerializer->writeEscaped( rCmd );
2180     m_pSerializer->endElementNS( XML_w, nTextToken );
2181 
2182 }
2183 
CmdField_Impl(const SwTextNode * pNode,sal_Int32 nPos,FieldInfos const & rInfos,bool bWriteRun)2184 void DocxAttributeOutput::CmdField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos const & rInfos, bool bWriteRun )
2185 {
2186     // Write the Field instruction
2187     if ( bWriteRun )
2188     {
2189         bool bWriteCombChars(false);
2190         m_pSerializer->startElementNS(XML_w, XML_r);
2191 
2192         if (rInfos.eType == ww::eEQ)
2193             bWriteCombChars = true;
2194 
2195         DoWriteFieldRunProperties( pNode, nPos, bWriteCombChars );
2196     }
2197 
2198     sal_Int32 nIdx { rInfos.sCmd.isEmpty() ? -1 : 0 };
2199     while ( nIdx >= 0 )
2200     {
2201         OUString sToken = rInfos.sCmd.getToken( 0, '\t', nIdx );
2202         if ( rInfos.eType ==  ww::eCREATEDATE
2203           || rInfos.eType ==  ww::eSAVEDATE
2204           || rInfos.eType ==  ww::ePRINTDATE
2205           || rInfos.eType ==  ww::eDATE
2206           || rInfos.eType ==  ww::eTIME )
2207         {
2208            sToken = sToken.replaceAll("NNNN", "dddd");
2209            sToken = sToken.replaceAll("NN", "ddd");
2210         }
2211         else if ( rInfos.eType == ww::eEquals )
2212         {
2213             // Use original OOXML formula, if it exists and its conversion hasn't been changed
2214             bool bIsChanged = true;
2215             if ( pNode->GetTableBox() )
2216             {
2217                 if ( const SfxGrabBagItem* pItem = pNode->GetTableBox()->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG) )
2218                 {
2219                     OUString sActualFormula = sToken.trim();
2220                     const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
2221                     std::map<OUString, uno::Any>::const_iterator aStoredFormula = rGrabBag.find("CellFormulaConverted");
2222                     if ( aStoredFormula != rGrabBag.end() && sActualFormula.indexOf('=') == 0 &&
2223                                     sActualFormula.copy(1).trim() == aStoredFormula->second.get<OUString>().trim() )
2224                     {
2225                         aStoredFormula = rGrabBag.find("CellFormula");
2226                         if ( aStoredFormula != rGrabBag.end() )
2227                         {
2228                             sToken = " =" + aStoredFormula->second.get<OUString>();
2229                             bIsChanged = false;
2230                         }
2231                     }
2232                 }
2233             }
2234 
2235             if ( bIsChanged )
2236             {
2237                 UErrorCode nErr(U_ZERO_ERROR);
2238                 icu::UnicodeString sInput(sToken.getStr());
2239                 // remove < and > around cell references, e.g. <A1> to A1, <A1:B2> to A1:B2
2240                 icu::RegexMatcher aMatcher("<([A-Z]{1,3}[0-9]+(:[A-Z]{1,3}[0-9]+)?)>", sInput, 0, nErr);
2241                 sInput = aMatcher.replaceAll(icu::UnicodeString("$1"), nErr);
2242                 // convert MEAN to AVERAGE
2243                 icu::RegexMatcher aMatcher2("\\bMEAN\\b", sInput, UREGEX_CASE_INSENSITIVE, nErr);
2244                 sToken = aMatcher2.replaceAll(icu::UnicodeString("AVERAGE"), nErr).getTerminatedBuffer();
2245             }
2246         }
2247 
2248         // Write the Field command
2249         DoWriteCmd( sToken );
2250 
2251         // Replace tabs by </instrText><tab/><instrText>
2252         if ( nIdx > 0 ) // Is another token expected?
2253             RunText( "\t" );
2254     }
2255 
2256     if ( bWriteRun )
2257     {
2258         m_pSerializer->endElementNS( XML_w, XML_r );
2259     }
2260 }
2261 
CmdEndField_Impl(SwTextNode const * const pNode,sal_Int32 const nPos,bool const bWriteRun)2262 void DocxAttributeOutput::CmdEndField_Impl(SwTextNode const*const pNode,
2263         sal_Int32 const nPos, bool const bWriteRun)
2264 {
2265     // Write the Field separator
2266         if ( bWriteRun )
2267         {
2268             m_pSerializer->startElementNS(XML_w, XML_r);
2269             DoWriteFieldRunProperties( pNode, nPos );
2270         }
2271 
2272         m_pSerializer->singleElementNS( XML_w, XML_fldChar,
2273               FSNS( XML_w, XML_fldCharType ), "separate" );
2274 
2275         if ( bWriteRun )
2276         {
2277             m_pSerializer->endElementNS( XML_w, XML_r );
2278         }
2279 }
2280 
2281 /// Writes properties for run that is used to separate field implementation.
2282 /// There are several runs are used:
2283 ///     <w:r>
2284 ///         <w:rPr>
2285 ///             <!-- properties written with StartRunProperties() / EndRunProperties().
2286 ///         </w:rPr>
2287 ///         <w:fldChar w:fldCharType="begin" />
2288 ///     </w:r>
2289 ///         <w:r>
2290 ///         <w:rPr>
2291 ///             <!-- properties written with DoWriteFieldRunProperties()
2292 ///         </w:rPr>
2293 ///         <w:instrText>TIME \@"HH:mm:ss"</w:instrText>
2294 ///     </w:r>
2295 ///     <w:r>
2296 ///         <w:rPr>
2297 ///             <!-- properties written with DoWriteFieldRunProperties()
2298 ///         </w:rPr>
2299 ///         <w:fldChar w:fldCharType="separate" />
2300 ///     </w:r>
2301 ///     <w:r>
2302 ///         <w:rPr>
2303 ///             <!-- properties written with DoWriteFieldRunProperties()
2304 ///         </w:rPr>
2305 ///         <w:t>14:01:13</w:t>
2306 ///         </w:r>
2307 ///     <w:r>
2308 ///         <w:rPr>
2309 ///             <!-- properties written with DoWriteFieldRunProperties()
2310 ///         </w:rPr>
2311 ///         <w:fldChar w:fldCharType="end" />
2312 ///     </w:r>
2313 /// See, tdf#38778
DoWriteFieldRunProperties(const SwTextNode * pNode,sal_Int32 nPos,bool bWriteCombChars)2314 void DocxAttributeOutput::DoWriteFieldRunProperties( const SwTextNode * pNode, sal_Int32 nPos, bool bWriteCombChars)
2315 {
2316     if (! pNode)
2317     {
2318         // nothing to do
2319         return;
2320     }
2321 
2322     m_bPreventDoubleFieldsHandling = true;
2323 
2324     {
2325         m_pSerializer->startElementNS(XML_w, XML_rPr);
2326 
2327         // 1. output webHidden flag
2328         if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
2329         {
2330             m_pSerializer->singleElementNS(XML_w, XML_webHidden);
2331         }
2332 
2333         // 2. find all active character properties
2334         SwWW8AttrIter aAttrIt( m_rExport, *pNode );
2335         aAttrIt.OutAttr( nPos, bWriteCombChars );
2336 
2337         // 3. write the character properties
2338         WriteCollectedRunProperties();
2339 
2340         m_pSerializer->endElementNS( XML_w, XML_rPr );
2341     }
2342 
2343     m_bPreventDoubleFieldsHandling = false;
2344 }
2345 
EndField_Impl(const SwTextNode * pNode,sal_Int32 nPos,FieldInfos & rInfos)2346 void DocxAttributeOutput::EndField_Impl( const SwTextNode* pNode, sal_Int32 nPos, FieldInfos& rInfos )
2347 {
2348     if (rInfos.eType == ww::eFORMDATE)
2349     {
2350         WriteSdtEnd();
2351         return;
2352     }
2353     if (rInfos.eType == ww::eFORMDROPDOWN && rInfos.pField)
2354     {
2355         // write selected item from End not Start to ensure that any bookmarks
2356         // precede it
2357         SwDropDownField const& rField(*static_cast<SwDropDownField const*>(rInfos.pField.get()));
2358         WriteSdtDropDownEnd(rField.GetSelectedItem(), rField.GetItemSequence());
2359         return;
2360     }
2361 
2362     // The command has to be written before for the hyperlinks
2363     if ( rInfos.pField )
2364     {
2365         CmdField_Impl( pNode, nPos, rInfos, true );
2366         CmdEndField_Impl( pNode, nPos, true );
2367     }
2368 
2369     // Write the bookmark start if any
2370     if ( !m_sFieldBkm.isEmpty() )
2371     {
2372         DoWriteBookmarkTagStart(m_sFieldBkm);
2373     }
2374 
2375     if (rInfos.pField ) // For hyperlinks and TOX
2376     {
2377         // Write the Field latest value
2378         m_pSerializer->startElementNS(XML_w, XML_r);
2379         DoWriteFieldRunProperties( pNode, nPos );
2380 
2381         OUString sExpand;
2382         if(rInfos.eType == ww::eCITATION)
2383         {
2384             sExpand = static_cast<SwAuthorityField const*>(rInfos.pField.get())
2385                         ->ExpandCitation(AUTH_FIELD_TITLE, nullptr);
2386         }
2387         else if(rInfos.eType != ww::eFORMDROPDOWN)
2388         {
2389             sExpand = rInfos.pField->ExpandField(true, nullptr);
2390         }
2391         // newlines embedded in fields are 0x0B in MSO and 0x0A for us
2392         RunText(sExpand.replace(0x0A, 0x0B));
2393 
2394         m_pSerializer->endElementNS( XML_w, XML_r );
2395     }
2396 
2397     // Write the bookmark end if any
2398     if ( !m_sFieldBkm.isEmpty() )
2399     {
2400         DoWriteBookmarkTagEnd(m_nNextBookmarkId);
2401 
2402         m_nNextBookmarkId++;
2403     }
2404 
2405     // Write the Field end
2406     if ( rInfos.bClose  )
2407     {
2408         m_bWritingField = false;
2409         m_pSerializer->startElementNS(XML_w, XML_r);
2410         DoWriteFieldRunProperties( pNode, nPos );
2411         m_pSerializer->singleElementNS(XML_w, XML_fldChar, FSNS(XML_w, XML_fldCharType), "end");
2412         m_pSerializer->endElementNS( XML_w, XML_r );
2413     }
2414     // Write the ref field if a bookmark had to be set and the field
2415     // should be visible
2416     if ( !rInfos.pField )
2417     {
2418         m_sFieldBkm.clear();
2419         return;
2420     }
2421 
2422     sal_uInt16 nSubType = rInfos.pField->GetSubType( );
2423     bool bIsSetField = rInfos.pField->GetTyp( )->Which( ) == SwFieldIds::SetExp;
2424     bool bShowRef = bIsSetField && ( nSubType & nsSwExtendedSubType::SUB_INVISIBLE ) == 0;
2425 
2426     if (!bShowRef)
2427     {
2428         m_sFieldBkm.clear();
2429     }
2430 
2431     if (m_sFieldBkm.isEmpty())
2432         return;
2433 
2434     // Write the field beginning
2435     m_pSerializer->startElementNS(XML_w, XML_r);
2436     m_pSerializer->singleElementNS( XML_w, XML_fldChar,
2437         FSNS( XML_w, XML_fldCharType ), "begin" );
2438     m_pSerializer->endElementNS( XML_w, XML_r );
2439 
2440     rInfos.sCmd = FieldString( ww::eREF );
2441     rInfos.sCmd += "\"";
2442     rInfos.sCmd += m_sFieldBkm;
2443     rInfos.sCmd += "\" ";
2444 
2445     // Clean the field bookmark data to avoid infinite loop
2446     m_sFieldBkm = OUString( );
2447 
2448     // Write the end of the field
2449     EndField_Impl( pNode, nPos, rInfos );
2450 }
2451 
StartRunProperties()2452 void DocxAttributeOutput::StartRunProperties()
2453 {
2454     // postpone the output so that we can later [in EndRunProperties()]
2455     // prepend the properties before the text
2456     m_pSerializer->mark(Tag_StartRunProperties);
2457 
2458     m_pSerializer->startElementNS(XML_w, XML_rPr);
2459 
2460     if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
2461     {
2462         m_pSerializer->singleElementNS(XML_w, XML_webHidden);
2463     }
2464     InitCollectedRunProperties();
2465 
2466     assert( !m_pPostponedGraphic );
2467     m_pPostponedGraphic.reset(new std::vector<PostponedGraphic>);
2468 
2469     assert( !m_pPostponedDiagrams );
2470     m_pPostponedDiagrams.reset(new std::vector<PostponedDiagram>);
2471 
2472     assert(!m_pPostponedDMLDrawings);
2473     m_pPostponedDMLDrawings.reset(new std::vector<PostponedDrawing>);
2474 
2475     assert( !m_pPostponedOLEs );
2476     m_pPostponedOLEs.reset(new std::vector<PostponedOLE>);
2477 }
2478 
InitCollectedRunProperties()2479 void DocxAttributeOutput::InitCollectedRunProperties()
2480 {
2481     m_pFontsAttrList = nullptr;
2482     m_pEastAsianLayoutAttrList = nullptr;
2483     m_pCharLangAttrList = nullptr;
2484 
2485     // Write the elements in the spec order
2486     static const sal_Int32 aOrder[] =
2487     {
2488         FSNS( XML_w, XML_rStyle ),
2489         FSNS( XML_w, XML_rFonts ),
2490         FSNS( XML_w, XML_b ),
2491         FSNS( XML_w, XML_bCs ),
2492         FSNS( XML_w, XML_i ),
2493         FSNS( XML_w, XML_iCs ),
2494         FSNS( XML_w, XML_caps ),
2495         FSNS( XML_w, XML_smallCaps ),
2496         FSNS( XML_w, XML_strike ),
2497         FSNS( XML_w, XML_dstrike ),
2498         FSNS( XML_w, XML_outline ),
2499         FSNS( XML_w, XML_shadow ),
2500         FSNS( XML_w, XML_emboss ),
2501         FSNS( XML_w, XML_imprint ),
2502         FSNS( XML_w, XML_noProof ),
2503         FSNS( XML_w, XML_snapToGrid ),
2504         FSNS( XML_w, XML_vanish ),
2505         FSNS( XML_w, XML_webHidden ),
2506         FSNS( XML_w, XML_color ),
2507         FSNS( XML_w, XML_spacing ),
2508         FSNS( XML_w, XML_w ),
2509         FSNS( XML_w, XML_kern ),
2510         FSNS( XML_w, XML_position ),
2511         FSNS( XML_w, XML_sz ),
2512         FSNS( XML_w, XML_szCs ),
2513         FSNS( XML_w, XML_highlight ),
2514         FSNS( XML_w, XML_u ),
2515         FSNS( XML_w, XML_effect ),
2516         FSNS( XML_w, XML_bdr ),
2517         FSNS( XML_w, XML_shd ),
2518         FSNS( XML_w, XML_fitText ),
2519         FSNS( XML_w, XML_vertAlign ),
2520         FSNS( XML_w, XML_rtl ),
2521         FSNS( XML_w, XML_cs ),
2522         FSNS( XML_w, XML_em ),
2523         FSNS( XML_w, XML_lang ),
2524         FSNS( XML_w, XML_eastAsianLayout ),
2525         FSNS( XML_w, XML_specVanish ),
2526         FSNS( XML_w, XML_oMath ),
2527         FSNS( XML_w, XML_rPrChange ),
2528         FSNS( XML_w, XML_del ),
2529         FSNS( XML_w14, XML_glow ),
2530         FSNS( XML_w14, XML_shadow ),
2531         FSNS( XML_w14, XML_reflection ),
2532         FSNS( XML_w14, XML_textOutline ),
2533         FSNS( XML_w14, XML_textFill ),
2534         FSNS( XML_w14, XML_scene3d ),
2535         FSNS( XML_w14, XML_props3d ),
2536         FSNS( XML_w14, XML_ligatures ),
2537         FSNS( XML_w14, XML_numForm ),
2538         FSNS( XML_w14, XML_numSpacing ),
2539         FSNS( XML_w14, XML_stylisticSets ),
2540         FSNS( XML_w14, XML_cntxtAlts ),
2541     };
2542 
2543     // postpone the output so that we can later [in EndParagraphProperties()]
2544     // prepend the properties before the run
2545     m_pSerializer->mark(Tag_InitCollectedRunProperties, comphelper::containerToSequence(aOrder));
2546 }
2547 
2548 namespace
2549 {
2550 
2551 struct NameToId
2552 {
2553     OUString  maName;
2554     sal_Int32 maId;
2555 };
2556 
2557 const NameToId constNameToIdMapping[] =
2558 {
2559     { OUString("glow"),         FSNS( XML_w14, XML_glow ) },
2560     { OUString("shadow"),       FSNS( XML_w14, XML_shadow ) },
2561     { OUString("reflection"),   FSNS( XML_w14, XML_reflection ) },
2562     { OUString("textOutline"),  FSNS( XML_w14, XML_textOutline ) },
2563     { OUString("textFill"),     FSNS( XML_w14, XML_textFill ) },
2564     { OUString("scene3d"),      FSNS( XML_w14, XML_scene3d ) },
2565     { OUString("props3d"),      FSNS( XML_w14, XML_props3d ) },
2566     { OUString("ligatures"),    FSNS( XML_w14, XML_ligatures ) },
2567     { OUString("numForm"),      FSNS( XML_w14, XML_numForm ) },
2568     { OUString("numSpacing"),   FSNS( XML_w14, XML_numSpacing ) },
2569     { OUString("stylisticSets"),FSNS( XML_w14, XML_stylisticSets ) },
2570     { OUString("cntxtAlts"),    FSNS( XML_w14, XML_cntxtAlts ) },
2571 
2572     { OUString("val"),          FSNS( XML_w14, XML_val ) },
2573     { OUString("rad"),          FSNS( XML_w14, XML_rad ) },
2574     { OUString("blurRad"),      FSNS( XML_w14, XML_blurRad ) },
2575     { OUString("stA"),          FSNS( XML_w14, XML_stA ) },
2576     { OUString("stPos"),        FSNS( XML_w14, XML_stPos ) },
2577     { OUString("endA"),         FSNS( XML_w14, XML_endA ) },
2578     { OUString("endPos"),       FSNS( XML_w14, XML_endPos ) },
2579     { OUString("dist"),         FSNS( XML_w14, XML_dist ) },
2580     { OUString("dir"),          FSNS( XML_w14, XML_dir ) },
2581     { OUString("fadeDir"),      FSNS( XML_w14, XML_fadeDir ) },
2582     { OUString("sx"),           FSNS( XML_w14, XML_sx ) },
2583     { OUString("sy"),           FSNS( XML_w14, XML_sy ) },
2584     { OUString("kx"),           FSNS( XML_w14, XML_kx ) },
2585     { OUString("ky"),           FSNS( XML_w14, XML_ky ) },
2586     { OUString("algn"),         FSNS( XML_w14, XML_algn ) },
2587     { OUString("w"),            FSNS( XML_w14, XML_w ) },
2588     { OUString("cap"),          FSNS( XML_w14, XML_cap ) },
2589     { OUString("cmpd"),         FSNS( XML_w14, XML_cmpd ) },
2590     { OUString("pos"),          FSNS( XML_w14, XML_pos ) },
2591     { OUString("ang"),          FSNS( XML_w14, XML_ang ) },
2592     { OUString("scaled"),       FSNS( XML_w14, XML_scaled ) },
2593     { OUString("path"),         FSNS( XML_w14, XML_path ) },
2594     { OUString("l"),            FSNS( XML_w14, XML_l ) },
2595     { OUString("t"),            FSNS( XML_w14, XML_t ) },
2596     { OUString("r"),            FSNS( XML_w14, XML_r ) },
2597     { OUString("b"),            FSNS( XML_w14, XML_b ) },
2598     { OUString("lim"),          FSNS( XML_w14, XML_lim ) },
2599     { OUString("prst"),         FSNS( XML_w14, XML_prst ) },
2600     { OUString("rig"),          FSNS( XML_w14, XML_rig ) },
2601     { OUString("lat"),          FSNS( XML_w14, XML_lat ) },
2602     { OUString("lon"),          FSNS( XML_w14, XML_lon ) },
2603     { OUString("rev"),          FSNS( XML_w14, XML_rev ) },
2604     { OUString("h"),            FSNS( XML_w14, XML_h ) },
2605     { OUString("extrusionH"),   FSNS( XML_w14, XML_extrusionH ) },
2606     { OUString("contourW"),     FSNS( XML_w14, XML_contourW ) },
2607     { OUString("prstMaterial"), FSNS( XML_w14, XML_prstMaterial ) },
2608     { OUString("id"),           FSNS( XML_w14, XML_id ) },
2609 
2610     { OUString("schemeClr"),    FSNS( XML_w14, XML_schemeClr ) },
2611     { OUString("srgbClr"),      FSNS( XML_w14, XML_srgbClr ) },
2612     { OUString("tint"),         FSNS( XML_w14, XML_tint ) },
2613     { OUString("shade"),        FSNS( XML_w14, XML_shade ) },
2614     { OUString("alpha"),        FSNS( XML_w14, XML_alpha ) },
2615     { OUString("hueMod"),       FSNS( XML_w14, XML_hueMod ) },
2616     { OUString("sat"),          FSNS( XML_w14, XML_sat ) },
2617     { OUString("satOff"),       FSNS( XML_w14, XML_satOff ) },
2618     { OUString("satMod"),       FSNS( XML_w14, XML_satMod ) },
2619     { OUString("lum"),          FSNS( XML_w14, XML_lum ) },
2620     { OUString("lumOff"),       FSNS( XML_w14, XML_lumOff ) },
2621     { OUString("lumMod"),       FSNS( XML_w14, XML_lumMod ) },
2622     { OUString("noFill"),       FSNS( XML_w14, XML_noFill ) },
2623     { OUString("solidFill"),    FSNS( XML_w14, XML_solidFill ) },
2624     { OUString("gradFill"),     FSNS( XML_w14, XML_gradFill ) },
2625     { OUString("gsLst"),        FSNS( XML_w14, XML_gsLst ) },
2626     { OUString("gs"),           FSNS( XML_w14, XML_gs ) },
2627     { OUString("pos"),          FSNS( XML_w14, XML_pos ) },
2628     { OUString("lin"),          FSNS( XML_w14, XML_lin ) },
2629     { OUString("path"),         FSNS( XML_w14, XML_path ) },
2630     { OUString("fillToRect"),   FSNS( XML_w14, XML_fillToRect ) },
2631     { OUString("prstDash"),     FSNS( XML_w14, XML_prstDash ) },
2632     { OUString("round"),        FSNS( XML_w14, XML_round ) },
2633     { OUString("bevel"),        FSNS( XML_w14, XML_bevel ) },
2634     { OUString("miter"),        FSNS( XML_w14, XML_miter ) },
2635     { OUString("camera"),       FSNS( XML_w14, XML_camera ) },
2636     { OUString("lightRig"),     FSNS( XML_w14, XML_lightRig ) },
2637     { OUString("rot"),          FSNS( XML_w14, XML_rot ) },
2638     { OUString("bevelT"),       FSNS( XML_w14, XML_bevelT ) },
2639     { OUString("bevelB"),       FSNS( XML_w14, XML_bevelB ) },
2640     { OUString("extrusionClr"), FSNS( XML_w14, XML_extrusionClr ) },
2641     { OUString("contourClr"),   FSNS( XML_w14, XML_contourClr ) },
2642     { OUString("styleSet"),     FSNS( XML_w14, XML_styleSet ) },
2643 };
2644 
lclGetElementIdForName(std::u16string_view rName)2645 std::optional<sal_Int32> lclGetElementIdForName(std::u16string_view rName)
2646 {
2647     for (auto const & i : constNameToIdMapping)
2648     {
2649         if (rName == i.maName)
2650         {
2651             return i.maId;
2652         }
2653     }
2654     return std::optional<sal_Int32>();
2655 }
2656 
lclProcessRecursiveGrabBag(sal_Int32 aElementId,const css::uno::Sequence<css::beans::PropertyValue> & rElements,sax_fastparser::FSHelperPtr const & pSerializer)2657 void lclProcessRecursiveGrabBag(sal_Int32 aElementId, const css::uno::Sequence<css::beans::PropertyValue>& rElements, sax_fastparser::FSHelperPtr const & pSerializer)
2658 {
2659     css::uno::Sequence<css::beans::PropertyValue> aAttributes;
2660     rtl::Reference<FastAttributeList> pAttributes = FastSerializerHelper::createAttrList();
2661 
2662     for (const auto& rElement : rElements)
2663     {
2664         if (rElement.Name == "attributes")
2665         {
2666             rElement.Value >>= aAttributes;
2667         }
2668     }
2669 
2670     for (const auto& rAttribute : std::as_const(aAttributes))
2671     {
2672         uno::Any aAny = rAttribute.Value;
2673         OString aValue;
2674 
2675         if(aAny.getValueType() == cppu::UnoType<sal_Int32>::get())
2676         {
2677             aValue = OString::number(aAny.get<sal_Int32>());
2678         }
2679         else if(aAny.getValueType() == cppu::UnoType<OUString>::get())
2680         {
2681             aValue =  OUStringToOString(aAny.get<OUString>(), RTL_TEXTENCODING_ASCII_US);
2682         }
2683 
2684         std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rAttribute.Name);
2685         if(aSubElementId)
2686             pAttributes->add(*aSubElementId, aValue);
2687     }
2688 
2689     pSerializer->startElement(aElementId, pAttributes);
2690 
2691     for (const auto& rElement : rElements)
2692     {
2693         css::uno::Sequence<css::beans::PropertyValue> aSumElements;
2694 
2695         std::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rElement.Name);
2696         if(aSubElementId)
2697         {
2698             rElement.Value >>= aSumElements;
2699             lclProcessRecursiveGrabBag(*aSubElementId, aSumElements, pSerializer);
2700         }
2701     }
2702 
2703     pSerializer->endElement(aElementId);
2704 }
2705 
2706 }
2707 
WriteCollectedRunProperties()2708 void DocxAttributeOutput::WriteCollectedRunProperties()
2709 {
2710     // Write all differed properties
2711     if ( m_pFontsAttrList.is() )
2712     {
2713         rtl::Reference<FastAttributeList> xAttrList = std::move( m_pFontsAttrList );
2714         m_pSerializer->singleElementNS( XML_w, XML_rFonts, xAttrList );
2715     }
2716 
2717     if ( m_pColorAttrList.is() )
2718     {
2719         rtl::Reference<FastAttributeList> xAttrList( m_pColorAttrList );
2720 
2721         m_pSerializer->singleElementNS( XML_w, XML_color, xAttrList );
2722     }
2723 
2724     if ( m_pEastAsianLayoutAttrList.is() )
2725     {
2726         rtl::Reference<FastAttributeList> xAttrList = std::move( m_pEastAsianLayoutAttrList );
2727         m_pSerializer->singleElementNS( XML_w, XML_eastAsianLayout, xAttrList );
2728     }
2729 
2730     if ( m_pCharLangAttrList.is() )
2731     {
2732         rtl::Reference<FastAttributeList> xAttrList = std::move( m_pCharLangAttrList );
2733         m_pSerializer->singleElementNS( XML_w, XML_lang, xAttrList );
2734     }
2735 
2736     if (m_nCharTransparence != 0 && m_pColorAttrList && m_aTextEffectsGrabBag.empty())
2737     {
2738         const char* pVal = nullptr;
2739         m_pColorAttrList->getAsChar(FSNS(XML_w, XML_val), pVal);
2740         if (std::string_view("auto") != pVal)
2741         {
2742             m_pSerializer->startElementNS(XML_w14, XML_textFill);
2743             m_pSerializer->startElementNS(XML_w14, XML_solidFill);
2744             m_pSerializer->startElementNS(XML_w14, XML_srgbClr, FSNS(XML_w14, XML_val), pVal);
2745             sal_Int32 nTransparence = m_nCharTransparence * oox::drawingml::MAX_PERCENT / 255.0;
2746             m_pSerializer->singleElementNS(XML_w14, XML_alpha, FSNS(XML_w14, XML_val), OString::number(nTransparence));
2747             m_pSerializer->endElementNS(XML_w14, XML_srgbClr);
2748             m_pSerializer->endElementNS(XML_w14, XML_solidFill);
2749             m_pSerializer->endElementNS(XML_w14, XML_textFill);
2750             m_nCharTransparence = 0;
2751         }
2752     }
2753     m_pColorAttrList.clear();
2754     for (const beans::PropertyValue & i : m_aTextEffectsGrabBag)
2755     {
2756         std::optional<sal_Int32> aElementId = lclGetElementIdForName(i.Name);
2757         if(aElementId)
2758         {
2759             uno::Sequence<beans::PropertyValue> aGrabBagSeq;
2760             i.Value >>= aGrabBagSeq;
2761             lclProcessRecursiveGrabBag(*aElementId, aGrabBagSeq, m_pSerializer);
2762         }
2763     }
2764     m_aTextEffectsGrabBag.clear();
2765 }
2766 
EndRunProperties(const SwRedlineData * pRedlineData)2767 void DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData )
2768 {
2769     // Call the 'Redline' function. This will add redline (change-tracking) information that regards to run properties.
2770     // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
2771     Redline( pRedlineData );
2772 
2773     WriteCollectedRunProperties();
2774 
2775     // Merge the marks for the ordered elements
2776     m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
2777 
2778     m_pSerializer->endElementNS( XML_w, XML_rPr );
2779 
2780     // write footnotes/endnotes if we have any
2781     FootnoteEndnoteReference();
2782 
2783     // merge the properties _before_ the run text (strictly speaking, just
2784     // after the start of the run)
2785     m_pSerializer->mergeTopMarks(Tag_StartRunProperties, sax_fastparser::MergeMarks::PREPEND);
2786 
2787     WritePostponedGraphic();
2788 
2789     WritePostponedDiagram();
2790     //We need to write w:drawing tag after the w:rPr.
2791     WritePostponedChart();
2792 
2793     //We need to write w:pict tag after the w:rPr.
2794     WritePostponedDMLDrawing();
2795 
2796     WritePostponedOLE();
2797 
2798     WritePostponedActiveXControl(true);
2799 }
2800 
GetSdtEndBefore(const SdrObject * pSdrObj)2801 void DocxAttributeOutput::GetSdtEndBefore(const SdrObject* pSdrObj)
2802 {
2803     if (!pSdrObj)
2804         return;
2805 
2806     uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY_THROW);
2807     uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
2808     if( !xPropSet.is() )
2809         return;
2810 
2811     uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
2812     uno::Sequence< beans::PropertyValue > aGrabBag;
2813     if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
2814     {
2815         xPropSet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
2816     }
2817     else if(xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
2818     {
2819         xPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
2820     }
2821 
2822     auto pProp = std::find_if(aGrabBag.begin(), aGrabBag.end(),
2823         [this](const beans::PropertyValue& rProp) {
2824             return "SdtEndBefore" == rProp.Name && m_bStartedCharSdt && !m_bEndCharSdt; });
2825     if (pProp != aGrabBag.end())
2826         pProp->Value >>= m_bEndCharSdt;
2827 }
2828 
WritePostponedGraphic()2829 void DocxAttributeOutput::WritePostponedGraphic()
2830 {
2831     for (const auto & rPostponedDiagram : *m_pPostponedGraphic)
2832         FlyFrameGraphic(rPostponedDiagram.grfNode, rPostponedDiagram.size,
2833             nullptr, nullptr,
2834             rPostponedDiagram.pSdrObj);
2835     m_pPostponedGraphic.reset();
2836 }
2837 
WritePostponedDiagram()2838 void DocxAttributeOutput::WritePostponedDiagram()
2839 {
2840     for( const auto & rPostponedDiagram : *m_pPostponedDiagrams )
2841         m_rExport.SdrExporter().writeDiagram(rPostponedDiagram.object,
2842             *rPostponedDiagram.frame, m_anchorId++);
2843     m_pPostponedDiagrams.reset();
2844 }
2845 
FootnoteEndnoteRefTag()2846 bool DocxAttributeOutput::FootnoteEndnoteRefTag()
2847 {
2848     if( m_footnoteEndnoteRefTag == 0 )
2849         return false;
2850 
2851     // output the character style for MS Word's benefit
2852     const SwEndNoteInfo& rInfo = m_footnoteEndnoteRefTag == XML_footnoteRef ?
2853         m_rExport.m_rDoc.GetFootnoteInfo() : m_rExport.m_rDoc.GetEndNoteInfo();
2854     const SwCharFormat* pCharFormat = rInfo.GetCharFormat( m_rExport.m_rDoc );
2855     if ( pCharFormat )
2856     {
2857         const OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat)));
2858         m_pSerializer->startElementNS(XML_w, XML_rPr);
2859         m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
2860         m_pSerializer->endElementNS( XML_w, XML_rPr );
2861     }
2862 
2863     if (m_footnoteCustomLabel.isEmpty())
2864         m_pSerializer->singleElementNS(XML_w, m_footnoteEndnoteRefTag);
2865     else
2866         RunText(m_footnoteCustomLabel);
2867     m_footnoteEndnoteRefTag = 0;
2868     return true;
2869 }
2870 
2871 /** Output sal_Unicode* as a run text (<t>the text</t>).
2872 
2873     When bMove is true, update rBegin to point _after_ the end of the text +
2874     1, meaning that it skips one character after the text.  This is to make
2875     the switch in DocxAttributeOutput::RunText() nicer ;-)
2876  */
impl_WriteRunText(FSHelperPtr const & pSerializer,sal_Int32 nTextToken,const sal_Unicode * & rBegin,const sal_Unicode * pEnd,bool bMove=true)2877 static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextToken,
2878         const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true )
2879 {
2880     const sal_Unicode *pBegin = rBegin;
2881 
2882     // skip one character after the end
2883     if ( bMove )
2884         rBegin = pEnd + 1;
2885 
2886     if ( pBegin >= pEnd )
2887         return false; // we want to write at least one character
2888 
2889     // we have to add 'preserve' when starting/ending with space
2890     if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' )
2891     {
2892         pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve");
2893     }
2894     else
2895         pSerializer->startElementNS(XML_w, nTextToken);
2896 
2897     pSerializer->writeEscaped( std::u16string_view( pBegin, pEnd - pBegin ) );
2898 
2899     pSerializer->endElementNS( XML_w, nTextToken );
2900 
2901     return true;
2902 }
2903 
RunText(const OUString & rText,rtl_TextEncoding)2904 void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/ )
2905 {
2906     if( m_closeHyperlinkInThisRun )
2907     {
2908         m_closeHyperlinkInPreviousRun = true;
2909     }
2910     m_bRunTextIsOn = true;
2911     // one text can be split into more <w:t>blah</w:t>'s by line breaks etc.
2912     const sal_Unicode *pBegin = rText.getStr();
2913     const sal_Unicode *pEnd = pBegin + rText.getLength();
2914 
2915     // the text run is usually XML_t, with the exception of the deleted text
2916     sal_Int32 nTextToken = XML_t;
2917     if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete )
2918         nTextToken = XML_delText;
2919 
2920     sal_Unicode prevUnicode = *pBegin;
2921 
2922     for ( const sal_Unicode *pIt = pBegin; pIt < pEnd; ++pIt )
2923     {
2924         switch ( *pIt )
2925         {
2926             case 0x09: // tab
2927                 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2928                 m_pSerializer->singleElementNS(XML_w, XML_tab);
2929                 prevUnicode = *pIt;
2930                 break;
2931             case 0x0b: // line break
2932                 {
2933                     if (impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ) || prevUnicode < 0x0020)
2934                     {
2935                         m_pSerializer->singleElementNS(XML_w, XML_br);
2936                         prevUnicode = *pIt;
2937                     }
2938                 }
2939                 break;
2940             case 0x1E: //non-breaking hyphen
2941                 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2942                 m_pSerializer->singleElementNS(XML_w, XML_noBreakHyphen);
2943                 prevUnicode = *pIt;
2944                 break;
2945             case 0x1F: //soft (on demand) hyphen
2946                 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2947                 m_pSerializer->singleElementNS(XML_w, XML_softHyphen);
2948                 prevUnicode = *pIt;
2949                 break;
2950             default:
2951                 if ( *pIt < 0x0020 ) // filter out the control codes
2952                 {
2953                     impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2954                     SAL_INFO("sw.ww8", "Ignored control code in a text run: " << unsigned(*pIt) );
2955                 }
2956                 prevUnicode = *pIt;
2957                 break;
2958         }
2959     }
2960 
2961     impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false );
2962 }
2963 
RawText(const OUString & rText,rtl_TextEncoding)2964 void DocxAttributeOutput::RawText(const OUString& rText, rtl_TextEncoding /*eCharSet*/)
2965 {
2966     m_sRawText = rText;
2967 }
2968 
StartRuby(const SwTextNode & rNode,sal_Int32 nPos,const SwFormatRuby & rRuby)2969 void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby )
2970 {
2971     WW8Ruby aWW8Ruby( rNode, rRuby, GetExport() );
2972     SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRuby( const SwTextNode& rNode, const SwFormatRuby& rRuby )" );
2973     EndRun( &rNode, nPos ); // end run before starting ruby to avoid nested runs, and overlap
2974     assert(!m_closeHyperlinkInThisRun); // check that no hyperlink overlaps ruby
2975     assert(!m_closeHyperlinkInPreviousRun);
2976     m_pSerializer->startElementNS(XML_w, XML_r);
2977     m_pSerializer->startElementNS(XML_w, XML_ruby);
2978     m_pSerializer->startElementNS(XML_w, XML_rubyPr);
2979 
2980     m_pSerializer->singleElementNS( XML_w, XML_rubyAlign,
2981             FSNS( XML_w, XML_val ), lclConvertWW8JCToOOXMLRubyAlign(aWW8Ruby.GetJC()) );
2982     sal_uInt32   nHps = (aWW8Ruby.GetRubyHeight() + 5) / 10;
2983     sal_uInt32   nHpsBaseText = (aWW8Ruby.GetBaseHeight() + 5) / 10;
2984     m_pSerializer->singleElementNS(XML_w, XML_hps, FSNS(XML_w, XML_val), OString::number(nHps));
2985 
2986     m_pSerializer->singleElementNS( XML_w, XML_hpsRaise,
2987             FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) );
2988 
2989     m_pSerializer->singleElementNS( XML_w, XML_hpsBaseText,
2990             FSNS( XML_w, XML_val ), OString::number(nHpsBaseText) );
2991 
2992     lang::Locale aLocale( SwBreakIt::Get()->GetLocale(
2993                 rNode.GetLang( nPos ) ) );
2994     OUString sLang( LanguageTag::convertToBcp47( aLocale) );
2995     m_pSerializer->singleElementNS(XML_w, XML_lid, FSNS(XML_w, XML_val), sLang);
2996 
2997     m_pSerializer->endElementNS( XML_w, XML_rubyPr );
2998 
2999     m_pSerializer->startElementNS(XML_w, XML_rt);
3000     StartRun( nullptr, nPos );
3001     StartRunProperties( );
3002 
3003     if (rRuby.GetTextRuby() && rRuby.GetTextRuby()->GetCharFormat())
3004     {
3005         const SwCharFormat* pFormat = rRuby.GetTextRuby()->GetCharFormat();
3006         sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType(rRuby.GetText(), 0);
3007         sal_uInt16 nWhichFont = (nScript == i18n::ScriptType::LATIN) ?  RES_CHRATR_FONT : RES_CHRATR_CJK_FONT;
3008         sal_uInt16 nWhichFontSize = (nScript == i18n::ScriptType::LATIN) ?  RES_CHRATR_FONTSIZE : RES_CHRATR_CJK_FONTSIZE;
3009 
3010         CharFont(ItemGet<SvxFontItem>(*pFormat, nWhichFont));
3011         CharFontSize(ItemGet<SvxFontHeightItem>(*pFormat, nWhichFontSize));
3012         CharFontSize(ItemGet<SvxFontHeightItem>(*pFormat, RES_CHRATR_CTL_FONTSIZE));
3013     }
3014 
3015     EndRunProperties( nullptr );
3016     RunText( rRuby.GetText( ) );
3017     EndRun( &rNode, nPos );
3018     m_pSerializer->endElementNS( XML_w, XML_rt );
3019 
3020     m_pSerializer->startElementNS(XML_w, XML_rubyBase);
3021     StartRun( nullptr, nPos );
3022 }
3023 
EndRuby(const SwTextNode & rNode,sal_Int32 nPos)3024 void DocxAttributeOutput::EndRuby(const SwTextNode& rNode, sal_Int32 nPos)
3025 {
3026     SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRuby()" );
3027     EndRun( &rNode, nPos );
3028     m_pSerializer->endElementNS( XML_w, XML_rubyBase );
3029     m_pSerializer->endElementNS( XML_w, XML_ruby );
3030     m_pSerializer->endElementNS( XML_w, XML_r );
3031     StartRun(nullptr, nPos); // open Run again so OutputTextNode loop can close it
3032 }
3033 
AnalyzeURL(const OUString & rUrl,const OUString & rTarget,OUString * pLinkURL,OUString * pMark)3034 bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark )
3035 {
3036     bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark );
3037 
3038     if ( !pMark->isEmpty() )
3039     {
3040         OUString sURL = *pLinkURL;
3041 
3042         if ( bBookMarkOnly )
3043             sURL = FieldString( ww::eHYPERLINK );
3044         else
3045             sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\"";
3046 
3047         sURL += " \\l \"" + *pMark + "\"";
3048 
3049         if ( !rTarget.isEmpty() )
3050             sURL += " \\n " + rTarget;
3051 
3052         *pLinkURL = sURL;
3053     }
3054 
3055     return bBookMarkOnly;
3056 }
3057 
WriteBookmarkInActParagraph(const OUString & rName,sal_Int32 nFirstRunPos,sal_Int32 nLastRunPos)3058 void DocxAttributeOutput::WriteBookmarkInActParagraph( const OUString& rName, sal_Int32 nFirstRunPos, sal_Int32 nLastRunPos )
3059 {
3060     m_aBookmarksOfParagraphStart.insert(std::pair<sal_Int32, OUString>(nFirstRunPos, rName));
3061     m_aBookmarksOfParagraphEnd.insert(std::pair<sal_Int32, OUString>(nLastRunPos, rName));
3062 }
3063 
StartURL(const OUString & rUrl,const OUString & rTarget)3064 bool DocxAttributeOutput::StartURL( const OUString& rUrl, const OUString& rTarget )
3065 {
3066     OUString sMark;
3067     OUString sUrl;
3068 
3069     bool bBookmarkOnly = AnalyzeURL( rUrl, rTarget, &sUrl, &sMark );
3070 
3071     m_hyperLinkAnchor = sMark;
3072 
3073     if ( !sMark.isEmpty() && !bBookmarkOnly )
3074     {
3075         m_rExport.OutputField( nullptr, ww::eHYPERLINK, sUrl );
3076     }
3077     else
3078     {
3079         // Output a hyperlink XML element
3080         m_pHyperlinkAttrList = FastSerializerHelper::createAttrList();
3081 
3082         if ( !bBookmarkOnly )
3083         {
3084             OString sId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
3085                         oox::getRelationship(Relationship::HYPERLINK),
3086                         sUrl, true ), RTL_TEXTENCODING_UTF8 );
3087 
3088             m_pHyperlinkAttrList->add(FSNS(XML_r, XML_id), sId);
3089         }
3090         else
3091         {
3092             // Is this a link to a sequence? Then try to replace that with a
3093             // normal bookmark, as Word won't understand our special
3094             // <seqname>!<index>|sequence syntax.
3095             if (sMark.endsWith("|sequence"))
3096             {
3097                 sal_Int32 nPos = sMark.indexOf('!');
3098                 if (nPos != -1)
3099                 {
3100                     // Extract <seqname>, the field instruction text has the name quoted.
3101                     OUString aSequenceName = sMark.copy(0, nPos);
3102                     // Extract <index>.
3103                     sal_uInt32 nIndex = sMark.copy(nPos + 1, sMark.getLength() - nPos - sizeof("|sequence")).toUInt32();
3104                     std::map<OUString, std::vector<OString> >::iterator it = m_aSeqBookmarksNames.find(aSequenceName);
3105                     if (it != m_aSeqBookmarksNames.end())
3106                     {
3107                         std::vector<OString>& rNames = it->second;
3108                         if (rNames.size() > nIndex)
3109                             // We know the bookmark name for this sequence and this index, do the replacement.
3110                             sMark = OStringToOUString(rNames[nIndex], RTL_TEXTENCODING_UTF8);
3111                     }
3112                 }
3113             }
3114             else if (sMark.endsWith("|toxmark"))
3115             {
3116                 if (auto const it = GetExport().m_TOXMarkBookmarksByURL.find(sMark);
3117                     it != GetExport().m_TOXMarkBookmarksByURL.end())
3118                 {
3119                     sMark = it->second;
3120                 }
3121             }
3122             // Spaces are prohibited in bookmark name.
3123             sMark = sMark.replace(' ', '_');
3124             m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ),
3125                     OUStringToOString( sMark, RTL_TEXTENCODING_UTF8 ) );
3126         }
3127 
3128         if ( !rTarget.isEmpty() )
3129         {
3130             OString soTarget = OUStringToOString( rTarget, RTL_TEXTENCODING_UTF8 );
3131             m_pHyperlinkAttrList->add(FSNS(XML_w, XML_tgtFrame), soTarget);
3132         }
3133     }
3134 
3135     return true;
3136 }
3137 
EndURL(bool const)3138 bool DocxAttributeOutput::EndURL(bool const)
3139 {
3140     m_closeHyperlinkInThisRun = true;
3141     if(m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc"))
3142     {
3143         m_endPageRef = true;
3144     }
3145     return true;
3146 }
3147 
FieldVanish(const OUString & rText,ww::eField const eType,OUString const * const pBookmarkName)3148 void DocxAttributeOutput::FieldVanish(const OUString& rText,
3149         ww::eField const eType, OUString const*const pBookmarkName)
3150 {
3151     WriteField_Impl(nullptr, eType, rText, FieldFlags::All, pBookmarkName);
3152 }
3153 
3154 // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
3155 // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
3156 // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
Redline(const SwRedlineData * pRedlineData)3157 void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData)
3158 {
3159     if ( !pRedlineData )
3160         return;
3161 
3162     OString aId( OString::number( pRedlineData->GetSeqNo() ) );
3163     const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
3164     OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) );
3165 
3166     switch( pRedlineData->GetType() )
3167     {
3168     case RedlineType::Insert:
3169         break;
3170 
3171     case RedlineType::Delete:
3172         break;
3173 
3174     case RedlineType::Format:
3175         m_pSerializer->startElementNS( XML_w, XML_rPrChange,
3176                 FSNS( XML_w, XML_id ), aId,
3177                 FSNS( XML_w, XML_author ), rAuthor,
3178                 FSNS( XML_w, XML_date ), aDate );
3179 
3180         m_pSerializer->endElementNS( XML_w, XML_rPrChange );
3181         break;
3182 
3183     case RedlineType::ParagraphFormat:
3184         m_pSerializer->startElementNS( XML_w, XML_pPrChange,
3185                 FSNS( XML_w, XML_id ), aId,
3186                 FSNS( XML_w, XML_author ), rAuthor,
3187                 FSNS( XML_w, XML_date ), aDate );
3188 
3189         // Check if there is any extra data stored in the redline object
3190         if (pRedlineData->GetExtraData())
3191         {
3192             const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData();
3193             const SwRedlineExtraData_FormatColl* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
3194 
3195             // Check if the extra data is of type 'formatting changes'
3196             if (pFormattingChanges)
3197             {
3198                 // Get the item set that holds all the changes properties
3199                 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
3200                 const OUString & sParaStyleName = pFormattingChanges->GetFormatName();
3201                 if (pChangesSet || !sParaStyleName.isEmpty())
3202                 {
3203                     m_pSerializer->mark(Tag_Redline_2);
3204 
3205                     m_pSerializer->startElementNS(XML_w, XML_pPr);
3206 
3207                     OString sStyleName = MSWordStyles::CreateStyleId( sParaStyleName );
3208                     if ( !sStyleName.isEmpty() )
3209                         m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), sStyleName);
3210 
3211                     // The 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' are used to hold information
3212                     // that should be collected by different properties in the core, and are all flushed together
3213                     // to the DOCX when the function 'WriteCollectedParagraphProperties' gets called.
3214                     // So we need to store the current status of these lists, so that we can revert back to them when
3215                     // we are done exporting the redline attributes.
3216                     rtl::Reference<sax_fastparser::FastAttributeList> pFlyAttrList_Original(m_rExport.SdrExporter().getFlyAttrList());
3217                     m_rExport.SdrExporter().getFlyAttrList().clear();
3218                     rtl::Reference<sax_fastparser::FastAttributeList> pParagraphSpacingAttrList_Original(m_pParagraphSpacingAttrList);
3219                     m_pParagraphSpacingAttrList.clear();
3220 
3221                     // Output the redline item set
3222                     if (pChangesSet)
3223                         m_rExport.OutputItemSet( *pChangesSet, true, false, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
3224 
3225                     // Write the collected paragraph properties that are stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
3226                     WriteCollectedParagraphProperties();
3227 
3228                     // Revert back the original values that were stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
3229                     m_rExport.SdrExporter().getFlyAttrList() = pFlyAttrList_Original;
3230                     m_pParagraphSpacingAttrList = pParagraphSpacingAttrList_Original;
3231 
3232                     m_pSerializer->endElementNS( XML_w, XML_pPr );
3233 
3234                     m_pSerializer->mergeTopMarks(Tag_Redline_2, sax_fastparser::MergeMarks::PREPEND);
3235                 }
3236             }
3237         }
3238         m_pSerializer->endElementNS( XML_w, XML_pPrChange );
3239         break;
3240 
3241     default:
3242         SAL_WARN("sw.ww8", "Unhandled redline type for export " << SwRedlineTypeToOUString(pRedlineData->GetType()));
3243         break;
3244     }
3245 }
3246 
3247 // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
3248 // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
3249 // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
StartRedline(const SwRedlineData * pRedlineData)3250 void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData )
3251 {
3252     if ( !pRedlineData )
3253         return;
3254 
3255     // write out stack of this redline recursively (first the oldest)
3256     StartRedline( pRedlineData->Next() );
3257 
3258     OString aId( OString::number( m_nRedlineId++ ) );
3259 
3260     const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
3261     OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
3262 
3263     OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) );
3264 
3265     switch ( pRedlineData->GetType() )
3266     {
3267         case RedlineType::Insert:
3268             m_pSerializer->startElementNS( XML_w, XML_ins,
3269                     FSNS( XML_w, XML_id ), aId,
3270                     FSNS( XML_w, XML_author ), aAuthor,
3271                     FSNS( XML_w, XML_date ), aDate );
3272             break;
3273 
3274         case RedlineType::Delete:
3275             m_pSerializer->startElementNS( XML_w, XML_del,
3276                     FSNS( XML_w, XML_id ), aId,
3277                     FSNS( XML_w, XML_author ), aAuthor,
3278                     FSNS( XML_w, XML_date ), aDate );
3279             break;
3280 
3281         case RedlineType::Format:
3282             SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::StartRedline()" );
3283             break;
3284         default:
3285             break;
3286     }
3287 }
3288 
EndRedline(const SwRedlineData * pRedlineData)3289 void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData )
3290 {
3291     if ( !pRedlineData || m_bWritingField )
3292         return;
3293 
3294     switch ( pRedlineData->GetType() )
3295     {
3296         case RedlineType::Insert:
3297             m_pSerializer->endElementNS( XML_w, XML_ins );
3298             break;
3299 
3300         case RedlineType::Delete:
3301             m_pSerializer->endElementNS( XML_w, XML_del );
3302             break;
3303 
3304         case RedlineType::Format:
3305             SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::EndRedline()" );
3306             break;
3307         default:
3308             break;
3309     }
3310 
3311     // write out stack of this redline recursively (first the newest)
3312     EndRedline( pRedlineData->Next() );
3313 }
3314 
FormatDrop(const SwTextNode &,const SwFormatDrop &,sal_uInt16,ww8::WW8TableNodeInfo::Pointer_t,ww8::WW8TableNodeInfoInner::Pointer_t)3315 void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t )
3316 {
3317     SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle )" );
3318 }
3319 
ParagraphStyle(sal_uInt16 nStyle)3320 void DocxAttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
3321 {
3322     OString aStyleId(m_rExport.m_pStyles->GetStyleId(nStyle));
3323 
3324     m_pSerializer->singleElementNS(XML_w, XML_pStyle, FSNS(XML_w, XML_val), aStyleId);
3325 }
3326 
impl_borderLine(FSHelperPtr const & pSerializer,sal_Int32 elementToken,const SvxBorderLine * pBorderLine,sal_uInt16 nDist,bool bWriteShadow,const table::BorderLine2 * rStyleProps=nullptr)3327 static void impl_borderLine( FSHelperPtr const & pSerializer, sal_Int32 elementToken, const SvxBorderLine* pBorderLine, sal_uInt16 nDist,
3328                              bool bWriteShadow, const table::BorderLine2* rStyleProps = nullptr )
3329 {
3330     // Compute val attribute value
3331     // Can be one of:
3332     //      single, double,
3333     //      basicWideOutline, basicWideInline
3334     // OOXml also supports those types of borders, but we'll try to play with the first ones.
3335     //      thickThinMediumGap, thickThinLargeGap, thickThinSmallGap
3336     //      thinThickLargeGap, thinThickMediumGap, thinThickSmallGap
3337     const char* pVal = "nil";
3338     if ( pBorderLine && !pBorderLine->isEmpty( ) )
3339     {
3340         switch (pBorderLine->GetBorderLineStyle())
3341         {
3342             case SvxBorderLineStyle::SOLID:
3343                 pVal = "single";
3344                 break;
3345             case SvxBorderLineStyle::DOTTED:
3346                 pVal = "dotted";
3347                 break;
3348             case SvxBorderLineStyle::DASHED:
3349                 pVal = "dashed";
3350                 break;
3351             case SvxBorderLineStyle::DOUBLE:
3352             case SvxBorderLineStyle::DOUBLE_THIN:
3353                 pVal = "double";
3354                 break;
3355             case SvxBorderLineStyle::THINTHICK_SMALLGAP:
3356                 pVal = "thinThickSmallGap";
3357                 break;
3358             case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
3359                 pVal = "thinThickMediumGap";
3360                 break;
3361             case SvxBorderLineStyle::THINTHICK_LARGEGAP:
3362                 pVal = "thinThickLargeGap";
3363                 break;
3364             case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
3365                 pVal = "thickThinSmallGap";
3366                 break;
3367             case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
3368                 pVal = "thickThinMediumGap";
3369                 break;
3370             case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
3371                 pVal = "thickThinLargeGap";
3372                 break;
3373             case SvxBorderLineStyle::EMBOSSED:
3374                 pVal = "threeDEmboss";
3375                 break;
3376             case SvxBorderLineStyle::ENGRAVED:
3377                 pVal = "threeDEngrave";
3378                 break;
3379             case SvxBorderLineStyle::OUTSET:
3380                 pVal = "outset";
3381                 break;
3382             case SvxBorderLineStyle::INSET:
3383                 pVal = "inset";
3384                 break;
3385             case SvxBorderLineStyle::FINE_DASHED:
3386                 pVal = "dashSmallGap";
3387                 break;
3388             case SvxBorderLineStyle::DASH_DOT:
3389                 pVal = "dotDash";
3390                 break;
3391             case SvxBorderLineStyle::DASH_DOT_DOT:
3392                 pVal = "dotDotDash";
3393                 break;
3394             case SvxBorderLineStyle::NONE:
3395             default:
3396                 break;
3397         }
3398     }
3399     else if ( !rStyleProps || !rStyleProps->LineWidth )
3400         // no line, and no line set by the style either:
3401         // there is no need to write the property
3402         return;
3403 
3404     // compare the properties with the theme properties before writing them:
3405     // if they are equal, it means that they were style-defined and there is
3406     // no need to write them.
3407     if( rStyleProps != nullptr && pBorderLine && !pBorderLine->isEmpty() &&
3408             pBorderLine->GetBorderLineStyle() == static_cast<SvxBorderLineStyle>(rStyleProps->LineStyle) &&
3409             pBorderLine->GetColor() == Color(ColorTransparency, rStyleProps->Color) &&
3410             pBorderLine->GetWidth() == convertMm100ToTwip( rStyleProps->LineWidth ) )
3411         return;
3412 
3413     rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
3414     pAttr->add( FSNS( XML_w, XML_val ), OString( pVal ) );
3415 
3416     if ( pBorderLine && !pBorderLine->isEmpty() )
3417     {
3418         // Compute the sz attribute
3419 
3420         double const fConverted( ::editeng::ConvertBorderWidthToWord(
3421                 pBorderLine->GetBorderLineStyle(), pBorderLine->GetWidth()));
3422         // The unit is the 8th of point
3423         sal_Int32 nWidth = sal_Int32( fConverted / 2.5 );
3424         const sal_Int32 nMinWidth = 2;
3425         const sal_Int32 nMaxWidth = 96;
3426 
3427         if ( nWidth > nMaxWidth )
3428             nWidth = nMaxWidth;
3429         else if ( nWidth < nMinWidth )
3430             nWidth = nMinWidth;
3431 
3432         pAttr->add( FSNS( XML_w, XML_sz ), OString::number( nWidth ) );
3433 
3434         // Get the distance (in pt)
3435         pAttr->add(FSNS(XML_w, XML_space), OString::number(rtl::math::round(nDist / 20.0)));
3436 
3437         // Get the color code as an RRGGBB hex value
3438         OString sColor( msfilter::util::ConvertColor( pBorderLine->GetColor( ) ) );
3439         pAttr->add( FSNS( XML_w, XML_color ), sColor );
3440     }
3441 
3442     if (bWriteShadow)
3443     {
3444         // Set the shadow value
3445         pAttr->add( FSNS( XML_w, XML_shadow ), "1" );
3446     }
3447 
3448     pSerializer->singleElementNS( XML_w, elementToken, pAttr );
3449 }
3450 
lcl_getTableCellBorderOptions(bool bEcma)3451 static OutputBorderOptions lcl_getTableCellBorderOptions(bool bEcma)
3452 {
3453     OutputBorderOptions rOptions;
3454 
3455     rOptions.tag = XML_tcBorders;
3456     rOptions.bUseStartEnd = !bEcma;
3457     rOptions.bWriteTag = true;
3458     rOptions.bWriteDistance = false;
3459 
3460     return rOptions;
3461 }
3462 
lcl_getBoxBorderOptions()3463 static OutputBorderOptions lcl_getBoxBorderOptions()
3464 {
3465     OutputBorderOptions rOptions;
3466 
3467     rOptions.tag = XML_pBdr;
3468     rOptions.bUseStartEnd = false;
3469     rOptions.bWriteTag = false;
3470     rOptions.bWriteDistance = true;
3471 
3472     return rOptions;
3473 }
3474 
impl_borders(FSHelperPtr const & pSerializer,const SvxBoxItem & rBox,const OutputBorderOptions & rOptions,std::map<SvxBoxItemLine,css::table::BorderLine2> & rTableStyleConf)3475 static void impl_borders( FSHelperPtr const & pSerializer,
3476                           const SvxBoxItem& rBox,
3477                           const OutputBorderOptions& rOptions,
3478                           std::map<SvxBoxItemLine,
3479                           css::table::BorderLine2> &rTableStyleConf )
3480 {
3481     static const SvxBoxItemLine aBorders[] =
3482     {
3483         SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
3484     };
3485 
3486     const sal_Int32 aXmlElements[] =
3487     {
3488         XML_top,
3489         rOptions.bUseStartEnd ? XML_start : XML_left,
3490         XML_bottom,
3491         rOptions.bUseStartEnd ? XML_end : XML_right
3492     };
3493     bool tagWritten = false;
3494     const SvxBoxItemLine* pBrd = aBorders;
3495 
3496     for( int i = 0; i < 4; ++i, ++pBrd )
3497     {
3498         const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
3499         const table::BorderLine2 *aStyleProps = nullptr;
3500         if( rTableStyleConf.find( *pBrd ) != rTableStyleConf.end() )
3501             aStyleProps = &rTableStyleConf[ *pBrd ];
3502 
3503         if (!tagWritten && rOptions.bWriteTag)
3504         {
3505             pSerializer->startElementNS(XML_w, rOptions.tag);
3506             tagWritten = true;
3507         }
3508 
3509         bool bWriteShadow = false;
3510         if (rOptions.aShadowLocation == SvxShadowLocation::NONE)
3511         {
3512             // The border has no shadow
3513         }
3514         else if (rOptions.aShadowLocation == SvxShadowLocation::BottomRight)
3515         {
3516             // Special case of 'Bottom-Right' shadow:
3517             // If the shadow location is 'Bottom-Right' - then turn on the shadow
3518             // for ALL the sides. This is because in Word - if you select a shadow
3519             // for a border - it turn on the shadow for ALL the sides (but shows only
3520             // the bottom-right one).
3521             // This is so that no information will be lost if passed through LibreOffice
3522             bWriteShadow = true;
3523         }
3524         else
3525         {
3526             // If there is a shadow, and it's not the regular 'Bottom-Right',
3527             // then write only the 'shadowed' sides of the border
3528             if  (
3529                     ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft    || rOptions.aShadowLocation == SvxShadowLocation::TopRight  ) && *pBrd == SvxBoxItemLine::TOP   ) ||
3530                     ((rOptions.aShadowLocation == SvxShadowLocation::TopLeft    || rOptions.aShadowLocation == SvxShadowLocation::BottomLeft) && *pBrd == SvxBoxItemLine::LEFT  ) ||
3531                     ((rOptions.aShadowLocation == SvxShadowLocation::BottomLeft                                                             ) && *pBrd == SvxBoxItemLine::BOTTOM) ||
3532                     ((rOptions.aShadowLocation == SvxShadowLocation::TopRight                                                               ) && *pBrd == SvxBoxItemLine::RIGHT )
3533                 )
3534             {
3535                 bWriteShadow = true;
3536             }
3537         }
3538 
3539         sal_uInt16 nDist = 0;
3540         if (rOptions.bWriteDistance)
3541         {
3542             if (rOptions.pDistances)
3543             {
3544                 if ( *pBrd == SvxBoxItemLine::TOP)
3545                     nDist = rOptions.pDistances->nTop;
3546                 else if ( *pBrd == SvxBoxItemLine::LEFT)
3547                     nDist = rOptions.pDistances->nLeft;
3548                 else if ( *pBrd == SvxBoxItemLine::BOTTOM)
3549                     nDist = rOptions.pDistances->nBottom;
3550                 else if ( *pBrd == SvxBoxItemLine::RIGHT)
3551                     nDist = rOptions.pDistances->nRight;
3552             }
3553             else
3554             {
3555                 nDist = rBox.GetDistance(*pBrd);
3556             }
3557         }
3558 
3559         impl_borderLine( pSerializer, aXmlElements[i], pLn, nDist, bWriteShadow, aStyleProps );
3560     }
3561     if (tagWritten && rOptions.bWriteTag) {
3562         pSerializer->endElementNS( XML_w, rOptions.tag );
3563     }
3564 }
3565 
impl_cellMargins(FSHelperPtr const & pSerializer,const SvxBoxItem & rBox,sal_Int32 tag,bool bUseStartEnd,const SvxBoxItem * pDefaultMargins=nullptr)3566 static void impl_cellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr)
3567 {
3568     static const SvxBoxItemLine aBorders[] =
3569     {
3570         SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
3571     };
3572 
3573     const sal_Int32 aXmlElements[] =
3574     {
3575         XML_top,
3576         bUseStartEnd ? XML_start : XML_left,
3577         XML_bottom,
3578         bUseStartEnd ? XML_end : XML_right
3579     };
3580     bool tagWritten = false;
3581     const SvxBoxItemLine* pBrd = aBorders;
3582     for( int i = 0; i < 4; ++i, ++pBrd )
3583     {
3584         sal_Int32 nDist = sal_Int32( rBox.GetDistance( *pBrd ) );
3585 
3586         if (pDefaultMargins)
3587         {
3588             // Skip output if cell margin == table default margin
3589             if (sal_Int32( pDefaultMargins->GetDistance( *pBrd ) ) == nDist)
3590                 continue;
3591         }
3592 
3593         if (!tagWritten) {
3594             pSerializer->startElementNS(XML_w, tag);
3595             tagWritten = true;
3596         }
3597         pSerializer->singleElementNS( XML_w, aXmlElements[i],
3598                FSNS( XML_w, XML_w ), OString::number(nDist),
3599                FSNS( XML_w, XML_type ), "dxa" );
3600     }
3601     if (tagWritten) {
3602         pSerializer->endElementNS( XML_w, tag );
3603     }
3604 }
3605 
TableCellProperties(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner,sal_uInt32 nCell,sal_uInt32 nRow)3606 void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
3607 {
3608     m_pSerializer->startElementNS(XML_w, XML_tcPr);
3609 
3610     const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
3611 
3612     bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
3613 
3614     // Output any table cell redlines if there are any attached to this specific cell
3615     TableCellRedline( pTableTextNodeInfoInner );
3616 
3617     // Cell preferred width
3618     SwTwips nWidth = GetGridCols( pTableTextNodeInfoInner )->at( nCell );
3619     if ( nCell )
3620         nWidth = nWidth - GetGridCols( pTableTextNodeInfoInner )->at( nCell - 1 );
3621     m_pSerializer->singleElementNS( XML_w, XML_tcW,
3622            FSNS( XML_w, XML_w ), OString::number(nWidth),
3623            FSNS( XML_w, XML_type ), "dxa" );
3624 
3625     // Horizontal spans
3626     const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
3627     SwWriteTableRow *pRow = rRows[ nRow ].get();
3628     const SwWriteTableCells& rTableCells =  pRow->GetCells();
3629     if (nCell < rTableCells.size() )
3630     {
3631         const SwWriteTableCell& rCell = *rTableCells[nCell];
3632         const sal_uInt16 nColSpan = rCell.GetColSpan();
3633         if ( nColSpan > 1 )
3634             m_pSerializer->singleElementNS( XML_w, XML_gridSpan,
3635                     FSNS( XML_w, XML_val ), OString::number(nColSpan) );
3636     }
3637 
3638     // Vertical merges
3639     ww8::RowSpansPtr xRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
3640     sal_Int32 vSpan = (*xRowSpans)[nCell];
3641     if ( vSpan > 1 )
3642     {
3643         m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "restart");
3644     }
3645     else if ( vSpan < 0 )
3646     {
3647         m_pSerializer->singleElementNS(XML_w, XML_vMerge, FSNS(XML_w, XML_val), "continue");
3648     }
3649 
3650     if (const SfxGrabBagItem* pItem = pTableBox->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG))
3651     {
3652         const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
3653         std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("CellCnfStyle");
3654         if (it != rGrabBag.end())
3655         {
3656             uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
3657             m_pTableStyleExport->CnfStyle(aAttributes);
3658         }
3659     }
3660 
3661 
3662     const SvxBoxItem& rBox = pTableBox->GetFrameFormat( )->GetBox( );
3663     const SvxBoxItem& rDefaultBox = (*tableFirstCells.rbegin())->getTableBox( )->GetFrameFormat( )->GetBox( );
3664     {
3665         // The cell borders
3666         impl_borders(m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma),
3667                      m_aTableStyleConfs.back());
3668     }
3669 
3670     TableBackgrounds( pTableTextNodeInfoInner );
3671 
3672     {
3673         // Cell margins
3674         impl_cellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox );
3675     }
3676 
3677     TableVerticalCell( pTableTextNodeInfoInner );
3678 
3679     m_pSerializer->endElementNS( XML_w, XML_tcPr );
3680 }
3681 
InitTableHelper(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner)3682 void DocxAttributeOutput::InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
3683 {
3684     const SwTable* pTable = pTableTextNodeInfoInner->getTable();
3685     if (m_xTableWrt && pTable == m_xTableWrt->GetTable())
3686         return;
3687 
3688     tools::Long nPageSize = 0;
3689     bool bRelBoxSize = false;
3690 
3691     // Create the SwWriteTable instance to use col spans (and maybe other infos)
3692     GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
3693 
3694     const SwFrameFormat *pFormat = pTable->GetFrameFormat( );
3695     const sal_uInt32 nTableSz = static_cast<sal_uInt32>(pFormat->GetFrameSize( ).GetWidth( ));
3696 
3697     const SwHTMLTableLayout *pLayout = pTable->GetHTMLTableLayout();
3698     if( pLayout && pLayout->IsExportable() )
3699         m_xTableWrt.reset(new SwWriteTable(pTable, pLayout));
3700     else
3701         m_xTableWrt.reset(new SwWriteTable(pTable, pTable->GetTabLines(), nPageSize, nTableSz, false));
3702 }
3703 
StartTable(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner)3704 void DocxAttributeOutput::StartTable( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
3705 {
3706     m_aTableStyleConfs.push_back({});
3707 
3708     // In case any paragraph SDT's are open, close them here.
3709     EndParaSdtBlock();
3710 
3711     m_pSerializer->startElementNS(XML_w, XML_tbl);
3712 
3713     tableFirstCells.push_back(pTableTextNodeInfoInner);
3714     lastOpenCell.push_back(-1);
3715     lastClosedCell.push_back(-1);
3716 
3717     InitTableHelper( pTableTextNodeInfoInner );
3718     TableDefinition( pTableTextNodeInfoInner );
3719 }
3720 
EndTable()3721 void DocxAttributeOutput::EndTable()
3722 {
3723     m_pSerializer->endElementNS( XML_w, XML_tbl );
3724 
3725     if ( m_tableReference->m_nTableDepth > 0 )
3726         --m_tableReference->m_nTableDepth;
3727 
3728     lastClosedCell.pop_back();
3729     lastOpenCell.pop_back();
3730     tableFirstCells.pop_back();
3731 
3732     // We closed the table; if it is a nested table, the cell that contains it
3733     // still continues
3734     // set to true only if we were in a nested table, not otherwise.
3735     if( !tableFirstCells.empty() )
3736         m_tableReference->m_bTableCellOpen = true;
3737 
3738     // Cleans the table helper
3739     m_xTableWrt.reset();
3740 
3741     m_aTableStyleConfs.pop_back();
3742 }
3743 
StartTableRow(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner)3744 void DocxAttributeOutput::StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
3745 {
3746     m_pSerializer->startElementNS(XML_w, XML_tr);
3747 
3748     // Output the row properties
3749     m_pSerializer->startElementNS(XML_w, XML_trPr);
3750 
3751     // Header row: tblHeader
3752     const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
3753     if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) )
3754         m_pSerializer->singleElementNS(XML_w, XML_tblHeader, FSNS(XML_w, XML_val), "true"); // TODO to overwrite table style may need explicit false
3755 
3756     TableRowRedline( pTableTextNodeInfoInner );
3757     TableHeight( pTableTextNodeInfoInner );
3758     TableCanSplit( pTableTextNodeInfoInner );
3759 
3760     const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox();
3761     const SwTableLine* pTableLine = pTableBox->GetUpper();
3762     if (const SfxGrabBagItem* pItem = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG))
3763     {
3764         const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
3765         std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("RowCnfStyle");
3766         if (it != rGrabBag.end())
3767         {
3768             uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
3769             m_pTableStyleExport->CnfStyle(aAttributes);
3770         }
3771     }
3772 
3773 
3774     m_pSerializer->endElementNS( XML_w, XML_trPr );
3775 }
3776 
EndTableRow()3777 void DocxAttributeOutput::EndTableRow( )
3778 {
3779     m_pSerializer->endElementNS( XML_w, XML_tr );
3780     lastOpenCell.back() = -1;
3781     lastClosedCell.back() = -1;
3782 }
3783 
StartTableCell(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner,sal_uInt32 nCell,sal_uInt32 nRow)3784 void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
3785 {
3786     lastOpenCell.back() = nCell;
3787 
3788     InitTableHelper( pTableTextNodeInfoInner );
3789 
3790     m_pSerializer->startElementNS(XML_w, XML_tc);
3791 
3792     // Write the cell properties here
3793     TableCellProperties( pTableTextNodeInfoInner, nCell, nRow );
3794 
3795     m_tableReference->m_bTableCellOpen = true;
3796 }
3797 
EndTableCell(sal_uInt32 nCell)3798 void DocxAttributeOutput::EndTableCell(sal_uInt32 nCell)
3799 {
3800     lastClosedCell.back() = nCell;
3801     lastOpenCell.back() = -1;
3802 
3803     if (m_tableReference->m_bTableCellParaSdtOpen)
3804         EndParaSdtBlock();
3805 
3806     m_pSerializer->endElementNS( XML_w, XML_tc );
3807 
3808     m_tableReference->m_bTableCellOpen = false;
3809     m_tableReference->m_bTableCellParaSdtOpen = false;
3810 }
3811 
TableInfoCell(ww8::WW8TableNodeInfoInner::Pointer_t)3812 void DocxAttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
3813 {
3814 }
3815 
TableInfoRow(ww8::WW8TableNodeInfoInner::Pointer_t)3816 void DocxAttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/ )
3817 {
3818 }
3819 
3820 namespace
3821 {
3822 
3823 /// Does the same as comphelper::string::padToLength(), but extends the start, not the end.
lcl_padStartToLength(OString const & aString,sal_Int32 nLen,char cFill)3824 OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, char cFill)
3825 {
3826     if (nLen > aString.getLength())
3827     {
3828         sal_Int32 nDiff = nLen - aString.getLength();
3829         OStringBuffer aBuffer;
3830         comphelper::string::padToLength(aBuffer, nDiff, cFill);
3831         aBuffer.append(aString);
3832         return aBuffer.makeStringAndClear();
3833     }
3834     else
3835         return aString;
3836 }
3837 
3838 //Keep this function in-sync with the one in writerfilter/.../SettingsTable.cxx
3839 //Since this is not import code, "-1" needs to be handled as the mode that LO will save as.
3840 //To identify how your code should handle a "-1", look in DocxExport::WriteSettings().
lcl_getWordCompatibilityMode(const SwDoc & rDoc)3841 sal_Int32 lcl_getWordCompatibilityMode( const SwDoc& rDoc )
3842 {
3843     uno::Reference< beans::XPropertySet >     xPropSet( rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
3844     uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
3845 
3846     sal_Int32 nWordCompatibilityMode = -1;
3847     if ( xPropSetInfo->hasPropertyByName( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) )
3848     {
3849         uno::Sequence< beans::PropertyValue > propList;
3850         xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= propList;
3851 
3852         for ( const auto& rProp : std::as_const(propList) )
3853         {
3854             if ( rProp.Name == "CompatSettings" )
3855             {
3856                 css::uno::Sequence< css::beans::PropertyValue > aCurrentCompatSettings;
3857                 rProp.Value >>= aCurrentCompatSettings;
3858 
3859                 for ( const auto& rCurrentCompatSetting : std::as_const(aCurrentCompatSettings) )
3860                 {
3861                     uno::Sequence< beans::PropertyValue > aCompatSetting;
3862                     rCurrentCompatSetting.Value >>= aCompatSetting;
3863 
3864                     OUString sName;
3865                     OUString sUri;
3866                     OUString sVal;
3867 
3868                     for ( const auto& rPropVal : std::as_const(aCompatSetting) )
3869                     {
3870                         if ( rPropVal.Name == "name" ) rPropVal.Value >>= sName;
3871                         if ( rPropVal.Name == "uri" )  rPropVal.Value >>= sUri;
3872                         if ( rPropVal.Name == "val" )  rPropVal.Value >>= sVal;
3873                     }
3874 
3875                     if ( sName == "compatibilityMode" && sUri == "http://schemas.microsoft.com/office/word" )
3876                     {
3877                         const sal_Int32 nValidMode = sVal.toInt32();
3878                         // if repeated, highest mode wins in MS Word. 11 is the first valid mode.
3879                         if ( nValidMode > 10 && nValidMode > nWordCompatibilityMode )
3880                             nWordCompatibilityMode = nValidMode;
3881 
3882                     }
3883                 }
3884             }
3885         }
3886     }
3887 
3888     // TODO: this is duplicated, better store it in DocxExport member?
3889     if (!rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING))
3890     {
3891         if (nWordCompatibilityMode == -1 || 14 < nWordCompatibilityMode)
3892         {
3893             nWordCompatibilityMode = 14;
3894         }
3895     }
3896 
3897     return nWordCompatibilityMode;
3898 }
3899 
3900 }
3901 
TableDefinition(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)3902 void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3903 {
3904     bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
3905 
3906     // Write the table properties
3907     m_pSerializer->startElementNS(XML_w, XML_tblPr);
3908 
3909     static const sal_Int32 aOrder[] =
3910     {
3911         FSNS( XML_w, XML_tblStyle ),
3912         FSNS( XML_w, XML_tblpPr ),
3913         FSNS( XML_w, XML_tblOverlap ),
3914         FSNS( XML_w, XML_bidiVisual ),
3915         FSNS( XML_w, XML_tblStyleRowBandSize ),
3916         FSNS( XML_w, XML_tblStyleColBandSize ),
3917         FSNS( XML_w, XML_tblW ),
3918         FSNS( XML_w, XML_jc ),
3919         FSNS( XML_w, XML_tblCellSpacing ),
3920         FSNS( XML_w, XML_tblInd ),
3921         FSNS( XML_w, XML_tblBorders ),
3922         FSNS( XML_w, XML_shd ),
3923         FSNS( XML_w, XML_tblLayout ),
3924         FSNS( XML_w, XML_tblCellMar ),
3925         FSNS( XML_w, XML_tblLook ),
3926         FSNS( XML_w, XML_tblPrChange )
3927     };
3928 
3929     // postpone the output so that we can later []
3930     // prepend the properties before the run
3931     m_pSerializer->mark(Tag_TableDefinition, comphelper::containerToSequence(aOrder));
3932 
3933     tools::Long nPageSize = 0;
3934     const char* widthType = "dxa";
3935 
3936     // If actual width of table is relative it should export is as "pct".`
3937     const SwTable *pTable = pTableTextNodeInfoInner->getTable();
3938     SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
3939     const SwFormatFrameSize &rSize = pTableFormat->GetFrameSize();
3940     int nWidthPercent = rSize.GetWidthPercent();
3941     // If we export a floating table: we use the widthPercent of the surrounding frame
3942     const ww8::Frame* pFloatingTableFrame = m_rExport.GetFloatingTableFrame();
3943     if (pFloatingTableFrame)
3944     {
3945         const SwFormatFrameSize &rFrameSize = pFloatingTableFrame->GetFrameFormat().GetFrameSize();
3946         nWidthPercent = rFrameSize.GetWidthPercent();
3947     }
3948     uno::Reference<beans::XPropertySet> xPropertySet(SwXTextTables::GetObject(*pTable->GetFrameFormat( )),uno::UNO_QUERY);
3949     bool isWidthRelative = false;
3950     xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
3951 
3952     if(isWidthRelative)
3953     {
3954        /**
3955        * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table Width Units)]
3956        * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html
3957        *
3958        * Fiftieths of a Percent :
3959        * http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
3960        * pct Width is in Fiftieths of a Percent
3961        *
3962        * ex. If the Table width is 50% then
3963        * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct
3964        **/
3965         nPageSize = nWidthPercent * 50 ;
3966         widthType = "pct" ;
3967     }
3968     else
3969     {
3970         bool bRelBoxSize = false;
3971         // Create the SwWriteTable instance to use col spans (and maybe other infos)
3972         GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
3973         if(nPageSize == 0)
3974             widthType = "auto";
3975     }
3976 
3977     // Output the table preferred width
3978     m_pSerializer->singleElementNS( XML_w, XML_tblW,
3979             FSNS( XML_w, XML_w ), OString::number(nPageSize),
3980             FSNS( XML_w, XML_type ), widthType );
3981 
3982     // Disable layout autofit, as it does not exist in LibreOffice yet
3983     m_pSerializer->singleElementNS( XML_w, XML_tblLayout,
3984             FSNS( XML_w, XML_type ), "fixed" );
3985 
3986     // Look for the table style property in the table grab bag
3987     std::map<OUString, css::uno::Any> aGrabBag =
3988             pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
3989 
3990     // We should clear the TableStyle map. In case of Table inside multiple tables it contains the
3991     // table border style of the previous table.
3992     std::map<SvxBoxItemLine, css::table::BorderLine2>& rTableStyleConf = m_aTableStyleConfs.back();
3993     rTableStyleConf.clear();
3994 
3995     // Extract properties from grab bag
3996     for( const auto & rGrabBagElement : aGrabBag )
3997     {
3998         if( rGrabBagElement.first == "TableStyleName")
3999         {
4000             OString sStyleName = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
4001             m_pSerializer->singleElementNS(XML_w, XML_tblStyle, FSNS(XML_w, XML_val), sStyleName);
4002         }
4003         else if( rGrabBagElement.first == "TableStyleTopBorder" )
4004             rTableStyleConf[SvxBoxItemLine::TOP] = rGrabBagElement.second.get<table::BorderLine2>();
4005         else if( rGrabBagElement.first == "TableStyleBottomBorder" )
4006             rTableStyleConf[SvxBoxItemLine::BOTTOM]
4007                 = rGrabBagElement.second.get<table::BorderLine2>();
4008         else if( rGrabBagElement.first == "TableStyleLeftBorder" )
4009             rTableStyleConf[SvxBoxItemLine::LEFT]
4010                 = rGrabBagElement.second.get<table::BorderLine2>();
4011         else if( rGrabBagElement.first == "TableStyleRightBorder" )
4012             rTableStyleConf[SvxBoxItemLine::RIGHT]
4013                 = rGrabBagElement.second.get<table::BorderLine2>();
4014         else if (rGrabBagElement.first == "TableStyleLook")
4015         {
4016             rtl::Reference<FastAttributeList> pAttributeList = FastSerializerHelper::createAttrList();
4017             const uno::Sequence<beans::PropertyValue> aAttributeList = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
4018 
4019             for (const auto& rAttribute : aAttributeList)
4020             {
4021                 if (rAttribute.Name == "val")
4022                     pAttributeList->add(FSNS(XML_w, XML_val), lcl_padStartToLength(OString::number(rAttribute.Value.get<sal_Int32>(), 16), 4, '0'));
4023                 else
4024                 {
4025                     static DocxStringTokenMap const aTokens[] =
4026                     {
4027                         {"firstRow", XML_firstRow},
4028                         {"lastRow", XML_lastRow},
4029                         {"firstColumn", XML_firstColumn},
4030                         {"lastColumn", XML_lastColumn},
4031                         {"noHBand", XML_noHBand},
4032                         {"noVBand", XML_noVBand},
4033                         {nullptr, 0}
4034                     };
4035 
4036                     if (sal_Int32 nToken = DocxStringGetToken(aTokens, rAttribute.Name))
4037                         pAttributeList->add(FSNS(XML_w, nToken), (rAttribute.Value.get<sal_Int32>() ? "1" : "0"));
4038                 }
4039             }
4040 
4041             m_pSerializer->singleElementNS(XML_w, XML_tblLook, pAttributeList);
4042         }
4043         else if (rGrabBagElement.first == "TablePosition" &&
4044                         // skip empty table position (tables in footnotes converted to
4045                         // floating tables temporarily, don't export this)
4046                         rGrabBagElement.second != uno::Any() )
4047         {
4048             rtl::Reference<FastAttributeList> attrListTablePos = FastSerializerHelper::createAttrList( );
4049             const uno::Sequence<beans::PropertyValue> aTablePosition = rGrabBagElement.second.get<uno::Sequence<beans::PropertyValue> >();
4050             // look for a surrounding frame and take it's position values
4051             const ww8::Frame* pFrame = m_rExport.GetFloatingTableFrame();
4052             if( pFrame )
4053             {
4054                 // we export the values of the surrounding Frame
4055                 OString sOrientation;
4056                 sal_Int32 nValue;
4057 
4058                 // If tblpXSpec or tblpYSpec are present, we do not write tblpX or tblpY!
4059                 OString sTblpXSpec = convertToOOXMLHoriOrient( pFrame->GetFrameFormat().GetHoriOrient().GetHoriOrient(), pFrame->GetFrameFormat().GetHoriOrient().IsPosToggle() );
4060                 OString sTblpYSpec = convertToOOXMLVertOrient( pFrame->GetFrameFormat().GetVertOrient().GetVertOrient() );
4061 
4062                 sOrientation = convertToOOXMLVertOrientRel( pFrame->GetFrameFormat().GetVertOrient().GetRelationOrient() );
4063                 attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
4064 
4065                 if( !sTblpYSpec.isEmpty() )
4066                     attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sTblpYSpec);
4067 
4068                 sOrientation = convertToOOXMLHoriOrientRel( pFrame->GetFrameFormat().GetHoriOrient().GetRelationOrient() );
4069                 attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
4070 
4071                 if( !sTblpXSpec.isEmpty() )
4072                     attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sTblpXSpec);
4073 
4074                 nValue = pFrame->GetFrameFormat().GetULSpace().GetLower();
4075                 if( nValue != 0 )
4076                     attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) );
4077 
4078                 nValue = pFrame->GetFrameFormat().GetLRSpace().GetLeft();
4079                 if( nValue != 0 )
4080                     attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) );
4081 
4082                 nValue = pFrame->GetFrameFormat().GetLRSpace().GetRight();
4083                 if( nValue != 0 )
4084                     attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) );
4085 
4086                 nValue = pFrame->GetFrameFormat().GetULSpace().GetUpper();
4087                 if( nValue != 0 )
4088                     attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) );
4089 
4090                 if( sTblpXSpec.isEmpty() ) // do not write tblpX if tblpXSpec is present
4091                 {
4092                     nValue = pFrame->GetFrameFormat().GetHoriOrient().GetPos();
4093                     // we need to revert the additional shift introduced by
4094                     // lcl_DecrementHoriOrientPosition() in writerfilter
4095                     // 1st: left distance of the table
4096                     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4097                     const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
4098                     const SvxBoxItem& rBox = pFrameFormat->GetBox( );
4099                     sal_uInt16 nLeftDistance = rBox.GetDistance(SvxBoxItemLine::LEFT);
4100                     nValue += nLeftDistance;
4101 
4102                     // 2nd: if a left border is given, revert the shift by half the width
4103                     // from lcl_DecrementHoriOrientPosition() in writerfilter
4104                     if (const editeng::SvxBorderLine* pLeftBorder = rBox.GetLeft())
4105                     {
4106                         tools::Long nWidth = pLeftBorder->GetWidth();
4107                         nValue += (nWidth / 2);
4108                     }
4109 
4110                     attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) );
4111                 }
4112 
4113                 if( sTblpYSpec.isEmpty() ) // do not write tblpY if tblpYSpec is present
4114                 {
4115                     nValue = pFrame->GetFrameFormat().GetVertOrient().GetPos();
4116                     attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) );
4117                 }
4118             }
4119             else // ( pFrame = 0 )
4120             {
4121                 // we export the values from the grabBag
4122                 for (const auto& rProp : aTablePosition)
4123                 {
4124                     if (rProp.Name == "vertAnchor" && !rProp.Value.get<OUString>().isEmpty())
4125                     {
4126                         OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4127                         attrListTablePos->add(FSNS(XML_w, XML_vertAnchor), sOrientation);
4128                     }
4129                     else if (rProp.Name == "tblpYSpec" && !rProp.Value.get<OUString>().isEmpty())
4130                     {
4131                         OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4132                         attrListTablePos->add(FSNS(XML_w, XML_tblpYSpec), sOrientation);
4133                     }
4134                     else if (rProp.Name == "horzAnchor" && !rProp.Value.get<OUString>().isEmpty())
4135                     {
4136                         OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4137                         attrListTablePos->add(FSNS(XML_w, XML_horzAnchor), sOrientation);
4138                     }
4139                     else if (rProp.Name == "tblpXSpec" && !rProp.Value.get<OUString>().isEmpty())
4140                     {
4141                         OString sOrientation = OUStringToOString( rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
4142                         attrListTablePos->add(FSNS(XML_w, XML_tblpXSpec), sOrientation);
4143                     }
4144                     else if (rProp.Name == "bottomFromText")
4145                     {
4146                         sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4147                         attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( nValue ) );
4148                     }
4149                     else if (rProp.Name == "leftFromText")
4150                     {
4151                         sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4152                         attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( nValue ) );
4153                     }
4154                     else if (rProp.Name == "rightFromText")
4155                     {
4156                         sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4157                         attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( nValue ) );
4158                     }
4159                     else if (rProp.Name == "topFromText")
4160                     {
4161                         sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4162                         attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( nValue ) );
4163                     }
4164                     else if (rProp.Name == "tblpX")
4165                     {
4166                         sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4167                         attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( nValue ) );
4168                     }
4169                     else if (rProp.Name == "tblpY")
4170                     {
4171                         sal_Int32 nValue = rProp.Value.get<sal_Int32>();
4172                         attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( nValue ) );
4173                     }
4174                 }
4175             }
4176 
4177             m_pSerializer->singleElementNS( XML_w, XML_tblpPr, attrListTablePos);
4178             attrListTablePos = nullptr;
4179         }
4180         else
4181             SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: " << rGrabBagElement.first);
4182     }
4183 
4184     // Output the table alignment
4185     const char* pJcVal;
4186     sal_Int32 nIndent = 0;
4187     switch ( pTableFormat->GetHoriOrient( ).GetHoriOrient( ) )
4188     {
4189         case text::HoriOrientation::CENTER:
4190             pJcVal = "center";
4191             break;
4192         case text::HoriOrientation::RIGHT:
4193             if ( bEcma )
4194                 pJcVal = "right";
4195             else
4196                 pJcVal = "end";
4197             break;
4198         default:
4199         case text::HoriOrientation::NONE:
4200         case text::HoriOrientation::LEFT_AND_WIDTH:
4201         {
4202             if ( bEcma )
4203                 pJcVal = "left";
4204             else
4205                 pJcVal = "start";
4206             nIndent = sal_Int32( pTableFormat->GetLRSpace().GetLeft() );
4207 
4208             // Table indentation has different meaning in Word, depending if the table is nested or not.
4209             // If nested, tblInd is added to parent table's left spacing and defines left edge position
4210             // If not nested, text position of left-most cell must be at absolute X = tblInd
4211             // so, table_spacing + table_spacing_to_content = tblInd
4212 
4213             // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
4214             // if no compatibilityMode is defined (which now should only happen on a new export to .docx),
4215             // LO uses a higher compatibility than 2010's 14.
4216             sal_Int32 nMode = lcl_getWordCompatibilityMode( m_rExport.m_rDoc );
4217 
4218             const SwFrameFormat* pFrameFormat = pTableTextNodeInfoInner->getTableBox()->GetFrameFormat();
4219             if ((0 < nMode && nMode <= 14) && m_tableReference->m_nTableDepth == 0)
4220                 nIndent += pFrameFormat->GetBox().GetDistance( SvxBoxItemLine::LEFT );
4221             else
4222             {
4223                 // adjust for SW considering table to start mid-border instead of nested/2013's left-side-of-border.
4224                 nIndent -= pFrameFormat->GetBox().CalcLineWidth( SvxBoxItemLine::LEFT ) / 2;
4225             }
4226 
4227             break;
4228         }
4229     }
4230     m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pJcVal);
4231 
4232     // Output the table background color (although cell value still needs to be specified)
4233     const SvxBrushItem *pColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4234     Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
4235     if ( aColor != COL_AUTO )
4236     {
4237         OString sColor = msfilter::util::ConvertColor( aColor );
4238         m_pSerializer->singleElementNS( XML_w, XML_shd,
4239                 FSNS( XML_w, XML_fill ), sColor,
4240                 FSNS( XML_w, XML_val ), "clear" );
4241     }
4242 
4243     // Output the table borders
4244     TableDefaultBorders( pTableTextNodeInfoInner );
4245 
4246     // Output the default cell margins
4247     TableDefaultCellMargins( pTableTextNodeInfoInner );
4248 
4249     TableBidi( pTableTextNodeInfoInner );
4250 
4251     // Table indent (need to get written even if == 0)
4252     m_pSerializer->singleElementNS( XML_w, XML_tblInd,
4253             FSNS( XML_w, XML_w ), OString::number(nIndent),
4254             FSNS( XML_w, XML_type ), "dxa" );
4255 
4256     // Merge the marks for the ordered elements
4257     m_pSerializer->mergeTopMarks(Tag_TableDefinition);
4258 
4259     m_pSerializer->endElementNS( XML_w, XML_tblPr );
4260 
4261     // Write the table grid infos
4262     m_pSerializer->startElementNS(XML_w, XML_tblGrid);
4263     sal_Int32 nPrv = 0;
4264     ww8::WidthsPtr pColumnWidths = GetColumnWidths( pTableTextNodeInfoInner );
4265     for ( auto aColumnWidth : *pColumnWidths )
4266     {
4267         sal_Int32 nWidth  =  sal_Int32( aColumnWidth ) - nPrv;
4268         m_pSerializer->singleElementNS( XML_w, XML_gridCol,
4269                FSNS( XML_w, XML_w ), OString::number(nWidth) );
4270         nPrv = sal_Int32( aColumnWidth );
4271     }
4272 
4273     m_pSerializer->endElementNS( XML_w, XML_tblGrid );
4274 }
4275 
TableDefaultBorders(ww8::WW8TableNodeInfoInner::Pointer_t)4276 void DocxAttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
4277 {
4278     // Table defaults should only be created IF m_aTableStyleConf contents haven't come from a table style.
4279     // Previously this function wrote out Cell A1 as the table default, causing problems with no benefit.
4280 }
4281 
TableDefaultCellMargins(ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner)4282 void DocxAttributeOutput::TableDefaultCellMargins( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
4283 {
4284     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4285     const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
4286     const SvxBoxItem& rBox = pFrameFormat->GetBox( );
4287     const bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
4288 
4289     impl_cellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma);
4290 }
4291 
TableBackgrounds(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)4292 void DocxAttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
4293 {
4294     const SwTable *pTable = pTableTextNodeInfoInner->getTable();
4295     const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
4296     const SwTableLine *pTableRow = pTableBox->GetUpper();
4297     const SwFrameFormat *pFormat = pTableBox->GetFrameFormat( );
4298 
4299     const SvxBrushItem *pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4300     Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
4301 
4302     const SwFrameFormat *pRowFormat = pTableRow->GetFrameFormat( );
4303     const SvxBrushItem *pRowColorProp = pRowFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4304     if ( pRowColorProp && aColor == COL_AUTO)
4305         aColor = pRowColorProp->GetColor();
4306 
4307     const SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
4308     const SvxBrushItem *pTableColorProp = pTableFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
4309     if ( pTableColorProp && aColor == COL_AUTO )
4310         aColor = pTableColorProp->GetColor();
4311 
4312     const OString sColor = msfilter::util::ConvertColor( aColor );
4313 
4314     std::map<OUString, css::uno::Any> aGrabBag =
4315             pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
4316 
4317     OString sOriginalColor;
4318     std::map<OUString, css::uno::Any>::iterator aGrabBagElement = aGrabBag.find("originalColor");
4319     if( aGrabBagElement != aGrabBag.end() )
4320         sOriginalColor = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
4321 
4322     if ( sOriginalColor != sColor )
4323     {
4324         // color changed by the user, or no grab bag: write sColor
4325         if ( sColor != "auto" )
4326         {
4327             m_pSerializer->singleElementNS( XML_w, XML_shd,
4328                 FSNS( XML_w, XML_fill ), sColor,
4329                 FSNS( XML_w, XML_val ), "clear" );
4330         }
4331     }
4332     else
4333     {
4334         rtl::Reference<sax_fastparser::FastAttributeList> pAttrList;
4335 
4336         for( const auto & rGrabBagElement : aGrabBag )
4337         {
4338             if (!rGrabBagElement.second.has<OUString>())
4339                 continue;
4340 
4341             OString sValue = OUStringToOString( rGrabBagElement.second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
4342             if( rGrabBagElement.first == "themeFill")
4343                 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFill ), sValue.getStr() );
4344             else if( rGrabBagElement.first == "themeFillTint")
4345                 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillTint ), sValue.getStr() );
4346             else if( rGrabBagElement.first == "themeFillShade")
4347                 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillShade ), sValue.getStr() );
4348             else if( rGrabBagElement.first == "fill" )
4349                 AddToAttrList( pAttrList, FSNS( XML_w, XML_fill ), sValue.getStr() );
4350             else if( rGrabBagElement.first == "themeColor")
4351                 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeColor ), sValue.getStr() );
4352             else if( rGrabBagElement.first == "themeTint")
4353                 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeTint ), sValue.getStr() );
4354             else if( rGrabBagElement.first == "themeShade")
4355                 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeShade ), sValue.getStr() );
4356             else if( rGrabBagElement.first == "color")
4357                 AddToAttrList( pAttrList, FSNS( XML_w, XML_color ), sValue.getStr() );
4358             else if( rGrabBagElement.first == "val")
4359                 AddToAttrList( pAttrList, FSNS( XML_w, XML_val ), sValue.getStr() );
4360         }
4361         m_pSerializer->singleElementNS( XML_w, XML_shd, pAttrList.get() );
4362     }
4363 }
4364 
TableRowRedline(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)4365 void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
4366 {
4367     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4368     const SwTableLine * pTabLine = pTabBox->GetUpper();
4369 
4370     // check table row property "HasTextChangesOnly" (used only for tracked deletion, yet)
4371     const SwRedlineTable& aRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
4372     const SvxPrintItem *pHasTextChangesOnlyProp =
4373             pTabLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
4374     if ( !aRedlineTable.empty() && pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
4375     {
4376         // Tracked row deletion is associated to the newest redline range in the row.
4377         // Search it to get the date and the mandatory author.
4378         const SwTableBoxes & rBoxes = pTabLine->GetTabBoxes();
4379         SwPosition aRowStart( SwNodeIndex( *rBoxes[0]->GetSttNd(), 0 ) );
4380         SwPosition aRowEnd( SwNodeIndex( *rBoxes[rBoxes.size() - 1]->GetSttNd()->EndOfSectionNode(), -1 ) );
4381         SwNodeIndex pEndNodeIndex(aRowEnd.nNode.GetNode());
4382 
4383         SwRedlineTable::size_type nLastDeletion = SwRedlineTable::npos;
4384         for( SwRedlineTable::size_type n = 0; n < aRedlineTable.size(); ++n )
4385         {
4386             const SwRangeRedline* pRedline = aRedlineTable[ n ];
4387 
4388             if ( pRedline->Start()->nNode > pEndNodeIndex )
4389                 break;
4390 
4391             if( RedlineType::Delete != pRedline->GetType() )
4392                 continue;
4393 
4394             // redline is in the table row, and newer, than the previous
4395             if ( aRowStart <= *pRedline->Start() )
4396             {
4397                 if ( nLastDeletion == SwRedlineTable::npos ||
4398                          aRedlineTable [ nLastDeletion ]->GetRedlineData().GetTimeStamp() <
4399                              pRedline->GetRedlineData().GetTimeStamp() )
4400                 {
4401                     nLastDeletion = n;
4402                 }
4403             }
4404         }
4405 
4406         if ( nLastDeletion != SwRedlineTable::npos )
4407         {
4408             const SwRedlineData& aRedlineData = aRedlineTable[ nLastDeletion ]->GetRedlineData();
4409             // Note: all redline ranges and table row redline (with the same author and timestamp)
4410             // use the same redline id in OOXML exported by MSO, but it seems, the recent solution
4411             // (different IDs for different ranges, also row changes) is also portable.
4412             OString aId( OString::number( m_nRedlineId++ ) );
4413             const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
4414             OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
4415 
4416             OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
4417 
4418             m_pSerializer->singleElementNS( XML_w, XML_del,
4419                             FSNS( XML_w, XML_id ), aId,
4420                             FSNS( XML_w, XML_author ), aAuthor,
4421                             FSNS( XML_w, XML_date ), aDate );
4422             return;
4423         }
4424     }
4425 
4426     // search next Redline (only deletion of empty rows and all insertions imported from a DOCX)
4427     const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
4428     for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
4429     {
4430         SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
4431         const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
4432         if (pTableRowRedline && &pTableRowRedline->GetTableLine() == pTabLine)
4433         {
4434             // Redline for this table row
4435             const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
4436             RedlineType nRedlineType = aRedlineData.GetType();
4437             switch (nRedlineType)
4438             {
4439                 case RedlineType::TableRowInsert:
4440                 case RedlineType::TableRowDelete:
4441                 {
4442                     OString aId( OString::number( m_nRedlineId++ ) );
4443                     const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
4444                     OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
4445 
4446                     OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
4447 
4448                     if (nRedlineType == RedlineType::TableRowInsert)
4449                         m_pSerializer->singleElementNS( XML_w, XML_ins,
4450                             FSNS( XML_w, XML_id ), aId,
4451                             FSNS( XML_w, XML_author ), aAuthor,
4452                             FSNS( XML_w, XML_date ), aDate );
4453                     else if (nRedlineType == RedlineType::TableRowDelete)
4454                         m_pSerializer->singleElementNS( XML_w, XML_del,
4455                             FSNS( XML_w, XML_id ), aId,
4456                             FSNS( XML_w, XML_author ), aAuthor,
4457                             FSNS( XML_w, XML_date ), aDate );
4458                 }
4459                 break;
4460                 default: break;
4461             }
4462         }
4463     }
4464 }
4465 
TableCellRedline(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)4466 void DocxAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
4467 {
4468     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4469 
4470     // search next Redline
4471     const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable();
4472     for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
4473     {
4474         SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
4475         const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
4476         if (pTableCellRedline && &pTableCellRedline->GetTableBox() == pTabBox)
4477         {
4478             // Redline for this table cell
4479             const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
4480             RedlineType nRedlineType = aRedlineData.GetType();
4481             switch (nRedlineType)
4482             {
4483                 case RedlineType::TableCellInsert:
4484                 case RedlineType::TableCellDelete:
4485                 {
4486                     OString aId( OString::number( m_nRedlineId++ ) );
4487                     const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
4488                     OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
4489 
4490                     OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
4491 
4492                     if (nRedlineType == RedlineType::TableCellInsert)
4493                         m_pSerializer->singleElementNS( XML_w, XML_cellIns,
4494                             FSNS( XML_w, XML_id ), aId,
4495                             FSNS( XML_w, XML_author ), aAuthor,
4496                             FSNS( XML_w, XML_date ), aDate );
4497                     else if (nRedlineType == RedlineType::TableCellDelete)
4498                         m_pSerializer->singleElementNS( XML_w, XML_cellDel,
4499                             FSNS( XML_w, XML_id ), aId,
4500                             FSNS( XML_w, XML_author ), aAuthor,
4501                             FSNS( XML_w, XML_date ), aDate );
4502                 }
4503                 break;
4504                 default: break;
4505             }
4506         }
4507     }
4508 }
4509 
TableHeight(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)4510 void DocxAttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
4511 {
4512     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4513     const SwTableLine * pTabLine = pTabBox->GetUpper();
4514     const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
4515 
4516     const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
4517     if ( !(SwFrameSize::Variable != rLSz.GetHeightSizeType() && rLSz.GetHeight()) )
4518         return;
4519 
4520     sal_Int32 nHeight = rLSz.GetHeight();
4521     const char *pRule = nullptr;
4522 
4523     switch ( rLSz.GetHeightSizeType() )
4524     {
4525         case SwFrameSize::Fixed: pRule = "exact"; break;
4526         case SwFrameSize::Minimum: pRule = "atLeast"; break;
4527         default:           break;
4528     }
4529 
4530     if ( pRule )
4531         m_pSerializer->singleElementNS( XML_w, XML_trHeight,
4532                 FSNS( XML_w, XML_val ), OString::number(nHeight),
4533                 FSNS( XML_w, XML_hRule ), pRule );
4534 }
4535 
TableCanSplit(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)4536 void DocxAttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
4537 {
4538     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4539     const SwTableLine * pTabLine = pTabBox->GetUpper();
4540     const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
4541 
4542     const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit( );
4543     // if rSplittable is true then no need to write <w:cantSplit w:val="false"/>
4544     // as default row prop is allow row to break across page.
4545     if( !rSplittable.GetValue( ) )
4546         m_pSerializer->singleElementNS(XML_w, XML_cantSplit, FSNS(XML_w, XML_val), "true");
4547 }
4548 
TableBidi(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)4549 void DocxAttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
4550 {
4551     const SwTable * pTable = pTableTextNodeInfoInner->getTable();
4552     const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
4553 
4554     if ( m_rExport.TrueFrameDirection( *pFrameFormat ) == SvxFrameDirection::Horizontal_RL_TB )
4555     {
4556         m_pSerializer->singleElementNS(XML_w, XML_bidiVisual, FSNS(XML_w, XML_val), "true");
4557     }
4558 }
4559 
TableVerticalCell(ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner)4560 void DocxAttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
4561 {
4562     const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
4563     const SwFrameFormat *pFrameFormat = pTabBox->GetFrameFormat( );
4564 
4565     if ( SvxFrameDirection::Vertical_RL_TB == m_rExport.TrueFrameDirection( *pFrameFormat ) )
4566         m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "tbRl");
4567     else if ( SvxFrameDirection::Vertical_LR_BT == m_rExport.TrueFrameDirection( *pFrameFormat ) )
4568     {
4569         m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), "btLr");
4570     }
4571 
4572     const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
4573     SwWriteTableRow *pRow = rRows[ pTableTextNodeInfoInner->getRow( ) ].get();
4574     sal_uInt32 nCell = pTableTextNodeInfoInner->getCell();
4575     const SwWriteTableCells& rTableCells =  pRow->GetCells();
4576     if (nCell >= rTableCells.size() )
4577         return;
4578 
4579     const SwWriteTableCell *const pCell = pRow->GetCells()[ nCell ].get();
4580     switch( pCell->GetVertOri())
4581     {
4582     case text::VertOrientation::TOP:
4583         break;
4584     case text::VertOrientation::CENTER:
4585         m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center");
4586         break;
4587     case text::VertOrientation::BOTTOM:
4588         m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom");
4589         break;
4590     }
4591 }
4592 
TableNodeInfoInner(ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner)4593 void DocxAttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner )
4594 {
4595     // This is called when the nested table ends in a cell, and there's no
4596     // paragraph behind that; so we must check for the ends of cell, rows,
4597     // tables
4598     // ['true' to write an empty paragraph, MS Word insists on that]
4599     FinishTableRowCell( pNodeInfoInner, true );
4600 }
4601 
TableOrientation(ww8::WW8TableNodeInfoInner::Pointer_t)4602 void DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
4603 {
4604     SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
4605 }
4606 
TableSpacing(ww8::WW8TableNodeInfoInner::Pointer_t)4607 void DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
4608 {
4609     SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
4610 }
4611 
TableRowEnd(sal_uInt32)4612 void DocxAttributeOutput::TableRowEnd( sal_uInt32 /*nDepth*/ )
4613 {
4614     SAL_INFO("sw.ww8", "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )" );
4615 }
4616 
StartStyles()4617 void DocxAttributeOutput::StartStyles()
4618 {
4619     m_pSerializer->startElementNS( XML_w, XML_styles,
4620             FSNS( XML_xmlns, XML_w ),   GetExport().GetFilter().getNamespaceURL(OOX_NS(doc)),
4621             FSNS( XML_xmlns, XML_w14 ), GetExport().GetFilter().getNamespaceURL(OOX_NS(w14)),
4622             FSNS( XML_xmlns, XML_mc ),  GetExport().GetFilter().getNamespaceURL(OOX_NS(mce)),
4623             FSNS( XML_mc, XML_Ignorable ), "w14" );
4624 
4625     DocDefaults();
4626     LatentStyles();
4627 }
4628 
DocxStringGetToken(DocxStringTokenMap const * pMap,std::u16string_view rName)4629 sal_Int32 DocxStringGetToken(DocxStringTokenMap const * pMap, std::u16string_view rName)
4630 {
4631     OString sName = OUStringToOString(rName, RTL_TEXTENCODING_UTF8);
4632     while (pMap->pToken)
4633     {
4634         if (sName == pMap->pToken)
4635             return pMap->nToken;
4636         ++pMap;
4637     }
4638     return 0;
4639 }
4640 
4641 namespace
4642 {
4643 
4644 DocxStringTokenMap const aDefaultTokens[] = {
4645     {"defQFormat", XML_defQFormat},
4646     {"defUnhideWhenUsed", XML_defUnhideWhenUsed},
4647     {"defSemiHidden", XML_defSemiHidden},
4648     {"count", XML_count},
4649     {"defUIPriority", XML_defUIPriority},
4650     {"defLockedState", XML_defLockedState},
4651     {nullptr, 0}
4652 };
4653 
4654 DocxStringTokenMap const aExceptionTokens[] = {
4655     {"name", XML_name},
4656     {"locked", XML_locked},
4657     {"uiPriority", XML_uiPriority},
4658     {"semiHidden", XML_semiHidden},
4659     {"unhideWhenUsed", XML_unhideWhenUsed},
4660     {"qFormat", XML_qFormat},
4661     {nullptr, 0}
4662 };
4663 
4664 }
4665 
LatentStyles()4666 void DocxAttributeOutput::LatentStyles()
4667 {
4668     // Do we have latent styles available?
4669     uno::Reference<beans::XPropertySet> xPropertySet(m_rExport.m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
4670     uno::Sequence<beans::PropertyValue> aInteropGrabBag;
4671     xPropertySet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
4672     uno::Sequence<beans::PropertyValue> aLatentStyles;
4673     auto pProp = std::find_if(aInteropGrabBag.begin(), aInteropGrabBag.end(),
4674         [](const beans::PropertyValue& rProp) { return rProp.Name == "latentStyles"; });
4675     if (pProp != aInteropGrabBag.end())
4676         pProp->Value >>= aLatentStyles;
4677     if (!aLatentStyles.hasElements())
4678         return;
4679 
4680     // Extract default attributes first.
4681     rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList = FastSerializerHelper::createAttrList();
4682     uno::Sequence<beans::PropertyValue> aLsdExceptions;
4683     for (const auto& rLatentStyle : std::as_const(aLatentStyles))
4684     {
4685         if (sal_Int32 nToken = DocxStringGetToken(aDefaultTokens, rLatentStyle.Name))
4686             pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(rLatentStyle.Value.get<OUString>(), RTL_TEXTENCODING_UTF8));
4687         else if (rLatentStyle.Name == "lsdExceptions")
4688             rLatentStyle.Value >>= aLsdExceptions;
4689     }
4690 
4691     m_pSerializer->startElementNS(XML_w, XML_latentStyles, pAttributeList);
4692     pAttributeList = nullptr;
4693 
4694     // Then handle the exceptions.
4695     for (const auto& rLsdException : std::as_const(aLsdExceptions))
4696     {
4697         pAttributeList = FastSerializerHelper::createAttrList();
4698 
4699         uno::Sequence<beans::PropertyValue> aAttributes;
4700         rLsdException.Value >>= aAttributes;
4701         for (const auto& rAttribute : std::as_const(aAttributes))
4702             if (sal_Int32 nToken = DocxStringGetToken(aExceptionTokens, rAttribute.Name))
4703                 pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(rAttribute.Value.get<OUString>(), RTL_TEXTENCODING_UTF8));
4704 
4705         m_pSerializer->singleElementNS(XML_w, XML_lsdException, pAttributeList);
4706         pAttributeList = nullptr;
4707     }
4708 
4709     m_pSerializer->endElementNS(XML_w, XML_latentStyles);
4710 }
4711 
OutputDefaultItem(const SfxPoolItem & rHt)4712 void DocxAttributeOutput::OutputDefaultItem(const SfxPoolItem& rHt)
4713 {
4714     bool bMustWrite = true;
4715     switch (rHt.Which())
4716     {
4717         case RES_CHRATR_CASEMAP:
4718             bMustWrite = static_cast< const SvxCaseMapItem& >(rHt).GetCaseMap() != SvxCaseMap::NotMapped;
4719             break;
4720         case RES_CHRATR_COLOR:
4721             bMustWrite = static_cast< const SvxColorItem& >(rHt).GetValue() != COL_AUTO;
4722             break;
4723         case RES_CHRATR_CONTOUR:
4724             bMustWrite = static_cast< const SvxContourItem& >(rHt).GetValue();
4725             break;
4726         case RES_CHRATR_CROSSEDOUT:
4727             bMustWrite = static_cast< const SvxCrossedOutItem& >(rHt).GetStrikeout() != STRIKEOUT_NONE;
4728             break;
4729         case RES_CHRATR_ESCAPEMENT:
4730             bMustWrite = static_cast< const SvxEscapementItem& >(rHt).GetEscapement() != SvxEscapement::Off;
4731             break;
4732         case RES_CHRATR_FONT:
4733             bMustWrite = true;
4734             break;
4735         case RES_CHRATR_FONTSIZE:
4736             bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default
4737             break;
4738         case RES_CHRATR_KERNING:
4739             bMustWrite = static_cast< const SvxKerningItem& >(rHt).GetValue() != 0;
4740             break;
4741         case RES_CHRATR_LANGUAGE:
4742             bMustWrite = true;
4743             break;
4744         case RES_CHRATR_POSTURE:
4745             bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE;
4746             break;
4747         case RES_CHRATR_SHADOWED:
4748             bMustWrite = static_cast< const SvxShadowedItem& >(rHt).GetValue();
4749             break;
4750         case RES_CHRATR_UNDERLINE:
4751             bMustWrite = static_cast< const SvxUnderlineItem& >(rHt).GetLineStyle() != LINESTYLE_NONE;
4752             break;
4753         case RES_CHRATR_WEIGHT:
4754             bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL;
4755             break;
4756         case RES_CHRATR_AUTOKERN:
4757             bMustWrite = static_cast< const SvxAutoKernItem& >(rHt).GetValue();
4758             break;
4759         case RES_CHRATR_BLINK:
4760             bMustWrite = static_cast< const SvxBlinkItem& >(rHt).GetValue();
4761             break;
4762         case RES_CHRATR_BACKGROUND:
4763             {
4764                 const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt);
4765                 bMustWrite = (rBrushItem.GetColor() != COL_AUTO ||
4766                               rBrushItem.GetShadingValue() != ShadingPattern::CLEAR ||
4767                               rBrushItem.GetGraphic() != nullptr ||
4768                               rBrushItem.GetGraphicObject() != nullptr);
4769             }
4770             break;
4771 
4772         case RES_CHRATR_CJK_FONT:
4773             bMustWrite = true;
4774             break;
4775         case RES_CHRATR_CJK_FONTSIZE:
4776             bMustWrite = false; // we have written it already as RES_CHRATR_FONTSIZE
4777             break;
4778         case RES_CHRATR_CJK_LANGUAGE:
4779             bMustWrite = true;
4780             break;
4781         case RES_CHRATR_CJK_POSTURE:
4782             bMustWrite = false; // we have written it already as RES_CHRATR_POSTURE
4783             break;
4784         case RES_CHRATR_CJK_WEIGHT:
4785             bMustWrite = false; // we have written it already as RES_CHRATR_WEIGHT
4786             break;
4787 
4788         case RES_CHRATR_CTL_FONT:
4789             bMustWrite = true;
4790             break;
4791         case RES_CHRATR_CTL_FONTSIZE:
4792             bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default
4793             break;
4794         case RES_CHRATR_CTL_LANGUAGE:
4795             bMustWrite = true;
4796             break;
4797         case RES_CHRATR_CTL_POSTURE:
4798             bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE;
4799             break;
4800         case RES_CHRATR_CTL_WEIGHT:
4801             bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL;
4802             break;
4803 
4804         case RES_CHRATR_ROTATE:
4805             bMustWrite = static_cast< const SvxCharRotateItem& >(rHt).GetValue() != 0_deg10;
4806             break;
4807         case RES_CHRATR_EMPHASIS_MARK:
4808             bMustWrite = static_cast< const SvxEmphasisMarkItem& >(rHt).GetEmphasisMark() != FontEmphasisMark::NONE;
4809             break;
4810         case RES_CHRATR_TWO_LINES:
4811             bMustWrite = static_cast< const SvxTwoLinesItem& >(rHt).GetValue();
4812             break;
4813         case RES_CHRATR_SCALEW:
4814             bMustWrite = static_cast< const SvxCharScaleWidthItem& >(rHt).GetValue() != 100;
4815             break;
4816         case RES_CHRATR_RELIEF:
4817             bMustWrite = static_cast< const SvxCharReliefItem& >(rHt).GetValue() != FontRelief::NONE;
4818             break;
4819         case RES_CHRATR_HIDDEN:
4820             bMustWrite = static_cast< const SvxCharHiddenItem& >(rHt).GetValue();
4821             break;
4822         case RES_CHRATR_BOX:
4823             {
4824                 const SvxBoxItem& rBoxItem = static_cast< const SvxBoxItem& >(rHt);
4825                 bMustWrite = rBoxItem.GetTop() || rBoxItem.GetLeft() ||
4826                              rBoxItem.GetBottom() || rBoxItem.GetRight() ||
4827                              rBoxItem.GetSmallestDistance();
4828             }
4829             break;
4830         case RES_CHRATR_HIGHLIGHT:
4831             {
4832                 const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt);
4833                 bMustWrite = (rBrushItem.GetColor() != COL_AUTO ||
4834                               rBrushItem.GetShadingValue() != ShadingPattern::CLEAR ||
4835                               rBrushItem.GetGraphic() != nullptr ||
4836                               rBrushItem.GetGraphicObject() != nullptr);
4837             }
4838             break;
4839 
4840         case RES_PARATR_LINESPACING:
4841             bMustWrite = static_cast< const SvxLineSpacingItem& >(rHt).GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off;
4842             break;
4843         case RES_PARATR_ADJUST:
4844             bMustWrite = static_cast< const SvxAdjustItem& >(rHt).GetAdjust() != SvxAdjust::Left;
4845             break;
4846         case RES_PARATR_SPLIT:
4847             bMustWrite = !static_cast< const SvxFormatSplitItem& >(rHt).GetValue();
4848             break;
4849         case RES_PARATR_WIDOWS:
4850             bMustWrite = static_cast< const SvxWidowsItem& >(rHt).GetValue();
4851             break;
4852         case RES_PARATR_TABSTOP:
4853             bMustWrite = static_cast< const SvxTabStopItem& >(rHt).Count() != 0;
4854             break;
4855         case RES_PARATR_HYPHENZONE:
4856             bMustWrite = true;
4857             break;
4858         case RES_PARATR_NUMRULE:
4859             bMustWrite = !static_cast< const SwNumRuleItem& >(rHt).GetValue().isEmpty();
4860             break;
4861         case RES_PARATR_SCRIPTSPACE:
4862             bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4863             break;
4864         case RES_PARATR_HANGINGPUNCTUATION:
4865             bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4866             break;
4867         case RES_PARATR_FORBIDDEN_RULES:
4868             bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4869             break;
4870         case RES_PARATR_VERTALIGN:
4871             bMustWrite = static_cast< const SvxParaVertAlignItem& >(rHt).GetValue() != SvxParaVertAlignItem::Align::Automatic;
4872             break;
4873         case RES_PARATR_SNAPTOGRID:
4874             bMustWrite = !static_cast< const SvxParaGridItem& >(rHt).GetValue();
4875             break;
4876         case RES_CHRATR_GRABBAG:
4877             bMustWrite = true;
4878             break;
4879 
4880         default:
4881             SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() );
4882             break;
4883     }
4884 
4885     if (bMustWrite)
4886         OutputItem(rHt);
4887 }
4888 
DocDefaults()4889 void DocxAttributeOutput::DocDefaults( )
4890 {
4891     // Write the '<w:docDefaults>' section here
4892     m_pSerializer->startElementNS(XML_w, XML_docDefaults);
4893 
4894     // Output the default run properties
4895     m_pSerializer->startElementNS(XML_w, XML_rPrDefault);
4896 
4897     StartStyleProperties(false, 0);
4898 
4899     for (int i = int(RES_CHRATR_BEGIN); i < int(RES_CHRATR_END); ++i)
4900         OutputDefaultItem(m_rExport.m_rDoc.GetDefault(i));
4901 
4902     EndStyleProperties(false);
4903 
4904     m_pSerializer->endElementNS(XML_w, XML_rPrDefault);
4905 
4906     // Output the default paragraph properties
4907     m_pSerializer->startElementNS(XML_w, XML_pPrDefault);
4908 
4909     StartStyleProperties(true, 0);
4910 
4911     for (int i = int(RES_PARATR_BEGIN); i < int(RES_PARATR_END); ++i)
4912         OutputDefaultItem(m_rExport.m_rDoc.GetDefault(i));
4913 
4914     EndStyleProperties(true);
4915 
4916     m_pSerializer->endElementNS(XML_w, XML_pPrDefault);
4917 
4918     m_pSerializer->endElementNS(XML_w, XML_docDefaults);
4919 }
4920 
EndStyles(sal_uInt16 nNumberOfStyles)4921 void DocxAttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles )
4922 {
4923     // HACK
4924     // Ms Office seems to have an internal limitation of 4091 styles
4925     // and refuses to load .docx with more, even though the spec seems to allow that;
4926     // so simply if there are more styles, don't export those
4927     const sal_Int32 nCountStylesToWrite = MSWORD_MAX_STYLES_LIMIT - nNumberOfStyles;
4928     m_pTableStyleExport->TableStyles(nCountStylesToWrite);
4929     m_pSerializer->endElementNS( XML_w, XML_styles );
4930 }
4931 
DefaultStyle()4932 void DocxAttributeOutput::DefaultStyle()
4933 {
4934     // are these the values of enum ww::sti (see ../inc/wwstyles.hxx)?
4935     SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::DefaultStyle()");
4936 }
4937 
4938 /* Writes <a:srcRect> tag back to document.xml if a file contains a cropped image.
4939 *  NOTE : Tested on images of type JPEG,EMF/WMF,BMP, PNG and GIF.
4940 */
WriteSrcRect(const css::uno::Reference<css::beans::XPropertySet> & xShapePropSet,const SwFrameFormat * pFrameFormat)4941 void DocxAttributeOutput::WriteSrcRect(
4942     const css::uno::Reference<css::beans::XPropertySet>& xShapePropSet,
4943     const SwFrameFormat* pFrameFormat)
4944 {
4945     uno::Reference<graphic::XGraphic> xGraphic;
4946     xShapePropSet->getPropertyValue("Graphic") >>= xGraphic;
4947     const Graphic aGraphic(xGraphic);
4948 
4949     Size aOriginalSize(aGraphic.GetPrefSize());
4950 
4951     const MapMode aMap100mm( MapUnit::Map100thMM );
4952     const MapMode& rMapMode = aGraphic.GetPrefMapMode();
4953     if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
4954     {
4955         aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, aMap100mm);
4956     }
4957 
4958     css::text::GraphicCrop aGraphicCropStruct;
4959     xShapePropSet->getPropertyValue("GraphicCrop") >>= aGraphicCropStruct;
4960     sal_Int32 nCropL = aGraphicCropStruct.Left;
4961     sal_Int32 nCropR = aGraphicCropStruct.Right;
4962     sal_Int32 nCropT = aGraphicCropStruct.Top;
4963     sal_Int32 nCropB = aGraphicCropStruct.Bottom;
4964 
4965     // simulate border padding as a negative crop.
4966     const SfxPoolItem* pItem;
4967     if (pFrameFormat && SfxItemState::SET == pFrameFormat->GetItemState(RES_BOX, false, &pItem))
4968     {
4969         const SvxBoxItem& rBox = *static_cast<const SvxBoxItem*>(pItem);
4970         nCropL -= rBox.GetDistance( SvxBoxItemLine::LEFT );
4971         nCropR -= rBox.GetDistance( SvxBoxItemLine::RIGHT );
4972         nCropT -= rBox.GetDistance( SvxBoxItemLine::TOP );
4973         nCropB -= rBox.GetDistance( SvxBoxItemLine::BOTTOM );
4974     }
4975 
4976     if ( !((0 != nCropL) || (0 != nCropT) || (0 != nCropR) || (0 != nCropB)) )
4977         return;
4978 
4979     double  widthMultiplier  = 100000.0/aOriginalSize.Width();
4980     double  heightMultiplier = 100000.0/aOriginalSize.Height();
4981 
4982     sal_Int32 left   = static_cast<sal_Int32>(rtl::math::round(nCropL * widthMultiplier));
4983     sal_Int32 right  = static_cast<sal_Int32>(rtl::math::round(nCropR * widthMultiplier));
4984     sal_Int32 top    = static_cast<sal_Int32>(rtl::math::round(nCropT * heightMultiplier));
4985     sal_Int32 bottom = static_cast<sal_Int32>(rtl::math::round(nCropB * heightMultiplier));
4986 
4987     m_pSerializer->singleElementNS( XML_a, XML_srcRect,
4988          XML_l, OString::number(left),
4989          XML_t, OString::number(top),
4990          XML_r, OString::number(right),
4991          XML_b, OString::number(bottom) );
4992 }
4993 
PushRelIdCache()4994 void DocxAttributeOutput::PushRelIdCache()
4995 {
4996     m_aRelIdCache.emplace();
4997     m_aSdrRelIdCache.emplace();
4998 }
4999 
FindRelId(BitmapChecksum nChecksum)5000 OUString DocxAttributeOutput::FindRelId(BitmapChecksum nChecksum)
5001 {
5002     OUString aRet;
5003 
5004     if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end())
5005         aRet = m_aSdrRelIdCache.top()[nChecksum].first;
5006 
5007     return aRet;
5008 }
5009 
FindFileName(BitmapChecksum nChecksum)5010 OUString DocxAttributeOutput::FindFileName(BitmapChecksum nChecksum)
5011 {
5012     OUString aRet;
5013 
5014     if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end())
5015         aRet = m_aSdrRelIdCache.top()[nChecksum].second;
5016 
5017     return aRet;
5018 }
5019 
CacheRelId(BitmapChecksum nChecksum,const OUString & rRelId,const OUString & rFileName)5020 void DocxAttributeOutput::CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId, const OUString& rFileName)
5021 {
5022     if (!m_aSdrRelIdCache.empty())
5023         m_aSdrRelIdCache.top()[nChecksum] = std::pair(rRelId, rFileName);
5024 }
5025 
GetUnoTextFrame(css::uno::Reference<css::drawing::XShape> xShape)5026 uno::Reference<css::text::XTextFrame> DocxAttributeOutput::GetUnoTextFrame(
5027     css::uno::Reference<css::drawing::XShape> xShape)
5028 {
5029     return SwTextBoxHelper::getUnoTextFrame(xShape);
5030 }
5031 
getExistingGraphicRelId(BitmapChecksum nChecksum)5032 std::pair<OString, OUString> DocxAttributeOutput::getExistingGraphicRelId(BitmapChecksum nChecksum)
5033 {
5034     std::pair<OString, OUString> aResult;
5035 
5036     if (m_aRelIdCache.empty())
5037         return aResult;
5038 
5039     auto pIterator = m_aRelIdCache.top().find(nChecksum);
5040 
5041     if (pIterator != m_aRelIdCache.top().end())
5042     {
5043         aResult = pIterator->second;
5044     }
5045 
5046     return aResult;
5047 }
5048 
cacheGraphicRelId(BitmapChecksum nChecksum,OString const & rRelId,OUString const & rFileName)5049 void DocxAttributeOutput::cacheGraphicRelId(BitmapChecksum nChecksum, OString const & rRelId, OUString const & rFileName)
5050 {
5051     if (!m_aRelIdCache.empty())
5052         m_aRelIdCache.top().emplace(nChecksum, std::pair(rRelId, rFileName));
5053 }
5054 
FlyFrameGraphic(const SwGrfNode * pGrfNode,const Size & rSize,const SwFlyFrameFormat * pOLEFrameFormat,SwOLENode * pOLENode,const SdrObject * pSdrObj)5055 void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj )
5056 {
5057     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" );
5058 
5059     GetSdtEndBefore(pSdrObj);
5060 
5061     // detect mis-use of the API
5062     assert(pGrfNode || (pOLEFrameFormat && pOLENode));
5063     const SwFrameFormat* pFrameFormat = pGrfNode ? pGrfNode->GetFlyFormat() : pOLEFrameFormat;
5064     // create the relation ID
5065     OString aRelId;
5066     sal_Int32 nImageType;
5067     if ( pGrfNode && pGrfNode->IsLinkedFile() )
5068     {
5069         // linked image, just create the relation
5070         OUString aFileName;
5071         pGrfNode->GetFileFilterNms( &aFileName, nullptr );
5072 
5073         sal_Int32 const nFragment(aFileName.indexOf('#'));
5074         sal_Int32 const nForbiddenU(aFileName.indexOf("%5C"));
5075         sal_Int32 const nForbiddenL(aFileName.indexOf("%5c"));
5076         if (   (nForbiddenU != -1 && (nFragment == -1 || nForbiddenU < nFragment))
5077             || (nForbiddenL != -1 && (nFragment == -1 || nForbiddenL < nFragment)))
5078         {
5079             SAL_WARN("sw.ww8", "DocxAttributeOutput::FlyFrameGraphic: ignoring image with invalid link URL");
5080             return;
5081         }
5082 
5083         // TODO Convert the file name to relative for better interoperability
5084 
5085         aRelId = m_rExport.AddRelation(
5086                     oox::getRelationship(Relationship::IMAGE),
5087                     aFileName );
5088 
5089         nImageType = XML_link;
5090     }
5091     else
5092     {
5093         // inline, we also have to write the image itself
5094         Graphic aGraphic;
5095         if (pGrfNode)
5096             aGraphic = pGrfNode->GetGrf();
5097         else
5098             aGraphic = *pOLENode->GetGraphic();
5099 
5100         BitmapChecksum aChecksum = aGraphic.GetChecksum();
5101         OUString aFileName;
5102         std::tie(aRelId, aFileName) = getExistingGraphicRelId(aChecksum);
5103         OUString aImageId;
5104 
5105         if (aRelId.isEmpty())
5106         {
5107             // Not in cache, then need to write it.
5108             m_rDrawingML.SetFS( m_pSerializer ); // to be sure that we write to the right stream
5109 
5110             aImageId = m_rDrawingML.WriteImage(aGraphic, false, &aFileName);
5111 
5112             aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 );
5113             cacheGraphicRelId(aChecksum, aRelId, aFileName);
5114         }
5115         else
5116         {
5117             // Include the same relation again. This makes it possible to
5118             // reuse an image across different headers.
5119             aImageId = m_rDrawingML.GetFB()->addRelation( m_pSerializer->getOutputStream(),
5120                 oox::getRelationship(Relationship::IMAGE),
5121                 aFileName );
5122 
5123             aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 );
5124         }
5125 
5126         nImageType = XML_embed;
5127     }
5128 
5129     // In case there are any grab-bag items on the graphic frame, emit them now.
5130     // These are always character grab-bags, as graphics are at-char or as-char in Word.
5131     const SfxPoolItem* pItem = nullptr;
5132     if (pFrameFormat->GetAttrSet().HasItem(RES_FRMATR_GRABBAG, &pItem))
5133     {
5134         const SfxGrabBagItem* pGrabBag = static_cast<const SfxGrabBagItem*>(pItem);
5135         CharGrabBag(*pGrabBag);
5136     }
5137 
5138     rtl::Reference<sax_fastparser::FastAttributeList> xFrameAttributes(
5139         FastSerializerHelper::createAttrList());
5140     if (pGrfNode)
5141     {
5142         const SwAttrSet& rSet = pGrfNode->GetSwAttrSet();
5143         MirrorGraph eMirror = rSet.Get(RES_GRFATR_MIRRORGRF).GetValue();
5144         if (eMirror == MirrorGraph::Vertical || eMirror == MirrorGraph::Both)
5145             // Mirror on the vertical axis is a horizontal flip.
5146             xFrameAttributes->add(XML_flipH, "1");
5147         // RES_GRFATR_ROTATION is sal_uInt16; use sal_uInt32 for multiplication later
5148         if (Degree10 nRot = rSet.Get(RES_GRFATR_ROTATION).GetValue())
5149         {
5150             // RES_GRFATR_ROTATION is in 10ths of degree; convert to 100ths for macro
5151             sal_uInt32 mOOXMLRot = oox::drawingml::ExportRotateClockwisify(toDegree100(nRot));
5152             xFrameAttributes->add(XML_rot, OString::number(mOOXMLRot));
5153         }
5154     }
5155 
5156     css::uno::Reference<css::beans::XPropertySet> xShapePropSet;
5157     if (pSdrObj)
5158     {
5159         css::uno::Reference<css::drawing::XShape> xShape(
5160             const_cast<SdrObject*>(pSdrObj)->getUnoShape(), css::uno::UNO_QUERY);
5161         xShapePropSet.set(xShape, css::uno::UNO_QUERY);
5162         assert(xShapePropSet);
5163     }
5164 
5165     Size aSize = rSize;
5166     // We need the original (cropped, but unrotated) size of object. So prefer the object data,
5167     // and only use passed frame size as fallback.
5168     if (xShapePropSet)
5169     {
5170         if (css::awt::Size val; xShapePropSet->getPropertyValue("Size") >>= val)
5171             aSize = Size(convertMm100ToTwip(val.Width), convertMm100ToTwip(val.Height));
5172     }
5173 
5174     m_rExport.SdrExporter().startDMLAnchorInline(pFrameFormat, aSize);
5175 
5176     // picture description (used for pic:cNvPr later too)
5177     rtl::Reference<::sax_fastparser::FastAttributeList> docPrattrList = FastSerializerHelper::createAttrList();
5178     docPrattrList->add( XML_id, OString::number( m_anchorId++).getStr());
5179     docPrattrList->add( XML_name, OUStringToOString( pFrameFormat->GetName(), RTL_TEXTENCODING_UTF8 ) );
5180     docPrattrList->add( XML_descr, OUStringToOString( pGrfNode ? pGrfNode->GetDescription() : pOLEFrameFormat->GetObjDescription(), RTL_TEXTENCODING_UTF8 ));
5181     if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT )
5182         docPrattrList->add( XML_title, OUStringToOString( pGrfNode ? pGrfNode->GetTitle() : pOLEFrameFormat->GetObjTitle(), RTL_TEXTENCODING_UTF8 ));
5183     m_pSerializer->startElementNS( XML_wp, XML_docPr, docPrattrList );
5184 
5185     OUString sURL, sRelId;
5186     if (xShapePropSet)
5187     {
5188         xShapePropSet->getPropertyValue("HyperLinkURL") >>= sURL;
5189         if(!sURL.isEmpty())
5190         {
5191             if (sURL.startsWith("#") && sURL.indexOf(' ') != -1 && !sURL.endsWith("|outline") && !sURL.endsWith("|table") &&
5192                 !sURL.endsWith("|frame") && !sURL.endsWith("|graphic") && !sURL.endsWith("|ole") && !sURL.endsWith("|region"))
5193             {
5194                 // Spaces are prohibited in bookmark name.
5195                 sURL = sURL.replace(' ', '_');
5196             }
5197             sRelId = GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
5198                         oox::getRelationship(Relationship::HYPERLINK),
5199                         sURL, !sURL.startsWith("#") );
5200             m_pSerializer->singleElementNS( XML_a, XML_hlinkClick,
5201                 FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
5202                 FSNS( XML_r, XML_id ), sRelId);
5203         }
5204     }
5205 
5206     m_pSerializer->endElementNS( XML_wp, XML_docPr );
5207 
5208     m_pSerializer->startElementNS(XML_wp, XML_cNvGraphicFramePr);
5209     // TODO change aspect?
5210     m_pSerializer->singleElementNS( XML_a, XML_graphicFrameLocks,
5211             FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)),
5212             XML_noChangeAspect, "1" );
5213     m_pSerializer->endElementNS( XML_wp, XML_cNvGraphicFramePr );
5214 
5215     m_pSerializer->startElementNS( XML_a, XML_graphic,
5216             FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)) );
5217     m_pSerializer->startElementNS( XML_a, XML_graphicData,
5218             XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/picture" );
5219 
5220     m_pSerializer->startElementNS( XML_pic, XML_pic,
5221             FSNS( XML_xmlns, XML_pic ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlPicture)) );
5222 
5223     m_pSerializer->startElementNS(XML_pic, XML_nvPicPr);
5224     // It seems pic:cNvpr and wp:docPr are pretty much the same thing with the same attributes
5225     m_pSerializer->startElementNS(XML_pic, XML_cNvPr, docPrattrList);
5226 
5227     if(!sURL.isEmpty())
5228         m_pSerializer->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
5229 
5230     m_pSerializer->endElementNS( XML_pic, XML_cNvPr );
5231 
5232     m_pSerializer->startElementNS(XML_pic, XML_cNvPicPr);
5233     // TODO change aspect?
5234     m_pSerializer->singleElementNS( XML_a, XML_picLocks,
5235             XML_noChangeAspect, "1", XML_noChangeArrowheads, "1" );
5236     m_pSerializer->endElementNS( XML_pic, XML_cNvPicPr );
5237     m_pSerializer->endElementNS( XML_pic, XML_nvPicPr );
5238 
5239     // the actual picture
5240     m_pSerializer->startElementNS(XML_pic, XML_blipFill);
5241 
5242 /* At this point we are certain that, WriteImage returns empty RelId
5243    for unhandled graphic type. Therefore we write the picture description
5244    and not the relation( coz there ain't any), so that the user knows
5245    there is an image/graphic in the doc but it is broken instead of
5246    completely discarding it.
5247 */
5248     if ( aRelId.isEmpty() )
5249         m_pSerializer->startElementNS(XML_a, XML_blip);
5250     else
5251         m_pSerializer->startElementNS(XML_a, XML_blip, FSNS(XML_r, nImageType), aRelId);
5252 
5253     pItem = nullptr;
5254 
5255     if ( pGrfNode && SfxItemState::SET == pGrfNode->GetSwAttrSet().GetItemState(RES_GRFATR_DRAWMODE, true, &pItem))
5256     {
5257         GraphicDrawMode nMode = static_cast<GraphicDrawMode>(static_cast<const SfxEnumItemInterface*>(pItem)->GetEnumValue());
5258         if (nMode == GraphicDrawMode::Greys)
5259             m_pSerializer->singleElementNS (XML_a, XML_grayscl);
5260         else if (nMode == GraphicDrawMode::Mono) //black/white has a 0,5 threshold in LibreOffice
5261             m_pSerializer->singleElementNS (XML_a, XML_biLevel, XML_thresh, OString::number(50000));
5262         else if (nMode == GraphicDrawMode::Watermark) //watermark has a brightness/luminance of 0,5 and contrast of -0.7 in LibreOffice
5263             m_pSerializer->singleElementNS( XML_a, XML_lum, XML_bright, OString::number(70000), XML_contrast, OString::number(-70000) );
5264     }
5265     m_pSerializer->endElementNS( XML_a, XML_blip );
5266 
5267     if (xShapePropSet)
5268         WriteSrcRect(xShapePropSet, pFrameFormat);
5269 
5270     m_pSerializer->startElementNS(XML_a, XML_stretch);
5271     m_pSerializer->singleElementNS(XML_a, XML_fillRect);
5272     m_pSerializer->endElementNS( XML_a, XML_stretch );
5273     m_pSerializer->endElementNS( XML_pic, XML_blipFill );
5274 
5275     // TODO setup the right values below
5276     m_pSerializer->startElementNS(XML_pic, XML_spPr, XML_bwMode, "auto");
5277 
5278     m_pSerializer->startElementNS(XML_a, XML_xfrm, xFrameAttributes);
5279 
5280     m_pSerializer->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
5281     OString aWidth( OString::number( TwipsToEMU( aSize.Width() ) ) );
5282     OString aHeight( OString::number( TwipsToEMU( aSize.Height() ) ) );
5283     m_pSerializer->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
5284     m_pSerializer->endElementNS( XML_a, XML_xfrm );
5285     m_pSerializer->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect");
5286     m_pSerializer->singleElementNS(XML_a, XML_avLst);
5287     m_pSerializer->endElementNS( XML_a, XML_prstGeom );
5288 
5289     const SvxBoxItem& rBoxItem = pFrameFormat->GetBox();
5290     const SvxBorderLine* pLeft = rBoxItem.GetLine(SvxBoxItemLine::LEFT);
5291     const SvxBorderLine* pRight = rBoxItem.GetLine(SvxBoxItemLine::RIGHT);
5292     const SvxBorderLine* pTop = rBoxItem.GetLine(SvxBoxItemLine::TOP);
5293     const SvxBorderLine* pBottom = rBoxItem.GetLine(SvxBoxItemLine::BOTTOM);
5294     if (pLeft || pRight || pTop || pBottom)
5295         m_rExport.SdrExporter().writeBoxItemLine(rBoxItem);
5296 
5297     m_rExport.SdrExporter().writeDMLEffectLst(*pFrameFormat);
5298 
5299     m_pSerializer->endElementNS( XML_pic, XML_spPr );
5300 
5301     m_pSerializer->endElementNS( XML_pic, XML_pic );
5302 
5303     m_pSerializer->endElementNS( XML_a, XML_graphicData );
5304     m_pSerializer->endElementNS( XML_a, XML_graphic );
5305     m_rExport.SdrExporter().endDMLAnchorInline(pFrameFormat);
5306 }
5307 
WriteOLE2Obj(const SdrObject * pSdrObj,SwOLENode & rOLENode,const Size & rSize,const SwFlyFrameFormat * pFlyFrameFormat,const sal_Int8 nFormulaAlignment)5308 void DocxAttributeOutput::WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rOLENode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat, const sal_Int8 nFormulaAlignment )
5309 {
5310     if( WriteOLEChart( pSdrObj, rSize, pFlyFrameFormat ))
5311         return;
5312     if( WriteOLEMath( rOLENode , nFormulaAlignment))
5313         return;
5314     PostponeOLE( rOLENode, rSize, pFlyFrameFormat );
5315 }
5316 
WriteOLEChart(const SdrObject * pSdrObj,const Size & rSize,const SwFlyFrameFormat * pFlyFrameFormat)5317 bool DocxAttributeOutput::WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
5318 {
5319     uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY );
5320     if (!xShape.is())
5321         return false;
5322 
5323     uno::Reference<beans::XPropertySet> const xPropSet(xShape, uno::UNO_QUERY);
5324     if (!xPropSet.is())
5325         return false;
5326 
5327     OUString clsid; // why is the property of type string, not sequence<byte>?
5328     xPropSet->getPropertyValue("CLSID") >>= clsid;
5329     assert(!clsid.isEmpty());
5330     SvGlobalName aClassID;
5331     bool const isValid(aClassID.MakeId(clsid));
5332     assert(isValid); (void)isValid;
5333 
5334     if (!SotExchange::IsChart(aClassID))
5335         return false;
5336 
5337     m_aPostponedCharts.push_back(PostponedChart(pSdrObj, rSize, pFlyFrameFormat));
5338     return true;
5339 }
5340 
5341 /*
5342  * Write chart hierarchy in w:drawing after end element of w:rPr tag.
5343  */
WritePostponedChart()5344 void DocxAttributeOutput::WritePostponedChart()
5345 {
5346     if (m_aPostponedCharts.empty())
5347         return;
5348 
5349     for (const PostponedChart& rChart : m_aPostponedCharts)
5350     {
5351         uno::Reference< chart2::XChartDocument > xChartDoc;
5352         uno::Reference< drawing::XShape > xShape(const_cast<SdrObject*>(rChart.object)->getUnoShape(), uno::UNO_QUERY );
5353         if( xShape.is() )
5354         {
5355             uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
5356             if( xPropSet.is() )
5357                 xChartDoc.set( xPropSet->getPropertyValue( "Model" ), uno::UNO_QUERY );
5358         }
5359 
5360         if( xChartDoc.is() )
5361         {
5362             SAL_INFO("sw.ww8", "DocxAttributeOutput::WriteOLE2Obj: export chart ");
5363 
5364             m_rExport.SdrExporter().startDMLAnchorInline(rChart.frame, rChart.size);
5365 
5366             OUString sName("Object 1");
5367             uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY );
5368             if( xNamed.is() )
5369                 sName = xNamed->getName();
5370 
5371             /* If there is a scenario where a chart is followed by a shape
5372                which is being exported as an alternate content then, the
5373                docPr Id is being repeated, ECMA 20.4.2.5 says that the
5374                docPr Id should be unique, ensuring the same here.
5375                */
5376             m_pSerializer->singleElementNS( XML_wp, XML_docPr,
5377                     XML_id, OString::number(m_anchorId++),
5378                     XML_name, sName );
5379 
5380             m_pSerializer->singleElementNS(XML_wp, XML_cNvGraphicFramePr);
5381 
5382             m_pSerializer->startElementNS( XML_a, XML_graphic,
5383                     FSNS( XML_xmlns, XML_a ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dml)) );
5384 
5385             m_pSerializer->startElementNS( XML_a, XML_graphicData,
5386                     XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" );
5387 
5388             OString aRelId;
5389             m_nChartCount++;
5390             aRelId = m_rExport.OutputChart( xChartDoc, m_nChartCount, m_pSerializer );
5391 
5392             m_pSerializer->singleElementNS( XML_c, XML_chart,
5393                     FSNS( XML_xmlns, XML_c ), GetExport().GetFilter().getNamespaceURL(OOX_NS(dmlChart)),
5394                     FSNS( XML_xmlns, XML_r ), GetExport().GetFilter().getNamespaceURL(OOX_NS(officeRel)),
5395                     FSNS( XML_r, XML_id ), aRelId );
5396 
5397             m_pSerializer->endElementNS( XML_a, XML_graphicData );
5398             m_pSerializer->endElementNS( XML_a, XML_graphic );
5399 
5400             m_rExport.SdrExporter().endDMLAnchorInline(rChart.frame);
5401         }
5402     }
5403 
5404     m_aPostponedCharts.clear();
5405 }
5406 
WriteOLEMath(const SwOLENode & rOLENode,const sal_Int8 nAlign)5407 bool DocxAttributeOutput::WriteOLEMath( const SwOLENode& rOLENode ,const sal_Int8 nAlign)
5408 {
5409     uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef());
5410     SvGlobalName aObjName(xObj->getClassID());
5411 
5412     if( !SotExchange::IsMath(aObjName) )
5413         return false;
5414 
5415     try
5416     {
5417         PostponedMathObjects aPostponedMathObject;
5418         aPostponedMathObject.pMathObject = const_cast<SwOLENode*>( &rOLENode);
5419         aPostponedMathObject.nMathObjAlignment = nAlign;
5420         m_aPostponedMaths.push_back(aPostponedMathObject);
5421     }
5422     catch (const uno::Exception&)
5423     {
5424     }
5425     return true;
5426 }
5427 
WritePostponedMath(const SwOLENode * pPostponedMath,sal_Int8 nAlign)5428 void DocxAttributeOutput::WritePostponedMath(const SwOLENode* pPostponedMath, sal_Int8 nAlign)
5429 {
5430     uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode*>(pPostponedMath)->GetOLEObj().GetOleRef());
5431     if (embed::EmbedStates::LOADED == xObj->getCurrentState())
5432     {
5433         // must be running so there is a Component
5434         try
5435         {
5436             xObj->changeState(embed::EmbedStates::RUNNING);
5437         }
5438         catch (const uno::Exception&)
5439         {
5440         }
5441     }
5442     uno::Reference< uno::XInterface > xInterface( xObj->getComponent(), uno::UNO_QUERY );
5443     if (!xInterface.is())
5444     {
5445         SAL_WARN("sw.ww8", "Broken math object");
5446         return;
5447     }
5448 // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class,
5449 // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated
5450 // to RTLD_GLOBAL, so most probably a gcc bug.
5451     oox::FormulaExportBase* formulaexport = dynamic_cast<oox::FormulaExportBase*>(dynamic_cast<SfxBaseModel*>(xInterface.get()));
5452     assert( formulaexport != nullptr );
5453     if (formulaexport)
5454         formulaexport->writeFormulaOoxml( m_pSerializer, GetExport().GetFilter().getVersion(),
5455                 oox::drawingml::DOCUMENT_DOCX, nAlign);
5456 }
5457 
WritePostponedFormControl(const SdrObject * pObject)5458 void DocxAttributeOutput::WritePostponedFormControl(const SdrObject* pObject)
5459 {
5460     if (!pObject || pObject->GetObjInventor() != SdrInventor::FmForm)
5461         return;
5462 
5463     SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
5464     if (!pFormObj)
5465         return;
5466 
5467     uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
5468     uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
5469     if (!xInfo.is())
5470         return;
5471 
5472     if (xInfo->supportsService("com.sun.star.form.component.DateField"))
5473     {
5474         // gather component properties
5475 
5476         OUString sDateFormat;
5477         uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
5478 
5479         OString sDate;
5480         OUString aContentText;
5481         bool bHasDate = false;
5482         css::util::Date aUNODate;
5483         if (xPropertySet->getPropertyValue("Date") >>= aUNODate)
5484         {
5485             bHasDate = true;
5486             Date aDate(aUNODate.Day, aUNODate.Month, aUNODate.Year);
5487             sDate = DateToOString(aDate);
5488             aContentText = OUString::createFromAscii(DateToDDMMYYYYOString(aDate).getStr());
5489             sDateFormat = "dd/MM/yyyy";
5490         }
5491         else
5492         {
5493             aContentText = xPropertySet->getPropertyValue("HelpText").get<OUString>();
5494             if(sDateFormat.isEmpty())
5495                 sDateFormat = "dd/MM/yyyy"; // Need to set date format even if there is no date set
5496         }
5497 
5498         // output component
5499 
5500         m_pSerializer->startElementNS(XML_w, XML_sdt);
5501         m_pSerializer->startElementNS(XML_w, XML_sdtPr);
5502 
5503         if (bHasDate)
5504             m_pSerializer->startElementNS(XML_w, XML_date, FSNS(XML_w, XML_fullDate), sDate);
5505         else
5506             m_pSerializer->startElementNS(XML_w, XML_date);
5507 
5508         m_pSerializer->singleElementNS(XML_w, XML_dateFormat, FSNS(XML_w, XML_val), sDateFormat);
5509         m_pSerializer->singleElementNS(XML_w, XML_lid,
5510                                        FSNS(XML_w, XML_val), "en-US");
5511         m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs,
5512                                        FSNS(XML_w, XML_val), "dateTime");
5513         m_pSerializer->singleElementNS(XML_w, XML_calendar,
5514                                        FSNS(XML_w, XML_val), "gregorian");
5515 
5516         m_pSerializer->endElementNS(XML_w, XML_date);
5517         m_pSerializer->endElementNS(XML_w, XML_sdtPr);
5518 
5519         m_pSerializer->startElementNS(XML_w, XML_sdtContent);
5520         m_pSerializer->startElementNS(XML_w, XML_r);
5521 
5522         RunText(aContentText);
5523         m_pSerializer->endElementNS(XML_w, XML_r);
5524         m_pSerializer->endElementNS(XML_w, XML_sdtContent);
5525 
5526         m_pSerializer->endElementNS(XML_w, XML_sdt);
5527     }
5528     else if (xInfo->supportsService("com.sun.star.form.component.ComboBox"))
5529     {
5530         // gather component properties
5531 
5532         uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
5533         OUString sText = xPropertySet->getPropertyValue("Text").get<OUString>();
5534         const uno::Sequence<OUString> aItems = xPropertySet->getPropertyValue("StringItemList").get< uno::Sequence<OUString> >();
5535 
5536         // output component
5537 
5538         m_pSerializer->startElementNS(XML_w, XML_sdt);
5539         m_pSerializer->startElementNS(XML_w, XML_sdtPr);
5540 
5541         m_pSerializer->startElementNS(XML_w, XML_dropDownList);
5542 
5543         for (const auto& rItem : aItems)
5544         {
5545             m_pSerializer->singleElementNS(XML_w, XML_listItem,
5546                                            FSNS(XML_w, XML_displayText), rItem,
5547                                            FSNS(XML_w, XML_value), rItem);
5548         }
5549 
5550         m_pSerializer->endElementNS(XML_w, XML_dropDownList);
5551         m_pSerializer->endElementNS(XML_w, XML_sdtPr);
5552 
5553         m_pSerializer->startElementNS(XML_w, XML_sdtContent);
5554         m_pSerializer->startElementNS(XML_w, XML_r);
5555         RunText(sText);
5556         m_pSerializer->endElementNS(XML_w, XML_r);
5557         m_pSerializer->endElementNS(XML_w, XML_sdtContent);
5558 
5559         m_pSerializer->endElementNS(XML_w, XML_sdt);
5560     }
5561 }
5562 
WritePostponedActiveXControl(bool bInsideRun)5563 void DocxAttributeOutput::WritePostponedActiveXControl(bool bInsideRun)
5564 {
5565     for( const auto & rPostponedDrawing : m_aPostponedActiveXControls )
5566     {
5567         WriteActiveXControl(rPostponedDrawing.object, *rPostponedDrawing.frame, bInsideRun);
5568     }
5569     m_aPostponedActiveXControls.clear();
5570 }
5571 
5572 
WriteActiveXControl(const SdrObject * pObject,const SwFrameFormat & rFrameFormat,bool bInsideRun)5573 void DocxAttributeOutput::WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat, bool bInsideRun)
5574 {
5575     SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
5576     if (!pFormObj)
5577         return;
5578 
5579     uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
5580     if (!xControlModel.is())
5581         return;
5582 
5583     const bool bAnchoredInline = rFrameFormat.GetAnchor().GetAnchorId() == static_cast<RndStdIds>(css::text::TextContentAnchorType_AS_CHARACTER);
5584 
5585     if(!bInsideRun)
5586     {
5587         m_pSerializer->startElementNS(XML_w, XML_r);
5588     }
5589 
5590     // w:pict for floating embedded control and w:object for inline embedded control
5591     if(bAnchoredInline)
5592         m_pSerializer->startElementNS(XML_w, XML_object);
5593     else
5594         m_pSerializer->startElementNS(XML_w, XML_pict);
5595 
5596     // write ActiveX fragment and ActiveX binary
5597     uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(), uno::UNO_QUERY);
5598     std::pair<OString,OString> sRelIdAndName = m_rExport.WriteActiveXObject(xShape, xControlModel);
5599 
5600     // VML shape definition
5601     m_rExport.VMLExporter().SetSkipwzName(true);
5602     m_rExport.VMLExporter().SetHashMarkForType(true);
5603     m_rExport.VMLExporter().OverrideShapeIDGen(true, "control_shape_");
5604     OString sShapeId;
5605     if(bAnchoredInline)
5606     {
5607         sShapeId = m_rExport.VMLExporter().AddInlineSdrObject(*pObject, true);
5608     }
5609     else
5610     {
5611         SwFormatFollowTextFlow const& rFlow(rFrameFormat.GetFollowTextFlow());
5612         const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
5613         const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
5614         SwFormatSurround const& rSurround(rFrameFormat.GetSurround());
5615         rtl::Reference<sax_fastparser::FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround));
5616         sShapeId = m_rExport.VMLExporter().AddSdrObject(*pObject,
5617             rFlow.GetValue(),
5618             rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(),
5619             rHoriOri.GetRelationOrient(),
5620             rVertOri.GetRelationOrient(),
5621             pAttrList.get(),
5622             true);
5623     }
5624     // Restore default values
5625     m_rExport.VMLExporter().SetSkipwzName(false);
5626     m_rExport.VMLExporter().SetHashMarkForType(false);
5627     m_rExport.VMLExporter().OverrideShapeIDGen(false);
5628 
5629     // control
5630     m_pSerializer->singleElementNS(XML_w, XML_control,
5631                                     FSNS(XML_r, XML_id), sRelIdAndName.first,
5632                                     FSNS(XML_w, XML_name), sRelIdAndName.second,
5633                                     FSNS(XML_w, XML_shapeid), sShapeId);
5634 
5635     if(bAnchoredInline)
5636         m_pSerializer->endElementNS(XML_w, XML_object);
5637     else
5638         m_pSerializer->endElementNS(XML_w, XML_pict);
5639 
5640     if(!bInsideRun)
5641     {
5642         m_pSerializer->endElementNS(XML_w, XML_r);
5643     }
5644 }
5645 
ExportAsActiveXControl(const SdrObject * pObject) const5646 bool DocxAttributeOutput::ExportAsActiveXControl(const SdrObject* pObject) const
5647 {
5648     SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
5649     if (!pFormObj)
5650         return false;
5651 
5652     uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
5653     if (!xControlModel.is())
5654         return false;
5655 
5656     uno::Reference< css::frame::XModel > xModel( m_rExport.m_rDoc.GetDocShell() ? m_rExport.m_rDoc.GetDocShell()->GetModel() : nullptr );
5657     if (!xModel.is())
5658         return false;
5659 
5660     uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
5661     if (!xInfo.is())
5662         return false;
5663 
5664     // See WritePostponedFormControl
5665     // By now date field and combobox is handled on a different way, so let's not interfere with the other method.
5666     if(xInfo->supportsService("com.sun.star.form.component.DateField") ||
5667        xInfo->supportsService("com.sun.star.form.component.ComboBox"))
5668         return false;
5669 
5670     oox::ole::OleFormCtrlExportHelper exportHelper(comphelper::getProcessComponentContext(), xModel, xControlModel);
5671     return exportHelper.isValid();
5672 }
5673 
PostponeOLE(SwOLENode & rNode,const Size & rSize,const SwFlyFrameFormat * pFlyFrameFormat)5674 void DocxAttributeOutput::PostponeOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
5675 {
5676     if( !m_pPostponedOLEs )
5677         //cannot be postponed, try to write now
5678         WriteOLE( rNode, rSize, pFlyFrameFormat );
5679     else
5680         m_pPostponedOLEs->push_back( PostponedOLE( &rNode, rSize, pFlyFrameFormat ) );
5681 }
5682 
5683 /*
5684  * Write w:object hierarchy for embedded objects after end element of w:rPr tag.
5685  */
WritePostponedOLE()5686 void DocxAttributeOutput::WritePostponedOLE()
5687 {
5688     if( !m_pPostponedOLEs )
5689         return;
5690 
5691     for( const auto & rPostponedOLE : *m_pPostponedOLEs )
5692     {
5693         WriteOLE( *rPostponedOLE.object, rPostponedOLE.size, rPostponedOLE.frame );
5694     }
5695 
5696     // clear list of postponed objects
5697     m_pPostponedOLEs.reset();
5698 }
5699 
WriteOLE(SwOLENode & rNode,const Size & rSize,const SwFlyFrameFormat * pFlyFrameFormat)5700 void DocxAttributeOutput::WriteOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
5701 {
5702     OSL_ASSERT(pFlyFrameFormat);
5703 
5704     // get interoperability information about embedded objects
5705     uno::Reference< beans::XPropertySet > xPropSet( m_rExport.m_rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
5706     uno::Sequence< beans::PropertyValue > aGrabBag, aObjectsInteropList,aObjectInteropAttributes;
5707     xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= aGrabBag;
5708     auto pProp = std::find_if(aGrabBag.begin(), aGrabBag.end(),
5709         [](const beans::PropertyValue& rProp) { return rProp.Name == "EmbeddedObjects"; });
5710     if (pProp != aGrabBag.end())
5711         pProp->Value >>= aObjectsInteropList;
5712 
5713     SwOLEObj& aObject = rNode.GetOLEObj();
5714     uno::Reference < embed::XEmbeddedObject > xObj( aObject.GetOleRef() );
5715     comphelper::EmbeddedObjectContainer* aContainer = aObject.GetObject().GetContainer();
5716     OUString sObjectName = aContainer->GetEmbeddedObjectName( xObj );
5717 
5718     // set some attributes according to the type of the embedded object
5719     OUString sProgID, sDrawAspect;
5720     switch (rNode.GetAspect())
5721     {
5722         case embed::Aspects::MSOLE_CONTENT: sDrawAspect = "Content"; break;
5723         case embed::Aspects::MSOLE_DOCPRINT: sDrawAspect = "DocPrint"; break;
5724         case embed::Aspects::MSOLE_ICON: sDrawAspect = "Icon"; break;
5725         case embed::Aspects::MSOLE_THUMBNAIL: sDrawAspect = "Thumbnail"; break;
5726         default:
5727             SAL_WARN("sw.ww8", "DocxAttributeOutput::WriteOLE: invalid aspect value");
5728     }
5729     auto pObjectsInterop = std::find_if(aObjectsInteropList.begin(), aObjectsInteropList.end(),
5730         [&sObjectName](const beans::PropertyValue& rProp) { return rProp.Name == sObjectName; });
5731     if (pObjectsInterop != aObjectsInteropList.end())
5732         pObjectsInterop->Value >>= aObjectInteropAttributes;
5733 
5734     for( const auto& rObjectInteropAttribute : std::as_const(aObjectInteropAttributes) )
5735     {
5736         if ( rObjectInteropAttribute.Name == "ProgID" )
5737         {
5738             rObjectInteropAttribute.Value >>= sProgID;
5739         }
5740     }
5741 
5742     // write embedded file
5743     OString sId = m_rExport.WriteOLEObject(aObject, sProgID);
5744 
5745     if( sId.isEmpty() )
5746     {
5747         // the embedded file could not be saved
5748         // fallback: save as an image
5749         FlyFrameGraphic( nullptr, rSize, pFlyFrameFormat, &rNode );
5750         return;
5751     }
5752 
5753     // write preview image
5754     const Graphic* pGraphic = rNode.GetGraphic();
5755     m_rDrawingML.SetFS(m_pSerializer);
5756     OUString sImageId = m_rDrawingML.WriteImage( *pGraphic );
5757 
5758     if ( sDrawAspect == "Content" )
5759     {
5760         try
5761         {
5762             awt::Size aSize = xObj->getVisualAreaSize( rNode.GetAspect() );
5763 
5764             MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( rNode.GetAspect() ) );
5765             Size aOriginalSize( OutputDevice::LogicToLogic(Size( aSize.Width, aSize.Height),
5766                                                 MapMode(aUnit), MapMode(MapUnit::MapTwip)));
5767 
5768             m_pSerializer->startElementNS( XML_w, XML_object,
5769                                    FSNS(XML_w, XML_dxaOrig), OString::number(aOriginalSize.Width()),
5770                                    FSNS(XML_w, XML_dyaOrig), OString::number(aOriginalSize.Height()) );
5771         }
5772         catch ( uno::Exception& )
5773         {
5774             m_pSerializer->startElementNS(XML_w, XML_object);
5775         }
5776     }
5777     else
5778     {
5779         m_pSerializer->startElementNS(XML_w, XML_object);
5780     }
5781 
5782     OString sShapeId = "ole_" + sId;
5783 
5784     //OLE Shape definition
5785     WriteOLEShape(*pFlyFrameFormat, rSize, sShapeId, sImageId);
5786 
5787     //OLE Object definition
5788     m_pSerializer->singleElementNS(XML_o, XML_OLEObject,
5789                                    XML_Type, "Embed",
5790                                    XML_ProgID, sProgID,
5791                                    XML_ShapeID, sShapeId.getStr(),
5792                                    XML_DrawAspect, sDrawAspect,
5793                                    XML_ObjectID, "_" + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())),
5794                                    FSNS( XML_r, XML_id ), sId );
5795 
5796     m_pSerializer->endElementNS(XML_w, XML_object);
5797 }
5798 
WriteOLEShape(const SwFlyFrameFormat & rFrameFormat,const Size & rSize,const OString & rShapeId,const OUString & rImageId)5799 void DocxAttributeOutput::WriteOLEShape(const SwFlyFrameFormat& rFrameFormat, const Size& rSize,
5800                                         const OString& rShapeId, const OUString& rImageId)
5801 {
5802     assert(m_pSerializer);
5803 
5804     //Here is an attribute list where we collect the attributes what we want to export
5805     rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
5806     pAttr->add(XML_id, rShapeId);
5807 
5808     //export the fixed shape type for picture frame
5809     m_pSerializer->write(vml::VMLExport::GetVMLShapeTypeDefinition(rShapeId, true));
5810     pAttr->add(XML_type, "_x0000_t" + rShapeId);
5811 
5812     //Export the style attribute for position and size
5813     pAttr->add(XML_style, GetOLEStyle(rFrameFormat, rSize));
5814     //Get the OLE frame
5815     const SvxBoxItem& rBox = rFrameFormat.GetAttrSet().GetBox();
5816     OString sLineType;
5817     OString sDashType;
5818     //Word does not handle differently the four sides,
5819     //so we have to choose, and the left one is the winner:
5820     if (rBox.GetLeft())
5821     {
5822         //Get the left border color and width
5823         const Color aLineColor = rBox.GetLeft()->GetColor();
5824         const tools::Long aLineWidth = rBox.GetLeft()->GetWidth();
5825 
5826         //Convert the left OLE border style to OOXML
5827         //FIXME improve if it's necessary
5828         switch (rBox.GetLeft()->GetBorderLineStyle())
5829         {
5830             case SvxBorderLineStyle::SOLID:
5831                 sLineType = OString("Single");
5832                 sDashType = OString("Solid");
5833                 break;
5834             case SvxBorderLineStyle::DASHED:
5835                 sLineType = OString("Single");
5836                 sDashType = OString("Dash");
5837                 break;
5838             case SvxBorderLineStyle::DASH_DOT:
5839                 sLineType = OString("Single");
5840                 sDashType = OString("DashDot");
5841                 break;
5842             case SvxBorderLineStyle::DASH_DOT_DOT:
5843                 sLineType = OString("Single");
5844                 sDashType = OString("ShortDashDotDot");
5845                 break;
5846             case SvxBorderLineStyle::DOTTED:
5847                 sLineType = OString("Single");
5848                 sDashType = OString("Dot");
5849                 break;
5850             case SvxBorderLineStyle::DOUBLE:
5851                 sLineType = OString("ThinThin");
5852                 sDashType = OString("Solid");
5853                 break;
5854             case SvxBorderLineStyle::DOUBLE_THIN:
5855                 sLineType = OString("ThinThin");
5856                 sDashType = OString("Solid");
5857                 break;
5858             case SvxBorderLineStyle::EMBOSSED:
5859                 sLineType = OString("Single");
5860                 sDashType = OString("Solid");
5861                 break;
5862             case SvxBorderLineStyle::ENGRAVED:
5863                 sLineType = OString("Single");
5864                 sDashType = OString("Solid");
5865                 break;
5866             case SvxBorderLineStyle::FINE_DASHED:
5867                 sLineType = OString("Single");
5868                 sDashType = OString("Dot");
5869                 break;
5870             case SvxBorderLineStyle::INSET:
5871                 sLineType = OString("Single");
5872                 sDashType = OString("Solid");
5873                 break;
5874             case SvxBorderLineStyle::OUTSET:
5875                 sLineType = OString("Single");
5876                 sDashType = OString("Solid");
5877                 break;
5878             case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
5879             case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
5880             case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
5881                 sLineType = OString("ThickThin");
5882                 sDashType = OString("Solid");
5883                 break;
5884             case SvxBorderLineStyle::THINTHICK_LARGEGAP:
5885             case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
5886             case SvxBorderLineStyle::THINTHICK_SMALLGAP:
5887                 sLineType = OString("ThinThick");
5888                 sDashType = OString("Solid");
5889                 break;
5890             case SvxBorderLineStyle::NONE:
5891                 sLineType = OString("");
5892                 sDashType = OString("");
5893                 break;
5894             default:
5895                 SAL_WARN("sw.ww8", "Unknown line type on OOXML ELE export!");
5896                 break;
5897         }
5898 
5899         //If there is a line add it for export
5900         if (!sLineType.isEmpty() && !sDashType.isEmpty())
5901         {
5902             pAttr->add(XML_stroked, "t");
5903             pAttr->add(XML_strokecolor, "#" + msfilter::util::ConvertColor(aLineColor));
5904             pAttr->add(XML_strokeweight, OString::number(aLineWidth / 20) + "pt");
5905         }
5906     }
5907 
5908     //Let's check the filltype of the OLE
5909     switch (rFrameFormat.GetAttrSet().Get(XATTR_FILLSTYLE).GetValue())
5910     {
5911         case drawing::FillStyle::FillStyle_SOLID:
5912         {
5913             //If solid, we get the color and add it to the exporter
5914             const Color rShapeColor = rFrameFormat.GetAttrSet().Get(XATTR_FILLCOLOR).GetColorValue();
5915             pAttr->add(XML_filled, "t");
5916             pAttr->add(XML_fillcolor, "#" + msfilter::util::ConvertColor(rShapeColor));
5917             break;
5918         }
5919         case drawing::FillStyle::FillStyle_GRADIENT:
5920         case drawing::FillStyle::FillStyle_HATCH:
5921         case drawing::FillStyle::FillStyle_BITMAP:
5922             //TODO
5923             break;
5924         case drawing::FillStyle::FillStyle_NONE:
5925         {
5926             pAttr->add(XML_filled, "f");
5927             break;
5928         }
5929         default:
5930             SAL_WARN("sw.ww8", "Unknown fill type on OOXML OLE export!");
5931             break;
5932     }
5933     pAttr->addNS(XML_o, XML_ole, ""); //compulsory, even if it's empty
5934     m_pSerializer->startElementNS(XML_v, XML_shape, pAttr);//Write the collected attrs...
5935 
5936     if (!sLineType.isEmpty() && !sDashType.isEmpty()) //If there is a line/dash style it is time to export it
5937     {
5938         m_pSerializer->singleElementNS(XML_v, XML_stroke, XML_linestyle, sLineType, XML_dashstyle, sDashType);
5939     }
5940 
5941     // shape filled with the preview image
5942     m_pSerializer->singleElementNS(XML_v, XML_imagedata,
5943                                    FSNS(XML_r, XML_id), rImageId,
5944                                    FSNS(XML_o, XML_title), "");
5945 
5946     //export wrap settings
5947     if (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) //As-char objs does not have surround.
5948         ExportOLESurround(rFrameFormat.GetSurround());
5949 
5950     m_pSerializer->endElementNS(XML_v, XML_shape);
5951 }
5952 
GetOLEStyle(const SwFlyFrameFormat & rFormat,const Size & rSize)5953 OString DocxAttributeOutput::GetOLEStyle(const SwFlyFrameFormat& rFormat, const Size& rSize)
5954 {
5955     //tdf#131539: Export OLE positions in docx:
5956     //This string will store the position output for the xml
5957     OString aPos;
5958     //This string will store the relative position for aPos
5959     OString aAnch;
5960 
5961     if (rFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
5962     {
5963         //Get the horizontal alignment of the OLE via the frame format, to aHAlign
5964         OString aHAlign = convertToOOXMLHoriOrient(rFormat.GetHoriOrient().GetHoriOrient(),
5965             rFormat.GetHoriOrient().IsPosToggle());
5966         //Get the vertical alignment of the OLE via the frame format to aVAlign
5967         OString aVAlign = convertToOOXMLVertOrient(rFormat.GetVertOrient().GetVertOrient());
5968 
5969         // Check if the OLE anchored to page:
5970         const bool bIsPageAnchor = rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE;
5971 
5972         //Get the relative horizontal positions for the anchors
5973         OString aHAnch
5974             = bIsPageAnchor
5975                   ? OString("page")
5976                   : convertToOOXMLHoriOrientRel(rFormat.GetHoriOrient().GetRelationOrient());
5977         //Get the relative vertical positions for the anchors
5978         OString aVAnch = convertToOOXMLVertOrientRel(rFormat.GetVertOrient().GetRelationOrient());
5979 
5980         //Choice that the horizontal position is relative or not
5981         if (!aHAlign.isEmpty())
5982             aHAlign = ";mso-position-horizontal:" + aHAlign;
5983         aHAlign = ";mso-position-horizontal-relative:" + aHAnch;
5984 
5985         //Choice that the vertical position is relative or not
5986         if (!aVAlign.isEmpty())
5987             aVAlign = ";mso-position-vertical:" + aVAlign;
5988         aVAlign = ";mso-position-vertical-relative:" + aVAnch;
5989 
5990         //Set the anchoring information into one string for aPos
5991         aAnch = aHAlign + aVAlign;
5992 
5993         //Query the positions to aPos from frameformat
5994         aPos =
5995             "position:absolute;margin-left:" + OString::number(double(rFormat.GetHoriOrient().GetPos()) / 20) +
5996             "pt;margin-top:" + OString::number(double(rFormat.GetVertOrient().GetPos()) / 20) + "pt;";
5997     }
5998 
5999     OString sShapeStyle = "width:" + OString::number( double( rSize.Width() ) / 20 ) +
6000                         "pt;height:" + OString::number( double( rSize.Height() ) / 20 ) +
6001                         "pt"; //from VMLExport::AddRectangleDimensions(), it does: value/20
6002 
6003     const SvxLRSpaceItem& rLRSpace = rFormat.GetLRSpace();
6004     if (rLRSpace.IsExplicitZeroMarginValLeft() || rLRSpace.GetLeft())
6005         sShapeStyle += ";mso-wrap-distance-left:" + OString::number(double(rLRSpace.GetLeft()) / 20) + "pt";
6006     if (rLRSpace.IsExplicitZeroMarginValRight() || rLRSpace.GetRight())
6007         sShapeStyle += ";mso-wrap-distance-right:" + OString::number(double(rLRSpace.GetRight()) / 20) + "pt";
6008     const SvxULSpaceItem& rULSpace = rFormat.GetULSpace();
6009     if (rULSpace.GetUpper())
6010         sShapeStyle += ";mso-wrap-distance-top:" + OString::number(double(rULSpace.GetUpper()) / 20) + "pt";
6011     if (rULSpace.GetLower())
6012         sShapeStyle += ";mso-wrap-distance-bottom:" + OString::number(double(rULSpace.GetLower()) / 20) + "pt";
6013 
6014     //Export anchor setting, if it exists
6015     if (!aPos.isEmpty() && !aAnch.isEmpty())
6016         sShapeStyle = aPos + sShapeStyle  + aAnch;
6017 
6018     return sShapeStyle;
6019 }
6020 
ExportOLESurround(const SwFormatSurround & rWrap)6021 void DocxAttributeOutput::ExportOLESurround(const SwFormatSurround& rWrap)
6022 {
6023     const bool bIsContour = rWrap.IsContour(); //Has the shape contour or not
6024     OString sSurround;
6025     OString sSide;
6026 
6027     //Map the ODF wrap settings to OOXML one
6028     switch (rWrap.GetSurround())
6029     {
6030         case text::WrapTextMode::WrapTextMode_NONE:
6031             sSurround = OString("topAndBottom");
6032             break;
6033         case text::WrapTextMode::WrapTextMode_PARALLEL:
6034             sSurround = bIsContour ? OString("tight") : OString("square");
6035             break;
6036         case text::WrapTextMode::WrapTextMode_DYNAMIC:
6037             sSide = OString("largest");
6038             sSurround = bIsContour ? OString("tight") : OString("square");
6039             break;
6040         case text::WrapTextMode::WrapTextMode_LEFT:
6041             sSide = OString("left");
6042             sSurround = bIsContour ? OString("tight") : OString("square");
6043             break;
6044         case text::WrapTextMode::WrapTextMode_RIGHT:
6045             sSide = OString("right");
6046             sSurround = bIsContour ? OString("tight") : OString("square");
6047             break;
6048         default:
6049             SAL_WARN("sw.ww8", "Unknown surround type on OOXML export!");
6050             break;
6051     }
6052 
6053     //if there is a setting export it:
6054     if (!sSurround.isEmpty())
6055     {
6056         if (sSide.isEmpty())
6057             m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, sSurround);
6058         else
6059             m_pSerializer->singleElementNS(XML_w10, XML_wrap, XML_type, sSurround, XML_side, sSide);
6060     }
6061 }
6062 
WritePostponedCustomShape()6063 void DocxAttributeOutput::WritePostponedCustomShape()
6064 {
6065     if (!m_pPostponedCustomShape)
6066         return;
6067 
6068     for( const auto & rPostponedDrawing : *m_pPostponedCustomShape)
6069     {
6070         if ( IsAlternateContentChoiceOpen() )
6071             m_rExport.SdrExporter().writeDMLDrawing(rPostponedDrawing.object, rPostponedDrawing.frame, m_anchorId++);
6072         else
6073             m_rExport.SdrExporter().writeDMLAndVMLDrawing(rPostponedDrawing.object, *rPostponedDrawing.frame, m_anchorId++);
6074     }
6075     m_pPostponedCustomShape.reset();
6076 }
6077 
WritePostponedDMLDrawing()6078 void DocxAttributeOutput::WritePostponedDMLDrawing()
6079 {
6080     if (!m_pPostponedDMLDrawings)
6081         return;
6082 
6083     // Clear the list early, this method may be called recursively.
6084     std::unique_ptr< std::vector<PostponedDrawing> > pPostponedDMLDrawings(std::move(m_pPostponedDMLDrawings));
6085     std::unique_ptr< std::vector<PostponedOLE> > pPostponedOLEs(std::move(m_pPostponedOLEs));
6086 
6087     for( const auto & rPostponedDrawing : *pPostponedDMLDrawings )
6088     {
6089         // Avoid w:drawing within another w:drawing.
6090         if ( IsAlternateContentChoiceOpen() && !( m_rExport.SdrExporter().IsDrawingOpen()) )
6091            m_rExport.SdrExporter().writeDMLDrawing(rPostponedDrawing.object, rPostponedDrawing.frame, m_anchorId++);
6092         else
6093             m_rExport.SdrExporter().writeDMLAndVMLDrawing(rPostponedDrawing.object, *rPostponedDrawing.frame, m_anchorId++);
6094     }
6095 
6096     m_pPostponedOLEs = std::move(pPostponedOLEs);
6097 }
6098 
WriteFlyFrame(const ww8::Frame & rFrame)6099 void DocxAttributeOutput::WriteFlyFrame(const ww8::Frame& rFrame)
6100 {
6101     m_pSerializer->mark(Tag_OutputFlyFrame);
6102 
6103     switch ( rFrame.GetWriterType() )
6104     {
6105         case ww8::Frame::eGraphic:
6106             {
6107                 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
6108                 const SwNode *pNode = rFrame.GetContent();
6109                 const SwGrfNode *pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
6110                 if ( pGrfNode )
6111                 {
6112                     if (!m_pPostponedGraphic)
6113                     {
6114                         m_bPostponedProcessingFly = false ;
6115                         FlyFrameGraphic( pGrfNode, rFrame.GetLayoutSize(), nullptr, nullptr, pSdrObj);
6116                     }
6117                     else // we are writing out attributes, but w:drawing should not be inside w:rPr,
6118                     {    // so write it out later
6119                         m_bPostponedProcessingFly = true ;
6120                         m_pPostponedGraphic->push_back(PostponedGraphic(pGrfNode, rFrame.GetLayoutSize(), pSdrObj));
6121                     }
6122                 }
6123             }
6124             break;
6125         case ww8::Frame::eDrawing:
6126             {
6127                 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
6128                 if ( pSdrObj )
6129                 {
6130                     uno::Reference<drawing::XShape> xShape(
6131                         const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
6132 
6133                     if (xShape.is() && oox::drawingml::DrawingML::IsDiagram(xShape))
6134                     {
6135                         if ( !m_pPostponedDiagrams )
6136                         {
6137                             m_bPostponedProcessingFly = false ;
6138                             m_rExport.SdrExporter().writeDiagram( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++);
6139                         }
6140                         else // we are writing out attributes, but w:drawing should not be inside w:rPr,
6141                         {    // so write it out later
6142                             m_bPostponedProcessingFly = true ;
6143                             m_pPostponedDiagrams->push_back( PostponedDiagram( pSdrObj, &(rFrame.GetFrameFormat()) ));
6144                         }
6145                     }
6146                     else
6147                     {
6148                         if (!m_pPostponedDMLDrawings)
6149                         {
6150                             if ( IsAlternateContentChoiceOpen() )
6151                             {
6152                                 // Do not write w:drawing inside w:drawing. Instead Postpone the Inner Drawing.
6153                                 if( m_rExport.SdrExporter().IsDrawingOpen() )
6154                                     m_pPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat())));
6155                                 else
6156                                     m_rExport.SdrExporter().writeDMLDrawing( pSdrObj, &rFrame.GetFrameFormat(), m_anchorId++);
6157                             }
6158                             else
6159                                 m_rExport.SdrExporter().writeDMLAndVMLDrawing( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++);
6160 
6161                             m_bPostponedProcessingFly = false ;
6162                         }
6163                         // IsAlternateContentChoiceOpen(): check is to ensure that only one object is getting added. Without this check, plus one object gets added
6164                         // m_bParagraphFrameOpen: check if the frame is open.
6165                         else if (IsAlternateContentChoiceOpen() && m_bParagraphFrameOpen)
6166                             m_pPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat())));
6167                         else
6168                         {
6169                             // we are writing out attributes, but w:drawing should not be inside w:rPr, so write it out later
6170                             m_bPostponedProcessingFly = true ;
6171                             m_pPostponedDMLDrawings->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat())));
6172                         }
6173                     }
6174                 }
6175             }
6176             break;
6177         case ww8::Frame::eTextBox:
6178             {
6179                 // If this is a TextBox of a shape, then ignore: it's handled in WriteTextBox().
6180                 if (DocxSdrExport::isTextBox(rFrame.GetFrameFormat()))
6181                     break;
6182 
6183                 // If this is a TextBox containing a table which we already exported directly, ignore it
6184                 if (m_aFloatingTablesOfParagraph.find(&rFrame.GetFrameFormat()) != m_aFloatingTablesOfParagraph.end())
6185                     break;
6186 
6187                 // The frame output is postponed to the end of the anchor paragraph
6188                 bool bDuplicate = false;
6189                 const OUString& rName = rFrame.GetFrameFormat().GetName();
6190                 unsigned nSize = m_aFramesOfParagraph.size() ? m_aFramesOfParagraph.top().size() : 0;
6191                 for( unsigned nIndex = 0; nIndex < nSize; ++nIndex )
6192                 {
6193                     const OUString& rNameExisting = m_aFramesOfParagraph.top()[nIndex].GetFrameFormat().GetName();
6194 
6195                     if (!rName.isEmpty() && !rNameExisting.isEmpty())
6196                     {
6197                         if (rName == rNameExisting)
6198                             bDuplicate = true;
6199                     }
6200                 }
6201 
6202                 if( !bDuplicate )
6203                 {
6204                     m_bPostponedProcessingFly = true ;
6205                     if ( m_aFramesOfParagraph.size() )
6206                         m_aFramesOfParagraph.top().emplace_back(rFrame);
6207                 }
6208             }
6209             break;
6210         case ww8::Frame::eOle:
6211             {
6212                 const SwFrameFormat &rFrameFormat = rFrame.GetFrameFormat();
6213                 const SdrObject *pSdrObj = rFrameFormat.FindRealSdrObject();
6214                 if ( pSdrObj )
6215                 {
6216                     SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
6217                     SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
6218 
6219                     //output variable for the formula alignment (default inline)
6220                     sal_Int8 nAlign(FormulaExportBase::eFormulaAlign::INLINE);
6221                     auto xObj(rOLENd.GetOLEObj().GetOleRef()); //get the xObject of the formula
6222 
6223                     //tdf133030: Export formula position
6224                     //If we have a formula with inline anchor...
6225                     if(SotExchange::IsMath(xObj->getClassID()) && rFrame.IsInline())
6226                     {
6227                         SwPosition const* const aAPos = rFrameFormat.GetAnchor().GetContentAnchor();
6228                         if(aAPos)
6229                         {
6230                             //Get the text node what the formula anchored to
6231                             const SwTextNode* pTextNode = aAPos->nNode.GetNode().GetTextNode();
6232                             if(pTextNode && pTextNode->Len() == 1)
6233                             {
6234                                 //Get the paragraph alignment
6235                                 auto aParaAdjust = pTextNode->GetSwAttrSet().GetAdjust().GetAdjust();
6236                                 //And set the formula according to the paragraph alignment
6237                                 if (aParaAdjust == SvxAdjust::Center)
6238                                     nAlign = FormulaExportBase::eFormulaAlign::CENTER;
6239                                 else if (aParaAdjust == SvxAdjust::Right)
6240                                     nAlign = FormulaExportBase::eFormulaAlign::RIGHT;
6241                                 else // left in the case of left and justified paragraph alignments
6242                                     nAlign = FormulaExportBase::eFormulaAlign::LEFT;
6243                             }
6244                         }
6245                     }
6246                     WriteOLE2Obj( pSdrObj, rOLENd, rFrame.GetLayoutSize(), dynamic_cast<const SwFlyFrameFormat*>( &rFrameFormat ), nAlign);
6247                     m_bPostponedProcessingFly = false ;
6248                 }
6249             }
6250             break;
6251         case ww8::Frame::eFormControl:
6252             {
6253                 const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject();
6254                 if(ExportAsActiveXControl(pObject))
6255                     m_aPostponedActiveXControls.emplace_back(pObject, &(rFrame.GetFrameFormat()));
6256                 else
6257                     m_aPostponedFormControls.push_back(pObject);
6258                 m_bPostponedProcessingFly = true ;
6259             }
6260             break;
6261         default:
6262             SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame& rFrame ) - frame type " <<
6263                     ( rFrame.GetWriterType() == ww8::Frame::eTextBox ? "eTextBox":
6264                       ( rFrame.GetWriterType() == ww8::Frame::eOle ? "eOle": "???" ) ) );
6265             break;
6266     }
6267 
6268     m_pSerializer->mergeTopMarks(Tag_OutputFlyFrame);
6269 }
6270 
OutputFlyFrame_Impl(const ww8::Frame & rFrame,const Point &)6271 void DocxAttributeOutput::OutputFlyFrame_Impl(const ww8::Frame& rFrame, const Point& /*rNdTopLeft*/)
6272 {
6273     /// The old OutputFlyFrame_Impl() moved to WriteFlyFrame().
6274     /// Now if a frame anchored inside another frame, it will
6275     /// not be exported immediately, because OOXML does not
6276     /// support that feature, instead it postponed and exported
6277     /// later when the original shape closed.
6278 
6279     if (rFrame.GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
6280         || rFrame.IsInline())
6281     {
6282         m_nEmbedFlyLevel++;
6283         WriteFlyFrame(rFrame);
6284         m_nEmbedFlyLevel--;
6285         return;
6286     }
6287 
6288     if (m_nEmbedFlyLevel == 0)
6289     {
6290         if (m_vPostponedFlys.empty())
6291         {
6292             m_nEmbedFlyLevel++;
6293             WriteFlyFrame(rFrame);
6294             m_nEmbedFlyLevel--;
6295         }
6296         else
6297             for (auto it = m_vPostponedFlys.begin(); it != m_vPostponedFlys.end();)
6298             {
6299                 m_nEmbedFlyLevel++;
6300                 WriteFlyFrame(*it);
6301                 it = m_vPostponedFlys.erase(it);
6302                 m_nEmbedFlyLevel--;
6303             }
6304     }
6305     else
6306     {
6307         bool bFound = false;
6308         for (const auto& i : m_vPostponedFlys)
6309         {
6310             if (i.RefersToSameFrameAs(rFrame))
6311             {
6312                 bFound = true;
6313                 break;
6314             }
6315         }
6316         if (!bFound)
6317         {
6318             if (auto pParentFly = rFrame.GetContentNode()->GetFlyFormat())
6319             {
6320                 auto aHori(rFrame.GetFrameFormat().GetHoriOrient());
6321                 aHori.SetPos(aHori.GetPos() + pParentFly->GetHoriOrient().GetPos());
6322                 auto aVori(rFrame.GetFrameFormat().GetVertOrient());
6323                 aVori.SetPos(aVori.GetPos() + pParentFly->GetVertOrient().GetPos());
6324 
6325                 const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(aHori);
6326                 const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(aVori);
6327                 const_cast<SwFrameFormat&>(rFrame.GetFrameFormat()).SetFormatAttr(pParentFly->GetAnchor());
6328 
6329                 m_vPostponedFlys.push_back(rFrame);
6330             }
6331 
6332         }
6333     }
6334 }
6335 
WriteOutliner(const OutlinerParaObject & rParaObj)6336 void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj)
6337 {
6338     const EditTextObject& rEditObj = rParaObj.GetTextObject();
6339     MSWord_SdrAttrIter aAttrIter( m_rExport, rEditObj, TXT_HFTXTBOX );
6340 
6341     sal_Int32 nPara = rEditObj.GetParagraphCount();
6342 
6343     m_pSerializer->startElementNS(XML_w, XML_txbxContent);
6344     for (sal_Int32 n = 0; n < nPara; ++n)
6345     {
6346         if( n )
6347             aAttrIter.NextPara( n );
6348 
6349         OUString aStr( rEditObj.GetText( n ));
6350         sal_Int32 nCurrentPos = 0;
6351         sal_Int32 nEnd = aStr.getLength();
6352 
6353         StartParagraph(ww8::WW8TableNodeInfo::Pointer_t(), false);
6354 
6355         // Write paragraph properties.
6356         StartParagraphProperties();
6357         aAttrIter.OutParaAttr(false);
6358         SfxItemSet aParagraphMarkerProperties(m_rExport.m_rDoc.GetAttrPool());
6359         EndParagraphProperties(aParagraphMarkerProperties, nullptr, nullptr, nullptr);
6360 
6361         do {
6362             const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
6363 
6364             m_pSerializer->startElementNS(XML_w, XML_r);
6365 
6366             // Write run properties.
6367             m_pSerializer->startElementNS(XML_w, XML_rPr);
6368             aAttrIter.OutAttr(nCurrentPos);
6369             WriteCollectedRunProperties();
6370             m_pSerializer->endElementNS(XML_w, XML_rPr);
6371 
6372             bool bTextAtr = aAttrIter.IsTextAttr( nCurrentPos );
6373             if( !bTextAtr )
6374             {
6375                 OUString aOut( aStr.copy( nCurrentPos, nNextAttr - nCurrentPos ) );
6376                 RunText(aOut);
6377             }
6378 
6379             if ( !m_sRawText.isEmpty() )
6380             {
6381                 RunText( m_sRawText );
6382                 m_sRawText.clear();
6383             }
6384 
6385             m_pSerializer->endElementNS( XML_w, XML_r );
6386 
6387             nCurrentPos = nNextAttr;
6388             aAttrIter.NextPos();
6389         }
6390         while( nCurrentPos < nEnd );
6391         EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t());
6392     }
6393     m_pSerializer->endElementNS( XML_w, XML_txbxContent );
6394 }
6395 
pushToTableExportContext(DocxTableExportContext & rContext)6396 void DocxAttributeOutput::pushToTableExportContext(DocxTableExportContext& rContext)
6397 {
6398     rContext.m_pTableInfo = m_rExport.m_pTableInfo;
6399     m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
6400 
6401     rContext.m_bTableCellOpen = m_tableReference->m_bTableCellOpen;
6402     m_tableReference->m_bTableCellOpen = false;
6403 
6404     rContext.m_nTableDepth = m_tableReference->m_nTableDepth;
6405     m_tableReference->m_nTableDepth = 0;
6406 
6407     rContext.m_bStartedParaSdt = m_bStartedParaSdt;
6408     m_bStartedParaSdt = false;
6409 }
6410 
popFromTableExportContext(DocxTableExportContext const & rContext)6411 void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext const & rContext)
6412 {
6413     m_rExport.m_pTableInfo = rContext.m_pTableInfo;
6414     m_tableReference->m_bTableCellOpen = rContext.m_bTableCellOpen;
6415     m_tableReference->m_nTableDepth = rContext.m_nTableDepth;
6416     m_bStartedParaSdt = rContext.m_bStartedParaSdt;
6417 }
6418 
WriteTextBox(uno::Reference<drawing::XShape> xShape)6419 void DocxAttributeOutput::WriteTextBox(uno::Reference<drawing::XShape> xShape)
6420 {
6421     DocxTableExportContext aTableExportContext(*this);
6422 
6423     SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
6424     assert(pTextBox);
6425     const SwPosition* pAnchor = nullptr;
6426     if (pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE) //tdf135711
6427     {
6428         auto pNdIdx = pTextBox->GetContent().GetContentIdx();
6429         if (pNdIdx) //Is that possible it is null?
6430             pAnchor = new SwPosition(*pNdIdx);
6431     }
6432     else
6433     {
6434         pAnchor = pTextBox->GetAnchor().GetContentAnchor();//This might be null
6435     }
6436 
6437     if (pAnchor) //pAnchor can be null, so that's why not assert here.
6438     {
6439         ww8::Frame aFrame(*pTextBox, *pAnchor);
6440         m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++, /*bTextBoxOnly=*/true);
6441         if (pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE)
6442         {
6443             delete pAnchor;
6444         }
6445     }
6446 }
6447 
WriteVMLTextBox(uno::Reference<drawing::XShape> xShape)6448 void DocxAttributeOutput::WriteVMLTextBox(uno::Reference<drawing::XShape> xShape)
6449 {
6450     DocxTableExportContext aTableExportContext(*this);
6451 
6452     SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
6453     assert(pTextBox);
6454     const SwPosition* pAnchor = nullptr;
6455     if (pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE) //tdf135711
6456     {
6457         auto pNdIdx = pTextBox->GetContent().GetContentIdx();
6458         if (pNdIdx) //Is that possible it is null?
6459             pAnchor = new SwPosition(*pNdIdx);
6460     }
6461     else
6462     {
6463         pAnchor = pTextBox->GetAnchor().GetContentAnchor();//This might be null
6464     }
6465 
6466     if (pAnchor) //pAnchor can be null, so that's why not assert here.
6467     {
6468         ww8::Frame aFrame(*pTextBox, *pAnchor);
6469         m_rExport.SdrExporter().writeVMLTextFrame(&aFrame, /*bTextBoxOnly=*/true);
6470         if (pTextBox->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE)
6471         {
6472             delete pAnchor;
6473         }
6474     }
6475 }
6476 
GetDrawingML()6477 oox::drawingml::DrawingML& DocxAttributeOutput::GetDrawingML()
6478 {
6479     return m_rDrawingML;
6480 }
6481 
MaybeOutputBrushItem(SfxItemSet const & rSet)6482 bool DocxAttributeOutput::MaybeOutputBrushItem(SfxItemSet const& rSet)
6483 {
6484     const XFillStyleItem* pXFillStyleItem(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE));
6485 
6486     if ((pXFillStyleItem && pXFillStyleItem->GetValue() != drawing::FillStyle_NONE)
6487         || !m_rExport.SdrExporter().getDMLTextFrameSyntax())
6488     {
6489         return false;
6490     }
6491 
6492     // sw text frames are opaque by default, even with fill none!
6493     std::unique_ptr<SfxItemSet> const pClone(rSet.Clone());
6494     XFillColorItem const aColor(OUString(), COL_WHITE);
6495     pClone->Put(aColor);
6496     // call getSvxBrushItemForSolid - this also takes XFillTransparenceItem into account
6497     XFillStyleItem const aSolid(drawing::FillStyle_SOLID);
6498     pClone->Put(aSolid);
6499     std::unique_ptr<SvxBrushItem> const pBrush(getSvxBrushItemFromSourceSet(*pClone, RES_BACKGROUND));
6500     FormatBackground(*pBrush);
6501     return true;
6502 }
6503 
6504 namespace {
6505 
6506 /// Functor to do case-insensitive ordering of OUString instances.
6507 struct OUStringIgnoreCase
6508 {
operator ()__anon2ebbbce40b11::OUStringIgnoreCase6509     bool operator() (const OUString& lhs, std::u16string_view rhs) const
6510     {
6511         return lhs.compareToIgnoreAsciiCase(rhs) < 0;
6512     }
6513 };
6514 
6515 }
6516 
6517 /// Guesses if a style created in Writer (no grab-bag) should be qFormat or not.
lcl_guessQFormat(const OUString & rName,sal_uInt16 nWwId)6518 static bool lcl_guessQFormat(const OUString& rName, sal_uInt16 nWwId)
6519 {
6520     // If the style has no dedicated STI number, then it's probably a custom style -> qFormat.
6521     if (nWwId == ww::stiUser)
6522         return true;
6523 
6524     // Allow exported built-in styles UI language neutral
6525     if ( nWwId == ww::stiNormal ||
6526         ( nWwId>= ww::stiLev1 && nWwId <= ww::stiLev9 ) ||
6527             nWwId == ww::stiCaption || nWwId == ww::stiTitle ||
6528             nWwId == ww::stiSubtitle || nWwId == ww::stiStrong ||
6529             nWwId == ww::stiEmphasis )
6530         return true;
6531 
6532     static o3tl::sorted_vector<OUString, OUStringIgnoreCase> const aAllowlist
6533     {
6534         "No Spacing",
6535         "List Paragraph",
6536         "Quote",
6537         "Intense Quote",
6538         "Subtle Emphasis,",
6539         "Intense Emphasis",
6540         "Subtle Reference",
6541         "Intense Reference",
6542         "Book Title",
6543         "TOC Heading",
6544     };
6545     // Not custom style? Then we have a list of standard styles which should be qFormat.
6546     return aAllowlist.find(rName) != aAllowlist.end();
6547 }
6548 
StartStyle(const OUString & rName,StyleType eType,sal_uInt16 nBase,sal_uInt16 nNext,sal_uInt16 nWwId,sal_uInt16 nId,bool bAutoUpdate)6549 void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType,
6550         sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nWwId, sal_uInt16 nId, bool bAutoUpdate )
6551 {
6552     bool bQFormat = false, bUnhideWhenUsed = false, bSemiHidden = false, bLocked = false, bDefault = false, bCustomStyle = false;
6553     OUString aLink, aRsid, aUiPriority;
6554     rtl::Reference<FastAttributeList> pStyleAttributeList = FastSerializerHelper::createAttrList();
6555     uno::Any aAny;
6556     if (eType == STYLE_TYPE_PARA || eType == STYLE_TYPE_CHAR)
6557     {
6558         const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nId);
6559         pFormat->GetGrabBagItem(aAny);
6560     }
6561     else
6562     {
6563         const SwNumRule* pRule = m_rExport.m_pStyles->GetSwNumRule(nId);
6564         pRule->GetGrabBagItem(aAny);
6565     }
6566     const uno::Sequence<beans::PropertyValue>& rGrabBag = aAny.get< uno::Sequence<beans::PropertyValue> >();
6567 
6568     for (const auto& rProp : rGrabBag)
6569     {
6570         if (rProp.Name == "uiPriority")
6571             aUiPriority = rProp.Value.get<OUString>();
6572         else if (rProp.Name == "qFormat")
6573             bQFormat = true;
6574         else if (rProp.Name == "link")
6575             aLink = rProp.Value.get<OUString>();
6576         else if (rProp.Name == "rsid")
6577             aRsid = rProp.Value.get<OUString>();
6578         else if (rProp.Name == "unhideWhenUsed")
6579             bUnhideWhenUsed = true;
6580         else if (rProp.Name == "semiHidden")
6581             bSemiHidden = true;
6582         else if (rProp.Name == "locked")
6583             bLocked = true;
6584         else if (rProp.Name == "default")
6585             bDefault = rProp.Value.get<bool>();
6586         else if (rProp.Name == "customStyle")
6587             bCustomStyle = rProp.Value.get<bool>();
6588         else
6589             SAL_WARN("sw.ww8", "Unhandled style property: " << rProp.Name);
6590     }
6591 
6592     // MSO exports English names and writerfilter only recognize them.
6593     const char *pEnglishName = nullptr;
6594     const char* pType = nullptr;
6595     switch (eType)
6596     {
6597         case STYLE_TYPE_PARA:
6598             pType = "paragraph";
6599             if ( nWwId < ww::stiMax)
6600                 pEnglishName = ww::GetEnglishNameFromSti( static_cast<ww::sti>(nWwId ) );
6601             break;
6602         case STYLE_TYPE_CHAR: pType = "character"; break;
6603         case STYLE_TYPE_LIST: pType = "numbering"; break;
6604     }
6605     pStyleAttributeList->add(FSNS( XML_w, XML_type ), pType);
6606     pStyleAttributeList->add(FSNS(XML_w, XML_styleId), m_rExport.m_pStyles->GetStyleId(nId));
6607     if (bDefault)
6608         pStyleAttributeList->add(FSNS(XML_w, XML_default), "1");
6609     if (bCustomStyle)
6610         pStyleAttributeList->add(FSNS(XML_w, XML_customStyle), "1");
6611     m_pSerializer->startElementNS( XML_w, XML_style, pStyleAttributeList);
6612     m_pSerializer->singleElementNS( XML_w, XML_name,
6613             FSNS( XML_w, XML_val ), pEnglishName ? pEnglishName : rName.toUtf8() );
6614 
6615     if ( nBase != 0x0FFF && eType != STYLE_TYPE_LIST)
6616     {
6617         m_pSerializer->singleElementNS( XML_w, XML_basedOn,
6618                 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nBase) );
6619     }
6620 
6621     if ( nNext != nId && eType != STYLE_TYPE_LIST)
6622     {
6623         m_pSerializer->singleElementNS( XML_w, XML_next,
6624                 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nNext) );
6625     }
6626 
6627     if (!aLink.isEmpty())
6628         m_pSerializer->singleElementNS(XML_w, XML_link, FSNS(XML_w, XML_val), aLink);
6629 
6630     if ( bAutoUpdate )
6631         m_pSerializer->singleElementNS(XML_w, XML_autoRedefine);
6632 
6633     if (!aUiPriority.isEmpty())
6634         m_pSerializer->singleElementNS(XML_w, XML_uiPriority, FSNS(XML_w, XML_val), aUiPriority);
6635     if (bSemiHidden)
6636         m_pSerializer->singleElementNS(XML_w, XML_semiHidden);
6637     if (bUnhideWhenUsed)
6638         m_pSerializer->singleElementNS(XML_w, XML_unhideWhenUsed);
6639 
6640     if (bQFormat || lcl_guessQFormat(rName, nWwId))
6641         m_pSerializer->singleElementNS(XML_w, XML_qFormat);
6642     if (bLocked)
6643         m_pSerializer->singleElementNS(XML_w, XML_locked);
6644     if (!aRsid.isEmpty())
6645         m_pSerializer->singleElementNS(XML_w, XML_rsid, FSNS(XML_w, XML_val), aRsid);
6646 }
6647 
EndStyle()6648 void DocxAttributeOutput::EndStyle()
6649 {
6650     m_pSerializer->endElementNS( XML_w, XML_style );
6651 }
6652 
StartStyleProperties(bool bParProp,sal_uInt16)6653 void DocxAttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 /*nStyle*/ )
6654 {
6655     if ( bParProp )
6656     {
6657         m_pSerializer->startElementNS(XML_w, XML_pPr);
6658         InitCollectedParagraphProperties();
6659     }
6660     else
6661     {
6662         m_pSerializer->startElementNS(XML_w, XML_rPr);
6663         InitCollectedRunProperties();
6664     }
6665 }
6666 
EndStyleProperties(bool bParProp)6667 void DocxAttributeOutput::EndStyleProperties( bool bParProp )
6668 {
6669     if ( bParProp )
6670     {
6671         WriteCollectedParagraphProperties();
6672 
6673         // Merge the marks for the ordered elements
6674         m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties);
6675 
6676         m_pSerializer->endElementNS( XML_w, XML_pPr );
6677     }
6678     else
6679     {
6680         WriteCollectedRunProperties();
6681 
6682         // Merge the marks for the ordered elements
6683         m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
6684 
6685         m_pSerializer->endElementNS( XML_w, XML_rPr );
6686     }
6687 }
6688 
OutlineNumbering(sal_uInt8 const)6689 void DocxAttributeOutput::OutlineNumbering(sal_uInt8 const /*nLvl*/)
6690 {
6691     // Handled by ParaOutlineLevel() instead.
6692 }
6693 
ParaOutlineLevel(const SfxUInt16Item & rItem)6694 void DocxAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem)
6695 {
6696     sal_uInt16 nOutLvl = std::min(rItem.GetValue(), sal_uInt16(WW8ListManager::nMaxLevel));
6697     // Outline Level: in LO Body Text = 0, in MS Body Text = 9
6698     nOutLvl = nOutLvl ? nOutLvl - 1 : 9;
6699     m_pSerializer->singleElementNS(XML_w, XML_outlineLvl, FSNS(XML_w, XML_val), OString::number(nOutLvl));
6700 }
6701 
PageBreakBefore(bool bBreak)6702 void DocxAttributeOutput::PageBreakBefore( bool bBreak )
6703 {
6704     if ( bBreak )
6705         m_pSerializer->singleElementNS(XML_w, XML_pageBreakBefore);
6706     else
6707         m_pSerializer->singleElementNS( XML_w, XML_pageBreakBefore,
6708                 FSNS( XML_w, XML_val ), "false" );
6709 }
6710 
SectionBreak(sal_uInt8 nC,bool bBreakAfter,const WW8_SepInfo * pSectionInfo,bool bExtraPageBreak)6711 void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, bool bBreakAfter, const WW8_SepInfo* pSectionInfo, bool bExtraPageBreak)
6712 {
6713     switch ( nC )
6714     {
6715         case msword::ColumnBreak:
6716             // The column break should be output in the next paragraph...
6717             if ( m_nColBreakStatus == COLBRK_WRITE )
6718                 m_nColBreakStatus = COLBRK_WRITEANDPOSTPONE;
6719             else
6720                 m_nColBreakStatus = COLBRK_POSTPONE;
6721             break;
6722         case msword::PageBreak:
6723             if ( pSectionInfo )
6724             {
6725                 // Detect when the current node is the last node in the
6726                 // document: the last section is written explicitly in
6727                 // DocxExport::WriteMainText(), don't duplicate that here.
6728                 SwNodeIndex aCurrentNode(m_rExport.m_pCurPam->GetNode());
6729                 SwNodeIndex aLastNode(m_rExport.m_rDoc.GetNodes().GetEndOfContent(), -1);
6730                 bool bEmit = aCurrentNode != aLastNode;
6731 
6732                 if (!bEmit)
6733                 {
6734                     // Need to still emit an empty section at the end of the
6735                     // document in case balanced columns are wanted, since the last
6736                     // section in Word is always balanced.
6737                     sal_uInt16 nColumns = 1;
6738                     bool bBalance = false;
6739                     if (const SwSectionFormat* pFormat = pSectionInfo->pSectionFormat)
6740                     {
6741                         if (pFormat != reinterpret_cast<SwSectionFormat*>(sal_IntPtr(-1)))
6742                         {
6743                             nColumns = pFormat->GetCol().GetNumCols();
6744                             const SwFormatNoBalancedColumns& rNoBalanced = pFormat->GetBalancedColumns();
6745                             bBalance = !rNoBalanced.GetValue();
6746                         }
6747                     }
6748                     bEmit = (nColumns > 1 && bBalance);
6749                 }
6750 
6751                 // don't add section properties if this will be the first
6752                 // paragraph in the document
6753                 if ( !m_bParagraphOpened && !m_bIsFirstParagraph && bEmit )
6754                 {
6755                     // Create a dummy paragraph if needed
6756                     m_pSerializer->startElementNS(XML_w, XML_p);
6757                     m_pSerializer->startElementNS(XML_w, XML_pPr);
6758 
6759                     m_rExport.SectionProperties( *pSectionInfo );
6760 
6761                     m_pSerializer->endElementNS( XML_w, XML_pPr );
6762                     if (bExtraPageBreak)
6763                     {
6764                         m_pSerializer->startElementNS(XML_w, XML_r);
6765                         m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
6766                         m_pSerializer->endElementNS(XML_w, XML_r);
6767                     }
6768                     m_pSerializer->endElementNS( XML_w, XML_p );
6769                 }
6770                 else
6771                 {
6772                     if (bExtraPageBreak && m_bParagraphOpened)
6773                     {
6774                         m_pSerializer->startElementNS(XML_w, XML_r);
6775                         m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
6776                         m_pSerializer->endElementNS(XML_w, XML_r);
6777                     }
6778                     // postpone the output of this; it has to be done inside the
6779                     // paragraph properties, so remember it until then
6780                     m_pSectionInfo.reset( new WW8_SepInfo( *pSectionInfo ));
6781                 }
6782             }
6783             else if ( m_bParagraphOpened )
6784             {
6785                 if (bBreakAfter)
6786                     // tdf#128889
6787                     m_bPageBreakAfter = true;
6788                 else
6789                 {
6790                     m_pSerializer->startElementNS(XML_w, XML_r);
6791                     m_pSerializer->singleElementNS(XML_w, XML_br, FSNS(XML_w, XML_type), "page");
6792                     m_pSerializer->endElementNS(XML_w, XML_r);
6793                 }
6794             }
6795             else
6796                 m_bPostponedPageBreak = true;
6797 
6798             break;
6799         default:
6800             SAL_INFO("sw.ww8", "Unknown section break to write: " << nC );
6801             break;
6802     }
6803 }
6804 
EndParaSdtBlock()6805 void DocxAttributeOutput::EndParaSdtBlock()
6806 {
6807     if (m_bStartedParaSdt)
6808     {
6809         // Paragraph-level SDT still open? Close it now.
6810         EndSdtBlock();
6811         m_bStartedParaSdt = false;
6812     }
6813 }
6814 
StartSection()6815 void DocxAttributeOutput::StartSection()
6816 {
6817     m_pSerializer->startElementNS(XML_w, XML_sectPr);
6818     m_bOpenedSectPr = true;
6819 
6820     // Write the elements in the spec order
6821     static const sal_Int32 aOrder[] =
6822     {
6823         FSNS( XML_w, XML_headerReference ),
6824         FSNS( XML_w, XML_footerReference ),
6825         FSNS( XML_w, XML_footnotePr ),
6826         FSNS( XML_w, XML_endnotePr ),
6827         FSNS( XML_w, XML_type ),
6828         FSNS( XML_w, XML_pgSz ),
6829         FSNS( XML_w, XML_pgMar ),
6830         FSNS( XML_w, XML_paperSrc ),
6831         FSNS( XML_w, XML_pgBorders ),
6832         FSNS( XML_w, XML_lnNumType ),
6833         FSNS( XML_w, XML_pgNumType ),
6834         FSNS( XML_w, XML_cols ),
6835         FSNS( XML_w, XML_formProt ),
6836         FSNS( XML_w, XML_vAlign ),
6837         FSNS( XML_w, XML_noEndnote ),
6838         FSNS( XML_w, XML_titlePg ),
6839         FSNS( XML_w, XML_textDirection ),
6840         FSNS( XML_w, XML_bidi ),
6841         FSNS( XML_w, XML_rtlGutter ),
6842         FSNS( XML_w, XML_docGrid ),
6843         FSNS( XML_w, XML_printerSettings ),
6844         FSNS( XML_w, XML_sectPrChange )
6845     };
6846 
6847     // postpone the output so that we can later [in EndParagraphProperties()]
6848     // prepend the properties before the run
6849     m_pSerializer->mark(Tag_StartSection, comphelper::containerToSequence(aOrder));
6850     m_bHadSectPr = true;
6851 }
6852 
EndSection()6853 void DocxAttributeOutput::EndSection()
6854 {
6855     // Write the section properties
6856     if ( m_pSectionSpacingAttrList.is() )
6857     {
6858         rtl::Reference<FastAttributeList> xAttrList = std::move( m_pSectionSpacingAttrList );
6859         m_pSerializer->singleElementNS( XML_w, XML_pgMar, xAttrList );
6860     }
6861 
6862     // Order the elements
6863     m_pSerializer->mergeTopMarks(Tag_StartSection);
6864 
6865     m_pSerializer->endElementNS( XML_w, XML_sectPr );
6866     m_bOpenedSectPr = false;
6867 }
6868 
SectionFormProtection(bool bProtected)6869 void DocxAttributeOutput::SectionFormProtection( bool bProtected )
6870 {
6871     if ( bProtected )
6872         m_pSerializer->singleElementNS(XML_w, XML_formProt, FSNS(XML_w, XML_val), "true");
6873     else
6874         m_pSerializer->singleElementNS(XML_w, XML_formProt, FSNS(XML_w, XML_val), "false");
6875 }
6876 
SectionRtlGutter(const SfxBoolItem & rRtlGutter)6877 void DocxAttributeOutput::SectionRtlGutter(const SfxBoolItem& rRtlGutter)
6878 {
6879     if (!rRtlGutter.GetValue())
6880     {
6881         return;
6882     }
6883 
6884     m_pSerializer->singleElementNS(XML_w, XML_rtlGutter);
6885 }
6886 
SectionLineNumbering(sal_uLong nRestartNo,const SwLineNumberInfo & rLnNumInfo)6887 void DocxAttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo )
6888 {
6889     rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
6890     pAttr->add( FSNS( XML_w, XML_countBy ), OString::number(rLnNumInfo.GetCountBy()).getStr());
6891     pAttr->add( FSNS( XML_w, XML_restart ), rLnNumInfo.IsRestartEachPage() ? "newPage" : "continuous" );
6892     if( rLnNumInfo.GetPosFromLeft())
6893         pAttr->add( FSNS( XML_w, XML_distance ), OString::number(rLnNumInfo.GetPosFromLeft()).getStr());
6894     if (nRestartNo > 0)
6895         // Writer is 1-based, Word is 0-based.
6896         pAttr->add(FSNS(XML_w, XML_start), OString::number(nRestartNo - 1).getStr());
6897     m_pSerializer->singleElementNS( XML_w, XML_lnNumType, pAttr );
6898 }
6899 
SectionTitlePage()6900 void DocxAttributeOutput::SectionTitlePage()
6901 {
6902     m_pSerializer->singleElementNS(XML_w, XML_titlePg);
6903 }
6904 
SectionPageBorders(const SwFrameFormat * pFormat,const SwFrameFormat *)6905 void DocxAttributeOutput::SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* /*pFirstPageFormat*/ )
6906 {
6907     // Output the margins
6908 
6909     const SvxBoxItem& rBox = pFormat->GetBox( );
6910 
6911     const SvxBorderLine* pLeft = rBox.GetLeft( );
6912     const SvxBorderLine* pTop = rBox.GetTop( );
6913     const SvxBorderLine* pRight = rBox.GetRight( );
6914     const SvxBorderLine* pBottom = rBox.GetBottom( );
6915 
6916     if ( !(pBottom || pTop || pLeft || pRight) )
6917         return;
6918 
6919     OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions();
6920 
6921     // Check if there is a shadow item
6922     const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW );
6923     if ( pItem )
6924     {
6925         const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
6926         aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation();
6927     }
6928 
6929     // By top margin, impl_borders() means the distance between the top of the page and the header frame.
6930     editeng::WordPageMargins aMargins = m_pageMargins;
6931     HdFtDistanceGlue aGlue(pFormat->GetAttrSet());
6932     if (aGlue.HasHeader())
6933         aMargins.nTop = aGlue.dyaHdrTop;
6934     // Ditto for bottom margin.
6935     if (aGlue.HasFooter())
6936         aMargins.nBottom = aGlue.dyaHdrBottom;
6937 
6938     if (pFormat->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::GUTTER_AT_TOP))
6939     {
6940         aMargins.nTop += pFormat->GetLRSpace().GetGutterMargin();
6941     }
6942     else
6943     {
6944         aMargins.nLeft += pFormat->GetLRSpace().GetGutterMargin();
6945     }
6946 
6947     aOutputBorderOptions.pDistances = std::make_shared<editeng::WordBorderDistances>();
6948     editeng::BorderDistancesToWord(rBox, aMargins, *aOutputBorderOptions.pDistances);
6949 
6950     // All distances are relative to the text margins
6951     m_pSerializer->startElementNS(XML_w, XML_pgBorders,
6952         FSNS(XML_w, XML_display), "allPages",
6953         FSNS(XML_w, XML_offsetFrom), aOutputBorderOptions.pDistances->bFromEdge ? "page" : "text");
6954 
6955     std::map<SvxBoxItemLine, css::table::BorderLine2> aEmptyMap; // empty styles map
6956     impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aEmptyMap );
6957 
6958     m_pSerializer->endElementNS( XML_w, XML_pgBorders );
6959 
6960 }
6961 
SectionBiDi(bool bBiDi)6962 void DocxAttributeOutput::SectionBiDi( bool bBiDi )
6963 {
6964     if ( bBiDi )
6965         m_pSerializer->singleElementNS(XML_w, XML_bidi);
6966 }
6967 
6968 // Converting Numbering Format Code to string
lcl_ConvertNumberingType(sal_Int16 nNumberingType,const SfxItemSet * pOutSet,OString & rFormat,const OString & sDefault="")6969 static OString lcl_ConvertNumberingType(sal_Int16 nNumberingType, const SfxItemSet* pOutSet, OString& rFormat, const OString& sDefault = "" )
6970 {
6971     OString aType = sDefault;
6972 
6973     switch ( nNumberingType )
6974     {
6975         case SVX_NUM_CHARS_UPPER_LETTER:
6976         case SVX_NUM_CHARS_UPPER_LETTER_N:  aType = "upperLetter"; break;
6977 
6978         case SVX_NUM_CHARS_LOWER_LETTER:
6979         case SVX_NUM_CHARS_LOWER_LETTER_N:  aType = "lowerLetter"; break;
6980 
6981         case SVX_NUM_ROMAN_UPPER:           aType = "upperRoman";  break;
6982         case SVX_NUM_ROMAN_LOWER:           aType = "lowerRoman";  break;
6983         case SVX_NUM_ARABIC:                aType = "decimal";     break;
6984 
6985         case SVX_NUM_BITMAP:
6986         case SVX_NUM_CHAR_SPECIAL:          aType = "bullet";      break;
6987 
6988         case style::NumberingType::CHARS_HEBREW: aType = "hebrew2"; break;
6989         case style::NumberingType::NUMBER_HEBREW: aType = "hebrew1"; break;
6990         case style::NumberingType::NUMBER_NONE: aType = "none"; break;
6991         case style::NumberingType::FULLWIDTH_ARABIC: aType="decimalFullWidth"; break;
6992         case style::NumberingType::TIAN_GAN_ZH: aType="ideographTraditional"; break;
6993         case style::NumberingType::DI_ZI_ZH: aType="ideographZodiac"; break;
6994         case style::NumberingType::NUMBER_LOWER_ZH:
6995             aType="taiwaneseCountingThousand";
6996             if (pOutSet) {
6997                 const SvxLanguageItem& rLang = pOutSet->Get( RES_CHRATR_CJK_LANGUAGE);
6998                 const LanguageType eLang = rLang.GetLanguage();
6999 
7000                 if (LANGUAGE_CHINESE_SIMPLIFIED == eLang) {
7001                     aType="chineseCountingThousand";
7002                 }
7003             }
7004         break;
7005         case style::NumberingType::NUMBER_UPPER_ZH_TW: aType="ideographLegalTraditional";break;
7006         case style::NumberingType::NUMBER_UPPER_ZH: aType="chineseLegalSimplified"; break;
7007         case style::NumberingType::NUMBER_TRADITIONAL_JA: aType="japaneseLegal";break;
7008         case style::NumberingType::AIU_FULLWIDTH_JA: aType="aiueoFullWidth";break;
7009         case style::NumberingType::AIU_HALFWIDTH_JA: aType="aiueo";break;
7010         case style::NumberingType::IROHA_FULLWIDTH_JA: aType="iroha";break;
7011         case style::NumberingType::IROHA_HALFWIDTH_JA: aType="irohaFullWidth";break;
7012         case style::NumberingType::HANGUL_SYLLABLE_KO: aType="ganada";break;
7013         case style::NumberingType::HANGUL_JAMO_KO: aType="chosung";break;
7014         case style::NumberingType::NUMBER_HANGUL_KO: aType="koreanDigital";break;
7015         case style::NumberingType::NUMBER_UPPER_KO: aType="koreanLegal"; break;
7016         case style::NumberingType::CIRCLE_NUMBER: aType="decimalEnclosedCircle"; break;
7017         case style::NumberingType::CHARS_ARABIC: aType="arabicAlpha"; break;
7018         case style::NumberingType::CHARS_ARABIC_ABJAD: aType="arabicAbjad"; break;
7019         case style::NumberingType::CHARS_THAI: aType="thaiLetters"; break;
7020         case style::NumberingType::CHARS_PERSIAN: aType="hindiVowels"; break;
7021         case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU:
7022         case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU: aType = "russianUpper"; break;
7023         case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU:
7024         case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU: aType = "russianLower"; break;
7025         case style::NumberingType::TEXT_NUMBER: aType="ordinal"; break;
7026         case style::NumberingType::TEXT_CARDINAL: aType="cardinalText"; break;
7027         case style::NumberingType::TEXT_ORDINAL: aType="ordinalText"; break;
7028         case style::NumberingType::SYMBOL_CHICAGO: aType="chicago"; break;
7029         case style::NumberingType::ARABIC_ZERO: aType = "decimalZero"; break;
7030         case style::NumberingType::ARABIC_ZERO3:
7031             aType = "custom";
7032             rFormat = "001, 002, 003, ...";
7033             break;
7034         case style::NumberingType::ARABIC_ZERO4:
7035             aType = "custom";
7036             rFormat = "0001, 0002, 0003, ...";
7037             break;
7038         case style::NumberingType::ARABIC_ZERO5:
7039             aType = "custom";
7040             rFormat = "00001, 00002, 00003, ...";
7041             break;
7042 /*
7043         Fallback the rest to the suggested default.
7044         case style::NumberingType::NATIVE_NUMBERING:
7045         case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
7046         case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
7047         case style::NumberingType::CHARS_GREEK_UPPER_LETTER:
7048         case style::NumberingType::CHARS_GREEK_LOWER_LETTER:
7049         case style::NumberingType::PAGE_DESCRIPTOR:
7050         case style::NumberingType::TRANSLITERATION:
7051         case style::NumberingType::CHARS_NEPALI:
7052         case style::NumberingType::CHARS_KHMER:
7053         case style::NumberingType::CHARS_LAO:
7054         case style::NumberingType::CHARS_TIBETAN:
7055         case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_BG:
7056         case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_BG:
7057         case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_BG:
7058         case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_BG:
7059         case style::NumberingType::CHARS_MYANMAR:
7060         case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_SR:
7061         case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_SR:
7062         case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_SR:
7063         case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_SR:
7064 */
7065         default: break;
7066     }
7067     return aType;
7068 }
7069 
7070 
SectionPageNumbering(sal_uInt16 nNumType,const::std::optional<sal_uInt16> & oPageRestartNumber)7071 void DocxAttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::std::optional<sal_uInt16>& oPageRestartNumber )
7072 {
7073     // FIXME Not called properly with page styles like "First Page"
7074 
7075     rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
7076 
7077     // std::nullopt means no restart: then don't output that attribute if it is negative
7078     if ( oPageRestartNumber )
7079        pAttr->add( FSNS( XML_w, XML_start ), OString::number( *oPageRestartNumber ) );
7080 
7081     // nNumType corresponds to w:fmt. See WW8Export::GetNumId() for more precisions
7082     OString aCustomFormat;
7083     OString aFormat(lcl_ConvertNumberingType(nNumType, nullptr, aCustomFormat));
7084     if (!aFormat.isEmpty() && aCustomFormat.isEmpty())
7085         pAttr->add(FSNS(XML_w, XML_fmt), aFormat);
7086 
7087     m_pSerializer->singleElementNS( XML_w, XML_pgNumType, pAttr );
7088 
7089     // see 2.6.12 pgNumType (Page Numbering Settings)
7090     SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::SectionPageNumbering()" );
7091 }
7092 
SectionType(sal_uInt8 nBreakCode)7093 void DocxAttributeOutput::SectionType( sal_uInt8 nBreakCode )
7094 {
7095     /*  break code:   0 No break, 1 New column
7096         2 New page, 3 Even page, 4 Odd page
7097         */
7098     const char* pType;
7099     switch ( nBreakCode )
7100     {
7101         case 1:  pType = "nextColumn"; break;
7102         case 2:  pType = "nextPage";   break;
7103         case 3:  pType = "evenPage";   break;
7104         case 4:  pType = "oddPage";    break;
7105         default: pType = "continuous"; break;
7106     }
7107 
7108     m_pSerializer->singleElementNS(XML_w, XML_type, FSNS(XML_w, XML_val), pType);
7109 }
7110 
TextVerticalAdjustment(const drawing::TextVerticalAdjust nVA)7111 void DocxAttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA )
7112 {
7113     switch( nVA )
7114     {
7115         case drawing::TextVerticalAdjust_CENTER:
7116             m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "center");
7117             break;
7118         case drawing::TextVerticalAdjust_BOTTOM:
7119             m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "bottom");
7120             break;
7121         case drawing::TextVerticalAdjust_BLOCK:  //justify
7122             m_pSerializer->singleElementNS(XML_w, XML_vAlign, FSNS(XML_w, XML_val), "both");
7123             break;
7124         default:
7125             break;
7126     }
7127 }
7128 
StartFont(const OUString & rFamilyName) const7129 void DocxAttributeOutput::StartFont( const OUString& rFamilyName ) const
7130 {
7131     m_pSerializer->startElementNS(XML_w, XML_font, FSNS(XML_w, XML_name), rFamilyName);
7132 }
7133 
EndFont() const7134 void DocxAttributeOutput::EndFont() const
7135 {
7136     m_pSerializer->endElementNS( XML_w, XML_font );
7137 }
7138 
FontAlternateName(const OUString & rName) const7139 void DocxAttributeOutput::FontAlternateName( const OUString& rName ) const
7140 {
7141     m_pSerializer->singleElementNS(XML_w, XML_altName, FSNS(XML_w, XML_val), rName);
7142 }
7143 
FontCharset(sal_uInt8 nCharSet,rtl_TextEncoding nEncoding) const7144 void DocxAttributeOutput::FontCharset( sal_uInt8 nCharSet, rtl_TextEncoding nEncoding ) const
7145 {
7146     rtl::Reference<FastAttributeList> pAttr = FastSerializerHelper::createAttrList();
7147 
7148     OString aCharSet( OString::number( nCharSet, 16 ) );
7149     if ( aCharSet.getLength() == 1 )
7150         aCharSet = "0" + aCharSet;
7151     pAttr->add(FSNS(XML_w, XML_val), aCharSet);
7152 
7153     if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT )
7154     {
7155         if( const char* charset = rtl_getMimeCharsetFromTextEncoding( nEncoding ))
7156             pAttr->add( FSNS( XML_w, XML_characterSet ), charset );
7157     }
7158 
7159     m_pSerializer->singleElementNS( XML_w, XML_charset, pAttr );
7160 }
7161 
FontFamilyType(FontFamily eFamily) const7162 void DocxAttributeOutput::FontFamilyType( FontFamily eFamily ) const
7163 {
7164     const char* pFamily;
7165     switch ( eFamily )
7166     {
7167         case FAMILY_ROMAN:      pFamily = "roman"; break;
7168         case FAMILY_SWISS:      pFamily = "swiss"; break;
7169         case FAMILY_MODERN:     pFamily = "modern"; break;
7170         case FAMILY_SCRIPT:     pFamily = "script"; break;
7171         case FAMILY_DECORATIVE: pFamily = "decorative"; break;
7172         default:                pFamily = "auto"; break; // no font family
7173     }
7174 
7175     m_pSerializer->singleElementNS(XML_w, XML_family, FSNS(XML_w, XML_val), pFamily);
7176 }
7177 
FontPitchType(FontPitch ePitch) const7178 void DocxAttributeOutput::FontPitchType( FontPitch ePitch ) const
7179 {
7180     const char* pPitch;
7181     switch ( ePitch )
7182     {
7183         case PITCH_VARIABLE: pPitch = "variable"; break;
7184         case PITCH_FIXED:    pPitch = "fixed"; break;
7185         default:             pPitch = "default"; break; // no info about the pitch
7186     }
7187 
7188     m_pSerializer->singleElementNS(XML_w, XML_pitch, FSNS(XML_w, XML_val), pPitch);
7189 }
7190 
EmbedFont(std::u16string_view name,FontFamily family,FontPitch pitch)7191 void DocxAttributeOutput::EmbedFont( std::u16string_view name, FontFamily family, FontPitch pitch )
7192 {
7193     if( !m_rExport.m_rDoc.getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS ))
7194         return; // no font embedding with this document
7195     EmbedFontStyle( name, XML_embedRegular, family, ITALIC_NONE, WEIGHT_NORMAL, pitch );
7196     EmbedFontStyle( name, XML_embedBold, family, ITALIC_NONE, WEIGHT_BOLD, pitch );
7197     EmbedFontStyle( name, XML_embedItalic, family, ITALIC_NORMAL, WEIGHT_NORMAL, pitch );
7198     EmbedFontStyle( name, XML_embedBoldItalic, family, ITALIC_NORMAL, WEIGHT_BOLD, pitch );
7199 }
7200 
toHexChar(int value)7201 static char toHexChar( int value )
7202 {
7203     return value >= 10 ? value + 'A' - 10 : value + '0';
7204 }
7205 
EmbedFontStyle(std::u16string_view name,int tag,FontFamily family,FontItalic italic,FontWeight weight,FontPitch pitch)7206 void DocxAttributeOutput::EmbedFontStyle( std::u16string_view name, int tag, FontFamily family, FontItalic italic,
7207     FontWeight weight, FontPitch pitch )
7208 {
7209     // Embed font if at least viewing is allowed (in which case the opening app must check
7210     // the font license rights too and open either read-only or not use the font for editing).
7211     OUString fontUrl = EmbeddedFontsHelper::fontFileUrl( name, family, italic, weight, pitch,
7212         EmbeddedFontsHelper::FontRights::ViewingAllowed );
7213     if( fontUrl.isEmpty())
7214         return;
7215     // TODO IDocumentSettingAccess::EMBED_SYSTEM_FONTS
7216     if( !fontFilesMap.count( fontUrl ))
7217     {
7218         osl::File file( fontUrl );
7219         if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None )
7220             return;
7221         uno::Reference< css::io::XOutputStream > xOutStream = m_rExport.GetFilter().openFragmentStream(
7222             "word/fonts/font" + OUString::number(m_nextFontId) + ".odttf",
7223             "application/vnd.openxmlformats-officedocument.obfuscatedFont" );
7224         // Not much point in trying hard with the obfuscation key, whoever reads the spec can read the font anyway,
7225         // so just alter the first and last part of the key.
7226         char fontKeyStr[] = "{00014A78-CABC-4EF0-12AC-5CD89AEFDE00}";
7227         sal_uInt8 fontKey[ 16 ] = { 0, 0xDE, 0xEF, 0x9A, 0xD8, 0x5C, 0xAC, 0x12, 0xF0, 0x4E,
7228             0xBC, 0xCA, 0x78, 0x4A, 0x01, 0 };
7229         fontKey[ 0 ] = fontKey[ 15 ] = m_nextFontId % 256;
7230         fontKeyStr[ 1 ] = fontKeyStr[ 35 ] = toHexChar(( m_nextFontId % 256 ) / 16 );
7231         fontKeyStr[ 2 ] = fontKeyStr[ 36 ] = toHexChar(( m_nextFontId % 256 ) % 16 );
7232         unsigned char buffer[ 4096 ];
7233         sal_uInt64 readSize;
7234         file.read( buffer, 32, readSize );
7235         if( readSize < 32 )
7236         {
7237             SAL_WARN( "sw.ww8", "Font file size too small (" << fontUrl << ")" );
7238             xOutStream->closeOutput();
7239             return;
7240         }
7241         for( int i = 0;
7242              i < 16;
7243              ++i )
7244         {
7245             buffer[ i ] ^= fontKey[ i ];
7246             buffer[ i + 16 ] ^= fontKey[ i ];
7247         }
7248         xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), 32 ));
7249         for(;;)
7250         {
7251             sal_Bool eof;
7252             if( file.isEndOfFile( &eof ) != osl::File::E_None )
7253             {
7254                 SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl );
7255                 xOutStream->closeOutput();
7256                 return;
7257             }
7258             if( eof )
7259                 break;
7260             if( file.read( buffer, 4096, readSize ) != osl::File::E_None )
7261             {
7262                 SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl );
7263                 xOutStream->closeOutput();
7264                 return;
7265             }
7266             if( readSize == 0 )
7267                 break;
7268             // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence
7269             xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), readSize ));
7270         }
7271         xOutStream->closeOutput();
7272         OString relId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
7273             oox::getRelationship(Relationship::FONT),
7274             OUString("fonts/font" + OUString::number( m_nextFontId ) + ".odttf") ), RTL_TEXTENCODING_UTF8 );
7275         EmbeddedFontRef ref;
7276         ref.relId = relId;
7277         ref.fontKey = fontKeyStr;
7278         fontFilesMap[ fontUrl ] = ref;
7279         ++m_nextFontId;
7280     }
7281     m_pSerializer->singleElementNS( XML_w, tag,
7282         FSNS( XML_r, XML_id ), fontFilesMap[ fontUrl ].relId,
7283         FSNS( XML_w, XML_fontKey ), fontFilesMap[ fontUrl ].fontKey );
7284 }
7285 
TransHighlightColor(sal_uInt8 nIco)7286 OString DocxAttributeOutput::TransHighlightColor( sal_uInt8 nIco )
7287 {
7288     switch (nIco)
7289     {
7290         case 0: return "none"; break;
7291         case 1: return "black"; break;
7292         case 2: return "blue"; break;
7293         case 3: return "cyan"; break;
7294         case 4: return "green"; break;
7295         case 5: return "magenta"; break;
7296         case 6: return "red"; break;
7297         case 7: return "yellow"; break;
7298         case 8: return "white"; break;
7299         case 9: return "darkBlue"; break;
7300         case 10: return "darkCyan"; break;
7301         case 11: return "darkGreen"; break;
7302         case 12: return "darkMagenta"; break;
7303         case 13: return "darkRed"; break;
7304         case 14: return "darkYellow"; break;
7305         case 15: return "darkGray"; break;
7306         case 16: return "lightGray"; break;
7307         default: return OString(); break;
7308     }
7309 }
7310 
NumberingDefinition(sal_uInt16 nId,const SwNumRule & rRule)7311 void DocxAttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule )
7312 {
7313     // nId is the same both for abstract numbering definition as well as the
7314     // numbering definition itself
7315     // TODO check that this is actually true & fix if not ;-)
7316     OString aId( OString::number( nId ) );
7317 
7318     m_pSerializer->startElementNS(XML_w, XML_num, FSNS(XML_w, XML_numId), aId);
7319 
7320     m_pSerializer->singleElementNS(XML_w, XML_abstractNumId, FSNS(XML_w, XML_val), aId);
7321 
7322 #if OSL_DEBUG_LEVEL > 1
7323     // TODO ww8 version writes this, anything to do about it here?
7324     if ( rRule.IsContinusNum() )
7325         SAL_INFO("sw", "TODO DocxAttributeOutput::NumberingDefinition()" );
7326 #else
7327     (void) rRule; // to quiet the warning...
7328 #endif
7329 
7330     m_pSerializer->endElementNS( XML_w, XML_num );
7331 }
7332 
7333 // Not all attributes of SwNumFormat are important for export, so can't just use embedded in
7334 // that classes comparison.
lcl_ListLevelsAreDifferentForExport(const SwNumFormat & rFormat1,const SwNumFormat & rFormat2)7335 static bool lcl_ListLevelsAreDifferentForExport(const SwNumFormat & rFormat1, const SwNumFormat & rFormat2)
7336 {
7337     if (rFormat1 == rFormat2)
7338         // They are equal, nothing to do
7339         return false;
7340 
7341     if (!rFormat1.GetCharFormat() != !rFormat2.GetCharFormat())
7342         // One has charformat, other not. they are different
7343         return true;
7344 
7345     if (rFormat1.GetCharFormat() && rFormat2.GetCharFormat())
7346     {
7347         const SwAttrSet & a1 = rFormat1.GetCharFormat()->GetAttrSet();
7348         const SwAttrSet & a2 = rFormat2.GetCharFormat()->GetAttrSet();
7349 
7350         if (!(a1 == a2))
7351             // Difference in charformat: they are different
7352             return true;
7353     }
7354 
7355     // Compare numformats with empty charformats
7356     SwNumFormat modified1 = rFormat1;
7357     SwNumFormat modified2 = rFormat2;
7358     modified1.SetCharFormatName(OUString());
7359     modified2.SetCharFormatName(OUString());
7360     modified1.SetCharFormat(nullptr);
7361     modified2.SetCharFormat(nullptr);
7362     return modified1 != modified2;
7363 }
7364 
OverrideNumberingDefinition(SwNumRule const & rRule,sal_uInt16 const nNum,sal_uInt16 const nAbstractNum,const std::map<size_t,size_t> & rLevelOverrides)7365 void DocxAttributeOutput::OverrideNumberingDefinition(
7366         SwNumRule const& rRule,
7367         sal_uInt16 const nNum, sal_uInt16 const nAbstractNum, const std::map< size_t, size_t > & rLevelOverrides )
7368 {
7369     m_pSerializer->startElementNS(XML_w, XML_num, FSNS(XML_w, XML_numId), OString::number(nNum));
7370 
7371     m_pSerializer->singleElementNS(XML_w, XML_abstractNumId, FSNS(XML_w, XML_val), OString::number(nAbstractNum));
7372 
7373     SwNumRule const& rAbstractRule = *(*m_rExport.m_pUsedNumTable)[nAbstractNum - 1];
7374     sal_uInt8 const nLevels = static_cast<sal_uInt8>(rRule.IsContinusNum()
7375         ? WW8ListManager::nMinLevel : WW8ListManager::nMaxLevel);
7376     for (sal_uInt8 nLevel = 0; nLevel < nLevels; ++nLevel)
7377     {
7378         const auto levelOverride = rLevelOverrides.find(nLevel);
7379         bool bListsAreDifferent = lcl_ListLevelsAreDifferentForExport(rRule.Get(nLevel), rAbstractRule.Get(nLevel));
7380 
7381         // Export list override only if it is different to abstract one
7382         // or we have a level numbering override
7383         if (bListsAreDifferent || levelOverride != rLevelOverrides.end())
7384         {
7385             m_pSerializer->startElementNS(XML_w, XML_lvlOverride, FSNS(XML_w, XML_ilvl), OString::number(nLevel));
7386 
7387             if (bListsAreDifferent)
7388             {
7389                 GetExport().NumberingLevel(rRule, nLevel);
7390             }
7391             if (levelOverride != rLevelOverrides.end())
7392             {
7393                 // list numbering restart override
7394                 m_pSerializer->singleElementNS(XML_w, XML_startOverride,
7395                     FSNS(XML_w, XML_val), OString::number(levelOverride->second));
7396             }
7397 
7398             m_pSerializer->endElementNS(XML_w, XML_lvlOverride);
7399         }
7400     }
7401 
7402     m_pSerializer->endElementNS( XML_w, XML_num );
7403 }
7404 
StartAbstractNumbering(sal_uInt16 nId)7405 void DocxAttributeOutput::StartAbstractNumbering( sal_uInt16 nId )
7406 {
7407     const SwNumRule* pRule = (*m_rExport.m_pUsedNumTable)[nId - 1];
7408     m_bExportingOutline = pRule && pRule->IsOutlineRule();
7409     m_pSerializer->startElementNS( XML_w, XML_abstractNum,
7410             FSNS( XML_w, XML_abstractNumId ), OString::number(nId) );
7411 }
7412 
EndAbstractNumbering()7413 void DocxAttributeOutput::EndAbstractNumbering()
7414 {
7415     m_pSerializer->endElementNS( XML_w, XML_abstractNum );
7416 }
7417 
NumberingLevel(sal_uInt8 nLevel,sal_uInt16 nStart,sal_uInt16 nNumberingType,SvxAdjust eAdjust,const sal_uInt8 *,sal_uInt8 nFollow,const wwFont * pFont,const SfxItemSet * pOutSet,sal_Int16 nIndentAt,sal_Int16 nFirstLineIndex,sal_Int16 nListTabPos,const OUString & rNumberingString,const SvxBrushItem * pBrush)7418 void DocxAttributeOutput::NumberingLevel( sal_uInt8 nLevel,
7419         sal_uInt16 nStart,
7420         sal_uInt16 nNumberingType,
7421         SvxAdjust eAdjust,
7422         const sal_uInt8 * /*pNumLvlPos*/,
7423         sal_uInt8 nFollow,
7424         const wwFont *pFont,
7425         const SfxItemSet *pOutSet,
7426         sal_Int16 nIndentAt,
7427         sal_Int16 nFirstLineIndex,
7428         sal_Int16 nListTabPos,
7429         const OUString &rNumberingString,
7430         const SvxBrushItem* pBrush)
7431 {
7432     m_pSerializer->startElementNS(XML_w, XML_lvl, FSNS(XML_w, XML_ilvl), OString::number(nLevel));
7433 
7434     // start with the nStart value. Do not write w:start if Numbered Lists
7435     // starts from zero.As it's an optional parameter.
7436     // refer ECMA 376 Second edition Part-1
7437     if(0 != nLevel || 0 != nStart)
7438     {
7439         m_pSerializer->singleElementNS( XML_w, XML_start,
7440                 FSNS( XML_w, XML_val ), OString::number(nStart) );
7441     }
7442 
7443     if (m_bExportingOutline)
7444     {
7445         sal_uInt16 nId = m_rExport.m_pStyles->GetHeadingParagraphStyleId( nLevel );
7446         if ( nId != SAL_MAX_UINT16 )
7447             m_pSerializer->singleElementNS( XML_w, XML_pStyle ,
7448                 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nId) );
7449     }
7450     // format
7451     OString aCustomFormat;
7452     OString aFormat(lcl_ConvertNumberingType(nNumberingType, pOutSet, aCustomFormat, "decimal"));
7453 
7454     {
7455         if (aCustomFormat.isEmpty())
7456         {
7457             m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), aFormat);
7458         }
7459         else
7460         {
7461             m_pSerializer->startElementNS(XML_mc, XML_AlternateContent);
7462             m_pSerializer->startElementNS(XML_mc, XML_Choice, XML_Requires, "w14");
7463 
7464             m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), aFormat,
7465                                            FSNS(XML_w, XML_format), aCustomFormat);
7466 
7467             m_pSerializer->endElementNS(XML_mc, XML_Choice);
7468             m_pSerializer->startElementNS(XML_mc, XML_Fallback);
7469             m_pSerializer->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), "decimal");
7470             m_pSerializer->endElementNS(XML_mc, XML_Fallback);
7471             m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);
7472         }
7473     }
7474 
7475     // suffix
7476     const char *pSuffix = nullptr;
7477     switch ( nFollow )
7478     {
7479         case 1:  pSuffix = "space";   break;
7480         case 2:  pSuffix = "nothing"; break;
7481         default: /*pSuffix = "tab";*/ break;
7482     }
7483     if ( pSuffix )
7484         m_pSerializer->singleElementNS(XML_w, XML_suff, FSNS(XML_w, XML_val), pSuffix);
7485 
7486     // text
7487     OUStringBuffer aBuffer( rNumberingString.getLength() + WW8ListManager::nMaxLevel );
7488 
7489     const sal_Unicode *pPrev = rNumberingString.getStr();
7490     const sal_Unicode *pIt = rNumberingString.getStr();
7491     while ( pIt < rNumberingString.getStr() + rNumberingString.getLength() )
7492     {
7493         // convert the level values to %NUMBER form
7494         // (we don't use pNumLvlPos at all)
7495         // FIXME so far we support the ww8 limit of levels only
7496         if ( *pIt < sal_Unicode( WW8ListManager::nMaxLevel ) )
7497         {
7498             aBuffer.append( pPrev, pIt - pPrev );
7499             aBuffer.append( '%' );
7500             aBuffer.append( sal_Int32( *pIt ) + 1 );
7501 
7502             pPrev = pIt + 1;
7503         }
7504         ++pIt;
7505     }
7506     if ( pPrev < pIt )
7507         aBuffer.append( pPrev, pIt - pPrev );
7508 
7509     // If bullet char is empty, set lvlText as empty
7510     if ( rNumberingString == OUStringChar('\0') && nNumberingType == SVX_NUM_CHAR_SPECIAL )
7511     {
7512         m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), "");
7513     }
7514     else
7515     {
7516         // Writer's "zero width space" suffix is necessary, so that LabelFollowedBy shows up, but Word doesn't require that.
7517         OUString aLevelText = aBuffer.makeStringAndClear();
7518         static OUString aZeroWidthSpace(u'\x200B');
7519         if (aLevelText == aZeroWidthSpace)
7520             aLevelText.clear();
7521         m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), aLevelText);
7522     }
7523 
7524     // bullet
7525     if (nNumberingType == SVX_NUM_BITMAP && pBrush)
7526     {
7527         int nIndex = m_rExport.GetGrfIndex(*pBrush);
7528         if (nIndex != -1)
7529         {
7530             m_pSerializer->singleElementNS(XML_w, XML_lvlPicBulletId,
7531                     FSNS(XML_w, XML_val), OString::number(nIndex));
7532         }
7533     }
7534 
7535     // justification
7536     const char *pJc;
7537     bool ecmaDialect = ( m_rExport.GetFilter().getVersion() == oox::core::ECMA_DIALECT );
7538     switch ( eAdjust )
7539     {
7540         case SvxAdjust::Center: pJc = "center"; break;
7541         case SvxAdjust::Right:  pJc = !ecmaDialect ? "end" : "right";  break;
7542         default:                pJc = !ecmaDialect ? "start" : "left";   break;
7543     }
7544     m_pSerializer->singleElementNS(XML_w, XML_lvlJc, FSNS(XML_w, XML_val), pJc);
7545 
7546     // indentation
7547     m_pSerializer->startElementNS(XML_w, XML_pPr);
7548     if( nListTabPos >= 0 )
7549     {
7550         m_pSerializer->startElementNS(XML_w, XML_tabs);
7551         m_pSerializer->singleElementNS( XML_w, XML_tab,
7552                 FSNS( XML_w, XML_val ), "num",
7553                 FSNS( XML_w, XML_pos ), OString::number(nListTabPos) );
7554         m_pSerializer->endElementNS( XML_w, XML_tabs );
7555     }
7556 
7557     sal_Int32 nToken = ecmaDialect ? XML_left : XML_start;
7558     sal_Int32 nIndentToken = nFirstLineIndex > 0 ? XML_firstLine : XML_hanging;
7559     m_pSerializer->singleElementNS( XML_w, XML_ind,
7560             FSNS( XML_w, nToken ), OString::number(nIndentAt),
7561             FSNS( XML_w, nIndentToken ), OString::number(abs(nFirstLineIndex)) );
7562     m_pSerializer->endElementNS( XML_w, XML_pPr );
7563 
7564     // font
7565     if ( pOutSet )
7566     {
7567         m_pSerializer->startElementNS(XML_w, XML_rPr);
7568 
7569         SfxItemSet aTempSet(*pOutSet);
7570         if ( pFont )
7571         {
7572             GetExport().GetId( *pFont ); // ensure font info is written to fontTable.xml
7573             OString aFamilyName( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
7574             m_pSerializer->singleElementNS( XML_w, XML_rFonts,
7575                     FSNS( XML_w, XML_ascii ), aFamilyName,
7576                     FSNS( XML_w, XML_hAnsi ), aFamilyName,
7577                     FSNS( XML_w, XML_cs ), aFamilyName,
7578                     FSNS( XML_w, XML_hint ), "default" );
7579             aTempSet.ClearItem(RES_CHRATR_FONT);
7580             aTempSet.ClearItem(RES_CHRATR_CTL_FONT);
7581         }
7582         m_rExport.OutputItemSet(aTempSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF);
7583 
7584         WriteCollectedRunProperties();
7585 
7586         m_pSerializer->endElementNS( XML_w, XML_rPr );
7587     }
7588 
7589     // TODO anything to do about nListTabPos?
7590 
7591     m_pSerializer->endElementNS( XML_w, XML_lvl );
7592 }
7593 
CharCaseMap(const SvxCaseMapItem & rCaseMap)7594 void DocxAttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap )
7595 {
7596     switch ( rCaseMap.GetValue() )
7597     {
7598         case SvxCaseMap::SmallCaps:
7599             m_pSerializer->singleElementNS(XML_w, XML_smallCaps);
7600             break;
7601         case SvxCaseMap::Uppercase:
7602             m_pSerializer->singleElementNS(XML_w, XML_caps);
7603             break;
7604         default: // Something that ooxml does not support
7605             m_pSerializer->singleElementNS(XML_w, XML_smallCaps, FSNS(XML_w, XML_val), "false");
7606             m_pSerializer->singleElementNS(XML_w, XML_caps, FSNS(XML_w, XML_val), "false");
7607             break;
7608     }
7609 }
7610 
CharColor(const SvxColorItem & rColor)7611 void DocxAttributeOutput::CharColor( const SvxColorItem& rColor )
7612 {
7613     const Color aColor( rColor.GetValue() );
7614     OString aColorString = msfilter::util::ConvertColor( aColor );
7615 
7616     const char* pExistingValue(nullptr);
7617     if (m_pColorAttrList.is() && m_pColorAttrList->getAsChar(FSNS(XML_w, XML_val), pExistingValue))
7618     {
7619         assert(aColorString.equalsL(pExistingValue, rtl_str_getLength(pExistingValue)));
7620         return;
7621     }
7622 
7623     AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_val ), aColorString.getStr() );
7624     m_nCharTransparence = 255 - aColor.GetAlpha();
7625 }
7626 
CharContour(const SvxContourItem & rContour)7627 void DocxAttributeOutput::CharContour( const SvxContourItem& rContour )
7628 {
7629     if ( rContour.GetValue() )
7630         m_pSerializer->singleElementNS(XML_w, XML_outline);
7631     else
7632         m_pSerializer->singleElementNS(XML_w, XML_outline, FSNS(XML_w, XML_val), "false");
7633 }
7634 
CharCrossedOut(const SvxCrossedOutItem & rCrossedOut)7635 void DocxAttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossedOut )
7636 {
7637     switch ( rCrossedOut.GetStrikeout() )
7638     {
7639         case STRIKEOUT_DOUBLE:
7640             m_pSerializer->singleElementNS(XML_w, XML_dstrike);
7641             break;
7642         case STRIKEOUT_NONE:
7643             m_pSerializer->singleElementNS(XML_w, XML_dstrike, FSNS(XML_w, XML_val), "false");
7644             m_pSerializer->singleElementNS(XML_w, XML_strike, FSNS(XML_w, XML_val), "false");
7645             break;
7646         default:
7647             m_pSerializer->singleElementNS(XML_w, XML_strike);
7648             break;
7649     }
7650 }
7651 
CharEscapement(const SvxEscapementItem & rEscapement)7652 void DocxAttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement )
7653 {
7654     OString sIss;
7655     short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight();
7656 
7657     // Simplify styles to avoid impossible complexity. Import and export as defaults only
7658     if ( m_rExport.m_bStyDef && nEsc )
7659     {
7660         nProp = DFLT_ESC_PROP;
7661         nEsc = (nEsc > 0) ? DFLT_ESC_AUTO_SUPER : DFLT_ESC_AUTO_SUB;
7662     }
7663 
7664     if ( !nEsc )
7665     {
7666         sIss = OString( "baseline" );
7667         nEsc = 0;
7668         nProp = 100;
7669     }
7670     else if ( DFLT_ESC_PROP == nProp || nProp < 1 || nProp > 100 )
7671     {
7672         if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
7673             sIss = OString( "subscript" );
7674         else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
7675             sIss = OString( "superscript" );
7676     }
7677     else if ( DFLT_ESC_AUTO_SUPER == nEsc )
7678     {
7679         // Raised by the differences between the ascenders (ascent = baseline to top of highest letter).
7680         // The ascent is generally about 80% of the total font height.
7681         // That is why DFLT_ESC_PROP (58) leads to 33% (DFLT_ESC_SUPER)
7682         nEsc = .8 * (100 - nProp);
7683     }
7684     else if ( DFLT_ESC_AUTO_SUB == nEsc )
7685     {
7686         // Lowered by the differences between the descenders (descent = baseline to bottom of lowest letter).
7687         // The descent is generally about 20% of the total font height.
7688         // That is why DFLT_ESC_PROP (58) leads to 8% (DFLT_ESC_SUB)
7689         nEsc = .2 * -(100 - nProp);
7690     }
7691 
7692     if ( !sIss.isEmpty() )
7693         m_pSerializer->singleElementNS(XML_w, XML_vertAlign, FSNS(XML_w, XML_val), sIss);
7694 
7695     if (!(sIss.isEmpty() || sIss.match("baseline")))
7696         return;
7697 
7698     const SvxFontHeightItem& rItem = m_rExport.GetItem(RES_CHRATR_FONTSIZE);
7699     float fHeight = rItem.GetHeight();
7700     OString sPos = OString::number( round(( fHeight * nEsc ) / 1000) );
7701     m_pSerializer->singleElementNS(XML_w, XML_position, FSNS(XML_w, XML_val), sPos);
7702 
7703     if( ( 100 != nProp || sIss.match( "baseline" ) ) && !m_rExport.m_bFontSizeWritten )
7704     {
7705         OString sSize = OString::number( round(( fHeight * nProp ) / 1000) );
7706         m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), sSize);
7707     }
7708 }
7709 
CharFont(const SvxFontItem & rFont)7710 void DocxAttributeOutput::CharFont( const SvxFontItem& rFont)
7711 {
7712     GetExport().GetId( rFont ); // ensure font info is written to fontTable.xml
7713     const OUString& sFontName(rFont.GetFamilyName());
7714     const OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
7715     if (sFontNameUtf8.isEmpty())
7716         return;
7717 
7718     if (m_pFontsAttrList &&
7719         (   m_pFontsAttrList->hasAttribute(FSNS( XML_w, XML_ascii )) ||
7720             m_pFontsAttrList->hasAttribute(FSNS( XML_w, XML_hAnsi ))    )
7721         )
7722     {
7723         // tdf#38778: do to fields output into DOC the font could be added before and after field declaration
7724         // that all sub runs of the field will have correct font inside.
7725         // For DOCX we should do not add the same font information twice in the same node
7726         return;
7727     }
7728 
7729     AddToAttrList( m_pFontsAttrList, 2,
7730         FSNS( XML_w, XML_ascii ), sFontNameUtf8.getStr(),
7731         FSNS( XML_w, XML_hAnsi ), sFontNameUtf8.getStr() );
7732 }
7733 
CharFontSize(const SvxFontHeightItem & rFontSize)7734 void DocxAttributeOutput::CharFontSize( const SvxFontHeightItem& rFontSize)
7735 {
7736     OString fontSize = OString::number( ( rFontSize.GetHeight() + 5 ) / 10 );
7737 
7738     switch ( rFontSize.Which() )
7739     {
7740         case RES_CHRATR_FONTSIZE:
7741         case RES_CHRATR_CJK_FONTSIZE:
7742             m_pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val), fontSize);
7743             break;
7744         case RES_CHRATR_CTL_FONTSIZE:
7745             m_pSerializer->singleElementNS(XML_w, XML_szCs, FSNS(XML_w, XML_val), fontSize);
7746             break;
7747     }
7748 }
7749 
CharKerning(const SvxKerningItem & rKerning)7750 void DocxAttributeOutput::CharKerning( const SvxKerningItem& rKerning )
7751 {
7752     OString aKerning = OString::number(  rKerning.GetValue() );
7753     m_pSerializer->singleElementNS(XML_w, XML_spacing, FSNS(XML_w, XML_val), aKerning);
7754 }
7755 
CharLanguage(const SvxLanguageItem & rLanguage)7756 void DocxAttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage )
7757 {
7758     OString aLanguageCode( OUStringToOString(
7759                 LanguageTag( rLanguage.GetLanguage()).getBcp47MS(),
7760                 RTL_TEXTENCODING_UTF8));
7761 
7762     switch ( rLanguage.Which() )
7763     {
7764         case RES_CHRATR_LANGUAGE:
7765             AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_val ), aLanguageCode.getStr() );
7766             break;
7767         case RES_CHRATR_CJK_LANGUAGE:
7768             AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_eastAsia ), aLanguageCode.getStr() );
7769             break;
7770         case RES_CHRATR_CTL_LANGUAGE:
7771             AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_bidi ), aLanguageCode.getStr() );
7772             break;
7773     }
7774 }
7775 
CharPosture(const SvxPostureItem & rPosture)7776 void DocxAttributeOutput::CharPosture( const SvxPostureItem& rPosture )
7777 {
7778     if ( rPosture.GetPosture() != ITALIC_NONE )
7779         m_pSerializer->singleElementNS(XML_w, XML_i);
7780     else
7781         m_pSerializer->singleElementNS(XML_w, XML_i, FSNS(XML_w, XML_val), "false");
7782 }
7783 
CharShadow(const SvxShadowedItem & rShadow)7784 void DocxAttributeOutput::CharShadow( const SvxShadowedItem& rShadow )
7785 {
7786     if ( rShadow.GetValue() )
7787         m_pSerializer->singleElementNS(XML_w, XML_shadow);
7788     else
7789         m_pSerializer->singleElementNS(XML_w, XML_shadow, FSNS(XML_w, XML_val), "false");
7790 }
7791 
CharUnderline(const SvxUnderlineItem & rUnderline)7792 void DocxAttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline )
7793 {
7794     const char *pUnderlineValue;
7795 
7796     switch ( rUnderline.GetLineStyle() )
7797     {
7798         case LINESTYLE_SINGLE:         pUnderlineValue = "single";          break;
7799         case LINESTYLE_BOLD:           pUnderlineValue = "thick";           break;
7800         case LINESTYLE_DOUBLE:         pUnderlineValue = "double";          break;
7801         case LINESTYLE_DOTTED:         pUnderlineValue = "dotted";          break;
7802         case LINESTYLE_DASH:           pUnderlineValue = "dash";            break;
7803         case LINESTYLE_DASHDOT:        pUnderlineValue = "dotDash";         break;
7804         case LINESTYLE_DASHDOTDOT:     pUnderlineValue = "dotDotDash";      break;
7805         case LINESTYLE_WAVE:           pUnderlineValue = "wave";            break;
7806         case LINESTYLE_BOLDDOTTED:     pUnderlineValue = "dottedHeavy";     break;
7807         case LINESTYLE_BOLDDASH:       pUnderlineValue = "dashedHeavy";     break;
7808         case LINESTYLE_LONGDASH:       pUnderlineValue = "dashLongHeavy";   break;
7809         case LINESTYLE_BOLDLONGDASH:   pUnderlineValue = "dashLongHeavy";   break;
7810         case LINESTYLE_BOLDDASHDOT:    pUnderlineValue = "dashDotHeavy";    break;
7811         case LINESTYLE_BOLDDASHDOTDOT: pUnderlineValue = "dashDotDotHeavy"; break;
7812         case LINESTYLE_BOLDWAVE:       pUnderlineValue = "wavyHeavy";       break;
7813         case LINESTYLE_DOUBLEWAVE:     pUnderlineValue = "wavyDouble";      break;
7814         case LINESTYLE_NONE:           // fall through
7815         default:                       pUnderlineValue = "none";            break;
7816     }
7817 
7818     Color aUnderlineColor = rUnderline.GetColor();
7819     bool  bUnderlineHasColor = !aUnderlineColor.IsTransparent();
7820     if (bUnderlineHasColor)
7821     {
7822         // Underline has a color
7823         m_pSerializer->singleElementNS( XML_w, XML_u,
7824                                         FSNS( XML_w, XML_val ), pUnderlineValue,
7825                                         FSNS( XML_w, XML_color ), msfilter::util::ConvertColor(aUnderlineColor) );
7826     }
7827     else
7828     {
7829         // Underline has no color
7830         m_pSerializer->singleElementNS(XML_w, XML_u, FSNS(XML_w, XML_val), pUnderlineValue);
7831     }
7832 }
7833 
CharWeight(const SvxWeightItem & rWeight)7834 void DocxAttributeOutput::CharWeight( const SvxWeightItem& rWeight )
7835 {
7836     if ( rWeight.GetWeight() == WEIGHT_BOLD )
7837         m_pSerializer->singleElementNS(XML_w, XML_b);
7838     else
7839         m_pSerializer->singleElementNS(XML_w, XML_b, FSNS(XML_w, XML_val), "false");
7840 }
7841 
CharAutoKern(const SvxAutoKernItem & rAutoKern)7842 void DocxAttributeOutput::CharAutoKern( const SvxAutoKernItem& rAutoKern )
7843 {
7844     // auto kerning is bound to a minimum font size in Word - but is just a boolean in Writer :-(
7845     // kerning is based on half-point sizes, so 2 enables kerning for fontsize 1pt or higher. (1 is treated as size 12, and 0 is treated as disabled.)
7846     const OString sFontSize = OString::number( static_cast<sal_uInt32>(rAutoKern.GetValue()) * 2 );
7847     m_pSerializer->singleElementNS(XML_w, XML_kern, FSNS(XML_w, XML_val), sFontSize);
7848 }
7849 
CharAnimatedText(const SvxBlinkItem & rBlink)7850 void DocxAttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
7851 {
7852     if ( rBlink.GetValue() )
7853         m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS(XML_w, XML_val), "blinkBackground");
7854     else
7855         m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS(XML_w, XML_val), "none");
7856 }
7857 
7858 #define MSWORD_CH_SHADING_FILL "FFFFFF" // The attribute w:fill of w:shd, for MS-Word's character shading,
7859 #define MSWORD_CH_SHADING_COLOR "auto" // The attribute w:color of w:shd, for MS-Word's character shading,
7860 #define MSWORD_CH_SHADING_VAL "pct15" // The attribute w:value of w:shd, for MS-Word's character shading,
7861 
CharBackground(const SvxBrushItem & rBrush)7862 void DocxAttributeOutput::CharBackground( const SvxBrushItem& rBrush )
7863 {
7864     // Check if the brush shading pattern is 'PCT15'. If so - write it back to the DOCX
7865     if (rBrush.GetShadingValue() == ShadingPattern::PCT15)
7866     {
7867         m_pSerializer->singleElementNS( XML_w, XML_shd,
7868             FSNS( XML_w, XML_val ), MSWORD_CH_SHADING_VAL,
7869             FSNS( XML_w, XML_color ), MSWORD_CH_SHADING_COLOR,
7870             FSNS( XML_w, XML_fill ), MSWORD_CH_SHADING_FILL );
7871     }
7872     else
7873     {
7874         m_pSerializer->singleElementNS( XML_w, XML_shd,
7875             FSNS( XML_w, XML_fill ), msfilter::util::ConvertColor(rBrush.GetColor()),
7876             FSNS( XML_w, XML_val ), "clear" );
7877     }
7878 }
7879 
CharFontCJK(const SvxFontItem & rFont)7880 void DocxAttributeOutput::CharFontCJK( const SvxFontItem& rFont )
7881 {
7882     if (m_pFontsAttrList && m_pFontsAttrList->hasAttribute(FSNS(XML_w, XML_eastAsia)))
7883     {
7884         // tdf#38778: do to fields output into DOC the font could be added before and after field declaration
7885         // that all sub runs of the field will have correct font inside.
7886         // For DOCX we should do not add the same font information twice in the same node
7887         return;
7888     }
7889 
7890     const OUString& sFontName(rFont.GetFamilyName());
7891     OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
7892     AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsia ), sFontNameUtf8.getStr() );
7893 }
7894 
CharPostureCJK(const SvxPostureItem & rPosture)7895 void DocxAttributeOutput::CharPostureCJK( const SvxPostureItem& rPosture )
7896 {
7897     if ( rPosture.GetPosture() != ITALIC_NONE )
7898         m_pSerializer->singleElementNS(XML_w, XML_i);
7899     else
7900         m_pSerializer->singleElementNS(XML_w, XML_i, FSNS(XML_w, XML_val), "false");
7901 }
7902 
CharWeightCJK(const SvxWeightItem & rWeight)7903 void DocxAttributeOutput::CharWeightCJK( const SvxWeightItem& rWeight )
7904 {
7905     if ( rWeight.GetWeight() == WEIGHT_BOLD )
7906         m_pSerializer->singleElementNS(XML_w, XML_b);
7907     else
7908         m_pSerializer->singleElementNS(XML_w, XML_b, FSNS(XML_w, XML_val), "false");
7909 }
7910 
CharFontCTL(const SvxFontItem & rFont)7911 void DocxAttributeOutput::CharFontCTL( const SvxFontItem& rFont )
7912 {
7913     if (m_pFontsAttrList && m_pFontsAttrList->hasAttribute(FSNS(XML_w, XML_cs)))
7914     {
7915         // tdf#38778: do to fields output into DOC the font could be added before and after field declaration
7916         // that all sub runs of the field will have correct font inside.
7917         // For DOCX we should do not add the same font information twice in the same node
7918         return;
7919     }
7920 
7921     const OUString& sFontName(rFont.GetFamilyName());
7922     OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
7923     AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cs ), sFontNameUtf8.getStr() );
7924 }
7925 
CharPostureCTL(const SvxPostureItem & rPosture)7926 void DocxAttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture)
7927 {
7928     if ( rPosture.GetPosture() != ITALIC_NONE )
7929         m_pSerializer->singleElementNS(XML_w, XML_iCs);
7930     else
7931         m_pSerializer->singleElementNS(XML_w, XML_iCs, FSNS(XML_w, XML_val), "false");
7932 }
7933 
CharWeightCTL(const SvxWeightItem & rWeight)7934 void DocxAttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight )
7935 {
7936     if ( rWeight.GetWeight() == WEIGHT_BOLD )
7937         m_pSerializer->singleElementNS(XML_w, XML_bCs);
7938     else
7939         m_pSerializer->singleElementNS(XML_w, XML_bCs, FSNS(XML_w, XML_val), "false");
7940 }
7941 
CharBidiRTL(const SfxPoolItem &)7942 void DocxAttributeOutput::CharBidiRTL( const SfxPoolItem& )
7943 {
7944 }
7945 
CharIdctHint(const SfxPoolItem &)7946 void DocxAttributeOutput::CharIdctHint( const SfxPoolItem& )
7947 {
7948 }
7949 
CharRotate(const SvxCharRotateItem & rRotate)7950 void DocxAttributeOutput::CharRotate( const SvxCharRotateItem& rRotate)
7951 {
7952     // Not rotated?
7953     if ( !rRotate.GetValue())
7954         return;
7955 
7956     AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vert ), "true" );
7957 
7958     if (rRotate.IsFitToLine())
7959         AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vertCompress ), "true" );
7960 }
7961 
CharEmphasisMark(const SvxEmphasisMarkItem & rEmphasisMark)7962 void DocxAttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark )
7963 {
7964     const char *pEmphasis;
7965     const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
7966 
7967     if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
7968         pEmphasis = "dot";
7969     else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
7970         pEmphasis = "comma";
7971     else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
7972         pEmphasis = "circle";
7973     else if (v == (FontEmphasisMark::Dot|FontEmphasisMark::PosBelow))
7974         pEmphasis = "underDot";
7975     else
7976         pEmphasis = "none";
7977 
7978     m_pSerializer->singleElementNS(XML_w, XML_em, FSNS(XML_w, XML_val), pEmphasis);
7979 }
7980 
CharTwoLines(const SvxTwoLinesItem & rTwoLines)7981 void DocxAttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
7982 {
7983     if ( !rTwoLines.GetValue() )
7984         return;
7985 
7986     AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combine ), "true" );
7987 
7988     sal_Unicode cStart = rTwoLines.GetStartBracket();
7989     sal_Unicode cEnd = rTwoLines.GetEndBracket();
7990 
7991     if (!cStart && !cEnd)
7992         return;
7993 
7994     OString sBracket;
7995     if ((cStart == '{') || (cEnd == '}'))
7996         sBracket = const_cast<char *>("curly");
7997     else if ((cStart == '<') || (cEnd == '>'))
7998         sBracket = const_cast<char *>("angle");
7999     else if ((cStart == '[') || (cEnd == ']'))
8000         sBracket = const_cast<char *>("square");
8001     else
8002         sBracket = const_cast<char *>("round");
8003     AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combineBrackets ), sBracket.getStr() );
8004 }
8005 
CharScaleWidth(const SvxCharScaleWidthItem & rScaleWidth)8006 void DocxAttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth )
8007 {
8008     // Clamp CharScaleWidth to OOXML limits ([1..600])
8009     const sal_Int16 nScaleWidth( std::max<sal_Int16>( 1,
8010         std::min<sal_Int16>( rScaleWidth.GetValue(), 600 ) ) );
8011     m_pSerializer->singleElementNS( XML_w, XML_w,
8012         FSNS( XML_w, XML_val ), OString::number(nScaleWidth) );
8013 }
8014 
CharRelief(const SvxCharReliefItem & rRelief)8015 void DocxAttributeOutput::CharRelief( const SvxCharReliefItem& rRelief )
8016 {
8017     switch ( rRelief.GetValue() )
8018     {
8019         case FontRelief::Embossed:
8020             m_pSerializer->singleElementNS(XML_w, XML_emboss);
8021             break;
8022         case FontRelief::Engraved:
8023             m_pSerializer->singleElementNS(XML_w, XML_imprint);
8024             break;
8025         default:
8026             m_pSerializer->singleElementNS(XML_w, XML_emboss, FSNS(XML_w, XML_val), "false");
8027             m_pSerializer->singleElementNS(XML_w, XML_imprint, FSNS(XML_w, XML_val), "false");
8028             break;
8029     }
8030 }
8031 
CharHidden(const SvxCharHiddenItem & rHidden)8032 void DocxAttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden )
8033 {
8034     if ( rHidden.GetValue() )
8035         m_pSerializer->singleElementNS(XML_w, XML_vanish);
8036     else
8037         m_pSerializer->singleElementNS(XML_w, XML_vanish, FSNS(XML_w, XML_val), "false");
8038 }
8039 
CharBorder(const SvxBorderLine * pAllBorder,const sal_uInt16 nDist,const bool bShadow)8040 void DocxAttributeOutput::CharBorder(
8041     const SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow )
8042 {
8043     css::table::BorderLine2 rStyleBorder;
8044     const SvxBoxItem* pInherited = nullptr;
8045     if ( GetExport().m_bStyDef && GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() )
8046         pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxBoxItem>(RES_CHRATR_BOX);
8047     else if ( m_rExport.m_pChpIter ) // incredibly undocumented, but this is the character-style info, right?
8048         pInherited = static_cast<const SvxBoxItem*>(GetExport().m_pChpIter->HasTextItem(RES_CHRATR_BOX));
8049 
8050     if ( pInherited )
8051         rStyleBorder = SvxBoxItem::SvxLineToLine(pInherited->GetRight(), false);
8052 
8053     impl_borderLine( m_pSerializer, XML_bdr, pAllBorder, nDist, bShadow, &rStyleBorder );
8054 }
8055 
CharHighlight(const SvxBrushItem & rHighlight)8056 void DocxAttributeOutput::CharHighlight( const SvxBrushItem& rHighlight )
8057 {
8058     const OString sColor = TransHighlightColor( msfilter::util::TransColToIco(rHighlight.GetColor()) );
8059     if ( !sColor.isEmpty() )
8060     {
8061         m_pSerializer->singleElementNS(XML_w, XML_highlight, FSNS(XML_w, XML_val), sColor);
8062     }
8063 }
8064 
TextINetFormat(const SwFormatINetFormat & rLink)8065 void DocxAttributeOutput::TextINetFormat( const SwFormatINetFormat& rLink )
8066 {
8067     OString aStyleId = MSWordStyles::CreateStyleId(rLink.GetINetFormat());
8068     if (!aStyleId.isEmpty() && !aStyleId.equalsIgnoreAsciiCase("DefaultStyle"))
8069         m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
8070 }
8071 
TextCharFormat(const SwFormatCharFormat & rCharFormat)8072 void DocxAttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat )
8073 {
8074     OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(rCharFormat.GetCharFormat())));
8075 
8076     m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
8077 }
8078 
RefField(const SwField & rField,const OUString & rRef)8079 void DocxAttributeOutput::RefField( const SwField&  rField, const OUString& rRef )
8080 {
8081     SwFieldIds nType = rField.GetTyp( )->Which( );
8082     if ( nType == SwFieldIds::GetExp )
8083     {
8084         OUString sCmd = FieldString( ww::eREF ) +
8085             "\"" + rRef + "\" ";
8086 
8087         m_rExport.OutputField( &rField, ww::eREF, sCmd );
8088     }
8089 
8090     // There is nothing to do here for the set fields
8091 }
8092 
HiddenField(const SwField & rField)8093 void DocxAttributeOutput::HiddenField(const SwField& rField)
8094 {
8095     auto eSubType = static_cast<SwFieldTypesEnum>(rField.GetSubType());
8096     if (eSubType == SwFieldTypesEnum::ConditionalText)
8097     {
8098         OUString aCond = rField.GetPar1();
8099         OUString aTrueFalse = rField.GetPar2();
8100         sal_Int32 nPos = aTrueFalse.indexOf('|');
8101         OUString aTrue;
8102         OUString aFalse;
8103         if (nPos == -1)
8104         {
8105             aTrue = aTrueFalse;
8106         }
8107         else
8108         {
8109             aTrue = aTrueFalse.subView(0, nPos);
8110             aFalse = aTrueFalse.subView(nPos + 1);
8111         }
8112         OUString aCmd = FieldString(ww::eIF) + aCond + " \"" + aTrue + "\" \"" + aFalse + "\"";
8113         m_rExport.OutputField(&rField, ww::eIF, aCmd);
8114         return;
8115     }
8116 
8117     SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::HiddenField()" );
8118 }
8119 
PostitField(const SwField * pField)8120 void DocxAttributeOutput::PostitField( const SwField* pField )
8121 {
8122     assert( dynamic_cast< const SwPostItField* >( pField ));
8123     const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField);
8124     OString aName = OUStringToOString(pPostItField->GetName(), RTL_TEXTENCODING_UTF8);
8125     sal_Int32 nId = 0;
8126     std::map< OString, sal_Int32 >::iterator it = m_rOpenedAnnotationMarksIds.find(aName);
8127     if (it != m_rOpenedAnnotationMarksIds.end())
8128         // If the postit field has an annotation mark associated, we already have an id.
8129         nId = it->second;
8130     else
8131         // Otherwise get a new one.
8132         nId = m_nNextAnnotationMarkId++;
8133     m_postitFields.emplace_back(pPostItField, PostItDOCXData{ nId });
8134 }
8135 
WritePostitFieldReference()8136 void DocxAttributeOutput::WritePostitFieldReference()
8137 {
8138     while( m_postitFieldsMaxId < m_postitFields.size())
8139     {
8140         OString idstr = OString::number(m_postitFields[m_postitFieldsMaxId].second.id);
8141 
8142         // In case this file is inside annotation marks, we want to write the
8143         // comment reference after the annotation mark is closed, not here.
8144         OString idname = OUStringToOString(m_postitFields[m_postitFieldsMaxId].first->GetName(), RTL_TEXTENCODING_UTF8);
8145         std::map< OString, sal_Int32 >::iterator it = m_rOpenedAnnotationMarksIds.find( idname );
8146         if ( it == m_rOpenedAnnotationMarksIds.end(  ) )
8147             m_pSerializer->singleElementNS(XML_w, XML_commentReference, FSNS(XML_w, XML_id), idstr);
8148         ++m_postitFieldsMaxId;
8149     }
8150 }
8151 
WritePostitFields()8152 DocxAttributeOutput::hasResolved DocxAttributeOutput::WritePostitFields()
8153 {
8154     hasResolved eResult = hasResolved::no;
8155     for (auto& [f, data] : m_postitFields)
8156     {
8157         OString idstr = OString::number(data.id);
8158         m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr,
8159             FSNS( XML_w, XML_author ), f->GetPar1(),
8160             FSNS( XML_w, XML_date ), DateTimeToOString(f->GetDateTime()),
8161             FSNS( XML_w, XML_initials ), f->GetInitials() );
8162 
8163         const bool bNeedParaId = f->GetResolved();
8164         if (bNeedParaId)
8165             eResult = hasResolved::yes;
8166 
8167         if (f->GetTextObject() != nullptr)
8168         {
8169             // richtext
8170             data.lastParaId = GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN, bNeedParaId);
8171         }
8172         else
8173         {
8174             // just plain text - eg. when the field was created via the
8175             // .uno:InsertAnnotation API
8176             std::optional<OUString> aParaId;
8177             if (bNeedParaId)
8178             {
8179                 data.lastParaId = m_nNextParaId++;
8180                 aParaId = NumberToHexBinary(data.lastParaId);
8181             }
8182             m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId);
8183             m_pSerializer->startElementNS(XML_w, XML_r);
8184             RunText(f->GetText());
8185             m_pSerializer->endElementNS(XML_w, XML_r);
8186             m_pSerializer->endElementNS(XML_w, XML_p);
8187         }
8188 
8189         m_pSerializer->endElementNS( XML_w, XML_comment );
8190     }
8191     return eResult;
8192 }
8193 
WritePostItFieldsResolved()8194 void DocxAttributeOutput::WritePostItFieldsResolved()
8195 {
8196     for (auto& [f, data] : m_postitFields)
8197     {
8198         if (!f->GetResolved())
8199             continue;
8200         OUString idstr = NumberToHexBinary(data.lastParaId);
8201         m_pSerializer->singleElementNS(XML_w15, XML_commentEx, FSNS(XML_w15, XML_paraId), idstr,
8202                                        FSNS(XML_w15, XML_done), "1");
8203     }
8204 }
8205 
DropdownField(const SwField * pField)8206 bool DocxAttributeOutput::DropdownField( const SwField* pField )
8207 {
8208     ww::eField eType = ww::eFORMDROPDOWN;
8209     OUString sCmd = FieldString( eType  );
8210     GetExport( ).OutputField( pField, eType, sCmd );
8211 
8212     return false;
8213 }
8214 
PlaceholderField(const SwField * pField)8215 bool DocxAttributeOutput::PlaceholderField( const SwField* pField )
8216 {
8217     assert( pendingPlaceholder == nullptr );
8218     pendingPlaceholder = pField;
8219     return false; // do not expand
8220 }
8221 
WritePendingPlaceholder()8222 void DocxAttributeOutput::WritePendingPlaceholder()
8223 {
8224     if( pendingPlaceholder == nullptr )
8225         return;
8226     const SwField* pField = pendingPlaceholder;
8227     pendingPlaceholder = nullptr;
8228     m_pSerializer->startElementNS(XML_w, XML_sdt);
8229     m_pSerializer->startElementNS(XML_w, XML_sdtPr);
8230     if( !pField->GetPar2().isEmpty())
8231         m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val), pField->GetPar2());
8232     m_pSerializer->singleElementNS(XML_w, XML_temporary);
8233     m_pSerializer->singleElementNS(XML_w, XML_showingPlcHdr);
8234     m_pSerializer->singleElementNS(XML_w, XML_text);
8235     m_pSerializer->endElementNS( XML_w, XML_sdtPr );
8236     m_pSerializer->startElementNS(XML_w, XML_sdtContent);
8237     m_pSerializer->startElementNS(XML_w, XML_r);
8238     RunText( pField->GetPar1());
8239     m_pSerializer->endElementNS( XML_w, XML_r );
8240     m_pSerializer->endElementNS( XML_w, XML_sdtContent );
8241     m_pSerializer->endElementNS( XML_w, XML_sdt );
8242 }
8243 
SetField(const SwField & rField,ww::eField eType,const OUString & rCmd)8244 void DocxAttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd )
8245 {
8246     // field bookmarks are handled in the EndRun method
8247     GetExport().OutputField(&rField, eType, rCmd );
8248 }
8249 
WriteExpand(const SwField * pField)8250 void DocxAttributeOutput::WriteExpand( const SwField* pField )
8251 {
8252     // Will be written in the next End Run
8253     m_rExport.OutputField( pField, ww::eUNKNOWN, OUString() );
8254 }
8255 
WriteField_Impl(const SwField * const pField,ww::eField const eType,const OUString & rFieldCmd,FieldFlags const nMode,OUString const * const pBookmarkName)8256 void DocxAttributeOutput::WriteField_Impl(const SwField *const pField,
8257     ww::eField const eType, const OUString& rFieldCmd, FieldFlags const nMode,
8258     OUString const*const pBookmarkName)
8259 {
8260     if (m_bPreventDoubleFieldsHandling)
8261         return;
8262 
8263     struct FieldInfos infos;
8264     if (pField)
8265         infos.pField = pField->CopyField();
8266     infos.sCmd = rFieldCmd;
8267     infos.eType = eType;
8268     infos.bClose = bool(FieldFlags::Close & nMode);
8269     infos.bSep = bool(FieldFlags::CmdEnd & nMode);
8270     infos.bOpen = bool(FieldFlags::Start & nMode);
8271     m_Fields.push_back( infos );
8272 
8273     if (pBookmarkName)
8274     {
8275         m_sFieldBkm = *pBookmarkName;
8276     }
8277 
8278     if ( !pField )
8279         return;
8280 
8281     SwFieldIds nType = pField->GetTyp( )->Which( );
8282     sal_uInt16 nSubType = pField->GetSubType();
8283 
8284     // TODO Any other field types here ?
8285     if ( ( nType == SwFieldIds::SetExp ) && ( nSubType & nsSwGetSetExpType::GSE_STRING ) )
8286     {
8287         const SwSetExpField *pSet = static_cast<const SwSetExpField*>( pField );
8288         m_sFieldBkm = pSet->GetPar1( );
8289     }
8290     else if ( nType == SwFieldIds::Dropdown )
8291     {
8292         const SwDropDownField* pDropDown = static_cast<const SwDropDownField*>( pField );
8293         m_sFieldBkm = pDropDown->GetName( );
8294     }
8295 }
8296 
WriteFormData_Impl(const::sw::mark::IFieldmark & rFieldmark)8297 void DocxAttributeOutput::WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark )
8298 {
8299     if ( !m_Fields.empty() )
8300         m_Fields.begin()->pFieldmark = &rFieldmark;
8301 }
8302 
WriteBookmarks_Impl(std::vector<OUString> & rStarts,std::vector<OUString> & rEnds)8303 void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds )
8304 {
8305     for ( const OUString & name : rStarts )
8306     {
8307         if (name.startsWith("permission-for-group:") ||
8308             name.startsWith("permission-for-user:"))
8309         {
8310             m_rPermissionsStart.push_back(name);
8311         }
8312         else
8313         {
8314             m_rBookmarksStart.push_back(name);
8315         }
8316     }
8317     rStarts.clear();
8318 
8319     for ( const OUString & name : rEnds )
8320     {
8321         if (name.startsWith("permission-for-group:") ||
8322             name.startsWith("permission-for-user:"))
8323         {
8324             m_rPermissionsEnd.push_back(name);
8325         }
8326         else
8327         {
8328             m_rBookmarksEnd.push_back(name);
8329         }
8330     }
8331     rEnds.clear();
8332 }
8333 
WriteFinalBookmarks_Impl(std::vector<OUString> & rStarts,std::vector<OUString> & rEnds)8334 void DocxAttributeOutput::WriteFinalBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds )
8335 {
8336     for ( const OUString & name : rStarts )
8337     {
8338         if (name.startsWith("permission-for-group:") ||
8339             name.startsWith("permission-for-user:"))
8340         {
8341             m_rPermissionsStart.push_back(name);
8342         }
8343         else
8344         {
8345             m_rFinalBookmarksStart.push_back(name);
8346         }
8347     }
8348     rStarts.clear();
8349 
8350     for ( const OUString & name : rEnds )
8351     {
8352         if (name.startsWith("permission-for-group:") ||
8353             name.startsWith("permission-for-user:"))
8354         {
8355             m_rPermissionsEnd.push_back(name);
8356         }
8357         else
8358         {
8359             m_rFinalBookmarksEnd.push_back(name);
8360         }
8361     }
8362     rEnds.clear();
8363 }
8364 
WriteAnnotationMarks_Impl(std::vector<OUString> & rStarts,std::vector<OUString> & rEnds)8365 void DocxAttributeOutput::WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts,
8366         std::vector< OUString >& rEnds )
8367 {
8368     for ( const auto & rAnnotationName : rStarts )
8369     {
8370         OString rName = OUStringToOString(rAnnotationName, RTL_TEXTENCODING_UTF8 ).getStr( );
8371         m_rAnnotationMarksStart.push_back( rName );
8372     }
8373     rStarts.clear();
8374 
8375     for ( const auto & rAnnotationName : rEnds )
8376     {
8377         OString rName = OUStringToOString( rAnnotationName, RTL_TEXTENCODING_UTF8 ).getStr( );
8378         m_rAnnotationMarksEnd.push_back( rName );
8379     }
8380     rEnds.clear();
8381 }
8382 
TextFootnote_Impl(const SwFormatFootnote & rFootnote)8383 void DocxAttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote )
8384 {
8385     const SwEndNoteInfo& rInfo = rFootnote.IsEndNote()?
8386         m_rExport.m_rDoc.GetEndNoteInfo(): m_rExport.m_rDoc.GetFootnoteInfo();
8387 
8388     // footnote/endnote run properties
8389     const SwCharFormat* pCharFormat = rInfo.GetAnchorCharFormat( m_rExport.m_rDoc );
8390 
8391     OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat)));
8392 
8393     m_pSerializer->singleElementNS(XML_w, XML_rStyle, FSNS(XML_w, XML_val), aStyleId);
8394 
8395     // remember the footnote/endnote to
8396     // 1) write the footnoteReference/endnoteReference in EndRunProperties()
8397     // 2) be able to dump them all to footnotes.xml/endnotes.xml
8398     if ( !rFootnote.IsEndNote() && m_rExport.m_rDoc.GetFootnoteInfo().m_ePos != FTNPOS_CHAPTER )
8399         m_pFootnotesList->add( rFootnote );
8400     else
8401         m_pEndnotesList->add( rFootnote );
8402 }
8403 
FootnoteEndnoteReference()8404 void DocxAttributeOutput::FootnoteEndnoteReference()
8405 {
8406     sal_Int32 nId;
8407     const SwFormatFootnote *pFootnote = m_pFootnotesList->getCurrent( nId );
8408     sal_Int32 nToken = XML_footnoteReference;
8409 
8410     // both cannot be set at the same time - if they are, it's a bug
8411     if ( !pFootnote )
8412     {
8413         pFootnote = m_pEndnotesList->getCurrent( nId );
8414         nToken = XML_endnoteReference;
8415     }
8416 
8417     if ( !pFootnote )
8418         return;
8419 
8420     // write it
8421     if ( pFootnote->GetNumStr().isEmpty() )
8422     {
8423         // autonumbered
8424         m_pSerializer->singleElementNS(XML_w, nToken, FSNS(XML_w, XML_id), OString::number(nId));
8425     }
8426     else
8427     {
8428         // not autonumbered
8429         m_pSerializer->singleElementNS( XML_w, nToken,
8430                 FSNS( XML_w, XML_customMarkFollows ), "1",
8431                 FSNS( XML_w, XML_id ), OString::number(nId) );
8432 
8433         RunText( pFootnote->GetNumStr() );
8434     }
8435 }
8436 
WriteFootnoteSeparatorHeight(::sax_fastparser::FSHelperPtr const & pSerializer,SwTwips const nHeight)8437 static void WriteFootnoteSeparatorHeight(
8438     ::sax_fastparser::FSHelperPtr const& pSerializer, SwTwips const nHeight)
8439 {
8440     // try to get the height by setting font size of the paragraph
8441     if (nHeight != 0)
8442     {
8443         pSerializer->startElementNS(XML_w, XML_pPr);
8444         pSerializer->startElementNS(XML_w, XML_rPr);
8445         pSerializer->singleElementNS(XML_w, XML_sz, FSNS(XML_w, XML_val),
8446             OString::number((nHeight + 5) / 10));
8447         pSerializer->endElementNS(XML_w, XML_rPr);
8448         pSerializer->endElementNS(XML_w, XML_pPr);
8449     }
8450 }
8451 
FootnotesEndnotes(bool bFootnotes)8452 void DocxAttributeOutput::FootnotesEndnotes( bool bFootnotes )
8453 {
8454     const FootnotesVector& rVector = bFootnotes? m_pFootnotesList->getVector(): m_pEndnotesList->getVector();
8455 
8456     sal_Int32 nBody = bFootnotes? XML_footnotes: XML_endnotes;
8457     sal_Int32 nItem = bFootnotes? XML_footnote:  XML_endnote;
8458 
8459     m_pSerializer->startElementNS( XML_w, nBody, m_rExport.MainXmlNamespaces() );
8460 
8461     sal_Int32 nIndex = 0;
8462 
8463     // separator
8464     // note: can only be defined for the whole document, not per section
8465     m_pSerializer->startElementNS( XML_w, nItem,
8466             FSNS( XML_w, XML_id ), OString::number(nIndex++),
8467             FSNS( XML_w, XML_type ), "separator" );
8468     m_pSerializer->startElementNS(XML_w, XML_p);
8469 
8470     bool bSeparator = true;
8471     SwTwips nHeight(0);
8472     if (bFootnotes)
8473     {
8474         const SwPageFootnoteInfo& rFootnoteInfo = m_rExport.m_rDoc.GetPageDesc(0).GetFootnoteInfo();
8475         // Request separator only if both width and thickness are non-zero.
8476         bSeparator = rFootnoteInfo.GetLineStyle() != SvxBorderLineStyle::NONE
8477                   && rFootnoteInfo.GetLineWidth() > 0
8478                   && double(rFootnoteInfo.GetWidth()) > 0;
8479         nHeight = sw::FootnoteSeparatorHeight(rFootnoteInfo);
8480     }
8481 
8482     WriteFootnoteSeparatorHeight(m_pSerializer, nHeight);
8483 
8484     m_pSerializer->startElementNS(XML_w, XML_r);
8485     if (bSeparator)
8486         m_pSerializer->singleElementNS(XML_w, XML_separator);
8487     m_pSerializer->endElementNS( XML_w, XML_r );
8488     m_pSerializer->endElementNS( XML_w, XML_p );
8489     m_pSerializer->endElementNS( XML_w, nItem );
8490 
8491     // separator
8492     m_pSerializer->startElementNS( XML_w, nItem,
8493             FSNS( XML_w, XML_id ), OString::number(nIndex++),
8494             FSNS( XML_w, XML_type ), "continuationSeparator" );
8495     m_pSerializer->startElementNS(XML_w, XML_p);
8496 
8497     WriteFootnoteSeparatorHeight(m_pSerializer, nHeight);
8498 
8499     m_pSerializer->startElementNS(XML_w, XML_r);
8500     if (bSeparator)
8501     {
8502         m_pSerializer->singleElementNS(XML_w, XML_continuationSeparator);
8503     }
8504     m_pSerializer->endElementNS( XML_w, XML_r );
8505     m_pSerializer->endElementNS( XML_w, XML_p );
8506     m_pSerializer->endElementNS( XML_w, nItem );
8507 
8508     // if new special ones are added, update also WriteFootnoteEndnotePr()
8509 
8510     // footnotes/endnotes themselves
8511     for ( const auto& rpItem : rVector )
8512     {
8513         m_footnoteEndnoteRefTag = bFootnotes ? XML_footnoteRef : XML_endnoteRef;
8514         m_footnoteCustomLabel = rpItem->GetNumStr();
8515 
8516         m_pSerializer->startElementNS(XML_w, nItem, FSNS(XML_w, XML_id), OString::number(nIndex));
8517 
8518         const SwNodeIndex* pIndex = rpItem->GetTextFootnote()->GetStartNode();
8519         m_rExport.WriteSpecialText( pIndex->GetIndex() + 1,
8520                 pIndex->GetNode().EndOfSectionIndex(),
8521                 bFootnotes? TXT_FTN: TXT_EDN );
8522 
8523         m_pSerializer->endElementNS( XML_w, nItem );
8524         ++nIndex;
8525     }
8526 
8527     m_pSerializer->endElementNS( XML_w, nBody );
8528 
8529 }
8530 
WriteFootnoteEndnotePr(::sax_fastparser::FSHelperPtr const & fs,int tag,const SwEndNoteInfo & info,int listtag)8531 void DocxAttributeOutput::WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & fs, int tag,
8532     const SwEndNoteInfo& info, int listtag )
8533 {
8534     fs->startElementNS(XML_w, tag);
8535     OString aCustomFormat;
8536     OString fmt = lcl_ConvertNumberingType(info.m_aFormat.GetNumberingType(), nullptr, aCustomFormat);
8537     if (!fmt.isEmpty() && aCustomFormat.isEmpty())
8538         fs->singleElementNS(XML_w, XML_numFmt, FSNS(XML_w, XML_val), fmt);
8539     if( info.m_nFootnoteOffset != 0 )
8540         fs->singleElementNS( XML_w, XML_numStart, FSNS( XML_w, XML_val ),
8541             OString::number(info.m_nFootnoteOffset + 1) );
8542 
8543     const SwFootnoteInfo* pFootnoteInfo = dynamic_cast<const SwFootnoteInfo*>(&info);
8544     if( pFootnoteInfo )
8545     {
8546         switch( pFootnoteInfo->m_eNum )
8547         {
8548             case FTNNUM_PAGE:       fmt = "eachPage"; break;
8549             case FTNNUM_CHAPTER:    fmt = "eachSect"; break;
8550             default:                fmt.clear();      break;
8551         }
8552         if (!fmt.isEmpty())
8553             fs->singleElementNS(XML_w, XML_numRestart, FSNS(XML_w, XML_val), fmt);
8554     }
8555 
8556     if( listtag != 0 ) // we are writing to settings.xml, write also special footnote/endnote list
8557     { // there are currently only two hardcoded ones ( see FootnotesEndnotes())
8558         fs->singleElementNS(XML_w, listtag, FSNS(XML_w, XML_id), "0");
8559         fs->singleElementNS(XML_w, listtag, FSNS(XML_w, XML_id), "1");
8560     }
8561     fs->endElementNS( XML_w, tag );
8562 }
8563 
SectFootnoteEndnotePr()8564 void DocxAttributeOutput::SectFootnoteEndnotePr()
8565 {
8566     if( HasFootnotes())
8567         WriteFootnoteEndnotePr( m_pSerializer, XML_footnotePr, m_rExport.m_rDoc.GetFootnoteInfo(), 0 );
8568     if( HasEndnotes())
8569         WriteFootnoteEndnotePr( m_pSerializer, XML_endnotePr, m_rExport.m_rDoc.GetEndNoteInfo(), 0 );
8570 }
8571 
ParaLineSpacing_Impl(short nSpace,short nMulti)8572 void DocxAttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti )
8573 {
8574     if ( nSpace < 0 )
8575     {
8576         AddToAttrList( m_pParagraphSpacingAttrList, 2,
8577                 FSNS( XML_w, XML_lineRule ), "exact",
8578                 FSNS( XML_w, XML_line ), OString::number( -nSpace ).getStr() );
8579     }
8580     else if( nSpace > 0 && nMulti )
8581     {
8582         AddToAttrList( m_pParagraphSpacingAttrList, 2,
8583                 FSNS( XML_w, XML_lineRule ), "auto",
8584                 FSNS( XML_w, XML_line ), OString::number( nSpace ).getStr() );
8585     }
8586     else
8587     {
8588         AddToAttrList( m_pParagraphSpacingAttrList, 2,
8589                 FSNS( XML_w, XML_lineRule ), "atLeast",
8590                 FSNS( XML_w, XML_line ), OString::number( nSpace ).getStr() );
8591     }
8592 }
8593 
ParaAdjust(const SvxAdjustItem & rAdjust)8594 void DocxAttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust )
8595 {
8596     const char *pAdjustString;
8597 
8598     bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
8599 
8600     const SfxItemSet* pItems = GetExport().GetCurItemSet();
8601     const SvxFrameDirectionItem* rFrameDir = pItems?
8602         pItems->GetItem( RES_FRAMEDIR ) : nullptr;
8603 
8604     SvxFrameDirection nDir = SvxFrameDirection::Environment;
8605     if( rFrameDir != nullptr )
8606         nDir = rFrameDir->GetValue();
8607     if ( nDir == SvxFrameDirection::Environment )
8608         nDir = GetExport( ).GetDefaultFrameDirection( );
8609     bool bRtl = ( nDir == SvxFrameDirection::Horizontal_RL_TB );
8610 
8611     switch ( rAdjust.GetAdjust() )
8612     {
8613         case SvxAdjust::Left:
8614             if ( bEcma )
8615             {
8616                 if ( bRtl )
8617                     pAdjustString = "right";
8618                 else
8619                     pAdjustString = "left";
8620             }
8621             else if ( bRtl )
8622                 pAdjustString = "end";
8623             else
8624                 pAdjustString = "start";
8625             break;
8626         case SvxAdjust::Right:
8627             if ( bEcma )
8628             {
8629                 if ( bRtl )
8630                     pAdjustString = "left";
8631                 else
8632                     pAdjustString = "right";
8633             }
8634             else if ( bRtl )
8635                 pAdjustString = "start";
8636             else
8637                 pAdjustString = "end";
8638             break;
8639         case SvxAdjust::BlockLine:
8640         case SvxAdjust::Block:
8641             if (rAdjust.GetLastBlock() == SvxAdjust::Block)
8642                 pAdjustString = "distribute";
8643             else
8644                 pAdjustString = "both";
8645             break;
8646         case SvxAdjust::Center:
8647             pAdjustString = "center";
8648             break;
8649         default:
8650             return; // not supported attribute
8651     }
8652     m_pSerializer->singleElementNS(XML_w, XML_jc, FSNS(XML_w, XML_val), pAdjustString);
8653 }
8654 
ParaSplit(const SvxFormatSplitItem & rSplit)8655 void DocxAttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit )
8656 {
8657     if (rSplit.GetValue())
8658         m_pSerializer->singleElementNS(XML_w, XML_keepLines, FSNS(XML_w, XML_val), "false");
8659     else
8660         m_pSerializer->singleElementNS(XML_w, XML_keepLines);
8661 }
8662 
ParaWidows(const SvxWidowsItem & rWidows)8663 void DocxAttributeOutput::ParaWidows( const SvxWidowsItem& rWidows )
8664 {
8665     if (rWidows.GetValue())
8666         m_pSerializer->singleElementNS(XML_w, XML_widowControl);
8667     else
8668         m_pSerializer->singleElementNS(XML_w, XML_widowControl, FSNS(XML_w, XML_val), "false");
8669 }
8670 
impl_WriteTabElement(FSHelperPtr const & pSerializer,const SvxTabStop & rTab,tools::Long tabsOffset)8671 static void impl_WriteTabElement( FSHelperPtr const & pSerializer,
8672                                   const SvxTabStop& rTab, tools::Long tabsOffset )
8673 {
8674     rtl::Reference<FastAttributeList> pTabElementAttrList = FastSerializerHelper::createAttrList();
8675 
8676     switch (rTab.GetAdjustment())
8677     {
8678     case SvxTabAdjust::Right:
8679         pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "right" ) );
8680         break;
8681     case SvxTabAdjust::Decimal:
8682         pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "decimal" ) );
8683         break;
8684     case SvxTabAdjust::Center:
8685         pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "center" ) );
8686         break;
8687     case SvxTabAdjust::Default:
8688     case SvxTabAdjust::Left:
8689     default:
8690         pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "left" ) );
8691         break;
8692     }
8693 
8694     // Write position according to used offset of the whole paragraph.
8695     // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins.
8696     // But in ODT, zero position could be page margins or paragraph indent according to used settings.
8697     // This is handled outside of this method and provided for us in tabsOffset parameter.
8698     pTabElementAttrList->add( FSNS( XML_w, XML_pos ), OString::number( rTab.GetTabPos() + tabsOffset ) );
8699 
8700     sal_Unicode cFillChar = rTab.GetFill();
8701 
8702     if ('.' == cFillChar )
8703         pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "dot" ) );
8704     else if ( '-' == cFillChar )
8705         pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "hyphen" ) );
8706     else if ( u'\x00B7' == cFillChar ) // middle dot
8707         pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "middleDot" ) );
8708     else if ( '_' == cFillChar )
8709         pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "underscore" ) );
8710     else
8711         pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "none" ) );
8712 
8713     pSerializer->singleElementNS(XML_w, XML_tab, pTabElementAttrList);
8714 }
8715 
ParaTabStop(const SvxTabStopItem & rTabStop)8716 void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop )
8717 {
8718     const SvxTabStopItem* pInheritedTabs = nullptr;
8719     if ( GetExport().m_pStyAttr )
8720         pInheritedTabs = GetExport().m_pStyAttr->GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
8721     else if ( GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() )
8722         pInheritedTabs = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxTabStopItem>(RES_PARATR_TABSTOP);
8723     const sal_uInt16 nInheritedTabCount = pInheritedTabs ? pInheritedTabs->Count() : 0;
8724     const sal_uInt16 nCount = rTabStop.Count();
8725 
8726     // <w:tabs> must contain at least one <w:tab>, so don't write it empty
8727     if ( !nCount && !nInheritedTabCount )
8728         return;
8729     if( nCount == 1 && rTabStop[ 0 ].GetAdjustment() == SvxTabAdjust::Default )
8730     {
8731         GetExport().setDefaultTabStop( rTabStop[ 0 ].GetTabPos());
8732         return;
8733     }
8734 
8735     // do not output inherited tabs twice (inside styles and inside inline properties)
8736     if ( nCount == nInheritedTabCount && nCount > 0 )
8737     {
8738         if ( *pInheritedTabs == rTabStop )
8739             return;
8740     }
8741 
8742     m_pSerializer->startElementNS(XML_w, XML_tabs);
8743 
8744     // Get offset for tabs
8745     // In DOCX, w:pos specifies the position of the current custom tab stop with respect to the current page margins.
8746     // But in ODT, zero position could be page margins or paragraph indent according to used settings.
8747     tools::Long tabsOffset = 0;
8748     if (m_rExport.m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT))
8749         tabsOffset = m_rExport.GetItem(RES_LR_SPACE).GetTextLeft();
8750 
8751     // clear unused inherited tabs - otherwise the style will add them back in
8752     sal_Int32 nCurrTab = 0;
8753     for ( sal_uInt16 i = 0; i < nInheritedTabCount; ++i )
8754     {
8755         while ( nCurrTab < nCount && rTabStop[nCurrTab] < pInheritedTabs->At(i) )
8756             ++nCurrTab;
8757 
8758         if ( nCurrTab == nCount || pInheritedTabs->At(i) < rTabStop[nCurrTab] )
8759         {
8760             m_pSerializer->singleElementNS( XML_w, XML_tab,
8761                 FSNS( XML_w, XML_val ), "clear",
8762                 FSNS( XML_w, XML_pos ), OString::number(pInheritedTabs->At(i).GetTabPos()) );
8763         }
8764     }
8765 
8766     for (sal_uInt16 i = 0; i < nCount; i++ )
8767     {
8768         if( rTabStop[i].GetAdjustment() != SvxTabAdjust::Default )
8769             impl_WriteTabElement( m_pSerializer, rTabStop[i], tabsOffset );
8770         else
8771             GetExport().setDefaultTabStop( rTabStop[i].GetTabPos());
8772     }
8773 
8774     m_pSerializer->endElementNS( XML_w, XML_tabs );
8775 }
8776 
ParaHyphenZone(const SvxHyphenZoneItem & rHyphenZone)8777 void DocxAttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone )
8778 {
8779     m_pSerializer->singleElementNS( XML_w, XML_suppressAutoHyphens,
8780             FSNS( XML_w, XML_val ), OString::boolean( !rHyphenZone.IsHyphen() ) );
8781 }
8782 
ParaNumRule_Impl(const SwTextNode * pTextNd,sal_Int32 nLvl,sal_Int32 nNumId)8783 void DocxAttributeOutput::ParaNumRule_Impl( const SwTextNode* pTextNd, sal_Int32 nLvl, sal_Int32 nNumId )
8784 {
8785     if ( USHRT_MAX == nNumId )
8786         return;
8787 
8788     // LibreOffice is not very flexible with "Outline Numbering" (aka "Outline" numbering style).
8789     // Only ONE numbering rule ("Outline") can be associated with a style-assigned-listLevel,
8790     // and no other style is able to inherit these numId/nLvl settings - only text nodes can.
8791     // So listLevel only exists in paragraph properties EXCEPT for up to ten styles that have been
8792     // assigned to one of these special Chapter Numbering listlevels (by default Heading 1-10).
8793     const sal_Int32 nTableSize = m_rExport.m_pUsedNumTable ? m_rExport.m_pUsedNumTable->size() : 0;
8794     const SwNumRule* pRule = nNumId > 0 && nNumId <= nTableSize ? (*m_rExport.m_pUsedNumTable)[nNumId-1] : nullptr;
8795     const SwTextFormatColl* pColl = pTextNd ? pTextNd->GetTextColl() : nullptr;
8796     // Do not duplicate numbering that is inherited from the (Chapter numbering) style
8797     // (since on import we duplicate style numbering/listlevel to the paragraph).
8798     if (pColl && pColl->IsAssignedToListLevelOfOutlineStyle()
8799         && nLvl == pColl->GetAssignedOutlineStyleLevel() && pRule && pRule->IsOutlineRule())
8800     {
8801         // By definition of how LO is implemented, assignToListLevel is only possible
8802         // when the style is also using OutlineRule for numbering. Adjust logic if that changes.
8803         assert(pRule->GetName() == pColl->GetNumRule(true).GetValue());
8804         return;
8805     }
8806 
8807     m_pSerializer->startElementNS(XML_w, XML_numPr);
8808     m_pSerializer->singleElementNS(XML_w, XML_ilvl, FSNS(XML_w, XML_val), OString::number(nLvl));
8809     m_pSerializer->singleElementNS(XML_w, XML_numId, FSNS(XML_w, XML_val), OString::number(nNumId));
8810     m_pSerializer->endElementNS(XML_w, XML_numPr);
8811 }
8812 
ParaScriptSpace(const SfxBoolItem & rScriptSpace)8813 void DocxAttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace )
8814 {
8815     m_pSerializer->singleElementNS( XML_w, XML_autoSpaceDE,
8816            FSNS( XML_w, XML_val ), OString::boolean( rScriptSpace.GetValue() ) );
8817 }
8818 
ParaHangingPunctuation(const SfxBoolItem & rItem)8819 void DocxAttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem )
8820 {
8821     m_pSerializer->singleElementNS( XML_w, XML_overflowPunct,
8822            FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) );
8823 }
8824 
ParaForbiddenRules(const SfxBoolItem & rItem)8825 void DocxAttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem )
8826 {
8827     m_pSerializer->singleElementNS( XML_w, XML_kinsoku,
8828            FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) );
8829 }
8830 
ParaVerticalAlign(const SvxParaVertAlignItem & rAlign)8831 void DocxAttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign )
8832 {
8833     const char *pAlignString;
8834 
8835     switch ( rAlign.GetValue() )
8836     {
8837         case SvxParaVertAlignItem::Align::Baseline:
8838             pAlignString = "baseline";
8839             break;
8840         case SvxParaVertAlignItem::Align::Top:
8841             pAlignString = "top";
8842             break;
8843         case SvxParaVertAlignItem::Align::Center:
8844             pAlignString = "center";
8845             break;
8846         case SvxParaVertAlignItem::Align::Bottom:
8847             pAlignString = "bottom";
8848             break;
8849         case SvxParaVertAlignItem::Align::Automatic:
8850             pAlignString = "auto";
8851             break;
8852         default:
8853             return; // not supported attribute
8854     }
8855     m_pSerializer->singleElementNS(XML_w, XML_textAlignment, FSNS(XML_w, XML_val), pAlignString);
8856 }
8857 
ParaSnapToGrid(const SvxParaGridItem & rGrid)8858 void DocxAttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid )
8859 {
8860     m_pSerializer->singleElementNS( XML_w, XML_snapToGrid,
8861             FSNS( XML_w, XML_val ), OString::boolean( rGrid.GetValue() ) );
8862 }
8863 
FormatFrameSize(const SwFormatFrameSize & rSize)8864 void DocxAttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize )
8865 {
8866     if (m_rExport.SdrExporter().getTextFrameSyntax() && m_rExport.SdrExporter().getFlyFrameSize())
8867     {
8868         const Size* pSize = m_rExport.SdrExporter().getFlyFrameSize();
8869         m_rExport.SdrExporter().getTextFrameStyle().append(";width:" + OString::number(double(pSize->Width()) / 20));
8870         m_rExport.SdrExporter().getTextFrameStyle().append("pt;height:" + OString::number(double(pSize->Height()) / 20) + "pt");
8871     }
8872     else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
8873     {
8874     }
8875     else if ( m_rExport.m_bOutFlyFrameAttrs )
8876     {
8877         if ( rSize.GetWidth() && rSize.GetWidthSizeType() == SwFrameSize::Fixed )
8878             AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(),
8879                     FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ).getStr() );
8880 
8881         if ( rSize.GetHeight() )
8882         {
8883             OString sRule( "exact" );
8884             if ( rSize.GetHeightSizeType() == SwFrameSize::Minimum )
8885                 sRule = OString( "atLeast" );
8886             AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2,
8887                     FSNS( XML_w, XML_hRule ), sRule.getStr(),
8888                     FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ).getStr() );
8889         }
8890     }
8891     else if ( m_rExport.m_bOutPageDescs )
8892     {
8893         rtl::Reference<FastAttributeList> attrList = FastSerializerHelper::createAttrList( );
8894         if ( m_rExport.m_pCurrentPageDesc->GetLandscape( ) )
8895             attrList->add( FSNS( XML_w, XML_orient ), "landscape" );
8896 
8897         attrList->add( FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ) );
8898         attrList->add( FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ) );
8899 
8900         m_pSerializer->singleElementNS( XML_w, XML_pgSz, attrList );
8901     }
8902 }
8903 
FormatPaperBin(const SvxPaperBinItem &)8904 void DocxAttributeOutput::FormatPaperBin( const SvxPaperBinItem& )
8905 {
8906     SAL_INFO("sw.ww8", "TODO DocxAttributeOutput::FormatPaperBin()" );
8907 }
8908 
FormatLRSpace(const SvxLRSpaceItem & rLRSpace)8909 void DocxAttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLRSpace )
8910 {
8911     bool bEcma = m_rExport.GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
8912 
8913     if (m_rExport.SdrExporter().getTextFrameSyntax())
8914     {
8915         m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-left:" + OString::number(double(rLRSpace.GetLeft()) / 20) + "pt");
8916         m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-right:" + OString::number(double(rLRSpace.GetRight()) / 20) + "pt");
8917     }
8918     else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
8919     {
8920     }
8921     else if ( m_rExport.m_bOutFlyFrameAttrs )
8922     {
8923         AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hSpace ),
8924                 OString::number(
8925                     ( rLRSpace.GetLeft() + rLRSpace.GetRight() ) / 2 ).getStr() );
8926     }
8927     else if ( m_rExport.m_bOutPageDescs )
8928     {
8929         m_pageMargins.nLeft = 0;
8930         m_pageMargins.nRight = 0;
8931 
8932         if ( auto pBoxItem = static_cast<const SvxBoxItem*>(m_rExport.HasItem( RES_BOX )) )
8933         {
8934             m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
8935             m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
8936         }
8937 
8938         m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
8939         m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
8940         sal_uInt16 nGutter = rLRSpace.GetGutterMargin();
8941 
8942         AddToAttrList( m_pSectionSpacingAttrList, 3,
8943                 FSNS( XML_w, XML_left ), OString::number( m_pageMargins.nLeft ).getStr(),
8944                 FSNS( XML_w, XML_right ), OString::number( m_pageMargins.nRight ).getStr(),
8945                 FSNS( XML_w, XML_gutter ), OString::number( nGutter ).getStr() );
8946     }
8947     else
8948     {
8949         SvxLRSpaceItem const* pLRSpace(&rLRSpace);
8950         ::std::optional<SvxLRSpaceItem> oLRSpace;
8951         if (dynamic_cast<SwContentNode const*>(GetExport().m_pOutFormatNode) != nullptr)
8952         {
8953             auto pTextNd(static_cast<SwTextNode const*>(GetExport().m_pOutFormatNode));
8954             // WW doesn't have a concept of a pararaph that's in a list but not
8955             // counted in the list - see AttributeOutputBase::ParaNumRule()
8956             // forcing non-existent numId="0" in this case.
8957             // This means WW won't apply the indents from the numbering,
8958             // so try to add them as paragraph properties here.
8959             if (!pTextNd->IsCountedInList())
8960             {
8961                 SfxItemSet temp(m_rExport.m_rDoc.GetAttrPool(), svl::Items<RES_LR_SPACE, RES_LR_SPACE>{});
8962                 pTextNd->GetParaAttr(temp, 0, 0, false, true, true, nullptr);
8963                 if (auto *const pItem = temp.GetItem(RES_LR_SPACE))
8964                 {
8965                     // but don't use first-line offset from list (should it be 0 or from node?)
8966                     oLRSpace.emplace(*pItem);
8967                     oLRSpace->SetTextFirstLineOffset(pLRSpace->GetTextFirstLineOffset());
8968                     pLRSpace = &*oLRSpace;
8969                 }
8970             }
8971         }
8972         rtl::Reference<FastAttributeList> pLRSpaceAttrList = FastSerializerHelper::createAttrList();
8973         if ((0 != pLRSpace->GetTextLeft()) || (pLRSpace->IsExplicitZeroMarginValLeft()))
8974         {
8975             pLRSpaceAttrList->add( FSNS(XML_w, (bEcma ? XML_left : XML_start)), OString::number(pLRSpace->GetTextLeft()) );
8976         }
8977         if ((0 != pLRSpace->GetRight()) || (pLRSpace->IsExplicitZeroMarginValRight()))
8978         {
8979             pLRSpaceAttrList->add( FSNS(XML_w, (bEcma ? XML_right : XML_end)), OString::number(pLRSpace->GetRight()) );
8980         }
8981         sal_Int32 const nFirstLineAdjustment = pLRSpace->GetTextFirstLineOffset();
8982         if (nFirstLineAdjustment > 0)
8983             pLRSpaceAttrList->add( FSNS( XML_w, XML_firstLine ), OString::number( nFirstLineAdjustment ) );
8984         else
8985             pLRSpaceAttrList->add( FSNS( XML_w, XML_hanging ), OString::number( - nFirstLineAdjustment ) );
8986         m_pSerializer->singleElementNS( XML_w, XML_ind, pLRSpaceAttrList );
8987     }
8988 }
8989 
FormatULSpace(const SvxULSpaceItem & rULSpace)8990 void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace )
8991 {
8992 
8993     if (m_rExport.SdrExporter().getTextFrameSyntax())
8994     {
8995         m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-top:" + OString::number(double(rULSpace.GetUpper()) / 20) + "pt");
8996         m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-bottom:" + OString::number(double(rULSpace.GetLower()) / 20) + "pt");
8997     }
8998     else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
8999     {
9000     }
9001     else if ( m_rExport.m_bOutFlyFrameAttrs )
9002     {
9003         AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vSpace ),
9004                 OString::number(
9005                     ( rULSpace.GetLower() + rULSpace.GetUpper() ) / 2 ).getStr() );
9006     }
9007     else if (m_rExport.m_bOutPageDescs )
9008     {
9009         OSL_ENSURE( m_rExport.GetCurItemSet(), "Impossible" );
9010         if ( !m_rExport.GetCurItemSet() )
9011             return;
9012 
9013         HdFtDistanceGlue aDistances( *m_rExport.GetCurItemSet() );
9014 
9015         sal_Int32 nHeader = 0;
9016         if ( aDistances.HasHeader() )
9017             nHeader = sal_Int32( aDistances.dyaHdrTop );
9018         else if (m_rExport.m_pFirstPageFormat)
9019         {
9020             HdFtDistanceGlue aFirstPageDistances(m_rExport.m_pFirstPageFormat->GetAttrSet());
9021             if (aFirstPageDistances.HasHeader())
9022             {
9023                 // The follow page style has no header, but the first page style has. In Word terms,
9024                 // this means that the header margin of "the" section is coming from the first page
9025                 // style.
9026                 nHeader = sal_Int32(aFirstPageDistances.dyaHdrTop);
9027             }
9028         }
9029 
9030         // Page top
9031         m_pageMargins.nTop = aDistances.dyaTop;
9032 
9033         sal_Int32 nFooter = 0;
9034         if ( aDistances.HasFooter() )
9035             nFooter = sal_Int32( aDistances.dyaHdrBottom );
9036         else if (m_rExport.m_pFirstPageFormat)
9037         {
9038             HdFtDistanceGlue aFirstPageDistances(m_rExport.m_pFirstPageFormat->GetAttrSet());
9039             if (aFirstPageDistances.HasFooter())
9040             {
9041                 // The follow page style has no footer, but the first page style has. In Word terms,
9042                 // this means that the footer margin of "the" section is coming from the first page
9043                 // style.
9044                 nFooter = sal_Int32(aFirstPageDistances.dyaHdrBottom);
9045             }
9046         }
9047 
9048         // Page Bottom
9049         m_pageMargins.nBottom = aDistances.dyaBottom;
9050 
9051         AddToAttrList( m_pSectionSpacingAttrList, 4,
9052                 FSNS( XML_w, XML_header ), OString::number( nHeader ).getStr(),
9053                 FSNS( XML_w, XML_top ), OString::number( m_pageMargins.nTop ).getStr(),
9054                 FSNS( XML_w, XML_footer ), OString::number( nFooter ).getStr(),
9055                 FSNS( XML_w, XML_bottom ), OString::number( m_pageMargins.nBottom ).getStr() );
9056     }
9057     else
9058     {
9059         SAL_INFO("sw.ww8", "DocxAttributeOutput::FormatULSpace: setting spacing" << rULSpace.GetUpper() );
9060         // check if before auto spacing was set during import and spacing we get from actual object is same
9061         // that we set in import. If yes just write beforeAutoSpacing tag.
9062         if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper())
9063         {
9064             AddToAttrList( m_pParagraphSpacingAttrList,
9065                     FSNS( XML_w, XML_beforeAutospacing ), "1" );
9066         }
9067         else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1)
9068         {
9069             AddToAttrList( m_pParagraphSpacingAttrList,
9070                     FSNS( XML_w, XML_beforeAutospacing ), "0" );
9071             AddToAttrList( m_pParagraphSpacingAttrList,
9072                     FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ).getStr() );
9073         }
9074         else
9075         {
9076             AddToAttrList( m_pParagraphSpacingAttrList,
9077                     FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ).getStr() );
9078         }
9079         m_bParaBeforeAutoSpacing = false;
9080         // check if after auto spacing was set during import and spacing we get from actual object is same
9081         // that we set in import. If yes just write afterAutoSpacing tag.
9082         if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower())
9083         {
9084             AddToAttrList( m_pParagraphSpacingAttrList,
9085                     FSNS( XML_w, XML_afterAutospacing ), "1" );
9086         }
9087         else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1)
9088         {
9089             AddToAttrList( m_pParagraphSpacingAttrList,
9090                     FSNS( XML_w, XML_afterAutospacing ), "0" );
9091             AddToAttrList( m_pParagraphSpacingAttrList,
9092                                 FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()).getStr() );
9093         }
9094         else
9095         {
9096             AddToAttrList( m_pParagraphSpacingAttrList,
9097                     FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()).getStr() );
9098         }
9099         m_bParaAfterAutoSpacing = false;
9100 
9101         if (rULSpace.GetContext())
9102             m_pSerializer->singleElementNS(XML_w, XML_contextualSpacing);
9103         else
9104         {
9105             // Write out Contextual Spacing = false if it would have inherited a true.
9106             const SvxULSpaceItem* pInherited = nullptr;
9107             if (auto pNd = dynamic_cast<const SwContentNode*>(m_rExport.m_pOutFormatNode)) //paragraph
9108                 pInherited = &static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetULSpace();
9109             else if (m_rExport.m_bStyDef && m_rExport.m_pCurrentStyle && m_rExport.m_pCurrentStyle->DerivedFrom()) //style
9110                 pInherited = &m_rExport.m_pCurrentStyle->DerivedFrom()->GetULSpace();
9111 
9112             if (pInherited && pInherited->GetContext())
9113                 m_pSerializer->singleElementNS(XML_w, XML_contextualSpacing, FSNS(XML_w, XML_val), "false");
9114         }
9115     }
9116 }
9117 
9118 namespace docx {
9119 
SurroundToVMLWrap(SwFormatSurround const & rSurround)9120 rtl::Reference<FastAttributeList> SurroundToVMLWrap(SwFormatSurround const& rSurround)
9121 {
9122     rtl::Reference<FastAttributeList> pAttrList;
9123     OString sType;
9124     OString sSide;
9125     switch (rSurround.GetSurround())
9126     {
9127         case css::text::WrapTextMode_NONE:
9128             sType = "topAndBottom";
9129             break;
9130         case css::text::WrapTextMode_PARALLEL:
9131             sType = "square";
9132             break;
9133         case css::text::WrapTextMode_DYNAMIC:
9134             sType = "square";
9135             sSide = "largest";
9136             break;
9137         case css::text::WrapTextMode_LEFT:
9138             sType = "square";
9139             sSide = "left";
9140             break;
9141         case css::text::WrapTextMode_RIGHT:
9142             sType = "square";
9143             sSide = "right";
9144             break;
9145         case css::text::WrapTextMode_THROUGH:
9146             /* empty type and side means through */
9147         default:
9148             sType = "none";
9149             break;
9150     }
9151     if (!sType.isEmpty() || !sSide.isEmpty())
9152     {
9153         pAttrList = FastSerializerHelper::createAttrList();
9154         if (!sType.isEmpty())
9155         {
9156             pAttrList->add(XML_type, sType);
9157         }
9158         if (!sSide.isEmpty())
9159         {
9160             pAttrList->add(XML_side, sSide);
9161         }
9162     }
9163     return pAttrList;
9164 }
9165 
9166 } // namespace docx
9167 
FormatSurround(const SwFormatSurround & rSurround)9168 void DocxAttributeOutput::FormatSurround( const SwFormatSurround& rSurround )
9169 {
9170     if (m_rExport.SdrExporter().getTextFrameSyntax())
9171     {
9172         rtl::Reference<FastAttributeList> pAttrList(docx::SurroundToVMLWrap(rSurround));
9173         if (pAttrList)
9174         {
9175             m_rExport.SdrExporter().setFlyWrapAttrList(pAttrList);
9176         }
9177     }
9178     else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
9179     {
9180     }
9181     else if ( m_rExport.m_bOutFlyFrameAttrs )
9182     {
9183         OString sWrap( "auto" );
9184         switch ( rSurround.GetSurround( ) )
9185         {
9186             case css::text::WrapTextMode_NONE:
9187                 sWrap = OString( "none" );
9188                 break;
9189             case css::text::WrapTextMode_THROUGH:
9190                 sWrap = OString( "through" );
9191                 break;
9192             case css::text::WrapTextMode_DYNAMIC:
9193             case css::text::WrapTextMode_PARALLEL:
9194             case css::text::WrapTextMode_LEFT:
9195             case css::text::WrapTextMode_RIGHT:
9196             default:
9197                 sWrap = OString( "around" );
9198         }
9199 
9200         AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_wrap ), sWrap.getStr() );
9201     }
9202 }
9203 
FormatVertOrientation(const SwFormatVertOrient & rFlyVert)9204 void DocxAttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert )
9205 {
9206     OString sAlign   = convertToOOXMLVertOrient( rFlyVert.GetVertOrient() );
9207     OString sVAnchor = convertToOOXMLVertOrientRel( rFlyVert.GetRelationOrient() );
9208 
9209     if (m_rExport.SdrExporter().getTextFrameSyntax())
9210     {
9211         m_rExport.SdrExporter().getTextFrameStyle().append(";margin-top:" + OString::number(double(rFlyVert.GetPos()) / 20) + "pt");
9212         if ( !sAlign.isEmpty() )
9213             m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical:" + sAlign);
9214         m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical-relative:" + sVAnchor);
9215     }
9216     else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
9217     {
9218     }
9219     else if ( m_rExport.m_bOutFlyFrameAttrs )
9220     {
9221         if ( !sAlign.isEmpty() )
9222             AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_yAlign ), sAlign.getStr() );
9223         else
9224             AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_y ),
9225                 OString::number( rFlyVert.GetPos() ).getStr() );
9226         AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vAnchor ), sVAnchor.getStr() );
9227     }
9228 }
9229 
FormatHorizOrientation(const SwFormatHoriOrient & rFlyHori)9230 void DocxAttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori )
9231 {
9232     OString sAlign   = convertToOOXMLHoriOrient( rFlyHori.GetHoriOrient(), rFlyHori.IsPosToggle() );
9233     OString sHAnchor = convertToOOXMLHoriOrientRel( rFlyHori.GetRelationOrient() );
9234 
9235     if (m_rExport.SdrExporter().getTextFrameSyntax())
9236     {
9237         m_rExport.SdrExporter().getTextFrameStyle().append(";margin-left:" + OString::number(double(rFlyHori.GetPos()) / 20) + "pt");
9238         if ( !sAlign.isEmpty() )
9239             m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal:" + sAlign);
9240         m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal-relative:" + sHAnchor);
9241     }
9242     else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
9243     {
9244     }
9245     else if ( m_rExport.m_bOutFlyFrameAttrs )
9246     {
9247         if ( !sAlign.isEmpty() )
9248             AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_xAlign ), sAlign.getStr() );
9249         else
9250             AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_x ),
9251                 OString::number( rFlyHori.GetPos() ).getStr() );
9252         AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hAnchor ), sHAnchor.getStr() );
9253     }
9254 }
9255 
FormatAnchor(const SwFormatAnchor &)9256 void DocxAttributeOutput::FormatAnchor( const SwFormatAnchor& )
9257 {
9258     // Fly frames: anchors here aren't matching the anchors in docx
9259 }
9260 
lcl_getDmlAlpha(const SvxBrushItem & rBrush)9261 static std::optional<sal_Int32> lcl_getDmlAlpha(const SvxBrushItem& rBrush)
9262 {
9263     std::optional<sal_Int32> oRet;
9264     sal_Int32 nTransparency = 255 - rBrush.GetColor().GetAlpha();
9265     if (nTransparency)
9266     {
9267         // Convert transparency to percent
9268         sal_Int8 nTransparencyPercent = SvxBrushItem::TransparencyToPercent(nTransparency);
9269 
9270         // Calculate alpha value
9271         // Consider oox/source/drawingml/color.cxx : getTransparency() function.
9272         sal_Int32 nAlpha = ::oox::drawingml::MAX_PERCENT - ( ::oox::drawingml::PER_PERCENT * nTransparencyPercent );
9273         oRet = nAlpha;
9274     }
9275     return oRet;
9276 }
9277 
FormatBackground(const SvxBrushItem & rBrush)9278 void DocxAttributeOutput::FormatBackground( const SvxBrushItem& rBrush )
9279 {
9280     const Color aColor = rBrush.GetColor();
9281     OString sColor = msfilter::util::ConvertColor( aColor.GetRGBColor() );
9282     std::optional<sal_Int32> oAlpha = lcl_getDmlAlpha(rBrush);
9283     if (m_rExport.SdrExporter().getTextFrameSyntax())
9284     {
9285         // Handle 'Opacity'
9286         if (oAlpha)
9287         {
9288             // Calculate opacity value
9289             // Consider oox/source/vml/vmlformatting.cxx : decodeColor() function.
9290             double fOpacity = static_cast<double>(*oAlpha) * 65535 / ::oox::drawingml::MAX_PERCENT;
9291             OUString sOpacity = OUString::number(fOpacity) + "f";
9292 
9293             AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_opacity, OUStringToOString(sOpacity, RTL_TEXTENCODING_UTF8).getStr() );
9294         }
9295 
9296         sColor = "#" + sColor;
9297         AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, sColor.getStr() );
9298     }
9299     else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
9300     {
9301         bool bImageBackground = false;
9302         const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE);
9303         if (pItem)
9304         {
9305             const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem);
9306             if(pFillStyle->GetValue() == drawing::FillStyle_BITMAP)
9307             {
9308                 bImageBackground = true;
9309             }
9310         }
9311         if (!bImageBackground)
9312         {
9313             m_pSerializer->startElementNS(XML_a, XML_solidFill);
9314             m_pSerializer->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
9315             if (oAlpha)
9316                 m_pSerializer->singleElementNS(XML_a, XML_alpha,
9317                                               XML_val, OString::number(*oAlpha));
9318             m_pSerializer->endElementNS(XML_a, XML_srgbClr);
9319             m_pSerializer->endElementNS(XML_a, XML_solidFill);
9320         }
9321     }
9322     else if ( !m_rExport.m_bOutPageDescs )
9323     {
9324         // compare fill color with the original fill color
9325         OString sOriginalFill = OUStringToOString(
9326                 m_sOriginalBackgroundColor, RTL_TEXTENCODING_UTF8 );
9327 
9328         if ( aColor == COL_AUTO )
9329             sColor = "auto";
9330 
9331         if( !m_pBackgroundAttrList.is() )
9332         {
9333             m_pBackgroundAttrList = FastSerializerHelper::createAttrList();
9334             m_pBackgroundAttrList->add(FSNS(XML_w, XML_fill), sColor);
9335             m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" );
9336         }
9337         else if ( sOriginalFill != sColor )
9338         {
9339             // fill was modified during edition, theme fill attribute must be dropped
9340             m_pBackgroundAttrList = FastSerializerHelper::createAttrList();
9341             m_pBackgroundAttrList->add(FSNS(XML_w, XML_fill), sColor);
9342             m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" );
9343         }
9344         m_sOriginalBackgroundColor.clear();
9345     }
9346 }
9347 
FormatFillStyle(const XFillStyleItem & rFillStyle)9348 void DocxAttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle )
9349 {
9350     if (!m_bIgnoreNextFill)
9351         m_oFillStyle = rFillStyle.GetValue();
9352     else
9353         m_bIgnoreNextFill = false;
9354 
9355     // Don't round-trip grabbag OriginalBackground if the background has been cleared.
9356     if ( m_pBackgroundAttrList.is() && m_sOriginalBackgroundColor != "auto" && rFillStyle.GetValue() == drawing::FillStyle_NONE )
9357         m_pBackgroundAttrList.clear();
9358 }
9359 
FormatFillGradient(const XFillGradientItem & rFillGradient)9360 void DocxAttributeOutput::FormatFillGradient( const XFillGradientItem& rFillGradient )
9361 {
9362     if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && !m_rExport.SdrExporter().getDMLTextFrameSyntax())
9363     {
9364         AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_type, "gradient" );
9365 
9366         const XGradient& rGradient = rFillGradient.GetGradientValue();
9367         OString sStartColor = msfilter::util::ConvertColor(rGradient.GetStartColor());
9368         OString sEndColor = msfilter::util::ConvertColor(rGradient.GetEndColor());
9369 
9370         // Calculate the angle that was originally in the imported DOCX file
9371         // (reverse calculate the angle that was converted in the file
9372         //     /oox/source/vml/vmlformatting.cxx :: FillModel::pushToPropMap
9373         // and also in
9374         //     /oox/source/drawingml/fillproperties.cxx :: FillProperties::pushToPropMap
9375         sal_Int32 nReverseAngle = (4500_deg10 - rGradient.GetAngle()).get();
9376         nReverseAngle = nReverseAngle / 10;
9377         nReverseAngle = (270 - nReverseAngle) % 360;
9378         if (nReverseAngle != 0)
9379             AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(),
9380                     XML_angle, OString::number( nReverseAngle ).getStr() );
9381 
9382         OString sColor1 = sStartColor;
9383         OString sColor2 = sEndColor;
9384 
9385         switch (rGradient.GetGradientStyle())
9386         {
9387             case css::awt::GradientStyle_AXIAL:
9388                 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_focus, "50%" );
9389                 // If it is an 'axial' gradient - swap the colors
9390                 // (because in the import process they were imported swapped)
9391                 sColor1 = sEndColor;
9392                 sColor2 = sStartColor;
9393                 break;
9394             case css::awt::GradientStyle_LINEAR: break;
9395             case css::awt::GradientStyle_RADIAL: break;
9396             case css::awt::GradientStyle_ELLIPTICAL: break;
9397             case css::awt::GradientStyle_SQUARE: break;
9398             case css::awt::GradientStyle_RECT: break;
9399             default:
9400                 break;
9401         }
9402 
9403         sColor1 = "#" + sColor1;
9404         sColor2 = "#" + sColor2;
9405         AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, sColor1.getStr() );
9406         AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_color2, sColor2.getStr() );
9407     }
9408     else if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && m_rExport.SdrExporter().getDMLTextFrameSyntax())
9409     {
9410         SwFrameFormat & rFormat(
9411                 const_cast<SwFrameFormat&>(m_rExport.m_pParentFrame->GetFrameFormat()));
9412         uno::Reference<beans::XPropertySet> const xPropertySet(
9413             SwXTextFrame::CreateXTextFrame(*rFormat.GetDoc(), &rFormat),
9414             uno::UNO_QUERY);
9415         m_rDrawingML.SetFS(m_pSerializer);
9416         m_rDrawingML.WriteGradientFill(xPropertySet);
9417     }
9418     m_oFillStyle.reset();
9419 }
9420 
FormatBox(const SvxBoxItem & rBox)9421 void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox )
9422 {
9423     if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
9424     {
9425         // ugh, exporting fill here is quite some hack... this OutputItemSet abstraction is quite leaky
9426         // <a:gradFill> should be before <a:ln>.
9427         const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE);
9428         if (pItem)
9429         {
9430             const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem);
9431             FormatFillStyle(*pFillStyle);
9432             if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_BITMAP)
9433             {
9434                 const SdrObject* pSdrObj = m_rExport.m_pParentFrame->GetFrameFormat().FindRealSdrObject();
9435                 if (pSdrObj)
9436                 {
9437                     uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY );
9438                     uno::Reference< beans::XPropertySet > xPropertySet( xShape, uno::UNO_QUERY );
9439                     m_rDrawingML.SetFS(m_pSerializer);
9440                     m_rDrawingML.WriteBlipFill(xPropertySet, "BackGraphic");
9441                 }
9442             }
9443         }
9444 
9445         pItem = GetExport().HasItem(XATTR_FILLGRADIENT);
9446         if (pItem)
9447         {
9448             const XFillGradientItem* pFillGradient = static_cast<const XFillGradientItem*>(pItem);
9449             FormatFillGradient(*pFillGradient);
9450         }
9451         m_bIgnoreNextFill = true;
9452     }
9453     if (m_rExport.SdrExporter().getTextFrameSyntax() || m_rExport.SdrExporter().getDMLTextFrameSyntax())
9454     {
9455         const SvxBorderLine* pLeft = rBox.GetLeft( );
9456         const SvxBorderLine* pTop = rBox.GetTop( );
9457         const SvxBorderLine* pRight = rBox.GetRight( );
9458         const SvxBorderLine* pBottom = rBox.GetBottom( );
9459 
9460         if (pLeft && pRight && pTop && pBottom &&
9461                 *pLeft == *pRight && *pLeft == *pTop && *pLeft == *pBottom)
9462         {
9463             // Check border style
9464             SvxBorderLineStyle eBorderStyle = pTop->GetBorderLineStyle();
9465             if (eBorderStyle == SvxBorderLineStyle::NONE)
9466             {
9467                 if (m_rExport.SdrExporter().getTextFrameSyntax())
9468                 {
9469                     AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2,
9470                             XML_stroked, "f", XML_strokeweight, "0pt" );
9471                 }
9472             }
9473             else
9474             {
9475                 OString sColor(msfilter::util::ConvertColor(pTop->GetColor()));
9476                 double const fConverted(editeng::ConvertBorderWidthToWord(pTop->GetBorderLineStyle(), pTop->GetWidth()));
9477 
9478                 if (m_rExport.SdrExporter().getTextFrameSyntax())
9479                 {
9480                     sColor = "#" + sColor;
9481                     sal_Int32 nWidth = sal_Int32(fConverted / 20);
9482                     OString sWidth = OString::number(nWidth) + "pt";
9483                     AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2,
9484                             XML_strokecolor, sColor.getStr(),
9485                             XML_strokeweight, sWidth.getStr() );
9486                     if( SvxBorderLineStyle::DASHED == pTop->GetBorderLineStyle() ) // Line Style is Dash type
9487                         AddToAttrList( m_rExport.SdrExporter().getDashLineStyle(),
9488                             XML_dashstyle, "dash" );
9489                 }
9490                 else
9491                     m_rExport.SdrExporter().writeBoxItemLine(rBox);
9492             }
9493         }
9494 
9495         if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
9496         {
9497             m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_lIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::LEFT))));
9498             m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_tIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::TOP))));
9499             m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_rIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::RIGHT))));
9500             m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_bIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::BOTTOM))));
9501             return;
9502         }
9503 
9504         // v:textbox's inset attribute: inner margin values for textbox text - write only non-default values
9505         double fDistanceLeftTwips = double(rBox.GetDistance(SvxBoxItemLine::LEFT));
9506         double fDistanceTopTwips = double(rBox.GetDistance(SvxBoxItemLine::TOP));
9507         double fDistanceRightTwips = double(rBox.GetDistance(SvxBoxItemLine::RIGHT));
9508         double fDistanceBottomTwips = double(rBox.GetDistance(SvxBoxItemLine::BOTTOM));
9509 
9510         // Convert 'TWIPS' to 'INCH' (because in Word the default values are in Inches)
9511         double fDistanceLeftInch = o3tl::convert(fDistanceLeftTwips, o3tl::Length::twip, o3tl::Length::in);
9512         double fDistanceTopInch = o3tl::convert(fDistanceTopTwips, o3tl::Length::twip, o3tl::Length::in);
9513         double fDistanceRightInch = o3tl::convert(fDistanceRightTwips, o3tl::Length::twip, o3tl::Length::in);
9514         double fDistanceBottomInch = o3tl::convert(fDistanceBottomTwips, o3tl::Length::twip, o3tl::Length::in);
9515 
9516         // This code will write ONLY the non-default values. The values are in 'left','top','right','bottom' order.
9517         // so 'bottom' is checked if it is default and if it is non-default - all the values will be written
9518         // otherwise - 'right' is checked if it is default and if it is non-default - all the values except for 'bottom' will be written
9519         // and so on.
9520         OStringBuffer aInset;
9521         if(!aInset.isEmpty() || fDistanceBottomInch != 0.05)
9522             aInset.insert(0, "," + OString::number(fDistanceBottomInch) + "in");
9523 
9524         if(!aInset.isEmpty() || fDistanceRightInch != 0.1)
9525             aInset.insert(0, "," + OString::number(fDistanceRightInch) + "in");
9526 
9527         if(!aInset.isEmpty() || fDistanceTopInch != 0.05)
9528             aInset.insert(0, "," + OString::number(fDistanceTopInch) + "in");
9529 
9530         if(!aInset.isEmpty() || fDistanceLeftInch != 0.1)
9531             aInset.insert(0, OString::number(fDistanceLeftInch) + "in");
9532 
9533         if (!aInset.isEmpty())
9534             m_rExport.SdrExporter().getTextboxAttrList()->add(XML_inset, aInset.makeStringAndClear());
9535 
9536         return;
9537     }
9538 
9539     OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions();
9540     // Check if there is a shadow item
9541     const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW );
9542     if ( pItem )
9543     {
9544         const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
9545         aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation();
9546     }
9547 
9548     if ( m_bOpenedSectPr && !GetWritingHeaderFooter())
9549         return;
9550 
9551     // Not inside a section
9552 
9553     // Open the paragraph's borders tag
9554     m_pSerializer->startElementNS(XML_w, XML_pBdr);
9555 
9556     std::map<SvxBoxItemLine, css::table::BorderLine2> aStyleBorders;
9557     const SvxBoxItem* pInherited = nullptr;
9558     if ( GetExport().m_pStyAttr )
9559         pInherited = GetExport().m_pStyAttr->GetItem<SvxBoxItem>(RES_BOX);
9560     else if ( GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom() )
9561         pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SvxBoxItem>(RES_BOX);
9562 
9563     if ( pInherited )
9564     {
9565         aStyleBorders[ SvxBoxItemLine::TOP ] = SvxBoxItem::SvxLineToLine(pInherited->GetTop(), /*bConvert=*/false);
9566         aStyleBorders[ SvxBoxItemLine::BOTTOM ] = SvxBoxItem::SvxLineToLine(pInherited->GetBottom(), false);
9567         aStyleBorders[ SvxBoxItemLine::LEFT ] = SvxBoxItem::SvxLineToLine(pInherited->GetLeft(), false);
9568         aStyleBorders[ SvxBoxItemLine::RIGHT ] = SvxBoxItem::SvxLineToLine(pInherited->GetRight(), false);
9569     }
9570 
9571     impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aStyleBorders );
9572 
9573     // Close the paragraph's borders tag
9574     m_pSerializer->endElementNS( XML_w, XML_pBdr );
9575 }
9576 
FormatColumns_Impl(sal_uInt16 nCols,const SwFormatCol & rCol,bool bEven,SwTwips nPageSize)9577 void DocxAttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven, SwTwips nPageSize )
9578 {
9579     // Get the columns attributes
9580     rtl::Reference<FastAttributeList> pColsAttrList = FastSerializerHelper::createAttrList();
9581 
9582     pColsAttrList->add( FSNS( XML_w, XML_num ),
9583             OString::number( nCols ). getStr( ) );
9584 
9585     const char* pEquals = "false";
9586     if ( bEven )
9587     {
9588         sal_uInt16 nWidth = rCol.GetGutterWidth( true );
9589         pColsAttrList->add( FSNS( XML_w, XML_space ),
9590                OString::number( nWidth ).getStr( ) );
9591 
9592         pEquals = "true";
9593     }
9594 
9595     pColsAttrList->add( FSNS( XML_w, XML_equalWidth ), pEquals );
9596 
9597     bool bHasSep = (COLADJ_NONE != rCol.GetLineAdj());
9598 
9599     pColsAttrList->add( FSNS( XML_w, XML_sep ), OString::boolean( bHasSep ) );
9600 
9601     // Write the element
9602     m_pSerializer->startElementNS( XML_w, XML_cols, pColsAttrList );
9603 
9604     // Write the columns width if non-equals
9605     const SwColumns & rColumns = rCol.GetColumns(  );
9606     if ( !bEven )
9607     {
9608         for ( sal_uInt16 n = 0; n < nCols; ++n )
9609         {
9610             rtl::Reference<FastAttributeList> pColAttrList = FastSerializerHelper::createAttrList();
9611             sal_uInt16 nWidth = rCol.CalcPrtColWidth( n, o3tl::narrowing<sal_uInt16>(nPageSize) );
9612             pColAttrList->add( FSNS( XML_w, XML_w ),
9613                     OString::number( nWidth ).getStr( ) );
9614 
9615             if ( n + 1 != nCols )
9616             {
9617                 sal_uInt16 nSpacing = rColumns[n].GetRight( ) + rColumns[n + 1].GetLeft( );
9618                 pColAttrList->add( FSNS( XML_w, XML_space ),
9619                     OString::number( nSpacing ).getStr( ) );
9620             }
9621 
9622             m_pSerializer->singleElementNS( XML_w, XML_col, pColAttrList );
9623         }
9624     }
9625 
9626     m_pSerializer->endElementNS( XML_w, XML_cols );
9627 }
9628 
FormatKeep(const SvxFormatKeepItem & rItem)9629 void DocxAttributeOutput::FormatKeep( const SvxFormatKeepItem& rItem )
9630 {
9631     m_pSerializer->singleElementNS( XML_w, XML_keepNext,
9632             FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ) );
9633 }
9634 
FormatTextGrid(const SwTextGridItem & rGrid)9635 void DocxAttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid )
9636 {
9637     rtl::Reference<FastAttributeList> pGridAttrList = FastSerializerHelper::createAttrList();
9638 
9639     OString sGridType;
9640     switch ( rGrid.GetGridType( ) )
9641     {
9642         default:
9643         case GRID_NONE:
9644             sGridType = OString( "default" );
9645             break;
9646         case GRID_LINES_ONLY:
9647             sGridType = OString( "lines" );
9648             break;
9649         case GRID_LINES_CHARS:
9650             if ( rGrid.IsSnapToChars( ) )
9651                 sGridType = OString( "snapToChars" );
9652             else
9653                 sGridType = OString( "linesAndChars" );
9654             break;
9655     }
9656     pGridAttrList->add(FSNS(XML_w, XML_type), sGridType);
9657 
9658     sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
9659     pGridAttrList->add( FSNS( XML_w, XML_linePitch ),
9660             OString::number( nHeight ).getStr( ) );
9661 
9662     pGridAttrList->add( FSNS( XML_w, XML_charSpace ),
9663             OString::number( GridCharacterPitch( rGrid ) ).getStr( ) );
9664 
9665     m_pSerializer->singleElementNS( XML_w, XML_docGrid, pGridAttrList );
9666 }
9667 
FormatLineNumbering(const SwFormatLineNumber & rNumbering)9668 void DocxAttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering )
9669 {
9670     if ( !rNumbering.IsCount( ) )
9671         m_pSerializer->singleElementNS(XML_w, XML_suppressLineNumbers);
9672 }
9673 
FormatFrameDirection(const SvxFrameDirectionItem & rDirection)9674 void DocxAttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection )
9675 {
9676     OString sTextFlow;
9677     bool bBiDi = false;
9678     SvxFrameDirection nDir = rDirection.GetValue();
9679 
9680     if ( nDir == SvxFrameDirection::Environment )
9681         nDir = GetExport( ).GetDefaultFrameDirection( );
9682 
9683     switch ( nDir )
9684     {
9685         default:
9686         case SvxFrameDirection::Horizontal_LR_TB:
9687             sTextFlow = OString( "lrTb" );
9688             break;
9689         case SvxFrameDirection::Horizontal_RL_TB:
9690             sTextFlow = OString( "lrTb" );
9691             bBiDi = true;
9692             break;
9693         case SvxFrameDirection::Vertical_LR_TB: // many things but not this one
9694         case SvxFrameDirection::Vertical_RL_TB:
9695             sTextFlow = OString( "tbRl" );
9696             break;
9697     }
9698 
9699     if ( m_rExport.m_bOutPageDescs )
9700     {
9701         m_pSerializer->singleElementNS(XML_w, XML_textDirection, FSNS(XML_w, XML_val), sTextFlow);
9702         if ( bBiDi )
9703             m_pSerializer->singleElementNS(XML_w, XML_bidi);
9704     }
9705     else if ( !m_rExport.m_bOutFlyFrameAttrs )
9706     {
9707         if ( bBiDi )
9708             m_pSerializer->singleElementNS(XML_w, XML_bidi, FSNS(XML_w, XML_val), "1");
9709         else
9710             m_pSerializer->singleElementNS(XML_w, XML_bidi, FSNS(XML_w, XML_val), "0");
9711     }
9712 }
9713 
ParaGrabBag(const SfxGrabBagItem & rItem)9714 void DocxAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem)
9715 {
9716     const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
9717     for ( const auto & rGrabBagElement : rMap )
9718     {
9719         if (rGrabBagElement.first == "MirrorIndents")
9720             m_pSerializer->singleElementNS(XML_w, XML_mirrorIndents);
9721         else if (rGrabBagElement.first == "ParaTopMarginBeforeAutoSpacing")
9722         {
9723             m_bParaBeforeAutoSpacing = true;
9724             // get fixed value which was set during import
9725             rGrabBagElement.second >>= m_nParaBeforeSpacing;
9726             m_nParaBeforeSpacing = convertMm100ToTwip(m_nParaBeforeSpacing);
9727             SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << rGrabBagElement.first << " : m_nParaBeforeSpacing= " << m_nParaBeforeSpacing);
9728         }
9729         else if (rGrabBagElement.first == "ParaBottomMarginAfterAutoSpacing")
9730         {
9731             m_bParaAfterAutoSpacing = true;
9732             // get fixed value which was set during import
9733             rGrabBagElement.second >>= m_nParaAfterSpacing;
9734             m_nParaAfterSpacing = convertMm100ToTwip(m_nParaAfterSpacing);
9735             SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << rGrabBagElement.first << " : m_nParaBeforeSpacing= " << m_nParaAfterSpacing);
9736         }
9737         else if (rGrabBagElement.first == "CharThemeFill")
9738         {
9739             uno::Sequence<beans::PropertyValue> aGrabBagSeq;
9740             rGrabBagElement.second >>= aGrabBagSeq;
9741 
9742             for (const auto& rProp : std::as_const(aGrabBagSeq))
9743             {
9744                 OString sVal = OUStringToOString(rProp.Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
9745 
9746                 if (sVal.isEmpty())
9747                     continue;
9748 
9749                 if (rProp.Name == "val")
9750                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_val), sVal.getStr());
9751                 else if (rProp.Name == "color")
9752                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_color), sVal.getStr());
9753                 else if (rProp.Name == "themeColor")
9754                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeColor), sVal.getStr());
9755                 else if (rProp.Name == "themeTint")
9756                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeTint), sVal.getStr());
9757                 else if (rProp.Name == "themeShade")
9758                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeShade), sVal.getStr());
9759                 else if (rProp.Name == "fill")
9760                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_fill), sVal.getStr());
9761                 else if (rProp.Name == "themeFill")
9762                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFill), sVal.getStr());
9763                 else if (rProp.Name == "themeFillTint")
9764                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillTint), sVal.getStr());
9765                 else if (rProp.Name == "themeFillShade")
9766                     AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillShade), sVal.getStr());
9767                 else if (rProp.Name == "originalColor")
9768                     rProp.Value >>= m_sOriginalBackgroundColor;
9769             }
9770         }
9771         else if (rGrabBagElement.first == "SdtPr")
9772         {
9773             const uno::Sequence<beans::PropertyValue> aGrabBagSdt =
9774                     rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
9775             for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt)
9776             {
9777                 if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" ||
9778                         aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
9779                 {
9780                     if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj")
9781                         m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartObj );
9782                     else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
9783                         m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartList );
9784 
9785                     uno::Sequence<beans::PropertyValue> aGrabBag;
9786                     aPropertyValue.Value >>= aGrabBag;
9787                     for (const auto& rProp : std::as_const(aGrabBag))
9788                     {
9789                         OUString sValue = rProp.Value.get<OUString>();
9790                         if (rProp.Name == "ooxml:CT_SdtDocPart_docPartGallery")
9791                             AddToAttrList( m_pParagraphSdtPrTokenChildren,
9792                                            FSNS( XML_w, XML_docPartGallery ),
9793                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9794                         else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartCategory")
9795                             AddToAttrList( m_pParagraphSdtPrTokenChildren,
9796                                            FSNS( XML_w, XML_docPartCategory ),
9797                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9798                         else if (rProp.Name == "ooxml:CT_SdtDocPart_docPartUnique")
9799                         {
9800                             if (sValue.isEmpty())
9801                                 sValue = "true";
9802                             AddToAttrList( m_pParagraphSdtPrTokenChildren, FSNS( XML_w, XML_docPartUnique ),
9803                             OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9804                         }
9805                     }
9806                 }
9807                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation")
9808                     m_nParagraphSdtPrToken = FSNS( XML_w, XML_equation );
9809                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture")
9810                     m_nParagraphSdtPrToken = FSNS( XML_w, XML_picture );
9811                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
9812                     m_nParagraphSdtPrToken = FSNS( XML_w, XML_citation );
9813                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group")
9814                     m_nParagraphSdtPrToken = FSNS( XML_w, XML_group );
9815                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text")
9816                 {
9817                     uno::Sequence<beans::PropertyValue> aGrabBag;
9818                     aPropertyValue.Value >>= aGrabBag;
9819                     if (aGrabBag.hasElements())
9820                     {
9821                         for (const auto& rProp : std::as_const(aGrabBag))
9822                         {
9823                             OUString sValue = rProp.Value.get<OUString>();
9824                             if (rProp.Name == "ooxml:LN_CT_SdtText_multiLine")
9825                                 AddToAttrList(m_pParagraphSdtPrTextAttrs,
9826                                     FSNS(XML_w, XML_multiLine),
9827                                     OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
9828                         }
9829                     }
9830                     else
9831                     {
9832                         // We still have w:text, but no attrs
9833                         m_nParagraphSdtPrToken = FSNS(XML_w, XML_text);
9834                     }
9835                 }
9836                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pParagraphSdtPrDataBindingAttrs.is())
9837                 {
9838                     uno::Sequence<beans::PropertyValue> aGrabBag;
9839                     aPropertyValue.Value >>= aGrabBag;
9840                     for (const auto& rProp : std::as_const(aGrabBag))
9841                     {
9842                         OUString sValue = rProp.Value.get<OUString>();
9843                         if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings")
9844                             AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
9845                                            FSNS( XML_w, XML_prefixMappings ),
9846                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9847                         else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
9848                             AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
9849                                            FSNS( XML_w, XML_xpath ),
9850                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9851                         else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID")
9852                             AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
9853                                            FSNS( XML_w, XML_storeItemID ),
9854                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9855                     }
9856                 }
9857                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aParagraphSdtPrAlias.isEmpty())
9858                 {
9859                     if (!(aPropertyValue.Value >>= m_aParagraphSdtPrAlias))
9860                         SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unexpected sdt alias value");
9861                     m_aStartedParagraphSdtPrAlias = m_aParagraphSdtPrAlias;
9862                 }
9863                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
9864                 {
9865                     m_nParagraphSdtPrToken = FSNS( XML_w14, XML_checkbox );
9866                     uno::Sequence<beans::PropertyValue> aGrabBag;
9867                     aPropertyValue.Value >>= aGrabBag;
9868                     for (const auto& rProp : std::as_const(aGrabBag))
9869                     {
9870                         OUString sValue = rProp.Value.get<OUString>();
9871                         if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
9872                             AddToAttrList( m_pParagraphSdtPrTokenChildren,
9873                                            FSNS( XML_w14, XML_checked ),
9874                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9875                         else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState")
9876                             AddToAttrList( m_pParagraphSdtPrTokenChildren,
9877                                            FSNS( XML_w14, XML_checkedState ),
9878                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9879                         else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState")
9880                             AddToAttrList( m_pParagraphSdtPrTokenChildren,
9881                                            FSNS( XML_w14, XML_uncheckedState ),
9882                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
9883                     }
9884                 }
9885                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id")
9886                     m_bParagraphSdtHasId = true;
9887                 else
9888                     SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled SdtPr grab bag property " << aPropertyValue.Name);
9889             }
9890         }
9891         else if (rGrabBagElement.first == "ParaCnfStyle")
9892         {
9893             uno::Sequence<beans::PropertyValue> aAttributes = rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
9894             m_pTableStyleExport->CnfStyle(aAttributes);
9895         }
9896         else if (rGrabBagElement.first == "ParaSdtEndBefore")
9897         {
9898             // Handled already in StartParagraph().
9899         }
9900         else
9901             SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled grab bag property " << rGrabBagElement.first );
9902     }
9903 }
9904 
CharGrabBag(const SfxGrabBagItem & rItem)9905 void DocxAttributeOutput::CharGrabBag( const SfxGrabBagItem& rItem )
9906 {
9907     if (m_bPreventDoubleFieldsHandling)
9908         return;
9909 
9910     const std::map< OUString, css::uno::Any >& rMap = rItem.GetGrabBag();
9911 
9912     // get original values of theme-derived properties to check if they have changed during the edition
9913     bool bWriteCSTheme = true;
9914     bool bWriteAsciiTheme = true;
9915     bool bWriteEastAsiaTheme = true;
9916     bool bWriteThemeFontColor = true;
9917     OUString sOriginalValue;
9918     for ( const auto & rGrabBagElement : rMap )
9919     {
9920         if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameCs" )
9921         {
9922             if ( rGrabBagElement.second >>= sOriginalValue )
9923                 bWriteCSTheme =
9924                         ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_cs ) ) == sOriginalValue );
9925         }
9926         else if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameAscii" )
9927         {
9928             if ( rGrabBagElement.second >>= sOriginalValue )
9929                 bWriteAsciiTheme =
9930                         ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_ascii ) ) == sOriginalValue );
9931         }
9932         else if ( m_pFontsAttrList.is() && rGrabBagElement.first == "CharThemeFontNameEastAsia" )
9933         {
9934             if ( rGrabBagElement.second >>= sOriginalValue )
9935                 bWriteEastAsiaTheme =
9936                         ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_eastAsia ) ) == sOriginalValue );
9937         }
9938         else if ( m_pColorAttrList.is() && rGrabBagElement.first == "CharThemeOriginalColor" )
9939         {
9940             if ( rGrabBagElement.second >>= sOriginalValue )
9941                 bWriteThemeFontColor =
9942                         ( m_pColorAttrList->getOptionalValue( FSNS( XML_w, XML_val ) ) == sOriginalValue );
9943         }
9944     }
9945 
9946     // save theme attributes back to the run properties
9947     OUString str;
9948     for ( const auto & rGrabBagElement : rMap )
9949     {
9950         if ( rGrabBagElement.first == "CharThemeNameAscii" && bWriteAsciiTheme )
9951         {
9952             rGrabBagElement.second >>= str;
9953             AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_asciiTheme ),
9954                     OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
9955         }
9956         else if ( rGrabBagElement.first == "CharThemeNameCs" && bWriteCSTheme )
9957         {
9958             rGrabBagElement.second >>= str;
9959             AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cstheme ),
9960                     OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
9961         }
9962         else if ( rGrabBagElement.first == "CharThemeNameEastAsia" && bWriteEastAsiaTheme )
9963         {
9964             rGrabBagElement.second >>= str;
9965             AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsiaTheme ),
9966                     OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
9967         }
9968         else if ( rGrabBagElement.first == "CharThemeNameHAnsi" && bWriteAsciiTheme )
9969         // this is not a mistake: in LibO we don't directly support the hAnsi family
9970         // of attributes so we save the same value from ascii attributes instead
9971         {
9972             rGrabBagElement.second >>= str;
9973             AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_hAnsiTheme ),
9974                     OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
9975         }
9976         else if ( rGrabBagElement.first == "CharThemeColor" && bWriteThemeFontColor )
9977         {
9978             rGrabBagElement.second >>= str;
9979             AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeColor ),
9980                     OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
9981         }
9982         else if ( rGrabBagElement.first == "CharThemeColorShade" )
9983         {
9984             rGrabBagElement.second >>= str;
9985             AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeShade ),
9986                     OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
9987         }
9988         else if ( rGrabBagElement.first == "CharThemeColorTint" )
9989         {
9990             rGrabBagElement.second >>= str;
9991             AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeTint ),
9992                     OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
9993         }
9994         else if( rGrabBagElement.first == "CharThemeFontNameCs"   ||
9995                 rGrabBagElement.first == "CharThemeFontNameAscii" ||
9996                 rGrabBagElement.first == "CharThemeFontNameEastAsia" ||
9997                 rGrabBagElement.first == "CharThemeOriginalColor" )
9998         {
9999             // just skip these, they were processed before
10000         }
10001         else if(rGrabBagElement.first == "CharGlowTextEffect" ||
10002                 rGrabBagElement.first == "CharShadowTextEffect" ||
10003                 rGrabBagElement.first == "CharReflectionTextEffect" ||
10004                 rGrabBagElement.first == "CharTextOutlineTextEffect" ||
10005                 rGrabBagElement.first == "CharTextFillTextEffect" ||
10006                 rGrabBagElement.first == "CharScene3DTextEffect" ||
10007                 rGrabBagElement.first == "CharProps3DTextEffect" ||
10008                 rGrabBagElement.first == "CharLigaturesTextEffect" ||
10009                 rGrabBagElement.first == "CharNumFormTextEffect" ||
10010                 rGrabBagElement.first == "CharNumSpacingTextEffect" ||
10011                 rGrabBagElement.first == "CharStylisticSetsTextEffect" ||
10012                 rGrabBagElement.first == "CharCntxtAltsTextEffect")
10013         {
10014             beans::PropertyValue aPropertyValue;
10015             rGrabBagElement.second >>= aPropertyValue;
10016             m_aTextEffectsGrabBag.push_back(aPropertyValue);
10017         }
10018         else if (rGrabBagElement.first == "SdtEndBefore")
10019         {
10020             if (m_bStartedCharSdt)
10021                 m_bEndCharSdt = true;
10022         }
10023         else if (rGrabBagElement.first == "SdtPr" && FLY_NOT_PROCESSED != m_nStateOfFlyFrame )
10024         {
10025             const uno::Sequence<beans::PropertyValue> aGrabBagSdt =
10026                     rGrabBagElement.second.get< uno::Sequence<beans::PropertyValue> >();
10027             for (const beans::PropertyValue& aPropertyValue : aGrabBagSdt)
10028             {
10029                 if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
10030                 {
10031                     m_nRunSdtPrToken = FSNS( XML_w14, XML_checkbox );
10032                     uno::Sequence<beans::PropertyValue> aGrabBag;
10033                     aPropertyValue.Value >>= aGrabBag;
10034                     for (const auto& rProp : std::as_const(aGrabBag))
10035                     {
10036                         OUString sValue = rProp.Value.get<OUString>();
10037                         if (rProp.Name == "ooxml:CT_SdtCheckbox_checked")
10038                             AddToAttrList( m_pRunSdtPrTokenChildren,
10039                                            FSNS( XML_w14, XML_checked ),
10040                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
10041                         else if (rProp.Name == "ooxml:CT_SdtCheckbox_checkedState")
10042                             AddToAttrList( m_pRunSdtPrTokenChildren,
10043                                            FSNS( XML_w14, XML_checkedState ),
10044                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
10045                         else if (rProp.Name == "ooxml:CT_SdtCheckbox_uncheckedState")
10046                             AddToAttrList( m_pRunSdtPrTokenChildren,
10047                                            FSNS( XML_w14, XML_uncheckedState ),
10048                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
10049                     }
10050                 }
10051                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text")
10052                 {
10053                     uno::Sequence<beans::PropertyValue> aGrabBag;
10054                     aPropertyValue.Value >>= aGrabBag;
10055                     for (const auto& rProp : std::as_const(aGrabBag))
10056                     {
10057                         OUString sValue = rProp.Value.get<OUString>();
10058                         if (rProp.Name == "ooxml:LN_CT_SdtText_multiLine")
10059                             AddToAttrList(m_pRunSdtPrTextAttrs,
10060                                 FSNS(XML_w, XML_multiLine),
10061                                 OUStringToOString(sValue, RTL_TEXTENCODING_UTF8).getStr());
10062                     }
10063                 }
10064                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pRunSdtPrDataBindingAttrs.is())
10065                 {
10066                     uno::Sequence<beans::PropertyValue> aGrabBag;
10067                     aPropertyValue.Value >>= aGrabBag;
10068                     for (const auto& rProp : std::as_const(aGrabBag))
10069                     {
10070                         OUString sValue = rProp.Value.get<OUString>();
10071                         if (rProp.Name == "ooxml:CT_DataBinding_prefixMappings")
10072                             AddToAttrList( m_pRunSdtPrDataBindingAttrs,
10073                                            FSNS( XML_w, XML_prefixMappings ),
10074                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
10075                         else if (rProp.Name == "ooxml:CT_DataBinding_xpath")
10076                             AddToAttrList( m_pRunSdtPrDataBindingAttrs,
10077                                            FSNS( XML_w, XML_xpath ),
10078                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
10079                         else if (rProp.Name == "ooxml:CT_DataBinding_storeItemID")
10080                             AddToAttrList( m_pRunSdtPrDataBindingAttrs,
10081                                            FSNS( XML_w, XML_storeItemID ),
10082                                            OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
10083                     }
10084                 }
10085                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aRunSdtPrAlias.isEmpty())
10086                 {
10087                     if (!(aPropertyValue.Value >>= m_aRunSdtPrAlias))
10088                         SAL_WARN("sw.ww8", "DocxAttributeOutput::CharGrabBag: unexpected sdt alias value");
10089                 }
10090                 //do not overwrite the parent node.
10091                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text" && !m_pRunSdtPrTokenChildren.is())
10092                     m_nRunSdtPrToken = FSNS( XML_w, XML_text );
10093                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id" && m_nRunSdtPrToken == 0)
10094                     // only write id token as a marker if no other exist
10095                     m_nRunSdtPrToken = FSNS( XML_w, XML_id );
10096                 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
10097                     m_nRunSdtPrToken = FSNS( XML_w, XML_citation );
10098             }
10099         }
10100         else
10101             SAL_INFO("sw.ww8", "DocxAttributeOutput::CharGrabBag: unhandled grab bag property " << rGrabBagElement.first);
10102     }
10103 }
10104 
DocxAttributeOutput(DocxExport & rExport,const FSHelperPtr & pSerializer,oox::drawingml::DrawingML * pDrawingML)10105 DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr& pSerializer, oox::drawingml::DrawingML* pDrawingML )
10106     : AttributeOutputBase(rExport.GetFilter().getFileUrl()),
10107       m_rExport( rExport ),
10108       m_pSerializer( pSerializer ),
10109       m_rDrawingML( *pDrawingML ),
10110       m_bEndCharSdt(false),
10111       m_bStartedCharSdt(false),
10112       m_bStartedParaSdt(false),
10113       m_endPageRef( false ),
10114       m_pFootnotesList( new ::docx::FootnotesList() ),
10115       m_pEndnotesList( new ::docx::FootnotesList() ),
10116       m_footnoteEndnoteRefTag( 0 ),
10117       m_footnoteCustomLabel(),
10118       m_pRedlineData( nullptr ),
10119       m_nRedlineId( 0 ),
10120       m_bOpenedSectPr( false ),
10121       m_bHadSectPr(false),
10122       m_bRunTextIsOn( false ),
10123       m_bWritingHeaderFooter( false ),
10124       m_bAnchorLinkedToNode(false),
10125       m_bWritingField( false ),
10126       m_bPreventDoubleFieldsHandling( false ),
10127       m_sFieldBkm( ),
10128       m_nNextBookmarkId( 0 ),
10129       m_nNextAnnotationMarkId( 0 ),
10130       m_nEmbedFlyLevel(0),
10131       m_pCurrentFrame( nullptr ),
10132       m_bParagraphOpened( false ),
10133       m_bParagraphFrameOpen( false ),
10134       m_bIsFirstParagraph( true ),
10135       m_bAlternateContentChoiceOpen( false ),
10136       m_bPostponedProcessingFly( false ),
10137       m_nColBreakStatus( COLBRK_NONE ),
10138       m_bPostponedPageBreak( false ),
10139       m_nTextFrameLevel( 0 ),
10140       m_closeHyperlinkInThisRun( false ),
10141       m_closeHyperlinkInPreviousRun( false ),
10142       m_startedHyperlink( false ),
10143       m_nHyperLinkCount(0),
10144       m_nFieldsInHyperlink( 0 ),
10145       m_bExportingOutline(false),
10146       m_nChartCount(0),
10147       pendingPlaceholder( nullptr ),
10148       m_postitFieldsMaxId( 0 ),
10149       m_anchorId( 1 ),
10150       m_nextFontId( 1 ),
10151       m_tableReference(new TableReference()),
10152       m_bIgnoreNextFill(false),
10153       m_pTableStyleExport(std::make_shared<DocxTableStyleExport>(rExport.m_rDoc, pSerializer)),
10154       m_bParaBeforeAutoSpacing(false),
10155       m_bParaAfterAutoSpacing(false),
10156       m_nParaBeforeSpacing(0),
10157       m_nParaAfterSpacing(0)
10158     , m_nParagraphSdtPrToken(0)
10159     , m_nRunSdtPrToken(0)
10160     , m_nStateOfFlyFrame( FLY_NOT_PROCESSED )
10161     , m_bParagraphSdtHasId(false)
10162 {
10163     // Push initial items to the RelId cache. In case the document contains no
10164     // special streams (headers, footers, etc.) then these items are used
10165     // during the full export.
10166     PushRelIdCache();
10167 }
10168 
~DocxAttributeOutput()10169 DocxAttributeOutput::~DocxAttributeOutput()
10170 {
10171 }
10172 
GetExport()10173 DocxExport& DocxAttributeOutput::GetExport()
10174 {
10175     return m_rExport;
10176 }
10177 
SetSerializer(::sax_fastparser::FSHelperPtr const & pSerializer)10178 void DocxAttributeOutput::SetSerializer( ::sax_fastparser::FSHelperPtr const & pSerializer )
10179 {
10180     m_pSerializer = pSerializer;
10181     m_pTableStyleExport->SetSerializer(pSerializer);
10182 }
10183 
HasFootnotes() const10184 bool DocxAttributeOutput::HasFootnotes() const
10185 {
10186     return !m_pFootnotesList->isEmpty();
10187 }
10188 
HasEndnotes() const10189 bool DocxAttributeOutput::HasEndnotes() const
10190 {
10191     return !m_pEndnotesList->isEmpty();
10192 }
10193 
HasPostitFields() const10194 bool DocxAttributeOutput::HasPostitFields() const
10195 {
10196     return !m_postitFields.empty();
10197 }
10198 
BulletDefinition(int nId,const Graphic & rGraphic,Size aSize)10199 void DocxAttributeOutput::BulletDefinition(int nId, const Graphic& rGraphic, Size aSize)
10200 {
10201     m_pSerializer->startElementNS(XML_w, XML_numPicBullet,
10202             FSNS(XML_w, XML_numPicBulletId), OString::number(nId));
10203 
10204     OStringBuffer aStyle;
10205     // Size is in twips, we need it in points.
10206     aStyle.append("width:" + OString::number(double(aSize.Width()) / 20));
10207     aStyle.append("pt;height:" + OString::number(double(aSize.Height()) / 20) + "pt");
10208     m_pSerializer->startElementNS(XML_w, XML_pict);
10209     m_pSerializer->startElementNS( XML_v, XML_shape,
10210             XML_style, aStyle.getStr(),
10211             FSNS(XML_o, XML_bullet), "t");
10212 
10213     OUString aRelId = m_rDrawingML.WriteImage(rGraphic);
10214     m_pSerializer->singleElementNS( XML_v, XML_imagedata,
10215             FSNS(XML_r, XML_id), OUStringToOString(aRelId, RTL_TEXTENCODING_UTF8),
10216             FSNS(XML_o, XML_title), "");
10217 
10218     m_pSerializer->endElementNS(XML_v, XML_shape);
10219     m_pSerializer->endElementNS(XML_w, XML_pict);
10220 
10221     m_pSerializer->endElementNS(XML_w, XML_numPicBullet);
10222 }
10223 
AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList> & pAttrList,sal_Int32 nAttrName,const char * sAttrValue)10224 void DocxAttributeOutput::AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const char* sAttrValue )
10225 {
10226     AddToAttrList( pAttrList, 1, nAttrName, sAttrValue );
10227 }
10228 
AddToAttrList(rtl::Reference<sax_fastparser::FastAttributeList> & pAttrList,sal_Int32 nAttrs,...)10229 void DocxAttributeOutput::AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ... )
10230 {
10231     if( !pAttrList.is() )
10232         pAttrList = FastSerializerHelper::createAttrList();
10233 
10234     va_list args;
10235     va_start( args, nAttrs );
10236     for( sal_Int32 i = 0; i<nAttrs; i++)
10237     {
10238         sal_Int32 nName = va_arg( args, sal_Int32 );
10239         const char* pValue = va_arg( args, const char* );
10240         if( pValue )
10241             pAttrList->add( nName, pValue );
10242     }
10243     va_end( args );
10244 }
10245 
10246 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
10247