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