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