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