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