build fix
[LibreOffice.git] / sw / source / filter / ww8 / docxattributeoutput.cxx
blob859b9925ae657df275cb92b516c5fb9f858b0a62
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "docxattributeoutput.hxx"
21 #include "docxhelper.hxx"
22 #include "docxsdrexport.hxx"
23 #include "docxexportfilter.hxx"
24 #include "docxfootnotes.hxx"
25 #include "writerwordglue.hxx"
26 #include "ww8par.hxx"
27 #include "fmtcntnt.hxx"
28 #include "fmtftn.hxx"
29 #include "fchrfmt.hxx"
30 #include "tgrditem.hxx"
31 #include "fmtruby.hxx"
32 #include "fmtanchr.hxx"
33 #include "breakit.hxx"
34 #include "redline.hxx"
35 #include "unocoll.hxx"
36 #include "unoframe.hxx"
37 #include "unodraw.hxx"
38 #include "textboxhelper.hxx"
39 #include "rdfhelper.hxx"
40 #include "wrtww8.hxx"
42 #include <comphelper/random.hxx>
43 #include <comphelper/string.hxx>
44 #include <comphelper/flagguard.hxx>
45 #include <oox/token/tokens.hxx>
46 #include <oox/export/utils.hxx>
47 #include <oox/mathml/export.hxx>
48 #include <oox/drawingml/drawingmltypes.hxx>
49 #include <oox/export/vmlexport.hxx>
50 #include <oox/ole/olehelper.hxx>
52 #include <editeng/autokernitem.hxx>
53 #include <editeng/unoprnms.hxx>
54 #include <editeng/fontitem.hxx>
55 #include <editeng/tstpitem.hxx>
56 #include <editeng/spltitem.hxx>
57 #include <editeng/widwitem.hxx>
58 #include <editeng/shaditem.hxx>
59 #include <editeng/brushitem.hxx>
60 #include <editeng/postitem.hxx>
61 #include <editeng/wghtitem.hxx>
62 #include <editeng/kernitem.hxx>
63 #include <editeng/crossedoutitem.hxx>
64 #include <editeng/cmapitem.hxx>
65 #include <editeng/udlnitem.hxx>
66 #include <editeng/langitem.hxx>
67 #include <editeng/lspcitem.hxx>
68 #include <editeng/escapementitem.hxx>
69 #include <editeng/fhgtitem.hxx>
70 #include <editeng/colritem.hxx>
71 #include <editeng/hyphenzoneitem.hxx>
72 #include <editeng/ulspitem.hxx>
73 #include <editeng/contouritem.hxx>
74 #include <editeng/shdditem.hxx>
75 #include <editeng/emphasismarkitem.hxx>
76 #include <editeng/twolinesitem.hxx>
77 #include <editeng/charscaleitem.hxx>
78 #include <editeng/charrotateitem.hxx>
79 #include <editeng/charreliefitem.hxx>
80 #include <editeng/paravertalignitem.hxx>
81 #include <editeng/pgrditem.hxx>
82 #include <editeng/frmdiritem.hxx>
83 #include <editeng/blinkitem.hxx>
84 #include <editeng/charhiddenitem.hxx>
85 #include <editeng/editobj.hxx>
86 #include <svx/xfillit0.hxx>
87 #include <svx/xflgrit.hxx>
88 #include <svx/fmglob.hxx>
89 #include <svx/svdouno.hxx>
90 #include <svl/grabbagitem.hxx>
91 #include <sfx2/sfxbasemodel.hxx>
92 #include <tools/datetimeutils.hxx>
93 #include <svl/whiter.hxx>
95 #include <docufld.hxx>
96 #include <authfld.hxx>
97 #include <flddropdown.hxx>
98 #include <fmtclds.hxx>
99 #include <fmtinfmt.hxx>
100 #include <fmtrowsplt.hxx>
101 #include <fmtline.hxx>
102 #include <ftninfo.hxx>
103 #include <htmltbl.hxx>
104 #include <lineinfo.hxx>
105 #include <ndgrf.hxx>
106 #include <ndole.hxx>
107 #include <ndtxt.hxx>
108 #include <pagedesc.hxx>
109 #include <paratr.hxx>
110 #include <charatr.hxx>
111 #include <swmodule.hxx>
112 #include <swtable.hxx>
113 #include <txtftn.hxx>
114 #include <txtinet.hxx>
115 #include <fmtautofmt.hxx>
116 #include <docsh.hxx>
117 #include <docary.hxx>
118 #include <IDocumentSettingAccess.hxx>
119 #include <IDocumentStylePoolAccess.hxx>
120 #include <IDocumentRedlineAccess.hxx>
121 #include <grfatr.hxx>
123 #include <osl/file.hxx>
124 #include <vcl/embeddedfontshelper.hxx>
125 #include <svtools/miscopt.hxx>
127 #include <com/sun/star/i18n/ScriptType.hpp>
128 #include <com/sun/star/chart2/XChartDocument.hpp>
129 #include <com/sun/star/drawing/ShadingPattern.hpp>
130 #include <com/sun/star/text/GraphicCrop.hpp>
131 #include <com/sun/star/drawing/LineStyle.hpp>
132 #include <com/sun/star/embed/EmbedStates.hpp>
133 #include <com/sun/star/text/TextContentAnchorType.hpp>
135 #include <algorithm>
137 using ::editeng::SvxBorderLine;
139 using namespace oox;
140 using namespace docx;
141 using namespace sax_fastparser;
142 using namespace nsSwDocInfoSubType;
143 using namespace nsFieldFlags;
144 using namespace sw::util;
145 using namespace ::com::sun::star;
146 using namespace ::com::sun::star::drawing;
148 static const sal_Int32 Tag_StartParagraph_1 = 1;
149 static const sal_Int32 Tag_StartParagraph_2 = 2;
150 static const sal_Int32 Tag_WriteSdtBlock = 3;
151 static const sal_Int32 Tag_StartParagraphProperties = 4;
152 static const sal_Int32 Tag_InitCollectedParagraphProperties = 5;
153 static const sal_Int32 Tag_StartRun_1 = 6;
154 static const sal_Int32 Tag_StartRun_2 = 7;
155 static const sal_Int32 Tag_StartRun_3 = 8;
156 static const sal_Int32 Tag_EndRun_1 = 9;
157 static const sal_Int32 Tag_EndRun_2 = 10;
158 static const sal_Int32 Tag_StartRunProperties = 11;
159 static const sal_Int32 Tag_InitCollectedRunProperties = 12;
160 static const sal_Int32 Tag_Redline_1 = 13;
161 static const sal_Int32 Tag_Redline_2 = 14;
162 static const sal_Int32 Tag_TableDefinition = 15;
163 static const sal_Int32 Tag_OutputFlyFrame = 16;
164 static const sal_Int32 Tag_StartSection = 17;
166 class FFDataWriterHelper
168 ::sax_fastparser::FSHelperPtr m_pSerializer;
169 void writeCommonStart( const OUString& rName )
171 m_pSerializer->startElementNS( XML_w, XML_ffData, FSEND );
172 m_pSerializer->singleElementNS( XML_w, XML_name,
173 FSNS( XML_w, XML_val ), OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ).getStr(),
174 FSEND );
175 m_pSerializer->singleElementNS( XML_w, XML_enabled, FSEND );
176 m_pSerializer->singleElementNS( XML_w, XML_calcOnExit,
177 FSNS( XML_w, XML_val ),
178 "0", FSEND );
180 void writeFinish()
182 m_pSerializer->endElementNS( XML_w, XML_ffData );
184 public:
185 explicit FFDataWriterHelper( const ::sax_fastparser::FSHelperPtr& rSerializer ) : m_pSerializer( rSerializer ){}
186 void WriteFormCheckbox( const OUString& rName, const OUString& rDefault, bool bChecked )
188 writeCommonStart( rName );
189 // Checkbox specific bits
190 m_pSerializer->startElementNS( XML_w, XML_checkBox, FSEND );
191 // currently hardcoding autosize
192 // #TODO check if this defaulted
193 m_pSerializer->startElementNS( XML_w, XML_sizeAuto, FSEND );
194 m_pSerializer->endElementNS( XML_w, XML_sizeAuto );
195 if ( !rDefault.isEmpty() )
197 m_pSerializer->singleElementNS( XML_w, XML_default,
198 FSNS( XML_w, XML_val ),
199 OUStringToOString( rDefault, RTL_TEXTENCODING_UTF8 ).getStr(), FSEND );
201 if ( bChecked )
202 m_pSerializer->singleElementNS( XML_w, XML_checked, FSEND );
203 m_pSerializer->endElementNS( XML_w, XML_checkBox );
204 writeFinish();
206 void WriteFormText( const OUString& rName, const OUString& rDefault )
208 writeCommonStart( rName );
209 if ( !rDefault.isEmpty() )
211 m_pSerializer->startElementNS( XML_w, XML_textInput, FSEND );
212 m_pSerializer->singleElementNS( XML_w, XML_default,
213 FSNS( XML_w, XML_val ),
214 OUStringToOString( rDefault, RTL_TEXTENCODING_UTF8 ).getStr(), FSEND );
215 m_pSerializer->endElementNS( XML_w, XML_textInput );
217 writeFinish();
221 class FieldMarkParamsHelper
223 const sw::mark::IFieldmark& mrFieldmark;
224 public:
225 explicit FieldMarkParamsHelper( const sw::mark::IFieldmark& rFieldmark ) : mrFieldmark( rFieldmark ) {}
226 OUString getName() { return mrFieldmark.GetName(); }
227 template < typename T >
228 bool extractParam( const OUString& rKey, T& rResult )
230 bool bResult = false;
231 if ( mrFieldmark.GetParameters() )
233 sw::mark::IFieldmark::parameter_map_t::const_iterator it = mrFieldmark.GetParameters()->find( rKey );
234 if ( it != mrFieldmark.GetParameters()->end() )
235 bResult = ( it->second >>= rResult );
237 return bResult;
240 void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ )
242 if (bIsRTL)
243 m_pSerializer->singleElementNS( XML_w, XML_rtl, FSNS( XML_w, XML_val ), "true", FSEND );
246 /// Are multiple paragraphs disallowed inside this type of SDT?
247 static bool lcl_isOnelinerSdt(const OUString& rName)
249 return rName == "Title" || rName == "Subtitle" || rName == "Company";
252 void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo )
254 if ( m_nColBreakStatus == COLBRK_POSTPONE )
255 m_nColBreakStatus = COLBRK_WRITE;
257 // Output table/table row/table cell starts if needed
258 if ( pTextNodeInfo.get() )
260 // New cell/row?
261 if ( m_tableReference->m_nTableDepth > 0 && !m_tableReference->m_bTableCellOpen )
263 ww8::WW8TableNodeInfoInner::Pointer_t pDeepInner( pTextNodeInfo->getInnerForDepth( m_tableReference->m_nTableDepth ) );
264 if ( pDeepInner->getCell() == 0 )
265 StartTableRow( pDeepInner );
267 const sal_uInt32 nCell = pDeepInner->getCell();
268 const sal_uInt32 nRow = pDeepInner->getRow();
270 SyncNodelessCells(pDeepInner, nCell, nRow);
271 StartTableCell(pDeepInner, nCell, nRow);
274 sal_uInt32 nRow = pTextNodeInfo->getRow();
275 sal_uInt32 nCell = pTextNodeInfo->getCell();
276 if (nCell == 0)
278 // Do we have to start the table?
279 // [If we are at the right depth already, it means that we
280 // continue the table cell]
281 sal_uInt32 nCurrentDepth = pTextNodeInfo->getDepth();
283 if ( nCurrentDepth > m_tableReference->m_nTableDepth )
285 // Start all the tables that begin here
286 for ( sal_uInt32 nDepth = m_tableReference->m_nTableDepth + 1; nDepth <= nCurrentDepth; ++nDepth )
288 ww8::WW8TableNodeInfoInner::Pointer_t pInner( pTextNodeInfo->getInnerForDepth( nDepth ) );
290 StartTable( pInner );
291 StartTableRow( pInner );
293 StartTableCell(pInner, 0, nDepth == nCurrentDepth ? nRow : 0);
296 m_tableReference->m_nTableDepth = nCurrentDepth;
301 // Look up the "sdt end before this paragraph" property early, when it
302 // would normally arrive, it would be too late (would be after the
303 // paragraph start has been written).
304 bool bEndParaSdt = false;
305 SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode();
306 if (pTextNode && pTextNode->GetpSwAttrSet())
308 const SfxItemSet* pSet = pTextNode->GetpSwAttrSet();
309 if (const SfxPoolItem* pItem = pSet->GetItem(RES_PARATR_GRABBAG))
311 const SfxGrabBagItem& rParaGrabBag = static_cast<const SfxGrabBagItem&>(*pItem);
312 const std::map<OUString, css::uno::Any>& rMap = rParaGrabBag.GetGrabBag();
313 bEndParaSdt = m_bStartedParaSdt && rMap.find("ParaSdtEndBefore") != rMap.end();
316 // TODO also avoid multiline paragarphs in those SDT types for shape text
317 bool bOneliner = m_bStartedParaSdt && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() && lcl_isOnelinerSdt(m_aStartedParagraphSdtPrAlias);
318 if (bEndParaSdt || (m_bStartedParaSdt && m_bHadSectPr) || bOneliner)
320 // This is the common case: "close sdt before the current paragraph" was requrested by the next paragraph.
321 EndSdtBlock();
322 m_bStartedParaSdt = false;
323 m_aStartedParagraphSdtPrAlias.clear();
325 m_bHadSectPr = false;
327 // this mark is used to be able to enclose the paragraph inside a sdr tag.
328 // We will only know if we have to do that later.
329 m_pSerializer->mark(Tag_StartParagraph_1);
331 m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
333 // postpone the output of the run (we get it before the paragraph
334 // properties, but must write it after them)
335 m_pSerializer->mark(Tag_StartParagraph_2);
337 // no section break in this paragraph yet; can be set in SectionBreak()
338 m_pSectionInfo.reset();
340 m_bParagraphOpened = true;
341 m_bIsFirstParagraph = false;
344 static void lcl_deleteAndResetTheLists( rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren, rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs, OUString& rSdtPrAlias)
346 if( pSdtPrTokenChildren.is() )
347 pSdtPrTokenChildren.clear();
348 if( pSdtPrDataBindingAttrs.is() )
349 pSdtPrDataBindingAttrs.clear();
350 if (!rSdtPrAlias.isEmpty())
351 rSdtPrAlias.clear();
354 void DocxAttributeOutput::PopulateFrameProperties(const SwFrameFormat* pFrameFormat, const Size& rSize)
357 sax_fastparser::FastAttributeList* attrList = FastSerializerHelper::createAttrList();
359 awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(), pFrameFormat->GetVertOrient().GetPos());
361 attrList->add( FSNS( XML_w, XML_w), OString::number(rSize.Width()));
362 attrList->add( FSNS( XML_w, XML_h), OString::number(rSize.Height()));
364 attrList->add( FSNS( XML_w, XML_x), OString::number(aPos.X));
365 attrList->add( FSNS( XML_w, XML_y), OString::number(aPos.Y));
367 const char* relativeFromH;
368 const char* relativeFromV;
369 switch (pFrameFormat->GetVertOrient().GetRelationOrient())
371 case text::RelOrientation::PAGE_PRINT_AREA:
372 relativeFromV = "margin";
373 break;
374 case text::RelOrientation::PAGE_FRAME:
375 relativeFromV = "page";
376 break;
377 case text::RelOrientation::FRAME:
378 case text::RelOrientation::TEXT_LINE:
379 default:
380 relativeFromV = "text";
381 break;
384 switch (pFrameFormat->GetHoriOrient().GetRelationOrient())
386 case text::RelOrientation::PAGE_PRINT_AREA:
387 relativeFromH = "margin";
388 break;
389 case text::RelOrientation::PAGE_FRAME:
390 relativeFromH = "page";
391 break;
392 case text::RelOrientation::CHAR:
393 case text::RelOrientation::PAGE_RIGHT:
394 case text::RelOrientation::FRAME:
395 default:
396 relativeFromH = "text";
397 break;
400 switch (pFrameFormat->GetSurround().GetValue())
402 case SURROUND_NONE:
403 attrList->add( FSNS( XML_w, XML_wrap), "none");
404 break;
405 case SURROUND_THROUGHT:
406 attrList->add( FSNS( XML_w, XML_wrap), "through");
407 break;
408 case SURROUND_PARALLEL:
409 attrList->add( FSNS( XML_w, XML_wrap), "notBeside");
410 break;
411 case SURROUND_IDEAL:
412 default:
413 attrList->add( FSNS( XML_w, XML_wrap), "auto");
414 break;
416 attrList->add( FSNS( XML_w, XML_vAnchor), relativeFromV);
417 attrList->add( FSNS( XML_w, XML_hAnchor), relativeFromH);
418 attrList->add( FSNS( XML_w, XML_hRule), "exact");
420 sax_fastparser::XFastAttributeListRef xAttrList(attrList);
421 m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList );
424 bool DocxAttributeOutput::TextBoxIsFramePr(const SwFrameFormat& rFrameFormat)
426 uno::Reference< drawing::XShape > xShape;
427 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
428 if (pSdrObj)
429 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
430 uno::Reference< beans::XPropertySet > xPropertySet(xShape, uno::UNO_QUERY);
431 uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
432 if (xPropertySet.is())
433 xPropSetInfo = xPropertySet->getPropertySetInfo();
434 uno::Any aFrameProperties ;
435 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
437 uno::Sequence< beans::PropertyValue > propList;
438 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
439 for (sal_Int32 nProp=0; nProp < propList.getLength(); ++nProp)
441 OUString propName = propList[nProp].Name;
442 if (propName == "ParaFrameProperties")
444 aFrameProperties = propList[nProp].Value ;
445 break;
449 bool bFrameProperties = false;
450 aFrameProperties >>= bFrameProperties;
451 return bFrameProperties;
454 void DocxAttributeOutput::EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner )
456 // write the paragraph properties + the run, already in the correct order
457 m_pSerializer->mergeTopMarks(Tag_StartParagraph_2);
458 std::vector< std::shared_ptr <ww8::Frame> > aFramePrTextbox;
459 // Write the anchored frame if any
460 // Word can't handle nested text boxes, so write them on the same level.
461 ++m_nTextFrameLevel;
462 if( m_nTextFrameLevel == 1 && !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
464 comphelper::FlagRestorationGuard aStartedParaSdtGuard(m_bStartedParaSdt, false);
466 assert(!m_pPostponedCustomShape);
467 m_pPostponedCustomShape.reset(new std::list<PostponedDrawing>());
468 for (size_t nIndex = 0; nIndex < m_aFramesOfParagraph.size(); ++nIndex)
470 m_bParagraphFrameOpen = true;
471 ww8::Frame aFrame = m_aFramesOfParagraph[nIndex];
472 const SwFrameFormat& rFrameFormat = aFrame.GetFrameFormat();
474 if (!TextBoxIsFramePr(rFrameFormat) || m_bWritingHeaderFooter)
476 if (m_bStartedCharSdt)
478 // Run-level SDT still open? Close it befor AlternateContent.
479 EndSdtBlock();
480 m_bStartedCharSdt = false;
482 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
483 m_pSerializer->startElementNS(XML_mc, XML_AlternateContent, FSEND);
484 m_pSerializer->startElementNS(XML_mc, XML_Choice,
485 XML_Requires, "wps",
486 FSEND);
488 This is to avoid AlternateContent within another AlternateContent.
489 So when Choice is Open, only write the DML Drawing instead of both DML
490 and VML Drawing in another AlternateContent.
492 SetAlternateContentChoiceOpen( true );
493 /** Save the table info's before writing the shape
494 as there might be a new table that might get
495 spawned from within the VML & DML block and alter
496 the contents.
498 ww8::WW8TableInfo::Pointer_t pOldTableInfo = m_rExport.m_pTableInfo;
499 //Reset the table infos after saving.
500 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
502 /** FDO#71834 :
503 Save the table reference attributes before calling WriteDMLTextFrame,
504 otherwise the StartParagraph function will use the previous existing
505 table reference attributes since the variable is being shared.
507 DocxTableExportContext aDMLTableExportContext;
508 pushToTableExportContext(aDMLTableExportContext);
509 m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++);
510 popFromTableExportContext(aDMLTableExportContext);
511 m_pSerializer->endElementNS(XML_mc, XML_Choice);
512 SetAlternateContentChoiceOpen( false );
514 // Reset table infos, otherwise the depth of the cells will be incorrect,
515 // in case the text frame had table(s) and we try to export the
516 // same table second time.
517 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
518 //reset the tableReference.
520 m_pSerializer->startElementNS(XML_mc, XML_Fallback, FSEND);
521 DocxTableExportContext aVMLTableExportContext;
522 pushToTableExportContext(aVMLTableExportContext);
523 m_rExport.SdrExporter().writeVMLTextFrame(&aFrame);
524 popFromTableExportContext(aVMLTableExportContext);
525 m_rExport.m_pTableInfo = pOldTableInfo;
527 m_pSerializer->endElementNS(XML_mc, XML_Fallback);
528 m_pSerializer->endElementNS(XML_mc, XML_AlternateContent);
529 m_pSerializer->endElementNS( XML_w, XML_r );
530 m_bParagraphFrameOpen = false;
532 else
534 std::shared_ptr<ww8::Frame> pFramePr;
535 pFramePr.reset(new ww8::Frame(aFrame));
536 aFramePrTextbox.push_back(pFramePr);
539 if (!m_pPostponedCustomShape->empty())
541 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
542 WritePostponedCustomShape();
543 m_pSerializer->endElementNS( XML_w, XML_r );
545 m_pPostponedCustomShape.reset(nullptr);
547 m_aFramesOfParagraph.clear();
550 --m_nTextFrameLevel;
552 /* If m_nHyperLinkCount > 0 that means hyperlink tag is not yet closed.
553 * This is due to nested hyperlink tags. So close it before end of paragraph.
555 if(m_nHyperLinkCount > 0)
557 for(sal_Int32 nHyperLinkToClose = 0; nHyperLinkToClose < m_nHyperLinkCount; ++nHyperLinkToClose)
558 m_pSerializer->endElementNS( XML_w, XML_hyperlink );
559 m_nHyperLinkCount = 0;
562 if (m_bStartedCharSdt)
564 // Run-level SDT still open? Close it now.
565 EndSdtBlock();
566 m_bStartedCharSdt = false;
569 m_pSerializer->endElementNS( XML_w, XML_p );
570 // on export sdt blocks are never nested ATM
571 if( !m_bAnchorLinkedToNode && !m_bStartedParaSdt )
572 WriteSdtBlock( m_nParagraphSdtPrToken, m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrTokenAttributes, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias, /*bPara=*/true );
573 else
575 //These should be written out to the actual Node and not to the anchor.
576 //Clear them as they will be repopulated when the node is processed.
577 m_nParagraphSdtPrToken = 0;
578 m_bParagraphSdtHasId = false;
579 lcl_deleteAndResetTheLists( m_pParagraphSdtPrTokenChildren, m_pParagraphSdtPrDataBindingAttrs, m_aParagraphSdtPrAlias );
582 //sdtcontent is written so Set m_bParagraphHasDrawing to false
583 m_rExport.SdrExporter().setParagraphHasDrawing( false );
584 m_bRunTextIsOn = false;
585 m_pSerializer->mergeTopMarks(Tag_StartParagraph_1);
587 // Write framePr
588 if(!aFramePrTextbox.empty())
590 for (std::vector< std::shared_ptr<ww8::Frame> > ::iterator it = aFramePrTextbox.begin() ; it != aFramePrTextbox.end(); ++it)
592 DocxTableExportContext aTableExportContext;
593 pushToTableExportContext(aTableExportContext);
594 m_pCurrentFrame = it->get();
595 m_rExport.SdrExporter().writeOnlyTextOfFrame(it->get());
596 m_pCurrentFrame = nullptr;
597 popFromTableExportContext(aTableExportContext);
599 aFramePrTextbox.clear();
601 // Check for end of cell, rows, tables here
602 FinishTableRowCell( pTextNodeInfoInner );
604 if( !m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen() )
605 m_bParagraphOpened = false;
609 void DocxAttributeOutput::WriteSdtBlock( sal_Int32& nSdtPrToken,
610 rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenChildren,
611 rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrTokenAttributes,
612 rtl::Reference<sax_fastparser::FastAttributeList>& pSdtPrDataBindingAttrs,
613 OUString& rSdtPrAlias,
614 bool bPara )
616 if( nSdtPrToken > 0 || pSdtPrDataBindingAttrs.is() )
618 // sdt start mark
619 m_pSerializer->mark(Tag_WriteSdtBlock);
621 m_pSerializer->startElementNS( XML_w, XML_sdt, FSEND );
623 // output sdt properties
624 m_pSerializer->startElementNS( XML_w, XML_sdtPr, FSEND );
626 if( nSdtPrToken > 0 && pSdtPrTokenChildren.is() )
628 if (!pSdtPrTokenAttributes.is())
629 m_pSerializer->startElement( nSdtPrToken, FSEND );
630 else
632 XFastAttributeListRef xAttrList(pSdtPrTokenAttributes.get());
633 pSdtPrTokenAttributes.clear();
634 m_pSerializer->startElement(nSdtPrToken, xAttrList);
637 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 )) {
638 uno::Sequence<xml::FastAttribute> aChildren = pSdtPrTokenChildren->getFastAttributes();
639 for( sal_Int32 i=0; i < aChildren.getLength(); ++i )
640 m_pSerializer->singleElement( aChildren[i].Token,
641 FSNS(XML_w, XML_val),
642 OUStringToOString( aChildren[i].Value, RTL_TEXTENCODING_UTF8 ).getStr(),
643 FSEND );
646 m_pSerializer->endElement( nSdtPrToken );
648 else if( (nSdtPrToken > 0) && nSdtPrToken != FSNS( XML_w, XML_id ) && !(m_bRunTextIsOn && m_rExport.SdrExporter().IsParagraphHasDrawing()))
650 if (!pSdtPrTokenAttributes.is())
651 m_pSerializer->singleElement( nSdtPrToken, FSEND );
652 else
654 XFastAttributeListRef xAttrList(pSdtPrTokenAttributes.get());
655 pSdtPrTokenAttributes.clear();
656 m_pSerializer->singleElement(nSdtPrToken, xAttrList);
660 if( nSdtPrToken == FSNS( XML_w, XML_id ) || ( bPara && m_bParagraphSdtHasId ) )
661 //Word won't open a document with an empty id tag, we fill it with a random number
662 m_pSerializer->singleElementNS(XML_w, XML_id, FSNS(XML_w, XML_val),
663 OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())),
664 FSEND);
666 if( pSdtPrDataBindingAttrs.is() && !m_rExport.SdrExporter().IsParagraphHasDrawing())
668 XFastAttributeListRef xAttrList( pSdtPrDataBindingAttrs.get() );
669 pSdtPrDataBindingAttrs.clear();
670 m_pSerializer->singleElementNS( XML_w, XML_dataBinding, xAttrList );
673 if (!rSdtPrAlias.isEmpty())
674 m_pSerializer->singleElementNS(XML_w, XML_alias, FSNS(XML_w, XML_val),
675 OUStringToOString(rSdtPrAlias, RTL_TEXTENCODING_UTF8).getStr(),
676 FSEND);
678 m_pSerializer->endElementNS( XML_w, XML_sdtPr );
680 // sdt contents start tag
681 m_pSerializer->startElementNS( XML_w, XML_sdtContent, FSEND );
683 // prepend the tags since the sdt start mark before the paragraph
684 m_pSerializer->mergeTopMarks(Tag_WriteSdtBlock, sax_fastparser::MergeMarks::PREPEND);
686 // write the ending tags after the paragraph
687 if (bPara)
689 m_bStartedParaSdt = true;
690 if (m_tableReference->m_bTableCellOpen)
691 m_tableReference->m_bTableCellParaSdtOpen = true;
692 if (m_rExport.SdrExporter().IsDMLAndVMLDrawingOpen())
693 m_rExport.SdrExporter().setParagraphSdtOpen(true);
695 else
696 // Support multiple runs inside a run-level SDT: don't close the SDT block yet.
697 m_bStartedCharSdt = true;
699 // clear sdt status
700 nSdtPrToken = 0;
701 pSdtPrTokenChildren.clear();
702 pSdtPrDataBindingAttrs.clear();
703 rSdtPrAlias.clear();
707 void DocxAttributeOutput::EndSdtBlock()
709 m_pSerializer->endElementNS( XML_w, XML_sdtContent );
710 m_pSerializer->endElementNS( XML_w, XML_sdt );
713 #define MAX_CELL_IN_WORD 62
715 void DocxAttributeOutput::SyncNodelessCells(ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, sal_Int32 nCell, sal_uInt32 nRow)
717 sal_Int32 nOpenCell = lastOpenCell.back();
718 if (nOpenCell != -1 && nOpenCell != nCell && nOpenCell < MAX_CELL_IN_WORD)
719 EndTableCell(pInner, nOpenCell, nRow);
721 sal_Int32 nClosedCell = lastClosedCell.back();
722 for (sal_Int32 i = nClosedCell+1; i < nCell; ++i)
724 if (i >= MAX_CELL_IN_WORD)
725 break;
727 if (i == 0)
728 StartTableRow(pInner);
730 StartTableCell(pInner, i, nRow);
731 m_pSerializer->singleElementNS( XML_w, XML_p, FSEND );
732 EndTableCell(pInner, i, nRow);
736 void DocxAttributeOutput::FinishTableRowCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pInner, bool bForceEmptyParagraph )
738 if ( pInner.get() )
740 // Where are we in the table
741 sal_uInt32 nRow = pInner->getRow();
742 sal_Int32 nCell = pInner->getCell();
744 InitTableHelper( pInner );
746 // HACK
747 // msoffice seems to have an internal limitation of 63 columns for tables
748 // and refuses to load .docx with more, even though the spec seems to allow that;
749 // so simply if there are more columns, don't close the last one msoffice will handle
750 // and merge the contents of the remaining ones into it (since we don't close the cell
751 // here, following ones will not be opened)
752 const bool limitWorkaround = (nCell >= MAX_CELL_IN_WORD && !pInner->isEndOfLine());
753 const bool bEndCell = pInner->isEndOfCell() && !limitWorkaround;
754 const bool bEndRow = pInner->isEndOfLine();
756 if (bEndCell)
758 while (pInner->getDepth() < m_tableReference->m_nTableDepth)
760 //we expect that the higher depth row was closed, and
761 //we are just missing the table close
762 assert(lastOpenCell.back() == -1 && lastClosedCell.back() == -1);
763 EndTable();
766 SyncNodelessCells(pInner, nCell, nRow);
768 sal_Int32 nClosedCell = lastClosedCell.back();
769 if (nCell == nClosedCell)
771 //Start missing trailing cell
772 ++nCell;
773 StartTableCell(pInner, nCell, nRow);
776 if (bForceEmptyParagraph)
778 m_pSerializer->singleElementNS( XML_w, XML_p, FSEND );
781 EndTableCell(pInner, nCell, nRow);
784 // This is a line end
785 if (bEndRow)
786 EndTableRow();
788 // This is the end of the table
789 if (pInner->isFinalEndOfLine())
790 EndTable();
794 void DocxAttributeOutput::EmptyParagraph()
796 m_pSerializer->singleElementNS( XML_w, XML_p, FSEND );
799 void DocxAttributeOutput::SectionBreaks(const SwNode& rNode)
801 // output page/section breaks
802 // Writer can have them at the beginning of a paragraph, or at the end, but
803 // in docx, we have to output them in the paragraph properties of the last
804 // paragraph in a section. To get it right, we have to switch to the next
805 // paragraph, and detect the section breaks there.
806 SwNodeIndex aNextIndex( rNode, 1 );
808 if (rNode.IsTextNode())
810 if (aNextIndex.GetNode().IsTextNode())
812 const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
813 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty());
815 else if (aNextIndex.GetNode().IsTableNode())
817 const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
818 const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
819 m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
822 else if (rNode.IsEndNode())
824 if (aNextIndex.GetNode().IsTextNode())
826 // Handle section break between a table and a text node following it.
827 // Also handle section endings
828 const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
829 if (rNode.StartOfSectionNode()->IsTableNode() || rNode.StartOfSectionNode()->IsSectionNode())
830 m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty());
835 void DocxAttributeOutput::StartParagraphProperties()
837 m_pSerializer->mark(Tag_StartParagraphProperties);
839 m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );
841 // and output the section break now (if it appeared)
842 if ( m_pSectionInfo && (!m_setFootnote))
844 m_rExport.SectionProperties( *m_pSectionInfo );
845 m_pSectionInfo.reset();
848 InitCollectedParagraphProperties();
851 void DocxAttributeOutput::InitCollectedParagraphProperties()
853 m_pParagraphSpacingAttrList.clear();
855 // Write the elements in the spec order
856 static const sal_Int32 aOrder[] =
858 FSNS( XML_w, XML_pStyle ),
859 FSNS( XML_w, XML_keepNext ),
860 FSNS( XML_w, XML_keepLines ),
861 FSNS( XML_w, XML_pageBreakBefore ),
862 FSNS( XML_w, XML_framePr ),
863 FSNS( XML_w, XML_widowControl ),
864 FSNS( XML_w, XML_numPr ),
865 FSNS( XML_w, XML_suppressLineNumbers ),
866 FSNS( XML_w, XML_pBdr ),
867 FSNS( XML_w, XML_shd ),
868 FSNS( XML_w, XML_tabs ),
869 FSNS( XML_w, XML_suppressAutoHyphens ),
870 FSNS( XML_w, XML_kinsoku ),
871 FSNS( XML_w, XML_wordWrap ),
872 FSNS( XML_w, XML_overflowPunct ),
873 FSNS( XML_w, XML_topLinePunct ),
874 FSNS( XML_w, XML_autoSpaceDE ),
875 FSNS( XML_w, XML_autoSpaceDN ),
876 FSNS( XML_w, XML_bidi ),
877 FSNS( XML_w, XML_adjustRightInd ),
878 FSNS( XML_w, XML_snapToGrid ),
879 FSNS( XML_w, XML_spacing ),
880 FSNS( XML_w, XML_ind ),
881 FSNS( XML_w, XML_contextualSpacing ),
882 FSNS( XML_w, XML_mirrorIndents ),
883 FSNS( XML_w, XML_suppressOverlap ),
884 FSNS( XML_w, XML_jc ),
885 FSNS( XML_w, XML_textDirection ),
886 FSNS( XML_w, XML_textAlignment ),
887 FSNS( XML_w, XML_textboxTightWrap ),
888 FSNS( XML_w, XML_outlineLvl ),
889 FSNS( XML_w, XML_divId ),
890 FSNS( XML_w, XML_cnfStyle ),
891 FSNS( XML_w, XML_rPr ),
892 FSNS( XML_w, XML_sectPr ),
893 FSNS( XML_w, XML_pPrChange )
896 // postpone the output so that we can later [in EndParagraphProperties()]
897 // prepend the properties before the run
898 sal_Int32 len = sizeof ( aOrder ) / sizeof( sal_Int32 );
899 uno::Sequence< sal_Int32 > aSeqOrder( len );
900 for ( sal_Int32 i = 0; i < len; i++ )
901 aSeqOrder[i] = aOrder[i];
903 m_pSerializer->mark(Tag_InitCollectedParagraphProperties, aSeqOrder);
906 void DocxAttributeOutput::WriteCollectedParagraphProperties()
908 if ( m_rExport.SdrExporter().getFlyAttrList().is() )
910 XFastAttributeListRef xAttrList( m_rExport.SdrExporter().getFlyAttrList().get() );
911 m_rExport.SdrExporter().getFlyAttrList().clear();
913 m_pSerializer->singleElementNS( XML_w, XML_framePr, xAttrList );
916 if ( m_pParagraphSpacingAttrList.is() )
918 XFastAttributeListRef xAttrList( m_pParagraphSpacingAttrList.get() );
919 m_pParagraphSpacingAttrList.clear();
921 m_pSerializer->singleElementNS( XML_w, XML_spacing, xAttrList );
924 if ( m_pBackgroundAttrList.is() )
926 XFastAttributeListRef xAttrList( m_pBackgroundAttrList.get() );
927 m_pBackgroundAttrList.clear();
929 m_pSerializer->singleElementNS( XML_w, XML_shd, xAttrList );
933 namespace
936 /// Outputs an item set, that contains the formatting of the paragraph marker.
937 void lcl_writeParagraphMarkerProperties(DocxAttributeOutput& rAttributeOutput, const SfxItemSet& rParagraphMarkerProperties)
939 SfxWhichIter aIter(rParagraphMarkerProperties);
940 sal_uInt16 nWhichId = aIter.FirstWhich();
941 const SfxPoolItem* pItem = nullptr;
942 // Did we already produce a <w:sz> element?
943 bool bFontSizeWritten = false;
944 while (nWhichId)
946 if (rParagraphMarkerProperties.GetItemState(nWhichId, true, &pItem) == SfxItemState::SET)
948 if (isCHRATR(nWhichId) || nWhichId == RES_TXTATR_CHARFMT)
950 // Will this item produce a <w:sz> element?
951 bool bFontSizeItem = nWhichId == RES_CHRATR_FONTSIZE || nWhichId == RES_CHRATR_CJK_FONTSIZE;
952 if (!bFontSizeWritten || !bFontSizeItem)
953 rAttributeOutput.OutputItem(*pItem);
954 if (bFontSizeItem)
955 bFontSizeWritten = true;
957 else if (nWhichId == RES_TXTATR_AUTOFMT)
959 const SwFormatAutoFormat* pAutoFormat = static_cast<const SwFormatAutoFormat*>(pItem);
960 lcl_writeParagraphMarkerProperties(rAttributeOutput, *pAutoFormat->GetStyleHandle());
963 nWhichId = aIter.NextWhich();
969 void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMarkerProperties, const SwRedlineData* pRedlineData, const SwRedlineData* pRedlineParagraphMarkerDeleted, const SwRedlineData* pRedlineParagraphMarkerInserted)
971 // Call the 'Redline' function. This will add redline (change-tracking) information that regards to paragraph properties.
972 // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
974 // If there is RedlineData present, call WriteCollectedParagraphProperties() for writing pPr before calling Redline().
975 // As there will be another pPr for redline and LO might mix both.
976 if(pRedlineData)
977 WriteCollectedParagraphProperties();
978 Redline( pRedlineData );
980 WriteCollectedParagraphProperties();
982 // Merge the marks for the ordered elements
983 m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties);
985 // Write 'Paragraph Mark' properties
986 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
987 // mark() before paragraph mark properties child elements.
988 InitCollectedRunProperties();
990 // The 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' are used to hold information
991 // that should be collected by different properties in the core, and are all flushed together
992 // to the DOCX when the function 'WriteCollectedRunProperties' gets called.
993 // So we need to store the current status of these lists, so that we can revert back to them when
994 // we are done exporting the redline attributes.
995 rtl::Reference<sax_fastparser::FastAttributeList> pFontsAttrList_Original(m_pFontsAttrList);
996 m_pFontsAttrList.clear();
997 rtl::Reference<sax_fastparser::FastAttributeList> pEastAsianLayoutAttrList_Original(m_pEastAsianLayoutAttrList);
998 m_pEastAsianLayoutAttrList.clear();
999 rtl::Reference<sax_fastparser::FastAttributeList> pCharLangAttrList_Original(m_pCharLangAttrList);
1000 m_pCharLangAttrList.clear();
1002 lcl_writeParagraphMarkerProperties(*this, rParagraphMarkerProperties);
1004 // Write the collected run properties that are stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
1005 WriteCollectedRunProperties();
1007 // Revert back the original values that were stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
1008 m_pFontsAttrList = pFontsAttrList_Original.get();
1009 m_pEastAsianLayoutAttrList = pEastAsianLayoutAttrList_Original.get();
1010 m_pCharLangAttrList = pCharLangAttrList_Original.get();
1012 if ( pRedlineParagraphMarkerDeleted )
1014 StartRedline( pRedlineParagraphMarkerDeleted );
1015 EndRedline( pRedlineParagraphMarkerDeleted );
1017 if ( pRedlineParagraphMarkerInserted )
1019 StartRedline( pRedlineParagraphMarkerInserted );
1020 EndRedline( pRedlineParagraphMarkerInserted );
1023 // mergeTopMarks() after paragraph mark properties child elements.
1024 m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
1025 m_pSerializer->endElementNS( XML_w, XML_rPr );
1027 if (!m_bWritingHeaderFooter && m_pCurrentFrame)
1029 const SwFrameFormat& rFrameFormat = m_pCurrentFrame->GetFrameFormat();
1030 if (TextBoxIsFramePr(rFrameFormat))
1032 const Size aSize = m_pCurrentFrame->GetSize();
1033 PopulateFrameProperties(&rFrameFormat, aSize);
1037 m_pSerializer->endElementNS( XML_w, XML_pPr );
1039 // RDF metadata for this text node.
1040 SwTextNode* pTextNode = m_rExport.m_pCurPam->GetNode().GetTextNode();
1041 std::map<OUString, OUString> aStatements = SwRDFHelper::getTextNodeStatements("urn:bails", *pTextNode);
1042 if (!aStatements.empty())
1044 m_pSerializer->startElementNS(XML_w, XML_smartTag,
1045 FSNS(XML_w, XML_uri), "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
1046 FSNS(XML_w, XML_element), "RDF",
1047 FSEND);
1048 m_pSerializer->startElementNS(XML_w, XML_smartTagPr, FSEND);
1049 for (const auto& rStatement : aStatements)
1050 m_pSerializer->singleElementNS(XML_w, XML_attr,
1051 FSNS(XML_w, XML_name), rStatement.first.toUtf8(),
1052 FSNS(XML_w, XML_val), rStatement.second.toUtf8(),
1053 FSEND);
1054 m_pSerializer->endElementNS(XML_w, XML_smartTagPr);
1055 m_pSerializer->endElementNS(XML_w, XML_smartTag);
1058 if ( m_nColBreakStatus == COLBRK_WRITE )
1060 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1061 m_pSerializer->singleElementNS( XML_w, XML_br,
1062 FSNS( XML_w, XML_type ), "column", FSEND );
1063 m_pSerializer->endElementNS( XML_w, XML_r );
1065 m_nColBreakStatus = COLBRK_NONE;
1068 if ( m_bPostponedPageBreak )
1070 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1071 m_pSerializer->singleElementNS( XML_w, XML_br,
1072 FSNS( XML_w, XML_type ), "page", FSEND );
1073 m_pSerializer->endElementNS( XML_w, XML_r );
1075 m_bPostponedPageBreak = false;
1078 // merge the properties _before_ the run (strictly speaking, just
1079 // after the start of the paragraph)
1080 m_pSerializer->mergeTopMarks(Tag_StartParagraphProperties, sax_fastparser::MergeMarks::PREPEND);
1083 void DocxAttributeOutput::SetStateOfFlyFrame( FlyProcessingState nStateOfFlyFrame )
1085 m_nStateOfFlyFrame = nStateOfFlyFrame;
1088 void DocxAttributeOutput::SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode )
1090 m_bAnchorLinkedToNode = bAnchorLinkedToNode ;
1093 void DocxAttributeOutput::ResetFlyProcessingFlag()
1095 m_bPostponedProcessingFly = false ;
1098 bool DocxAttributeOutput::IsFlyProcessingPostponed()
1100 return m_bPostponedProcessingFly;
1103 void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, bool /*bSingleEmptyRun*/ )
1105 // Don't start redline data here, possibly there is a hyperlink later, and
1106 // that has to be started first.
1107 m_pRedlineData = pRedlineData;
1109 // this mark is used to be able to enclose the run inside a sdr tag.
1110 m_pSerializer->mark(Tag_StartRun_1);
1112 // postpone the output of the start of a run (there are elements that need
1113 // to be written before the start of the run, but we learn which they are
1114 // _inside_ of the run)
1115 m_pSerializer->mark(Tag_StartRun_2); // let's call it "postponed run start"
1117 // postpone the output of the text (we get it before the run properties,
1118 // but must write it after them)
1119 m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text"
1122 void DocxAttributeOutput::EndRun()
1124 int nFieldsInPrevHyperlink = m_nFieldsInHyperlink;
1125 // Reset m_nFieldsInHyperlink if a new hyperlink is about to start
1126 if ( m_pHyperlinkAttrList.is() )
1128 m_nFieldsInHyperlink = 0;
1131 // Write field starts
1132 for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin() + nFieldsInPrevHyperlink; pIt != m_Fields.end(); )
1134 // Add the fields starts for all but hyperlinks and TOCs
1135 if ( pIt->bOpen && pIt->pField )
1137 StartField_Impl( *pIt );
1139 // Remove the field from the stack if only the start has to be written
1140 // Unknown fields should be removed too
1141 if ( !pIt->bClose || ( pIt->eType == ww::eUNKNOWN ) )
1143 pIt = m_Fields.erase( pIt );
1144 continue;
1147 if (m_startedHyperlink || m_pHyperlinkAttrList.is())
1149 ++m_nFieldsInHyperlink;
1152 ++pIt;
1155 // write the run properties + the text, already in the correct order
1156 m_pSerializer->mergeTopMarks(Tag_StartRun_3); // merges with "postponed text", see above
1158 // level down, to be able to prepend the actual run start attribute (just
1159 // before "postponed run start")
1160 m_pSerializer->mark(Tag_EndRun_1); // let's call it "actual run start"
1161 bool bCloseEarlierSDT = false;
1163 if (m_bEndCharSdt)
1165 // This is the common case: "close sdt before the current run" was requrested by the next run.
1167 // if another sdt starts in this run, then wait
1168 // as closing the sdt now, might cause nesting of sdts
1169 if (m_nRunSdtPrToken > 0)
1170 bCloseEarlierSDT = true;
1171 else
1172 EndSdtBlock();
1173 m_bEndCharSdt = false;
1174 m_bStartedCharSdt = false;
1177 if ( m_closeHyperlinkInPreviousRun )
1179 if ( m_startedHyperlink )
1181 for ( int i = 0; i < nFieldsInPrevHyperlink; i++ )
1183 // If fields begin before hyperlink then
1184 // it should end before hyperlink close
1185 EndField_Impl( m_Fields.back( ) );
1186 m_Fields.pop_back();
1188 m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1189 m_startedHyperlink = false;
1190 m_endPageRef = false;
1191 m_nHyperLinkCount--;
1193 m_closeHyperlinkInPreviousRun = false;
1196 // Write the hyperlink and toc fields starts
1197 for ( std::vector<FieldInfos>::iterator pIt = m_Fields.begin(); pIt != m_Fields.end(); )
1199 // Add the fields starts for hyperlinks, TOCs and index marks
1200 if ( pIt->bOpen && !pIt->pField )
1202 StartField_Impl( *pIt, true );
1204 if (m_startedHyperlink)
1205 ++m_nFieldsInHyperlink;
1207 // Remove the field if no end needs to be written
1208 if ( !pIt->bClose ) {
1209 pIt = m_Fields.erase( pIt );
1210 continue;
1213 ++pIt;
1216 // Start the hyperlink after the fields separators or we would generate invalid file
1217 if ( m_pHyperlinkAttrList.is() )
1219 XFastAttributeListRef xAttrList ( m_pHyperlinkAttrList.get() );
1220 m_pHyperlinkAttrList.clear();
1222 m_pSerializer->startElementNS( XML_w, XML_hyperlink, xAttrList );
1223 m_startedHyperlink = true;
1224 m_nHyperLinkCount++;
1227 // if there is some redlining in the document, output it
1228 StartRedline( m_pRedlineData );
1230 DoWriteBookmarks( );
1231 DoWriteAnnotationMarks( );
1233 if( m_closeHyperlinkInThisRun && m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc"))
1235 OUString sToken;
1236 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1237 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
1238 m_pSerializer->singleElementNS( XML_w, XML_webHidden, FSEND );
1239 m_pSerializer->endElementNS( XML_w, XML_rPr );
1240 m_pSerializer->startElementNS( XML_w, XML_fldChar,
1241 FSNS( XML_w, XML_fldCharType ), "begin",
1242 FSEND );
1243 m_pSerializer->endElementNS( XML_w, XML_fldChar );
1244 m_pSerializer->endElementNS( XML_w, XML_r );
1247 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1248 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
1249 m_pSerializer->singleElementNS( XML_w, XML_webHidden, FSEND );
1250 m_pSerializer->endElementNS( XML_w, XML_rPr );
1251 sToken = "PAGEREF " + m_hyperLinkAnchor + " \\h"; // '\h' Creates a hyperlink to the bookmarked paragraph.
1252 DoWriteCmd( sToken );
1253 m_pSerializer->endElementNS( XML_w, XML_r );
1255 // Write the Field separator
1256 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1257 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
1258 m_pSerializer->singleElementNS( XML_w, XML_webHidden, FSEND );
1259 m_pSerializer->endElementNS( XML_w, XML_rPr );
1260 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1261 FSNS( XML_w, XML_fldCharType ), "separate",
1262 FSEND );
1263 m_pSerializer->endElementNS( XML_w, XML_r );
1264 // At start of every "PAGEREF" field m_endPageRef value should be true.
1265 m_endPageRef = true;
1268 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1269 if(GetExport().m_bTabInTOC && m_pHyperlinkAttrList.is())
1271 RunText("\t") ;
1273 m_pSerializer->mergeTopMarks(Tag_EndRun_1, sax_fastparser::MergeMarks::PREPEND); // merges with "postponed run start", see above
1275 // write the run start + the run content
1276 m_pSerializer->mergeTopMarks(Tag_StartRun_2); // merges the "actual run start"
1277 // append the actual run end
1278 m_pSerializer->endElementNS( XML_w, XML_r );
1280 // if there is some redlining in the document, output it
1281 EndRedline( m_pRedlineData );
1283 // enclose in a sdt block, if necessary: if one is already started, then don't do it for now
1284 // (so on export sdt blocks are never nested ATM)
1285 if ( !m_bAnchorLinkedToNode && !m_bStartedCharSdt )
1287 rtl::Reference<sax_fastparser::FastAttributeList> pRunSdtPrTokenAttributes;
1288 WriteSdtBlock( m_nRunSdtPrToken, m_pRunSdtPrTokenChildren, pRunSdtPrTokenAttributes, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias, /*bPara=*/false );
1290 else
1292 //These should be written out to the actual Node and not to the anchor.
1293 //Clear them as they will be repopulated when the node is processed.
1294 m_nRunSdtPrToken = 0;
1295 lcl_deleteAndResetTheLists( m_pRunSdtPrTokenChildren, m_pRunSdtPrDataBindingAttrs, m_aRunSdtPrAlias );
1298 if (bCloseEarlierSDT)
1300 m_pSerializer->mark(Tag_EndRun_2);
1301 EndSdtBlock();
1302 m_pSerializer->mergeTopMarks(Tag_EndRun_2, sax_fastparser::MergeMarks::PREPEND);
1305 m_pSerializer->mergeTopMarks(Tag_StartRun_1);
1307 for (std::vector<const SwOLENode*>::iterator it = m_aPostponedMaths.begin(); it != m_aPostponedMaths.end(); ++it)
1308 WritePostponedMath(*it);
1309 m_aPostponedMaths.clear();
1311 for (std::vector<const SdrObject*>::iterator it = m_aPostponedFormControls.begin(); it != m_aPostponedFormControls.end(); ++it)
1312 WritePostponedFormControl(*it);
1313 m_aPostponedFormControls.clear();
1315 WritePostponedActiveXControl(false);
1317 WritePendingPlaceholder();
1319 m_pRedlineData = nullptr;
1321 if ( m_closeHyperlinkInThisRun )
1323 if ( m_startedHyperlink )
1325 if( m_endPageRef )
1327 // Hyperlink is started and fldchar "end" needs to be written for PAGEREF
1328 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1329 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
1330 m_pSerializer->singleElementNS( XML_w, XML_webHidden, FSEND );
1331 m_pSerializer->endElementNS( XML_w, XML_rPr );
1332 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1333 FSNS( XML_w, XML_fldCharType ), "end",
1334 FSEND );
1335 m_pSerializer->endElementNS( XML_w, XML_r );
1336 m_endPageRef = false;
1337 m_hyperLinkAnchor.clear();
1339 for ( int i = 0; i < m_nFieldsInHyperlink; i++ )
1341 // If fields begin after hyperlink start then
1342 // it should end before hyperlink close
1343 EndField_Impl( m_Fields.back( ) );
1344 m_Fields.pop_back();
1346 m_nFieldsInHyperlink = 0;
1348 m_pSerializer->endElementNS( XML_w, XML_hyperlink );
1349 m_startedHyperlink = false;
1350 m_nHyperLinkCount--;
1352 m_closeHyperlinkInThisRun = false;
1355 if (!m_startedHyperlink)
1357 while ( m_Fields.begin() != m_Fields.end() )
1359 EndField_Impl( m_Fields.front( ) );
1360 m_Fields.erase( m_Fields.begin( ) );
1362 m_nFieldsInHyperlink = 0;
1366 void DocxAttributeOutput::DoWriteBookmarks()
1368 // Write the start bookmarks
1369 for ( std::vector< OString >::const_iterator it = m_rBookmarksStart.begin(), end = m_rBookmarksStart.end();
1370 it != end; ++it )
1372 const OString& rName = *it;
1374 // Output the bookmark
1375 const sal_Int32 nId = m_nNextBookmarkId++;
1376 m_rOpenedBookmarksIds[rName] = nId;
1377 m_pSerializer->singleElementNS( XML_w, XML_bookmarkStart,
1378 FSNS( XML_w, XML_id ), OString::number( nId ).getStr( ),
1379 FSNS( XML_w, XML_name ), rName.getStr(),
1380 FSEND );
1381 m_sLastOpenedBookmark = rName;
1383 m_rBookmarksStart.clear();
1385 // export the end bookmarks
1386 for ( std::vector< OString >::const_iterator it = m_rBookmarksEnd.begin(), end = m_rBookmarksEnd.end();
1387 it != end; ++it )
1389 const OString& rName = *it;
1391 // Get the id of the bookmark
1392 std::map< OString, sal_Int32 >::iterator pPos = m_rOpenedBookmarksIds.find( rName );
1393 if ( pPos != m_rOpenedBookmarksIds.end( ) )
1395 const sal_Int32 nId = ( *pPos ).second;
1396 m_pSerializer->singleElementNS( XML_w, XML_bookmarkEnd,
1397 FSNS( XML_w, XML_id ), OString::number( nId ).getStr( ),
1398 FSEND );
1399 m_rOpenedBookmarksIds.erase( rName );
1402 m_rBookmarksEnd.clear();
1405 void DocxAttributeOutput::DoWriteAnnotationMarks()
1407 // Write the start annotation marks
1408 for ( std::vector< OString >::const_iterator it = m_rAnnotationMarksStart.begin(), end = m_rAnnotationMarksStart.end();
1409 it != end; ++it )
1411 const OString& rName = *it;
1413 // Output the annotation mark
1414 /* Ensure that the existing Annotation Marks are not overwritten
1415 as it causes discrepancy when DocxAttributeOutput::PostitField
1416 refers to this map & while mapping comment id's in document.xml &
1417 comment.xml.
1419 if ( m_rOpenedAnnotationMarksIds.end() == m_rOpenedAnnotationMarksIds.find( rName ) )
1421 const sal_Int32 nId = m_nNextAnnotationMarkId++;
1422 m_rOpenedAnnotationMarksIds[rName] = nId;
1423 m_pSerializer->singleElementNS( XML_w, XML_commentRangeStart,
1424 FSNS( XML_w, XML_id ), OString::number( nId ).getStr( ),
1425 FSEND );
1426 m_sLastOpenedAnnotationMark = rName;
1429 m_rAnnotationMarksStart.clear();
1431 // export the end annotation marks
1432 for ( std::vector< OString >::const_iterator it = m_rAnnotationMarksEnd.begin(), end = m_rAnnotationMarksEnd.end();
1433 it != end; ++it )
1435 const OString& rName = *it;
1437 // Get the id of the annotation mark
1438 std::map< OString, sal_Int32 >::iterator pPos = m_rOpenedAnnotationMarksIds.find( rName );
1439 if ( pPos != m_rOpenedAnnotationMarksIds.end( ) )
1441 const sal_Int32 nId = ( *pPos ).second;
1442 m_pSerializer->singleElementNS( XML_w, XML_commentRangeEnd,
1443 FSNS( XML_w, XML_id ), OString::number( nId ).getStr( ),
1444 FSEND );
1445 m_rOpenedAnnotationMarksIds.erase( rName );
1447 m_pSerializer->startElementNS(XML_w, XML_r, FSEND);
1448 m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ),
1449 OString::number( nId ).getStr(),
1450 FSEND );
1451 m_pSerializer->endElementNS(XML_w, XML_r);
1454 m_rAnnotationMarksEnd.clear();
1457 void DocxAttributeOutput::WriteFFData( const FieldInfos& rInfos )
1459 const ::sw::mark::IFieldmark& rFieldmark = *rInfos.pFieldmark;
1460 if ( rInfos.eType == ww::eFORMDROPDOWN )
1462 uno::Sequence< OUString> vListEntries;
1463 OUString sName, sHelp, sToolTip, sSelected;
1465 FieldMarkParamsHelper params( rFieldmark );
1466 params.extractParam( ODF_FORMDROPDOWN_LISTENTRY, vListEntries );
1467 sName = params.getName();
1468 sal_Int32 nSelectedIndex = 0;
1470 if ( params.extractParam( ODF_FORMDROPDOWN_RESULT, nSelectedIndex ) )
1472 if (nSelectedIndex < vListEntries.getLength() )
1473 sSelected = vListEntries[ nSelectedIndex ];
1476 GetExport().DoComboBox( sName, sHelp, sToolTip, sSelected, vListEntries );
1478 else if ( rInfos.eType == ww::eFORMCHECKBOX )
1480 OUString sName;
1481 bool bChecked = false;
1483 FieldMarkParamsHelper params( rFieldmark );
1484 params.extractParam( ODF_FORMCHECKBOX_NAME, sName );
1486 const sw::mark::ICheckboxFieldmark* pCheckboxFm = dynamic_cast<const sw::mark::ICheckboxFieldmark*>(&rFieldmark);
1487 if ( pCheckboxFm && pCheckboxFm->IsChecked() )
1488 bChecked = true;
1490 FFDataWriterHelper ffdataOut( m_pSerializer );
1491 ffdataOut.WriteFormCheckbox( sName, OUString(), bChecked );
1493 else if ( rInfos.eType == ww::eFORMTEXT )
1495 FieldMarkParamsHelper params( rFieldmark );
1496 FFDataWriterHelper ffdataOut( m_pSerializer );
1497 ffdataOut.WriteFormText( params.getName(), OUString() );
1501 void DocxAttributeOutput::StartField_Impl( FieldInfos& rInfos, bool bWriteRun )
1503 if ( rInfos.pField && rInfos.eType == ww::eUNKNOWN )
1505 // Expand unsupported fields
1506 RunText( rInfos.pField->GetFieldName() );
1508 else if ( rInfos.eType != ww::eNONE ) // HYPERLINK fields are just commands
1510 if ( bWriteRun )
1511 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1513 if ( rInfos.eType == ww::eFORMDROPDOWN )
1515 m_pSerializer->startElementNS( XML_w, XML_fldChar,
1516 FSNS( XML_w, XML_fldCharType ), "begin",
1517 FSEND );
1518 if ( rInfos.pFieldmark && !rInfos.pField )
1519 WriteFFData( rInfos );
1520 if ( rInfos.pField )
1522 const SwDropDownField& rField2 = *static_cast<const SwDropDownField*>(rInfos.pField.get());
1523 uno::Sequence<OUString> aItems =
1524 rField2.GetItemSequence();
1525 GetExport().DoComboBox(rField2.GetName(),
1526 rField2.GetHelp(),
1527 rField2.GetToolTip(),
1528 rField2.GetSelectedItem(), aItems);
1530 m_pSerializer->endElementNS( XML_w, XML_fldChar );
1532 if ( bWriteRun )
1533 m_pSerializer->endElementNS( XML_w, XML_r );
1534 if ( !rInfos.pField )
1535 CmdField_Impl( rInfos );
1538 else
1540 // Write the field start
1541 if ( rInfos.pField && rInfos.pField->GetSubType() & FIXEDFLD )
1543 m_pSerializer->startElementNS( XML_w, XML_fldChar,
1544 FSNS( XML_w, XML_fldCharType ), "begin",
1545 FSNS( XML_w, XML_fldLock ), "true",
1546 FSEND );
1548 else
1550 m_pSerializer->startElementNS( XML_w, XML_fldChar,
1551 FSNS( XML_w, XML_fldCharType ), "begin",
1552 FSEND );
1555 if ( rInfos.pFieldmark )
1556 WriteFFData( rInfos );
1558 m_pSerializer->endElementNS( XML_w, XML_fldChar );
1560 if ( bWriteRun )
1561 m_pSerializer->endElementNS( XML_w, XML_r );
1563 // The hyperlinks fields can't be expanded: the value is
1564 // normally in the text run
1565 if ( !rInfos.pField )
1566 CmdField_Impl( rInfos );
1571 void DocxAttributeOutput::DoWriteCmd( const OUString& rCmd )
1573 OUString sCmd = rCmd.trim();
1574 if (sCmd.startsWith("SEQ"))
1576 OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\').trim();
1577 m_aSeqBookmarksNames[sSeqName].push_back(m_sLastOpenedBookmark);
1579 // Write the Field command
1580 m_pSerializer->startElementNS( XML_w, XML_instrText, FSEND );
1581 m_pSerializer->writeEscaped( rCmd );
1582 m_pSerializer->endElementNS( XML_w, XML_instrText );
1586 void DocxAttributeOutput::CmdField_Impl( FieldInfos& rInfos )
1588 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1589 sal_Int32 nNbToken = comphelper::string::getTokenCount(rInfos.sCmd, '\t');
1591 for ( sal_Int32 i = 0; i < nNbToken; i++ )
1593 OUString sToken = rInfos.sCmd.getToken( i, '\t' );
1594 if ( rInfos.eType == ww::eCREATEDATE
1595 || rInfos.eType == ww::eSAVEDATE
1596 || rInfos.eType == ww::ePRINTDATE
1597 || rInfos.eType == ww::eDATE
1598 || rInfos.eType == ww::eTIME )
1600 sToken = sToken.replaceAll("NNNN", "dddd");
1601 sToken = sToken.replaceAll("NN", "ddd");
1604 // Write the Field command
1605 DoWriteCmd( sToken );
1607 // Replace tabs by </instrText><tab/><instrText>
1608 if ( i < ( nNbToken - 1 ) )
1609 RunText( "\t" );
1612 m_pSerializer->endElementNS( XML_w, XML_r );
1614 // Write the Field separator
1615 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1616 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1617 FSNS( XML_w, XML_fldCharType ), "separate",
1618 FSEND );
1619 m_pSerializer->endElementNS( XML_w, XML_r );
1622 void DocxAttributeOutput::EndField_Impl( FieldInfos& rInfos )
1624 // The command has to be written before for the hyperlinks
1625 if ( rInfos.pField )
1627 CmdField_Impl( rInfos );
1630 // Write the bookmark start if any
1631 OUString aBkmName( m_sFieldBkm );
1632 if ( !aBkmName.isEmpty() )
1634 m_pSerializer->singleElementNS( XML_w, XML_bookmarkStart,
1635 FSNS( XML_w, XML_id ), OString::number( m_nNextBookmarkId ).getStr( ),
1636 FSNS( XML_w, XML_name ), OUStringToOString( aBkmName, RTL_TEXTENCODING_UTF8 ).getStr( ),
1637 FSEND );
1640 if (rInfos.pField ) // For hyperlinks and TOX
1642 // Write the Field latest value
1643 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1644 OUString sExpand;
1645 if(rInfos.eType == ww::eCITATION)
1647 sExpand = static_cast<SwAuthorityField const*>(rInfos.pField.get())
1648 ->ExpandCitation(AUTH_FIELD_TITLE);
1650 else
1652 sExpand = rInfos.pField->ExpandField( true );
1654 // newlines embedded in fields are 0x0B in MSO and 0x0A for us
1655 RunText(sExpand.replace(0x0A, 0x0B));
1657 m_pSerializer->endElementNS( XML_w, XML_r );
1660 // Write the bookmark end if any
1661 if ( !aBkmName.isEmpty() )
1663 m_pSerializer->singleElementNS( XML_w, XML_bookmarkEnd,
1664 FSNS( XML_w, XML_id ), OString::number( m_nNextBookmarkId ).getStr( ),
1665 FSEND );
1667 m_nNextBookmarkId++;
1670 // Write the Field end
1671 if ( rInfos.bClose )
1673 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1674 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1675 FSNS( XML_w, XML_fldCharType ), "end",
1676 FSEND );
1677 m_pSerializer->endElementNS( XML_w, XML_r );
1679 // Write the ref field if a bookmark had to be set and the field
1680 // should be visible
1681 if ( rInfos.pField )
1683 sal_uInt16 nSubType = rInfos.pField->GetSubType( );
1684 bool bIsSetField = rInfos.pField->GetTyp( )->Which( ) == RES_SETEXPFLD;
1685 bool bShowRef = bIsSetField && ( nSubType & nsSwExtendedSubType::SUB_INVISIBLE ) == 0;
1687 if ( ( !m_sFieldBkm.isEmpty() ) && bShowRef )
1689 // Write the field beginning
1690 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
1691 m_pSerializer->singleElementNS( XML_w, XML_fldChar,
1692 FSNS( XML_w, XML_fldCharType ), "begin",
1693 FSEND );
1694 m_pSerializer->endElementNS( XML_w, XML_r );
1696 rInfos.sCmd = FieldString( ww::eREF );
1697 rInfos.sCmd += "\"";
1698 rInfos.sCmd += m_sFieldBkm;
1699 rInfos.sCmd += "\" ";
1701 // Clean the field bookmark data to avoid infinite loop
1702 m_sFieldBkm = OUString( );
1704 // Write the end of the field
1705 EndField_Impl( rInfos );
1710 void DocxAttributeOutput::StartRunProperties()
1712 // postpone the output so that we can later [in EndRunProperties()]
1713 // prepend the properties before the text
1714 m_pSerializer->mark(Tag_StartRunProperties);
1716 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
1718 if(GetExport().m_bHideTabLeaderAndPageNumbers && m_pHyperlinkAttrList.is() )
1720 m_pSerializer->singleElementNS( XML_w, XML_webHidden, FSEND );
1722 InitCollectedRunProperties();
1724 OSL_ASSERT( !m_pPostponedGraphic );
1725 m_pPostponedGraphic.reset(new std::list<PostponedGraphic>());
1727 OSL_ASSERT( !m_pPostponedDiagrams );
1728 m_pPostponedDiagrams.reset(new std::list<PostponedDiagram>());
1730 OSL_ASSERT( !m_pPostponedVMLDrawings );
1731 m_pPostponedVMLDrawings.reset(new std::list<PostponedDrawing>());
1733 assert(!m_pPostponedDMLDrawings);
1734 m_pPostponedDMLDrawings.reset(new std::list<PostponedDrawing>());
1736 assert( !m_pPostponedOLEs );
1737 m_pPostponedOLEs.reset(new std::list<PostponedOLE>());
1740 void DocxAttributeOutput::InitCollectedRunProperties()
1742 m_pFontsAttrList = nullptr;
1743 m_pEastAsianLayoutAttrList = nullptr;
1744 m_pCharLangAttrList = nullptr;
1746 // Write the elements in the spec order
1747 static const sal_Int32 aOrder[] =
1749 FSNS( XML_w, XML_rStyle ),
1750 FSNS( XML_w, XML_rFonts ),
1751 FSNS( XML_w, XML_b ),
1752 FSNS( XML_w, XML_bCs ),
1753 FSNS( XML_w, XML_i ),
1754 FSNS( XML_w, XML_iCs ),
1755 FSNS( XML_w, XML_caps ),
1756 FSNS( XML_w, XML_smallCaps ),
1757 FSNS( XML_w, XML_strike ),
1758 FSNS( XML_w, XML_dstrike ),
1759 FSNS( XML_w, XML_outline ),
1760 FSNS( XML_w, XML_shadow ),
1761 FSNS( XML_w, XML_emboss ),
1762 FSNS( XML_w, XML_imprint ),
1763 FSNS( XML_w, XML_noProof ),
1764 FSNS( XML_w, XML_snapToGrid ),
1765 FSNS( XML_w, XML_vanish ),
1766 FSNS( XML_w, XML_webHidden ),
1767 FSNS( XML_w, XML_color ),
1768 FSNS( XML_w, XML_spacing ),
1769 FSNS( XML_w, XML_w ),
1770 FSNS( XML_w, XML_kern ),
1771 FSNS( XML_w, XML_position ),
1772 FSNS( XML_w, XML_sz ),
1773 FSNS( XML_w, XML_szCs ),
1774 FSNS( XML_w, XML_highlight ),
1775 FSNS( XML_w, XML_u ),
1776 FSNS( XML_w, XML_effect ),
1777 FSNS( XML_w, XML_bdr ),
1778 FSNS( XML_w, XML_shd ),
1779 FSNS( XML_w, XML_fitText ),
1780 FSNS( XML_w, XML_vertAlign ),
1781 FSNS( XML_w, XML_rtl ),
1782 FSNS( XML_w, XML_cs ),
1783 FSNS( XML_w, XML_em ),
1784 FSNS( XML_w, XML_lang ),
1785 FSNS( XML_w, XML_eastAsianLayout ),
1786 FSNS( XML_w, XML_specVanish ),
1787 FSNS( XML_w, XML_oMath ),
1788 FSNS( XML_w, XML_rPrChange ),
1789 FSNS( XML_w14, XML_glow ),
1790 FSNS( XML_w14, XML_shadow ),
1791 FSNS( XML_w14, XML_reflection ),
1792 FSNS( XML_w14, XML_textOutline ),
1793 FSNS( XML_w14, XML_textFill ),
1794 FSNS( XML_w14, XML_scene3d ),
1795 FSNS( XML_w14, XML_props3d ),
1796 FSNS( XML_w14, XML_ligatures ),
1797 FSNS( XML_w14, XML_numForm ),
1798 FSNS( XML_w14, XML_numSpacing ),
1799 FSNS( XML_w14, XML_stylisticSets ),
1800 FSNS( XML_w14, XML_cntxtAlts ),
1803 // postpone the output so that we can later [in EndParagraphProperties()]
1804 // prepend the properties before the run
1805 sal_Int32 len = sizeof ( aOrder ) / sizeof( sal_Int32 );
1806 uno::Sequence< sal_Int32 > aSeqOrder( len );
1807 for ( sal_Int32 i = 0; i < len; i++ )
1808 aSeqOrder[i] = aOrder[i];
1810 m_pSerializer->mark(Tag_InitCollectedRunProperties, aSeqOrder);
1813 namespace
1816 struct NameToId
1818 OUString maName;
1819 sal_Int32 maId;
1822 const NameToId constNameToIdMapping[] =
1824 { OUString("glow"), FSNS( XML_w14, XML_glow ) },
1825 { OUString("shadow"), FSNS( XML_w14, XML_shadow ) },
1826 { OUString("reflection"), FSNS( XML_w14, XML_reflection ) },
1827 { OUString("textOutline"), FSNS( XML_w14, XML_textOutline ) },
1828 { OUString("textFill"), FSNS( XML_w14, XML_textFill ) },
1829 { OUString("scene3d"), FSNS( XML_w14, XML_scene3d ) },
1830 { OUString("props3d"), FSNS( XML_w14, XML_props3d ) },
1831 { OUString("ligatures"), FSNS( XML_w14, XML_ligatures ) },
1832 { OUString("numForm"), FSNS( XML_w14, XML_numForm ) },
1833 { OUString("numSpacing"), FSNS( XML_w14, XML_numSpacing ) },
1834 { OUString("stylisticSets"),FSNS( XML_w14, XML_stylisticSets ) },
1835 { OUString("cntxtAlts"), FSNS( XML_w14, XML_cntxtAlts ) },
1837 { OUString("val"), FSNS( XML_w14, XML_val ) },
1838 { OUString("rad"), FSNS( XML_w14, XML_rad ) },
1839 { OUString("blurRad"), FSNS( XML_w14, XML_blurRad ) },
1840 { OUString("stA"), FSNS( XML_w14, XML_stA ) },
1841 { OUString("stPos"), FSNS( XML_w14, XML_stPos ) },
1842 { OUString("endA"), FSNS( XML_w14, XML_endA ) },
1843 { OUString("endPos"), FSNS( XML_w14, XML_endPos ) },
1844 { OUString("dist"), FSNS( XML_w14, XML_dist ) },
1845 { OUString("dir"), FSNS( XML_w14, XML_dir ) },
1846 { OUString("fadeDir"), FSNS( XML_w14, XML_fadeDir ) },
1847 { OUString("sx"), FSNS( XML_w14, XML_sx ) },
1848 { OUString("sy"), FSNS( XML_w14, XML_sy ) },
1849 { OUString("kx"), FSNS( XML_w14, XML_kx ) },
1850 { OUString("ky"), FSNS( XML_w14, XML_ky ) },
1851 { OUString("algn"), FSNS( XML_w14, XML_algn ) },
1852 { OUString("w"), FSNS( XML_w14, XML_w ) },
1853 { OUString("cap"), FSNS( XML_w14, XML_cap ) },
1854 { OUString("cmpd"), FSNS( XML_w14, XML_cmpd ) },
1855 { OUString("pos"), FSNS( XML_w14, XML_pos ) },
1856 { OUString("ang"), FSNS( XML_w14, XML_ang ) },
1857 { OUString("scaled"), FSNS( XML_w14, XML_scaled ) },
1858 { OUString("path"), FSNS( XML_w14, XML_path ) },
1859 { OUString("l"), FSNS( XML_w14, XML_l ) },
1860 { OUString("t"), FSNS( XML_w14, XML_t ) },
1861 { OUString("r"), FSNS( XML_w14, XML_r ) },
1862 { OUString("b"), FSNS( XML_w14, XML_b ) },
1863 { OUString("lim"), FSNS( XML_w14, XML_lim ) },
1864 { OUString("prst"), FSNS( XML_w14, XML_prst ) },
1865 { OUString("rig"), FSNS( XML_w14, XML_rig ) },
1866 { OUString("lat"), FSNS( XML_w14, XML_lat ) },
1867 { OUString("lon"), FSNS( XML_w14, XML_lon ) },
1868 { OUString("rev"), FSNS( XML_w14, XML_rev ) },
1869 { OUString("h"), FSNS( XML_w14, XML_h ) },
1870 { OUString("extrusionH"), FSNS( XML_w14, XML_extrusionH ) },
1871 { OUString("contourW"), FSNS( XML_w14, XML_contourW ) },
1872 { OUString("prstMaterial"), FSNS( XML_w14, XML_prstMaterial ) },
1873 { OUString("id"), FSNS( XML_w14, XML_id ) },
1875 { OUString("schemeClr"), FSNS( XML_w14, XML_schemeClr ) },
1876 { OUString("srgbClr"), FSNS( XML_w14, XML_srgbClr ) },
1877 { OUString("tint"), FSNS( XML_w14, XML_tint ) },
1878 { OUString("shade"), FSNS( XML_w14, XML_shade ) },
1879 { OUString("alpha"), FSNS( XML_w14, XML_alpha ) },
1880 { OUString("hueMod"), FSNS( XML_w14, XML_hueMod ) },
1881 { OUString("sat"), FSNS( XML_w14, XML_sat ) },
1882 { OUString("satOff"), FSNS( XML_w14, XML_satOff ) },
1883 { OUString("satMod"), FSNS( XML_w14, XML_satMod ) },
1884 { OUString("lum"), FSNS( XML_w14, XML_lum ) },
1885 { OUString("lumOff"), FSNS( XML_w14, XML_lumOff ) },
1886 { OUString("lumMod"), FSNS( XML_w14, XML_lumMod ) },
1887 { OUString("noFill"), FSNS( XML_w14, XML_noFill ) },
1888 { OUString("solidFill"), FSNS( XML_w14, XML_solidFill ) },
1889 { OUString("gradFill"), FSNS( XML_w14, XML_gradFill ) },
1890 { OUString("gsLst"), FSNS( XML_w14, XML_gsLst ) },
1891 { OUString("gs"), FSNS( XML_w14, XML_gs ) },
1892 { OUString("pos"), FSNS( XML_w14, XML_pos ) },
1893 { OUString("lin"), FSNS( XML_w14, XML_lin ) },
1894 { OUString("path"), FSNS( XML_w14, XML_path ) },
1895 { OUString("fillToRect"), FSNS( XML_w14, XML_fillToRect ) },
1896 { OUString("prstDash"), FSNS( XML_w14, XML_prstDash ) },
1897 { OUString("round"), FSNS( XML_w14, XML_round ) },
1898 { OUString("bevel"), FSNS( XML_w14, XML_bevel ) },
1899 { OUString("miter"), FSNS( XML_w14, XML_miter ) },
1900 { OUString("camera"), FSNS( XML_w14, XML_camera ) },
1901 { OUString("lightRig"), FSNS( XML_w14, XML_lightRig ) },
1902 { OUString("rot"), FSNS( XML_w14, XML_rot ) },
1903 { OUString("bevelT"), FSNS( XML_w14, XML_bevelT ) },
1904 { OUString("bevelB"), FSNS( XML_w14, XML_bevelB ) },
1905 { OUString("extrusionClr"), FSNS( XML_w14, XML_extrusionClr ) },
1906 { OUString("contourClr"), FSNS( XML_w14, XML_contourClr ) },
1907 { OUString("styleSet"), FSNS( XML_w14, XML_styleSet ) },
1910 boost::optional<sal_Int32> lclGetElementIdForName(const OUString& rName)
1912 sal_Int32 aLength = sizeof (constNameToIdMapping) / sizeof(NameToId);
1913 for (sal_Int32 i=0; i < aLength; ++i)
1915 if (rName == constNameToIdMapping[i].maName)
1917 return constNameToIdMapping[i].maId;
1920 return boost::optional<sal_Int32>();
1923 void lclProcessRecursiveGrabBag(sal_Int32 aElementId, const css::uno::Sequence<css::beans::PropertyValue>& rElements, sax_fastparser::FSHelperPtr const & pSerializer)
1925 css::uno::Sequence<css::beans::PropertyValue> aAttributes;
1926 FastAttributeList* pAttributes = FastSerializerHelper::createAttrList();
1928 for (sal_Int32 j=0; j < rElements.getLength(); ++j)
1930 if (rElements[j].Name == "attributes")
1932 rElements[j].Value >>= aAttributes;
1936 for (sal_Int32 j=0; j < aAttributes.getLength(); ++j)
1938 uno::Any aAny = aAttributes[j].Value;
1939 OString aValue;
1941 if(aAny.getValueType() == cppu::UnoType<sal_Int32>::get())
1943 aValue = OString::number(aAny.get<sal_Int32>());
1945 else if(aAny.getValueType() == cppu::UnoType<OUString>::get())
1947 aValue = OUStringToOString(aAny.get<OUString>(), RTL_TEXTENCODING_ASCII_US);
1950 boost::optional<sal_Int32> aSubElementId = lclGetElementIdForName(aAttributes[j].Name);
1951 if(aSubElementId)
1952 pAttributes->add(*aSubElementId, aValue.getStr());
1955 XFastAttributeListRef xAttributesList( pAttributes );
1957 pSerializer->startElement(aElementId, xAttributesList);
1959 for (sal_Int32 j=0; j < rElements.getLength(); ++j)
1961 css::uno::Sequence<css::beans::PropertyValue> aSumElements;
1963 boost::optional<sal_Int32> aSubElementId = lclGetElementIdForName(rElements[j].Name);
1964 if(aSubElementId)
1966 rElements[j].Value >>= aSumElements;
1967 lclProcessRecursiveGrabBag(*aSubElementId, aSumElements, pSerializer);
1971 pSerializer->endElement(aElementId);
1976 void DocxAttributeOutput::WriteCollectedRunProperties()
1978 // Write all differed properties
1979 if ( m_pFontsAttrList.is() )
1981 XFastAttributeListRef xAttrList( m_pFontsAttrList.get() );
1982 m_pFontsAttrList.clear();
1983 m_pSerializer->singleElementNS( XML_w, XML_rFonts, xAttrList );
1986 if ( m_pColorAttrList.is() )
1988 XFastAttributeListRef xAttrList( m_pColorAttrList.get() );
1989 m_pColorAttrList.clear();
1991 m_pSerializer->singleElementNS( XML_w, XML_color, xAttrList );
1994 if ( m_pEastAsianLayoutAttrList.is() )
1996 XFastAttributeListRef xAttrList( m_pEastAsianLayoutAttrList.get() );
1997 m_pEastAsianLayoutAttrList.clear();
1998 m_pSerializer->singleElementNS( XML_w, XML_eastAsianLayout, xAttrList );
2001 if ( m_pCharLangAttrList.is() )
2003 XFastAttributeListRef xAttrList( m_pCharLangAttrList.get() );
2004 m_pCharLangAttrList.clear();
2005 m_pSerializer->singleElementNS( XML_w, XML_lang, xAttrList );
2008 if (!m_aTextEffectsGrabBag.empty())
2010 for (beans::PropertyValue & i : m_aTextEffectsGrabBag)
2012 boost::optional<sal_Int32> aElementId = lclGetElementIdForName(i.Name);
2013 if(aElementId)
2015 uno::Sequence<beans::PropertyValue> aGrabBagSeq;
2016 i.Value >>= aGrabBagSeq;
2017 lclProcessRecursiveGrabBag(*aElementId, aGrabBagSeq, m_pSerializer);
2020 m_aTextEffectsGrabBag.clear();
2024 void DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData )
2026 // Call the 'Redline' function. This will add redline (change-tracking) information that regards to run properties.
2027 // This includes changes like 'Bold', 'Underline', 'Strikethrough' etc.
2028 Redline( pRedlineData );
2030 WriteCollectedRunProperties();
2032 // Merge the marks for the ordered elements
2033 m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
2035 m_pSerializer->endElementNS( XML_w, XML_rPr );
2037 // write footnotes/endnotes if we have any
2038 FootnoteEndnoteReference();
2040 WritePostponedGraphic();
2042 WritePostponedDiagram();
2043 //We need to write w:drawing tag after the w:rPr.
2044 WritePostponedChart();
2046 //We need to write w:pict tag after the w:rPr.
2047 WritePostponedVMLDrawing();
2048 WritePostponedDMLDrawing();
2050 WritePostponedOLE();
2052 WritePostponedActiveXControl(true);
2054 // merge the properties _before_ the run text (strictly speaking, just
2055 // after the start of the run)
2056 m_pSerializer->mergeTopMarks(Tag_StartRunProperties, sax_fastparser::MergeMarks::PREPEND);
2059 void DocxAttributeOutput::GetSdtEndBefore(const SdrObject* pSdrObj)
2061 if (pSdrObj)
2063 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY_THROW);
2064 if( xShape.is() )
2066 uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
2067 uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
2068 if( xPropSet.is() )
2070 xPropSetInfo = xPropSet->getPropertySetInfo();
2071 uno::Sequence< beans::PropertyValue > aGrabBag;
2072 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
2074 xPropSet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
2076 else if(xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
2078 xPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
2081 for (sal_Int32 nProp=0; nProp < aGrabBag.getLength(); ++nProp)
2083 if ("SdtEndBefore" == aGrabBag[nProp].Name && m_bStartedCharSdt && !m_bEndCharSdt)
2085 aGrabBag[nProp].Value >>= m_bEndCharSdt;
2086 break;
2094 void DocxAttributeOutput::WritePostponedGraphic()
2096 for( std::list< PostponedGraphic >::const_iterator it = m_pPostponedGraphic->begin();
2097 it != m_pPostponedGraphic->end();
2098 ++it )
2099 FlyFrameGraphic( it->grfNode, it->size, it->mOLEFrameFormat, it->mOLENode, it->pSdrObj );
2100 m_pPostponedGraphic.reset(nullptr);
2103 void DocxAttributeOutput::WritePostponedDiagram()
2105 for( std::list< PostponedDiagram >::const_iterator it = m_pPostponedDiagrams->begin();
2106 it != m_pPostponedDiagrams->end();
2107 ++it )
2108 m_rExport.SdrExporter().writeDiagram( it->object, *(it->frame), m_anchorId++ );
2109 m_pPostponedDiagrams.reset(nullptr);
2112 void DocxAttributeOutput::FootnoteEndnoteRefTag()
2114 if( m_footnoteEndnoteRefTag == 0 )
2115 return;
2116 m_pSerializer->singleElementNS( XML_w, m_footnoteEndnoteRefTag, FSEND );
2117 m_footnoteEndnoteRefTag = 0;
2120 /** Output sal_Unicode* as a run text (<t>the text</t>).
2122 When bMove is true, update rBegin to point _after_ the end of the text +
2123 1, meaning that it skips one character after the text. This is to make
2124 the switch in DocxAttributeOutput::RunText() nicer ;-)
2126 static bool impl_WriteRunText( FSHelperPtr const & pSerializer, sal_Int32 nTextToken,
2127 const sal_Unicode* &rBegin, const sal_Unicode* pEnd, bool bMove = true )
2129 const sal_Unicode *pBegin = rBegin;
2131 // skip one character after the end
2132 if ( bMove )
2133 rBegin = pEnd + 1;
2135 if ( pBegin >= pEnd )
2136 return false; // we want to write at least one character
2138 // we have to add 'preserve' when starting/ending with space
2139 if ( *pBegin == ' ' || *( pEnd - 1 ) == ' ' )
2141 pSerializer->startElementNS( XML_w, nTextToken, FSNS( XML_xml, XML_space ), "preserve", FSEND );
2143 else
2144 pSerializer->startElementNS( XML_w, nTextToken, FSEND );
2146 pSerializer->writeEscaped( OUString( pBegin, pEnd - pBegin ) );
2148 pSerializer->endElementNS( XML_w, nTextToken );
2150 return true;
2153 void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCharSet*/ )
2155 if( m_closeHyperlinkInThisRun )
2157 m_closeHyperlinkInPreviousRun = true;
2159 m_bRunTextIsOn = true;
2160 // one text can be split into more <w:t>blah</w:t>'s by line breaks etc.
2161 const sal_Unicode *pBegin = rText.getStr();
2162 const sal_Unicode *pEnd = pBegin + rText.getLength();
2164 // the text run is usually XML_t, with the exception of the deleted text
2165 sal_Int32 nTextToken = XML_t;
2166 if ( m_pRedlineData && m_pRedlineData->GetType() == nsRedlineType_t::REDLINE_DELETE )
2167 nTextToken = XML_delText;
2169 sal_Unicode prevUnicode = *pBegin;
2171 for ( const sal_Unicode *pIt = pBegin; pIt < pEnd; ++pIt )
2173 switch ( *pIt )
2175 case 0x09: // tab
2176 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2177 m_pSerializer->singleElementNS( XML_w, XML_tab, FSEND );
2178 prevUnicode = *pIt;
2179 break;
2180 case 0x0b: // line break
2182 if (impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt ) || (prevUnicode == *pIt))
2184 m_pSerializer->singleElementNS( XML_w, XML_br, FSEND );
2185 prevUnicode = *pIt;
2188 break;
2189 case 0x1E: //non-breaking hyphen
2190 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2191 m_pSerializer->singleElementNS( XML_w, XML_noBreakHyphen, FSEND );
2192 prevUnicode = *pIt;
2193 break;
2194 case 0x1F: //soft (on demand) hyphen
2195 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2196 m_pSerializer->singleElementNS( XML_w, XML_softHyphen, FSEND );
2197 prevUnicode = *pIt;
2198 break;
2199 default:
2200 if ( *pIt < 0x0020 ) // filter out the control codes
2202 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pIt );
2203 OSL_TRACE( "Ignored control code %x in a text run.", *pIt );
2205 prevUnicode = *pIt;
2206 break;
2210 impl_WriteRunText( m_pSerializer, nTextToken, pBegin, pEnd, false );
2213 void DocxAttributeOutput::RawText(const OUString& /*rText*/, rtl_TextEncoding /*eCharSet*/)
2215 OSL_TRACE("TODO DocxAttributeOutput::RawText( const String& rText, bool bForceUnicode, rtl_TextEncoding eCharSet )" );
2218 void DocxAttributeOutput::StartRuby( const SwTextNode& rNode, sal_Int32 nPos, const SwFormatRuby& rRuby )
2220 OSL_TRACE("TODO DocxAttributeOutput::StartRuby( const SwTextNode& rNode, const SwFormatRuby& rRuby )" );
2221 EndRun(); // end run before starting ruby to avoid nested runs, and overlap
2222 assert(!m_closeHyperlinkInThisRun); // check that no hyperlink overlaps ruby
2223 assert(!m_closeHyperlinkInPreviousRun);
2224 m_pSerializer->startElementNS( XML_w, XML_ruby, FSEND );
2225 m_pSerializer->startElementNS( XML_w, XML_rubyPr, FSEND );
2226 // hps
2227 // hpsBaseText
2228 // hpsRaise
2229 // lid
2230 lang::Locale aLocale( SwBreakIt::Get()->GetLocale(
2231 rNode.GetLang( nPos ) ) );
2232 OUString sLang( LanguageTag::convertToBcp47( aLocale) );
2233 m_pSerializer->singleElementNS( XML_w, XML_lid,
2234 FSNS( XML_w, XML_val ),
2235 OUStringToOString( sLang, RTL_TEXTENCODING_UTF8 ).getStr( ), FSEND );
2237 OString sAlign ( "center" );
2238 switch ( rRuby.GetAdjustment( ) )
2240 case 0:
2241 sAlign = OString( "left" );
2242 break;
2243 case 1:
2244 // Defaults to center
2245 break;
2246 case 2:
2247 sAlign = OString( "right" );
2248 break;
2249 case 3:
2250 sAlign = OString( "distributeLetter" );
2251 break;
2252 case 4:
2253 sAlign = OString( "distributeSpace" );
2254 break;
2255 default:
2256 break;
2258 m_pSerializer->singleElementNS( XML_w, XML_rubyAlign,
2259 FSNS( XML_w, XML_val ), sAlign.getStr(), FSEND );
2260 m_pSerializer->endElementNS( XML_w, XML_rubyPr );
2262 m_pSerializer->startElementNS( XML_w, XML_rt, FSEND );
2263 StartRun( nullptr );
2264 StartRunProperties( );
2265 SwWW8AttrIter aAttrIt( m_rExport, rNode );
2266 aAttrIt.OutAttr( nPos, true );
2268 sal_uInt16 nStyle = m_rExport.GetId( rRuby.GetTextRuby()->GetCharFormat() );
2269 OString aStyleId(m_rExport.m_pStyles->GetStyleId(nStyle));
2270 m_pSerializer->singleElementNS( XML_w, XML_rStyle,
2271 FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
2273 EndRunProperties( nullptr );
2274 RunText( rRuby.GetText( ) );
2275 EndRun( );
2276 m_pSerializer->endElementNS( XML_w, XML_rt );
2278 m_pSerializer->startElementNS( XML_w, XML_rubyBase, FSEND );
2279 StartRun( nullptr );
2282 void DocxAttributeOutput::EndRuby()
2284 OSL_TRACE( "TODO DocxAttributeOutput::EndRuby()" );
2285 EndRun( );
2286 m_pSerializer->endElementNS( XML_w, XML_rubyBase );
2287 m_pSerializer->endElementNS( XML_w, XML_ruby );
2288 StartRun(nullptr); // open Run again so OutputTextNode loop can close it
2291 bool DocxAttributeOutput::AnalyzeURL( const OUString& rUrl, const OUString& rTarget, OUString* pLinkURL, OUString* pMark )
2293 bool bBookMarkOnly = AttributeOutputBase::AnalyzeURL( rUrl, rTarget, pLinkURL, pMark );
2295 if ( !pMark->isEmpty() )
2297 OUString sURL = *pLinkURL;
2299 if ( bBookMarkOnly )
2300 sURL = FieldString( ww::eHYPERLINK );
2301 else
2302 sURL = FieldString( ww::eHYPERLINK ) + "\"" + sURL + "\"";
2304 sURL += " \\l \"" + *pMark + "\"";
2306 if ( !rTarget.isEmpty() )
2307 sURL += " \\n " + rTarget;
2309 *pLinkURL = sURL;
2312 return bBookMarkOnly;
2315 bool DocxAttributeOutput::StartURL( const OUString& rUrl, const OUString& rTarget )
2317 OUString sMark;
2318 OUString sUrl;
2320 bool bBookmarkOnly = AnalyzeURL( rUrl, rTarget, &sUrl, &sMark );
2322 m_hyperLinkAnchor = sMark;
2324 if ( !sMark.isEmpty() && !bBookmarkOnly )
2326 m_rExport.OutputField( nullptr, ww::eHYPERLINK, sUrl );
2328 else
2330 // Output a hyperlink XML element
2331 m_pHyperlinkAttrList = FastSerializerHelper::createAttrList();
2333 if ( !bBookmarkOnly )
2335 OString sId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
2336 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
2337 sUrl, true ), RTL_TEXTENCODING_UTF8 );
2339 m_pHyperlinkAttrList->add( FSNS( XML_r, XML_id), sId.getStr());
2341 else
2343 // Is this a link to a sequence? Then try to replace that with a
2344 // normal bookmark, as Word won't understand our special
2345 // <seqname>!<index>|sequence syntax.
2346 if (sMark.endsWith("|sequence"))
2348 sal_Int32 nPos = sMark.indexOf('!');
2349 if (nPos != -1)
2351 // Extract <seqname>, the field instruction text has the name quoted.
2352 OUString aSequenceName = sMark.copy(0, nPos);
2353 // Extract <index>.
2354 sal_uInt32 nIndex = sMark.copy(nPos + 1, sMark.getLength() - nPos - sizeof("|sequence")).toInt32();
2355 std::map<OUString, std::vector<OString> >::iterator it = m_aSeqBookmarksNames.find(aSequenceName);
2356 if (it != m_aSeqBookmarksNames.end())
2358 std::vector<OString>& rNames = it->second;
2359 if (rNames.size() > nIndex)
2360 // We know the bookmark name for this sequence and this index, do the replacement.
2361 sMark = OStringToOUString(rNames[nIndex], RTL_TEXTENCODING_UTF8);
2365 m_pHyperlinkAttrList->add( FSNS( XML_w, XML_anchor ),
2366 OUStringToOString( sMark, RTL_TEXTENCODING_UTF8 ).getStr( ) );
2369 if ( !rTarget.isEmpty() )
2371 OString soTarget = OUStringToOString( rTarget, RTL_TEXTENCODING_UTF8 );
2372 m_pHyperlinkAttrList->add(FSNS( XML_w, XML_tgtFrame ), soTarget.getStr());
2376 return true;
2379 bool DocxAttributeOutput::EndURL(bool const)
2381 m_closeHyperlinkInThisRun = true;
2382 if(m_startedHyperlink && !m_hyperLinkAnchor.isEmpty() && m_hyperLinkAnchor.startsWith("_Toc"))
2384 m_endPageRef = true;
2386 return true;
2389 void DocxAttributeOutput::FieldVanish( const OUString& rText, ww::eField eType )
2391 WriteField_Impl( nullptr, eType, rText, WRITEFIELD_ALL );
2394 // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
2395 // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
2396 // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
2397 void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData)
2399 if ( !pRedlineData )
2400 return;
2402 OString aId( OString::number( pRedlineData->GetSeqNo() ) );
2403 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
2404 OString aAuthor( rAuthor.toUtf8() );
2405 OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) );
2407 switch( pRedlineData->GetType() )
2409 case nsRedlineType_t::REDLINE_INSERT:
2410 break;
2412 case nsRedlineType_t::REDLINE_DELETE:
2413 break;
2415 case nsRedlineType_t::REDLINE_FORMAT:
2416 m_pSerializer->startElementNS( XML_w, XML_rPrChange,
2417 FSNS( XML_w, XML_id ), aId.getStr(),
2418 FSNS( XML_w, XML_author ), aAuthor.getStr(),
2419 FSNS( XML_w, XML_date ), aDate.getStr(),
2420 FSEND );
2422 // Check if there is any extra data stored in the redline object
2423 if (pRedlineData->GetExtraData())
2425 const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData();
2426 const SwRedlineExtraData_FormattingChanges* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormattingChanges*>(pExtraData);
2428 // Check if the extra data is of type 'formatting changes'
2429 if (pFormattingChanges)
2431 // Get the item set that holds all the changes properties
2432 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
2433 if (pChangesSet)
2435 m_pSerializer->mark(Tag_Redline_1);
2437 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
2439 // The 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList' are used to hold information
2440 // that should be collected by different properties in the core, and are all flushed together
2441 // to the DOCX when the function 'WriteCollectedRunProperties' gets called.
2442 // So we need to store the current status of these lists, so that we can revert back to them when
2443 // we are done exporting the redline attributes.
2444 rtl::Reference<sax_fastparser::FastAttributeList> pFontsAttrList_Original(m_pFontsAttrList);
2445 m_pFontsAttrList.clear();
2446 rtl::Reference<sax_fastparser::FastAttributeList> pEastAsianLayoutAttrList_Original(m_pEastAsianLayoutAttrList);
2447 m_pEastAsianLayoutAttrList.clear();
2448 rtl::Reference<sax_fastparser::FastAttributeList> pCharLangAttrList_Original(m_pCharLangAttrList);
2449 m_pCharLangAttrList.clear();
2451 // Output the redline item set
2452 m_rExport.OutputItemSet( *pChangesSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
2454 // Write the collected run properties that are stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
2455 WriteCollectedRunProperties();
2457 // Revert back the original values that were stored in 'm_pFontsAttrList', 'm_pEastAsianLayoutAttrList', 'm_pCharLangAttrList'
2458 m_pFontsAttrList = pFontsAttrList_Original;
2459 m_pEastAsianLayoutAttrList = pEastAsianLayoutAttrList_Original;
2460 m_pCharLangAttrList = pCharLangAttrList_Original;
2462 m_pSerializer->endElementNS( XML_w, XML_rPr );
2464 m_pSerializer->mergeTopMarks(Tag_Redline_1, sax_fastparser::MergeMarks::PREPEND);
2468 m_pSerializer->endElementNS( XML_w, XML_rPrChange );
2469 break;
2471 case nsRedlineType_t::REDLINE_PARAGRAPH_FORMAT:
2472 m_pSerializer->startElementNS( XML_w, XML_pPrChange,
2473 FSNS( XML_w, XML_id ), aId.getStr(),
2474 FSNS( XML_w, XML_author ), aAuthor.getStr(),
2475 FSNS( XML_w, XML_date ), aDate.getStr(),
2476 FSEND );
2478 // Check if there is any extra data stored in the redline object
2479 if (pRedlineData->GetExtraData())
2481 const SwRedlineExtraData* pExtraData = pRedlineData->GetExtraData();
2482 const SwRedlineExtraData_FormattingChanges* pFormattingChanges = dynamic_cast<const SwRedlineExtraData_FormattingChanges*>(pExtraData);
2484 // Check if the extra data is of type 'formatting changes'
2485 if (pFormattingChanges)
2487 // Get the item set that holds all the changes properties
2488 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
2489 if (pChangesSet)
2491 m_pSerializer->mark(Tag_Redline_2);
2493 m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );
2495 // The 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList' are used to hold information
2496 // that should be collected by different properties in the core, and are all flushed together
2497 // to the DOCX when the function 'WriteCollectedParagraphProperties' gets called.
2498 // So we need to store the current status of these lists, so that we can revert back to them when
2499 // we are done exporting the redline attributes.
2500 rtl::Reference<sax_fastparser::FastAttributeList> pFlyAttrList_Original(m_rExport.SdrExporter().getFlyAttrList());
2501 m_rExport.SdrExporter().getFlyAttrList().clear();
2502 rtl::Reference<sax_fastparser::FastAttributeList> pParagraphSpacingAttrList_Original(m_pParagraphSpacingAttrList);
2503 m_pParagraphSpacingAttrList.clear();
2505 // Output the redline item set
2506 m_rExport.OutputItemSet( *pChangesSet, true, false, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
2508 // Write the collected paragraph properties that are stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
2509 WriteCollectedParagraphProperties();
2511 // Revert back the original values that were stored in 'm_rExport.SdrExporter().getFlyAttrList()', 'm_pParagraphSpacingAttrList'
2512 m_rExport.SdrExporter().getFlyAttrList() = pFlyAttrList_Original;
2513 m_pParagraphSpacingAttrList = pParagraphSpacingAttrList_Original;
2515 m_pSerializer->endElementNS( XML_w, XML_pPr );
2517 m_pSerializer->mergeTopMarks(Tag_Redline_2, sax_fastparser::MergeMarks::PREPEND);
2521 m_pSerializer->endElementNS( XML_w, XML_pPrChange );
2522 break;
2524 default:
2525 SAL_WARN("sw.ww8", "Unhandled redline type for export " << pRedlineData->GetType());
2526 break;
2530 // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that:
2531 // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node)
2532 // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node)
2533 void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData )
2535 if ( !pRedlineData )
2536 return;
2538 // FIXME check if it's necessary to travel over the Next()'s in pRedlineData
2540 OString aId( OString::number( m_nRedlineId++ ) );
2542 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) );
2543 OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
2545 OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) );
2547 switch ( pRedlineData->GetType() )
2549 case nsRedlineType_t::REDLINE_INSERT:
2550 m_pSerializer->startElementNS( XML_w, XML_ins,
2551 FSNS( XML_w, XML_id ), aId.getStr(),
2552 FSNS( XML_w, XML_author ), aAuthor.getStr(),
2553 FSNS( XML_w, XML_date ), aDate.getStr(),
2554 FSEND );
2555 break;
2557 case nsRedlineType_t::REDLINE_DELETE:
2558 m_pSerializer->startElementNS( XML_w, XML_del,
2559 FSNS( XML_w, XML_id ), aId.getStr(),
2560 FSNS( XML_w, XML_author ), aAuthor.getStr(),
2561 FSNS( XML_w, XML_date ), aDate.getStr(),
2562 FSEND );
2563 break;
2565 case nsRedlineType_t::REDLINE_FORMAT:
2566 OSL_TRACE( "TODO DocxAttributeOutput::StartRedline()" );
2567 break;
2568 default:
2569 break;
2573 void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData )
2575 if ( !pRedlineData )
2576 return;
2578 switch ( pRedlineData->GetType() )
2580 case nsRedlineType_t::REDLINE_INSERT:
2581 m_pSerializer->endElementNS( XML_w, XML_ins );
2582 break;
2584 case nsRedlineType_t::REDLINE_DELETE:
2585 m_pSerializer->endElementNS( XML_w, XML_del );
2586 break;
2588 case nsRedlineType_t::REDLINE_FORMAT:
2589 OSL_TRACE( "TODO DocxAttributeOutput::EndRedline()" );
2590 break;
2591 default:
2592 break;
2596 void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t )
2598 OSL_TRACE( "TODO DocxAttributeOutput::FormatDrop( const SwTextNode& rNode, const SwFormatDrop& rSwFormatDrop, sal_uInt16 nStyle )" );
2601 void DocxAttributeOutput::ParagraphStyle( sal_uInt16 nStyle )
2603 OString aStyleId(m_rExport.m_pStyles->GetStyleId(nStyle));
2605 m_pSerializer->singleElementNS( XML_w, XML_pStyle, FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
2608 static void impl_borderLine( FSHelperPtr const & pSerializer, sal_Int32 elementToken, const SvxBorderLine* pBorderLine, sal_uInt16 nDist,
2609 bool bWriteShadow, const table::BorderLine2* rStyleProps = nullptr )
2611 // Compute val attribute value
2612 // Can be one of:
2613 // single, double,
2614 // basicWideOutline, basicWideInline
2615 // OOXml also supports those types of borders, but we'll try to play with the first ones.
2616 // thickThinMediumGap, thickThinLargeGap, thickThinSmallGap
2617 // thinThickLargeGap, thinThickMediumGap, thinThickSmallGap
2618 const char* pVal = "nil";
2619 if ( pBorderLine && !pBorderLine->isEmpty( ) )
2621 switch (pBorderLine->GetBorderLineStyle())
2623 case table::BorderLineStyle::SOLID:
2624 pVal = "single";
2625 break;
2626 case table::BorderLineStyle::DOTTED:
2627 pVal = "dotted";
2628 break;
2629 case table::BorderLineStyle::DASHED:
2630 pVal = "dashed";
2631 break;
2632 case table::BorderLineStyle::DOUBLE:
2633 pVal = "double";
2634 break;
2635 case table::BorderLineStyle::THINTHICK_SMALLGAP:
2636 pVal = "thinThickSmallGap";
2637 break;
2638 case table::BorderLineStyle::THINTHICK_MEDIUMGAP:
2639 pVal = "thinThickMediumGap";
2640 break;
2641 case table::BorderLineStyle::THINTHICK_LARGEGAP:
2642 pVal = "thinThickLargeGap";
2643 break;
2644 case table::BorderLineStyle::THICKTHIN_SMALLGAP:
2645 pVal = "thickThinSmallGap";
2646 break;
2647 case table::BorderLineStyle::THICKTHIN_MEDIUMGAP:
2648 pVal = "thickThinMediumGap";
2649 break;
2650 case table::BorderLineStyle::THICKTHIN_LARGEGAP:
2651 pVal = "thickThinLargeGap";
2652 break;
2653 case table::BorderLineStyle::EMBOSSED:
2654 pVal = "threeDEmboss";
2655 break;
2656 case table::BorderLineStyle::ENGRAVED:
2657 pVal = "threeDEngrave";
2658 break;
2659 case table::BorderLineStyle::OUTSET:
2660 pVal = "outset";
2661 break;
2662 case table::BorderLineStyle::INSET:
2663 pVal = "inset";
2664 break;
2665 case table::BorderLineStyle::FINE_DASHED:
2666 pVal = "dashSmallGap";
2667 break;
2668 case table::BorderLineStyle::NONE:
2669 default:
2670 break;
2673 else if( rStyleProps == nullptr )
2674 // no line, and no line set by the style either:
2675 // there is no need to write the property
2676 return;
2678 // compare the properties with the theme properties before writing them:
2679 // if they are equal, it means that they were style-defined and there is
2680 // no need to write them.
2681 if( rStyleProps != nullptr && pBorderLine && !pBorderLine->isEmpty() &&
2682 pBorderLine->GetBorderLineStyle() == rStyleProps->LineStyle &&
2683 pBorderLine->GetColor() == rStyleProps->Color &&
2684 pBorderLine->GetWidth() == convertMm100ToTwip( rStyleProps->LineWidth ) )
2685 return;
2687 FastAttributeList* pAttr = FastSerializerHelper::createAttrList();
2688 pAttr->add( FSNS( XML_w, XML_val ), OString( pVal ) );
2690 if ( pBorderLine && !pBorderLine->isEmpty() )
2692 // Compute the sz attribute
2694 double const fConverted( ::editeng::ConvertBorderWidthToWord(
2695 pBorderLine->GetBorderLineStyle(), pBorderLine->GetWidth()));
2696 // The unit is the 8th of point
2697 sal_Int32 nWidth = sal_Int32( fConverted / 2.5 );
2698 const sal_Int32 nMinWidth = 2;
2699 const sal_Int32 nMaxWidth = 96;
2701 if ( nWidth > nMaxWidth )
2702 nWidth = nMaxWidth;
2703 else if ( nWidth < nMinWidth )
2704 nWidth = nMinWidth;
2706 pAttr->add( FSNS( XML_w, XML_sz ), OString::number( nWidth ) );
2708 // Get the distance (in pt)
2709 pAttr->add( FSNS( XML_w, XML_space ), OString::number( nDist / 20 ) );
2711 // Get the color code as an RRGGBB hex value
2712 OString sColor( msfilter::util::ConvertColor( pBorderLine->GetColor( ) ) );
2713 pAttr->add( FSNS( XML_w, XML_color ), sColor );
2716 if (bWriteShadow)
2718 // Set the shadow value
2719 pAttr->add( FSNS( XML_w, XML_shadow ), "1" );
2722 XFastAttributeListRef xAttrs( pAttr );
2723 pSerializer->singleElementNS( XML_w, elementToken, xAttrs );
2726 static OutputBorderOptions lcl_getTableDefaultBorderOptions(bool bEcma)
2728 OutputBorderOptions rOptions;
2730 rOptions.tag = XML_tblBorders;
2731 rOptions.bUseStartEnd = !bEcma;
2732 rOptions.bWriteTag = true;
2733 rOptions.bWriteInsideHV = true;
2734 rOptions.bWriteDistance = false;
2736 return rOptions;
2739 static OutputBorderOptions lcl_getTableCellBorderOptions(bool bEcma)
2741 OutputBorderOptions rOptions;
2743 rOptions.tag = XML_tcBorders;
2744 rOptions.bUseStartEnd = !bEcma;
2745 rOptions.bWriteTag = true;
2746 rOptions.bWriteInsideHV = true;
2747 rOptions.bWriteDistance = false;
2749 return rOptions;
2752 static OutputBorderOptions lcl_getBoxBorderOptions()
2754 OutputBorderOptions rOptions;
2756 rOptions.tag = XML_pBdr;
2757 rOptions.bUseStartEnd = false;
2758 rOptions.bWriteTag = false;
2759 rOptions.bWriteInsideHV = false;
2760 rOptions.bWriteDistance = true;
2762 return rOptions;
2765 static void impl_borders( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, const OutputBorderOptions& rOptions,
2766 std::map<SvxBoxItemLine, css::table::BorderLine2> &rTableStyleConf )
2768 static const SvxBoxItemLine aBorders[] =
2770 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
2773 const sal_Int32 aXmlElements[] =
2775 XML_top,
2776 rOptions.bUseStartEnd ? XML_start : XML_left,
2777 XML_bottom,
2778 rOptions.bUseStartEnd ? XML_end : XML_right
2780 bool tagWritten = false;
2781 const SvxBoxItemLine* pBrd = aBorders;
2783 bool bWriteInsideH = false;
2784 bool bWriteInsideV = false;
2785 for( int i = 0; i < 4; ++i, ++pBrd )
2787 const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
2788 const table::BorderLine2 *aStyleProps = nullptr;
2789 if( rTableStyleConf.find( *pBrd ) != rTableStyleConf.end() )
2790 aStyleProps = &rTableStyleConf[ *pBrd ];
2792 if (!tagWritten && rOptions.bWriteTag)
2794 pSerializer->startElementNS( XML_w, rOptions.tag, FSEND );
2795 tagWritten = true;
2798 bool bWriteShadow = false;
2799 if (rOptions.aShadowLocation == SVX_SHADOW_NONE)
2801 // The border has no shadow
2803 else if (rOptions.aShadowLocation == SVX_SHADOW_BOTTOMRIGHT)
2805 // Special case of 'Bottom-Right' shadow:
2806 // If the shadow location is 'Bottom-Right' - then turn on the shadow
2807 // for ALL the sides. This is because in Word - if you select a shadow
2808 // for a border - it turn on the shadow for ALL the sides (but shows only
2809 // the bottom-right one).
2810 // This is so that no information will be lost if passed through LibreOffice
2811 bWriteShadow = true;
2813 else
2815 // If there is a shadow, and it's not the regular 'Bottom-Right',
2816 // then write only the 'shadowed' sides of the border
2817 if (
2818 ( ( rOptions.aShadowLocation == SVX_SHADOW_TOPLEFT || rOptions.aShadowLocation == SVX_SHADOW_TOPRIGHT ) && *pBrd == SvxBoxItemLine::TOP ) ||
2819 ( ( rOptions.aShadowLocation == SVX_SHADOW_TOPLEFT || rOptions.aShadowLocation == SVX_SHADOW_BOTTOMLEFT ) && *pBrd == SvxBoxItemLine::LEFT ) ||
2820 ( ( rOptions.aShadowLocation == SVX_SHADOW_BOTTOMLEFT || rOptions.aShadowLocation == SVX_SHADOW_BOTTOMRIGHT ) && *pBrd == SvxBoxItemLine::BOTTOM) ||
2821 ( ( rOptions.aShadowLocation == SVX_SHADOW_TOPRIGHT || rOptions.aShadowLocation == SVX_SHADOW_BOTTOMRIGHT ) && *pBrd == SvxBoxItemLine::RIGHT )
2824 bWriteShadow = true;
2828 sal_uInt16 nDist = 0;
2829 if (rOptions.bWriteDistance)
2831 if (rOptions.pDistances)
2833 if ( *pBrd == SvxBoxItemLine::TOP)
2834 nDist = rOptions.pDistances->nTop;
2835 else if ( *pBrd == SvxBoxItemLine::LEFT)
2836 nDist = rOptions.pDistances->nLeft;
2837 else if ( *pBrd == SvxBoxItemLine::BOTTOM)
2838 nDist = rOptions.pDistances->nBottom;
2839 else if ( *pBrd == SvxBoxItemLine::RIGHT)
2840 nDist = rOptions.pDistances->nRight;
2842 else
2844 nDist = rBox.GetDistance(*pBrd);
2848 impl_borderLine( pSerializer, aXmlElements[i], pLn, nDist, bWriteShadow, aStyleProps );
2850 // When exporting default borders, we need to export these 2 attr
2851 if ( rOptions.bWriteInsideHV) {
2852 if ( i == 2 )
2853 bWriteInsideH = true;
2854 else if ( i == 3 )
2855 bWriteInsideV = true;
2858 if (bWriteInsideH)
2860 const table::BorderLine2 *aStyleProps = nullptr;
2861 if( rTableStyleConf.find( SvxBoxItemLine::BOTTOM ) != rTableStyleConf.end() )
2862 aStyleProps = &rTableStyleConf[ SvxBoxItemLine::BOTTOM ];
2863 impl_borderLine( pSerializer, XML_insideH, rBox.GetLine(SvxBoxItemLine::BOTTOM), 0, false, aStyleProps );
2865 if (bWriteInsideV)
2867 const table::BorderLine2 *aStyleProps = nullptr;
2868 if( rTableStyleConf.find( SvxBoxItemLine::RIGHT ) != rTableStyleConf.end() )
2869 aStyleProps = &rTableStyleConf[ SvxBoxItemLine::RIGHT ];
2870 impl_borderLine( pSerializer, XML_insideV, rBox.GetLine(SvxBoxItemLine::RIGHT), 0, false, aStyleProps );
2872 if (tagWritten && rOptions.bWriteTag) {
2873 pSerializer->endElementNS( XML_w, rOptions.tag );
2877 static void impl_cellMargins( FSHelperPtr const & pSerializer, const SvxBoxItem& rBox, sal_Int32 tag, bool bUseStartEnd, const SvxBoxItem* pDefaultMargins = nullptr)
2879 static const SvxBoxItemLine aBorders[] =
2881 SvxBoxItemLine::TOP, SvxBoxItemLine::LEFT, SvxBoxItemLine::BOTTOM, SvxBoxItemLine::RIGHT
2884 const sal_Int32 aXmlElements[] =
2886 XML_top,
2887 bUseStartEnd ? XML_start : XML_left,
2888 XML_bottom,
2889 bUseStartEnd ? XML_end : XML_right
2891 bool tagWritten = false;
2892 const SvxBoxItemLine* pBrd = aBorders;
2893 for( int i = 0; i < 4; ++i, ++pBrd )
2895 sal_Int32 nDist = sal_Int32( rBox.GetDistance( *pBrd ) );
2897 if ( aBorders[i] == SvxBoxItemLine::LEFT ) {
2898 // Office's cell margin is measured from the right of the border.
2899 // While LO's cell spacing is measured from the center of the border.
2900 // So we add half left-border width to tblIndent value
2901 const SvxBorderLine* pLn = rBox.GetLine( *pBrd );
2902 if (pLn)
2903 nDist -= pLn->GetWidth() * 0.5;
2906 if (pDefaultMargins)
2908 // Skip output if cell margin == table default margin
2909 if (sal_Int32( pDefaultMargins->GetDistance( *pBrd ) ) == nDist)
2910 continue;
2913 if (!tagWritten) {
2914 pSerializer->startElementNS( XML_w, tag, FSEND );
2915 tagWritten = true;
2917 pSerializer->singleElementNS( XML_w, aXmlElements[i],
2918 FSNS( XML_w, XML_w ), OString::number( nDist ).getStr( ),
2919 FSNS( XML_w, XML_type ), "dxa",
2920 FSEND );
2922 if (tagWritten) {
2923 pSerializer->endElementNS( XML_w, tag );
2927 void DocxAttributeOutput::TableCellProperties( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
2929 m_pSerializer->startElementNS( XML_w, XML_tcPr, FSEND );
2931 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
2933 bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
2935 // Output any table cell redlines if there are any attached to this specific cell
2936 TableCellRedline( pTableTextNodeInfoInner );
2938 // Cell preferred width
2939 SwTwips nWidth = GetGridCols( pTableTextNodeInfoInner )->at( nCell );
2940 if ( nCell )
2941 nWidth = nWidth - GetGridCols( pTableTextNodeInfoInner )->at( nCell - 1 );
2942 m_pSerializer->singleElementNS( XML_w, XML_tcW,
2943 FSNS( XML_w, XML_w ), OString::number( nWidth ).getStr( ),
2944 FSNS( XML_w, XML_type ), "dxa",
2945 FSEND );
2947 // Horizontal spans
2948 const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
2949 SwWriteTableRow *pRow = rRows[ nRow ];
2950 const SwWriteTableCells& rTableCells = pRow->GetCells();
2951 if (nCell < rTableCells.size() )
2953 const SwWriteTableCell& rCell = *rTableCells[nCell];
2954 const sal_uInt16 nColSpan = rCell.GetColSpan();
2955 if ( nColSpan > 1 )
2956 m_pSerializer->singleElementNS( XML_w, XML_gridSpan,
2957 FSNS( XML_w, XML_val ), OString::number( nColSpan ).getStr(),
2958 FSEND );
2961 // Vertical merges
2962 ww8::RowSpansPtr xRowSpans = pTableTextNodeInfoInner->getRowSpansOfRow();
2963 sal_Int32 vSpan = (*xRowSpans)[nCell];
2964 if ( vSpan > 1 )
2966 m_pSerializer->singleElementNS( XML_w, XML_vMerge,
2967 FSNS( XML_w, XML_val ), "restart",
2968 FSEND );
2970 else if ( vSpan < 0 )
2972 m_pSerializer->singleElementNS( XML_w, XML_vMerge,
2973 FSNS( XML_w, XML_val ), "continue",
2974 FSEND );
2977 if (const SfxGrabBagItem* pItem = pTableBox->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG))
2979 const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
2980 std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("CellCnfStyle");
2981 if (it != rGrabBag.end())
2983 uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
2984 m_pTableStyleExport->CnfStyle(aAttributes);
2989 const SvxBoxItem& rBox = pTableBox->GetFrameFormat( )->GetBox( );
2990 const SvxBoxItem& rDefaultBox = (*tableFirstCells.rbegin())->getTableBox( )->GetFrameFormat( )->GetBox( );
2992 // The cell borders
2993 impl_borders( m_pSerializer, rBox, lcl_getTableCellBorderOptions(bEcma), m_aTableStyleConf );
2996 TableBackgrounds( pTableTextNodeInfoInner );
2999 // Cell margins
3000 impl_cellMargins( m_pSerializer, rBox, XML_tcMar, !bEcma, &rDefaultBox );
3003 TableVerticalCell( pTableTextNodeInfoInner );
3005 m_pSerializer->endElementNS( XML_w, XML_tcPr );
3008 void DocxAttributeOutput::InitTableHelper( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
3010 const SwTable* pTable = pTableTextNodeInfoInner->getTable();
3011 if (m_xTableWrt && pTable == m_xTableWrt->GetTable())
3012 return;
3014 long nPageSize = 0;
3015 bool bRelBoxSize = false;
3017 // Create the SwWriteTable instance to use col spans (and maybe other infos)
3018 GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
3020 const SwFrameFormat *pFormat = pTable->GetFrameFormat( );
3021 const sal_uInt32 nTableSz = static_cast<sal_uInt32>(pFormat->GetFrameSize( ).GetWidth( ));
3023 const SwHTMLTableLayout *pLayout = pTable->GetHTMLTableLayout();
3024 if( pLayout && pLayout->IsExportable() )
3025 m_xTableWrt.reset(new SwWriteTable(pTable, pLayout));
3026 else
3027 m_xTableWrt.reset(new SwWriteTable(pTable, pTable->GetTabLines(), nPageSize, nTableSz, false));
3030 void DocxAttributeOutput::StartTable( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
3032 // In case any paragraph SDT's are open, close them here.
3033 EndParaSdtBlock();
3035 m_pSerializer->startElementNS( XML_w, XML_tbl, FSEND );
3037 tableFirstCells.push_back(pTableTextNodeInfoInner);
3038 lastOpenCell.push_back(-1);
3039 lastClosedCell.push_back(-1);
3041 InitTableHelper( pTableTextNodeInfoInner );
3042 TableDefinition( pTableTextNodeInfoInner );
3045 void DocxAttributeOutput::EndTable()
3047 m_pSerializer->endElementNS( XML_w, XML_tbl );
3049 if ( m_tableReference->m_nTableDepth > 0 )
3050 --m_tableReference->m_nTableDepth;
3052 lastClosedCell.pop_back();
3053 lastOpenCell.pop_back();
3054 tableFirstCells.pop_back();
3056 // We closed the table; if it is a nested table, the cell that contains it
3057 // still continues
3058 // set to true only if we were in a nested table, not otherwise.
3059 if( 0 != tableFirstCells.size() )
3060 m_tableReference->m_bTableCellOpen = true;
3062 // Cleans the table helper
3063 m_xTableWrt.reset(nullptr);
3065 m_aTableStyleConf.clear();
3068 void DocxAttributeOutput::StartTableRow( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
3070 m_pSerializer->startElementNS( XML_w, XML_tr, FSEND );
3072 // Output the row properties
3073 m_pSerializer->startElementNS( XML_w, XML_trPr, FSEND );
3075 // Header row: tblHeader
3076 const SwTable *pTable = pTableTextNodeInfoInner->getTable( );
3077 if ( pTable->GetRowsToRepeat( ) > pTableTextNodeInfoInner->getRow( ) )
3078 m_pSerializer->singleElementNS( XML_w, XML_tblHeader,
3079 FSNS( XML_w, XML_val ), "true",
3080 FSEND );
3082 TableRowRedline( pTableTextNodeInfoInner );
3083 TableHeight( pTableTextNodeInfoInner );
3084 TableCanSplit( pTableTextNodeInfoInner );
3086 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox();
3087 const SwTableLine* pTableLine = pTableBox->GetUpper();
3088 if (const SfxGrabBagItem* pItem = pTableLine->GetFrameFormat()->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG))
3090 const std::map<OUString, uno::Any>& rGrabBag = pItem->GetGrabBag();
3091 std::map<OUString, uno::Any>::const_iterator it = rGrabBag.find("RowCnfStyle");
3092 if (it != rGrabBag.end())
3094 uno::Sequence<beans::PropertyValue> aAttributes = it->second.get< uno::Sequence<beans::PropertyValue> >();
3095 m_pTableStyleExport->CnfStyle(aAttributes);
3100 m_pSerializer->endElementNS( XML_w, XML_trPr );
3103 void DocxAttributeOutput::EndTableRow( )
3105 m_pSerializer->endElementNS( XML_w, XML_tr );
3106 lastOpenCell.back() = -1;
3107 lastClosedCell.back() = -1;
3110 void DocxAttributeOutput::StartTableCell( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner, sal_uInt32 nCell, sal_uInt32 nRow )
3112 lastOpenCell.back() = nCell;
3114 InitTableHelper( pTableTextNodeInfoInner );
3116 m_pSerializer->startElementNS( XML_w, XML_tc, FSEND );
3118 // Write the cell properties here
3119 TableCellProperties( pTableTextNodeInfoInner, nCell, nRow );
3121 m_tableReference->m_bTableCellOpen = true;
3124 void DocxAttributeOutput::EndTableCell(ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ const &, sal_uInt32 nCell, sal_uInt32 /*nRow*/)
3126 lastClosedCell.back() = nCell;
3127 lastOpenCell.back() = -1;
3129 if (m_tableReference->m_bTableCellParaSdtOpen)
3130 EndParaSdtBlock();
3132 m_pSerializer->endElementNS( XML_w, XML_tc );
3134 m_bBtLr = false;
3135 m_tableReference->m_bTableCellOpen = false;
3136 m_tableReference->m_bTableCellParaSdtOpen = false;
3139 void DocxAttributeOutput::TableInfoCell( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
3143 void DocxAttributeOutput::TableInfoRow( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfo*/ )
3147 namespace
3150 /// Does the same as comphelper::string::padToLength(), but extends the start, not the end.
3151 OString lcl_padStartToLength(OString const & aString, sal_Int32 nLen, sal_Char cFill)
3153 if (nLen > aString.getLength())
3155 sal_Int32 nDiff = nLen - aString.getLength();
3156 OStringBuffer aBuffer;
3157 comphelper::string::padToLength(aBuffer, nDiff, cFill);
3158 aBuffer.append(aString);
3159 return aBuffer.makeStringAndClear();
3161 else
3162 return aString;
3167 void DocxAttributeOutput::TableDefinition( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3169 bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
3171 // Write the table properties
3172 m_pSerializer->startElementNS( XML_w, XML_tblPr, FSEND );
3174 static const sal_Int32 aOrder[] =
3176 FSNS( XML_w, XML_tblStyle ),
3177 FSNS( XML_w, XML_tblpPr ),
3178 FSNS( XML_w, XML_tblOverlap ),
3179 FSNS( XML_w, XML_bidiVisual ),
3180 FSNS( XML_w, XML_tblStyleRowBandSize ),
3181 FSNS( XML_w, XML_tblStyleColBandSize ),
3182 FSNS( XML_w, XML_tblW ),
3183 FSNS( XML_w, XML_jc ),
3184 FSNS( XML_w, XML_tblCellSpacing ),
3185 FSNS( XML_w, XML_tblInd ),
3186 FSNS( XML_w, XML_tblBorders ),
3187 FSNS( XML_w, XML_shd ),
3188 FSNS( XML_w, XML_tblLayout ),
3189 FSNS( XML_w, XML_tblCellMar ),
3190 FSNS( XML_w, XML_tblLook ),
3191 FSNS( XML_w, XML_tblPrChange )
3194 // postpone the output so that we can later []
3195 // prepend the properties before the run
3196 sal_Int32 len = sizeof ( aOrder ) / sizeof( sal_Int32 );
3197 uno::Sequence< sal_Int32 > aSeqOrder( len );
3198 for ( sal_Int32 i = 0; i < len; i++ )
3199 aSeqOrder[i] = aOrder[i];
3201 m_pSerializer->mark(Tag_TableDefinition, aSeqOrder);
3203 long nPageSize = 0;
3204 const char* widthType = "dxa";
3206 // If actual width of table is relative it should export is as "pct".`
3207 const SwTable *pTable = pTableTextNodeInfoInner->getTable();
3208 SwFrameFormat *pTableFormat = pTable->GetFrameFormat( );
3209 const SwFormatFrameSize &rSize = pTableFormat->GetFrameSize();
3210 int nWidthPercent = rSize.GetWidthPercent();
3211 uno::Reference<beans::XPropertySet> xPropertySet(SwXTextTables::GetObject(const_cast<SwTableFormat&>(*pTable->GetFrameFormat( ))),uno::UNO_QUERY);
3212 bool isWidthRelative = false;
3213 xPropertySet->getPropertyValue("IsWidthRelative") >>= isWidthRelative;
3215 if(isWidthRelative)
3218 * As per ECMA Specification : ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference [ 17.18.90 ST_TableWidth (Table Width Units)]
3219 * http://www.schemacentral.com/sc/ooxml/a-w_type-7.html
3221 * Fiftieths of a Percent :
3222 * http://startbigthinksmall.wordpress.com/2010/01/04/points-inches-and-emus-measuring-units-in-office-open-xml/
3223 * pct Width is in Fiftieths of a Percent
3225 * ex. If the Table width is 50% then
3226 * Width in Fiftieths of a percent is (50 * 50) % or 0.5 * 5000 = 2500pct
3228 nPageSize = nWidthPercent * 50 ;
3229 widthType = "pct" ;
3231 else
3233 bool bRelBoxSize = false;
3234 // Create the SwWriteTable instance to use col spans (and maybe other infos)
3235 GetTablePageSize( pTableTextNodeInfoInner.get(), nPageSize, bRelBoxSize );
3236 if(nPageSize == 0)
3237 widthType = "auto";
3240 // Output the table preferred width
3241 m_pSerializer->singleElementNS( XML_w, XML_tblW,
3242 FSNS( XML_w, XML_w ), OString::number( nPageSize ).getStr( ),
3243 FSNS( XML_w, XML_type ), widthType,
3244 FSEND );
3246 // Look for the table style property in the table grab bag
3247 std::map<OUString, css::uno::Any> aGrabBag =
3248 pTableFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
3250 // We should clear the TableStyle map. In case of Table inside multiple tables it contains the
3251 // table border style of the previous table.
3252 if (! m_aTableStyleConf.empty())
3253 m_aTableStyleConf.clear();
3255 // Extract properties from grab bag
3256 std::map<OUString, css::uno::Any>::iterator aGrabBagElement;
3257 for( aGrabBagElement = aGrabBag.begin(); aGrabBagElement != aGrabBag.end(); ++aGrabBagElement )
3259 if( aGrabBagElement->first == "TableStyleName")
3261 OString sStyleName = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
3262 m_pSerializer->singleElementNS( XML_w, XML_tblStyle,
3263 FSNS( XML_w, XML_val ), sStyleName.getStr(),
3264 FSEND );
3266 else if( aGrabBagElement->first == "TableStyleTopBorder" )
3267 m_aTableStyleConf[ SvxBoxItemLine::TOP ] = aGrabBagElement->second.get<table::BorderLine2>();
3268 else if( aGrabBagElement->first == "TableStyleBottomBorder" )
3269 m_aTableStyleConf[ SvxBoxItemLine::BOTTOM ] = aGrabBagElement->second.get<table::BorderLine2>();
3270 else if( aGrabBagElement->first == "TableStyleLeftBorder" )
3271 m_aTableStyleConf[ SvxBoxItemLine::LEFT ] = aGrabBagElement->second.get<table::BorderLine2>();
3272 else if( aGrabBagElement->first == "TableStyleRightBorder" )
3273 m_aTableStyleConf[ SvxBoxItemLine::RIGHT ] = aGrabBagElement->second.get<table::BorderLine2>();
3274 else if (aGrabBagElement->first == "TableStyleLook")
3276 FastAttributeList* pAttributeList = FastSerializerHelper::createAttrList();
3277 uno::Sequence<beans::PropertyValue> aAttributeList = aGrabBagElement->second.get< uno::Sequence<beans::PropertyValue> >();
3279 for (sal_Int32 i = 0; i < aAttributeList.getLength(); ++i)
3281 if (aAttributeList[i].Name == "val")
3282 pAttributeList->add(FSNS(XML_w, XML_val), lcl_padStartToLength(OString::number(aAttributeList[i].Value.get<sal_Int32>(), 16), 4, '0'));
3283 else
3285 static DocxStringTokenMap const aTokens[] =
3287 {"firstRow", XML_firstRow},
3288 {"lastRow", XML_lastRow},
3289 {"firstColumn", XML_firstColumn},
3290 {"lastColumn", XML_lastColumn},
3291 {"noHBand", XML_noHBand},
3292 {"noVBand", XML_noVBand},
3293 {nullptr, 0}
3296 if (sal_Int32 nToken = DocxStringGetToken(aTokens, aAttributeList[i].Name))
3297 pAttributeList->add(FSNS(XML_w, nToken), (aAttributeList[i].Value.get<sal_Int32>() ? "1" : "0"));
3301 XFastAttributeListRef xAttributeList(pAttributeList);
3302 m_pSerializer->singleElementNS(XML_w, XML_tblLook, xAttributeList);
3304 else if (aGrabBagElement->first == "TablePosition" )
3306 FastAttributeList *attrListTablePos = FastSerializerHelper::createAttrList( );
3307 uno::Sequence<beans::PropertyValue> aTablePosition = aGrabBagElement->second.get<uno::Sequence<beans::PropertyValue> >();
3308 for (sal_Int32 i = 0; i < aTablePosition.getLength(); ++i)
3310 if (aTablePosition[i].Name == "vertAnchor" && !aTablePosition[i].Value.get<OUString>().isEmpty())
3312 OString strTemp = OUStringToOString(aTablePosition[i].Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
3313 attrListTablePos->add( FSNS( XML_w, XML_vertAnchor ), strTemp.getStr() );
3315 else if (aTablePosition[i].Name == "tblpYSpec" && !aTablePosition[i].Value.get<OUString>().isEmpty())
3317 OString strTemp = OUStringToOString(aTablePosition[i].Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
3318 attrListTablePos->add( FSNS( XML_w, XML_tblpYSpec ), strTemp.getStr() );
3320 else if (aTablePosition[i].Name == "horzAnchor" && !aTablePosition[i].Value.get<OUString>().isEmpty())
3322 OString strTemp = OUStringToOString(aTablePosition[i].Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
3323 attrListTablePos->add( FSNS( XML_w, XML_horzAnchor ), strTemp.getStr() );
3325 else if (aTablePosition[i].Name == "tblpXSpec" && !aTablePosition[i].Value.get<OUString>().isEmpty())
3327 OString strTemp = OUStringToOString(aTablePosition[i].Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
3328 attrListTablePos->add( FSNS( XML_w, XML_tblpXSpec ), strTemp.getStr() );
3330 else if (aTablePosition[i].Name == "bottomFromText")
3332 attrListTablePos->add( FSNS( XML_w, XML_bottomFromText ), OString::number( aTablePosition[i].Value.get<sal_Int32>() ) );
3334 else if (aTablePosition[i].Name == "leftFromText")
3336 attrListTablePos->add( FSNS( XML_w, XML_leftFromText ), OString::number( aTablePosition[i].Value.get<sal_Int32>() ) );
3338 else if (aTablePosition[i].Name == "rightFromText")
3340 attrListTablePos->add( FSNS( XML_w, XML_rightFromText ), OString::number( aTablePosition[i].Value.get<sal_Int32>() ) );
3342 else if (aTablePosition[i].Name == "topFromText")
3344 attrListTablePos->add( FSNS( XML_w, XML_topFromText ), OString::number( aTablePosition[i].Value.get<sal_Int32>() ) );
3346 else if (aTablePosition[i].Name == "tblpX")
3348 attrListTablePos->add( FSNS( XML_w, XML_tblpX ), OString::number( aTablePosition[i].Value.get<sal_Int32>() ) );
3350 else if (aTablePosition[i].Name == "tblpY")
3352 attrListTablePos->add( FSNS( XML_w, XML_tblpY ), OString::number( aTablePosition[i].Value.get<sal_Int32>() ) );
3356 XFastAttributeListRef xAttrListTablePosRef( attrListTablePos );
3358 m_pSerializer->singleElementNS( XML_w, XML_tblpPr, xAttrListTablePosRef);
3359 attrListTablePos = nullptr;
3361 else
3362 SAL_WARN("sw.ww8", "DocxAttributeOutput::TableDefinition: unhandled property: " << aGrabBagElement->first);
3365 // Output the table alignement
3366 const char* pJcVal;
3367 sal_Int32 nIndent = 0;
3368 switch ( pTableFormat->GetHoriOrient( ).GetHoriOrient( ) )
3370 case text::HoriOrientation::CENTER:
3371 pJcVal = "center";
3372 break;
3373 case text::HoriOrientation::RIGHT:
3374 if ( bEcma )
3375 pJcVal = "right";
3376 else
3377 pJcVal = "end";
3378 break;
3379 default:
3380 case text::HoriOrientation::NONE:
3381 case text::HoriOrientation::LEFT_AND_WIDTH:
3383 if ( bEcma )
3384 pJcVal = "left";
3385 else
3386 pJcVal = "start";
3387 nIndent = sal_Int32( pTableFormat->GetLRSpace( ).GetLeft( ) );
3388 // Table indentation has different meaning in Word, depending if the table is nested or not.
3389 // If nested, tblInd is added to parent table's left spacing and defines left edge position
3390 // If not nested, text position of left-most cell must be at absolute X = tblInd
3391 // so, table_spacing + table_spacing_to_content = tblInd
3392 if (m_tableReference->m_nTableDepth == 0)
3394 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
3395 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
3396 nIndent += sal_Int32( pFrameFormat->GetBox( ).GetDistance( SvxBoxItemLine::LEFT ) );
3398 break;
3401 m_pSerializer->singleElementNS( XML_w, XML_jc,
3402 FSNS( XML_w, XML_val ), pJcVal,
3403 FSEND );
3405 // Output the table borders
3406 TableDefaultBorders( pTableTextNodeInfoInner );
3408 // Output the default cell margins
3409 TableDefaultCellMargins( pTableTextNodeInfoInner );
3411 TableBidi( pTableTextNodeInfoInner );
3413 // Table indent (need to get written even if == 0)
3414 m_pSerializer->singleElementNS( XML_w, XML_tblInd,
3415 FSNS( XML_w, XML_w ), OString::number( nIndent ).getStr( ),
3416 FSNS( XML_w, XML_type ), "dxa",
3417 FSEND );
3419 // Merge the marks for the ordered elements
3420 m_pSerializer->mergeTopMarks(Tag_TableDefinition);
3422 m_pSerializer->endElementNS( XML_w, XML_tblPr );
3424 // Write the table grid infos
3425 m_pSerializer->startElementNS( XML_w, XML_tblGrid, FSEND );
3426 sal_Int32 nPrv = 0;
3427 ww8::WidthsPtr pColumnWidths = GetColumnWidths( pTableTextNodeInfoInner );
3428 for ( ww8::Widths::const_iterator it = pColumnWidths->begin(); it != pColumnWidths->end(); ++it )
3430 sal_Int32 nWidth = sal_Int32( *it ) - nPrv;
3431 m_pSerializer->singleElementNS( XML_w, XML_gridCol,
3432 FSNS( XML_w, XML_w ), OString::number( nWidth ).getStr( ),
3433 FSEND );
3434 nPrv = sal_Int32( *it );
3437 m_pSerializer->endElementNS( XML_w, XML_tblGrid );
3440 void DocxAttributeOutput::TableDefaultBorders( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3442 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
3443 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
3445 bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
3447 // Don't write table defaults based on the top-left cell if we have a table style available.
3448 if (m_aTableStyleConf.empty())
3450 // the defaults of the table are taken from the top-left cell
3451 impl_borders(m_pSerializer, pFrameFormat->GetBox(), lcl_getTableDefaultBorderOptions(bEcma), m_aTableStyleConf);
3455 void DocxAttributeOutput::TableDefaultCellMargins( ww8::WW8TableNodeInfoInner::Pointer_t const & pTableTextNodeInfoInner )
3457 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
3458 const SwFrameFormat * pFrameFormat = pTabBox->GetFrameFormat();
3459 const SvxBoxItem& rBox = pFrameFormat->GetBox( );
3460 const bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
3462 impl_cellMargins(m_pSerializer, rBox, XML_tblCellMar, !bEcma);
3465 void DocxAttributeOutput::TableBackgrounds( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3467 const SwTableBox *pTableBox = pTableTextNodeInfoInner->getTableBox( );
3468 const SwFrameFormat *pFormat = pTableBox->GetFrameFormat( );
3470 const SvxBrushItem *pColorProp = pFormat->GetAttrSet().GetItem<SvxBrushItem>(RES_BACKGROUND);
3471 Color aColor = pColorProp ? pColorProp->GetColor() : COL_AUTO;
3472 OString sColor = msfilter::util::ConvertColor( aColor );
3474 std::map<OUString, css::uno::Any> aGrabBag =
3475 pFormat->GetAttrSet().GetItem<SfxGrabBagItem>(RES_FRMATR_GRABBAG)->GetGrabBag();
3477 OString sOriginalColor;
3478 std::map<OUString, css::uno::Any>::iterator aGrabBagElement = aGrabBag.find("originalColor");
3479 if( aGrabBagElement != aGrabBag.end() )
3480 sOriginalColor = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
3482 if ( sOriginalColor != sColor )
3484 // color changed by the user, or no grab bag: write sColor
3485 m_pSerializer->singleElementNS( XML_w, XML_shd,
3486 FSNS( XML_w, XML_fill ), sColor.getStr( ),
3487 FSNS( XML_w, XML_val ), "clear",
3488 FSEND );
3490 else
3492 rtl::Reference<sax_fastparser::FastAttributeList> pAttrList;
3494 for( aGrabBagElement = aGrabBag.begin(); aGrabBagElement != aGrabBag.end(); ++aGrabBagElement )
3496 if (!aGrabBagElement->second.has<OUString>())
3497 continue;
3499 OString sValue = OUStringToOString( aGrabBagElement->second.get<OUString>(), RTL_TEXTENCODING_UTF8 );
3500 if( aGrabBagElement->first == "themeFill")
3501 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFill ), sValue.getStr() );
3502 else if( aGrabBagElement->first == "themeFillTint")
3503 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillTint ), sValue.getStr() );
3504 else if( aGrabBagElement->first == "themeFillShade")
3505 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeFillShade ), sValue.getStr() );
3506 else if( aGrabBagElement->first == "fill" )
3507 AddToAttrList( pAttrList, FSNS( XML_w, XML_fill ), sValue.getStr() );
3508 else if( aGrabBagElement->first == "themeColor")
3509 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeColor ), sValue.getStr() );
3510 else if( aGrabBagElement->first == "themeTint")
3511 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeTint ), sValue.getStr() );
3512 else if( aGrabBagElement->first == "themeShade")
3513 AddToAttrList( pAttrList, FSNS( XML_w, XML_themeShade ), sValue.getStr() );
3514 else if( aGrabBagElement->first == "color")
3515 AddToAttrList( pAttrList, FSNS( XML_w, XML_color ), sValue.getStr() );
3516 else if( aGrabBagElement->first == "val")
3517 AddToAttrList( pAttrList, FSNS( XML_w, XML_val ), sValue.getStr() );
3519 m_pSerializer->singleElementNS( XML_w, XML_shd, pAttrList.get() );
3523 void DocxAttributeOutput::TableRowRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3525 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
3526 const SwTableLine * pTabLine = pTabBox->GetUpper();
3528 // search next Redline
3529 const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_pDoc->getIDocumentRedlineAccess().GetExtraRedlineTable();
3530 for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
3532 SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
3533 const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
3534 const SwTableLine *pRedTabLine = pTableRowRedline ? &pTableRowRedline->GetTableLine() : nullptr;
3535 if (pRedTabLine == pTabLine)
3537 // Redline for this table row
3538 const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
3539 sal_uInt16 nRedlineType = aRedlineData.GetType();
3540 switch (nRedlineType)
3542 case nsRedlineType_t::REDLINE_TABLE_ROW_INSERT:
3543 case nsRedlineType_t::REDLINE_TABLE_ROW_DELETE:
3545 OString aId( OString::number( m_nRedlineId++ ) );
3546 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
3547 OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
3549 OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
3551 if (nRedlineType == nsRedlineType_t::REDLINE_TABLE_ROW_INSERT)
3552 m_pSerializer->singleElementNS( XML_w, XML_ins,
3553 FSNS( XML_w, XML_id ), aId.getStr(),
3554 FSNS( XML_w, XML_author ), aAuthor.getStr(),
3555 FSNS( XML_w, XML_date ), aDate.getStr(),
3556 FSEND );
3557 else if (nRedlineType == nsRedlineType_t::REDLINE_TABLE_ROW_DELETE)
3558 m_pSerializer->singleElementNS( XML_w, XML_del,
3559 FSNS( XML_w, XML_id ), aId.getStr(),
3560 FSNS( XML_w, XML_author ), aAuthor.getStr(),
3561 FSNS( XML_w, XML_date ), aDate.getStr(),
3562 FSEND );
3564 break;
3570 void DocxAttributeOutput::TableCellRedline( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3572 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
3574 // search next Redline
3575 const SwExtraRedlineTable& aExtraRedlineTable = m_rExport.m_pDoc->getIDocumentRedlineAccess().GetExtraRedlineTable();
3576 for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < aExtraRedlineTable.GetSize(); ++nCurRedlinePos )
3578 SwExtraRedline* pExtraRedline = aExtraRedlineTable.GetRedline(nCurRedlinePos);
3579 const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
3580 const SwTableBox *pRedTabBox = pTableCellRedline ? &pTableCellRedline->GetTableBox() : nullptr;
3581 if (pRedTabBox == pTabBox)
3583 // Redline for this table cell
3584 const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
3585 sal_uInt16 nRedlineType = aRedlineData.GetType();
3586 switch (nRedlineType)
3588 case nsRedlineType_t::REDLINE_TABLE_CELL_INSERT:
3589 case nsRedlineType_t::REDLINE_TABLE_CELL_DELETE:
3591 OString aId( OString::number( m_nRedlineId++ ) );
3592 const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( aRedlineData.GetAuthor() ) );
3593 OString aAuthor( OUStringToOString( rAuthor, RTL_TEXTENCODING_UTF8 ) );
3595 OString aDate( DateTimeToOString( aRedlineData.GetTimeStamp() ) );
3597 if (nRedlineType == nsRedlineType_t::REDLINE_TABLE_CELL_INSERT)
3598 m_pSerializer->singleElementNS( XML_w, XML_cellIns,
3599 FSNS( XML_w, XML_id ), aId.getStr(),
3600 FSNS( XML_w, XML_author ), aAuthor.getStr(),
3601 FSNS( XML_w, XML_date ), aDate.getStr(),
3602 FSEND );
3603 else if (nRedlineType == nsRedlineType_t::REDLINE_TABLE_CELL_DELETE)
3604 m_pSerializer->singleElementNS( XML_w, XML_cellDel,
3605 FSNS( XML_w, XML_id ), aId.getStr(),
3606 FSNS( XML_w, XML_author ), aAuthor.getStr(),
3607 FSNS( XML_w, XML_date ), aDate.getStr(),
3608 FSEND );
3610 break;
3616 void DocxAttributeOutput::TableHeight( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3618 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
3619 const SwTableLine * pTabLine = pTabBox->GetUpper();
3620 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
3622 const SwFormatFrameSize& rLSz = pLineFormat->GetFrameSize();
3623 if ( ATT_VAR_SIZE != rLSz.GetHeightSizeType() && rLSz.GetHeight() )
3625 sal_Int32 nHeight = rLSz.GetHeight();
3626 const char *pRule = nullptr;
3628 switch ( rLSz.GetHeightSizeType() )
3630 case ATT_FIX_SIZE: pRule = "exact"; break;
3631 case ATT_MIN_SIZE: pRule = "atLeast"; break;
3632 default: break;
3635 if ( pRule )
3636 m_pSerializer->singleElementNS( XML_w, XML_trHeight,
3637 FSNS( XML_w, XML_val ), OString::number( nHeight ).getStr( ),
3638 FSNS( XML_w, XML_hRule ), pRule,
3639 FSEND );
3643 void DocxAttributeOutput::TableCanSplit( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3645 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
3646 const SwTableLine * pTabLine = pTabBox->GetUpper();
3647 const SwFrameFormat * pLineFormat = pTabLine->GetFrameFormat();
3649 const SwFormatRowSplit& rSplittable = pLineFormat->GetRowSplit( );
3650 // if rSplittable is true then no need to write <w:cantSplit w:val="false"/>
3651 // as default row prop is allow row to break across page.
3652 if( !rSplittable.GetValue( ) )
3653 m_pSerializer->singleElementNS( XML_w, XML_cantSplit,
3654 FSNS( XML_w, XML_val ), "true",
3655 FSEND );
3658 void DocxAttributeOutput::TableBidi( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3660 const SwTable * pTable = pTableTextNodeInfoInner->getTable();
3661 const SwFrameFormat * pFrameFormat = pTable->GetFrameFormat();
3663 if ( m_rExport.TrueFrameDirection( *pFrameFormat ) == FRMDIR_HORI_RIGHT_TOP )
3665 m_pSerializer->singleElementNS( XML_w, XML_bidiVisual,
3666 FSNS( XML_w, XML_val ), "true",
3667 FSEND );
3671 void DocxAttributeOutput::TableVerticalCell( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )
3673 const SwTableBox * pTabBox = pTableTextNodeInfoInner->getTableBox();
3674 const SwFrameFormat *pFrameFormat = pTabBox->GetFrameFormat( );
3676 if ( FRMDIR_VERT_TOP_RIGHT == m_rExport.TrueFrameDirection( *pFrameFormat ) )
3677 m_pSerializer->singleElementNS( XML_w, XML_textDirection,
3678 FSNS( XML_w, XML_val ), "tbRl",
3679 FSEND );
3680 else if ( FRMDIR_HORI_LEFT_TOP == m_rExport.TrueFrameDirection( *pFrameFormat ) )
3682 // Undo the text direction mangling done by the btLr handler in writerfilter::dmapper::DomainMapperTableManager::sprm()
3683 const SwStartNode* pSttNd = pTabBox->GetSttNd();
3684 if (pSttNd)
3686 SwPaM aPam(*pSttNd, 0);
3687 ++aPam.GetPoint()->nNode;
3688 if (aPam.GetPoint()->nNode.GetNode().IsTextNode())
3690 const SwTextNode& rTextNode = static_cast<const SwTextNode&>(aPam.GetPoint()->nNode.GetNode());
3691 if( const SwAttrSet* pAttrSet = rTextNode.GetpSwAttrSet())
3693 const SvxCharRotateItem& rCharRotate = pAttrSet->GetCharRotate();
3694 if (rCharRotate.GetValue() == 900)
3696 m_pSerializer->singleElementNS( XML_w, XML_textDirection, FSNS( XML_w, XML_val ), "btLr", FSEND );
3697 m_bBtLr = true;
3704 const SwWriteTableRows& rRows = m_xTableWrt->GetRows( );
3705 SwWriteTableRow *pRow = rRows[ pTableTextNodeInfoInner->getRow( ) ];
3706 sal_uInt32 nCell = pTableTextNodeInfoInner->getCell();
3707 const SwWriteTableCells& rTableCells = pRow->GetCells();
3708 if (nCell < rTableCells.size() )
3710 const SwWriteTableCell *const pCell = pRow->GetCells()[ nCell ].get();
3711 switch( pCell->GetVertOri())
3713 case text::VertOrientation::TOP:
3714 break;
3715 case text::VertOrientation::CENTER:
3716 m_pSerializer->singleElementNS( XML_w, XML_vAlign,
3717 FSNS( XML_w, XML_val ), "center", FSEND );
3718 break;
3719 case text::VertOrientation::BOTTOM:
3720 m_pSerializer->singleElementNS( XML_w, XML_vAlign,
3721 FSNS( XML_w, XML_val ), "bottom", FSEND );
3722 break;
3727 void DocxAttributeOutput::TableNodeInfoInner( ww8::WW8TableNodeInfoInner::Pointer_t pNodeInfoInner )
3729 // This is called when the nested table ends in a cell, and there's no
3730 // paragraph benhind that; so we must check for the ends of cell, rows,
3731 // tables
3732 // ['true' to write an empty paragraph, MS Word insists on that]
3733 FinishTableRowCell( pNodeInfoInner, true );
3736 void DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
3738 OSL_TRACE( "TODO: DocxAttributeOutput::TableOrientation( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )" );
3741 void DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t /*pTableTextNodeInfoInner*/ )
3743 OSL_TRACE( "TODO: DocxAttributeOutput::TableSpacing( ww8::WW8TableNodeInfoInner::Pointer_t pTableTextNodeInfoInner )\n" );
3746 void DocxAttributeOutput::TableRowEnd( sal_uInt32 /*nDepth*/ )
3748 OSL_TRACE( "TODO: DocxAttributeOutput::TableRowEnd( sal_uInt32 nDepth = 1 )" );
3751 void DocxAttributeOutput::StartStyles()
3753 m_pSerializer->startElementNS( XML_w, XML_styles,
3754 FSNS( XML_xmlns, XML_w ), "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
3755 FSNS( XML_xmlns, XML_w14 ), "http://schemas.microsoft.com/office/word/2010/wordml",
3756 FSNS( XML_xmlns, XML_mc ), "http://schemas.openxmlformats.org/markup-compatibility/2006",
3757 FSNS( XML_mc, XML_Ignorable ), "w14",
3758 FSEND );
3760 DocDefaults();
3761 LatentStyles();
3764 sal_Int32 DocxStringGetToken(DocxStringTokenMap const * pMap, const OUString& rName)
3766 OString sName = OUStringToOString(rName, RTL_TEXTENCODING_UTF8);
3767 while (pMap->pToken)
3769 if (sName == pMap->pToken)
3770 return pMap->nToken;
3771 ++pMap;
3773 return 0;
3776 namespace
3779 DocxStringTokenMap const aDefaultTokens[] = {
3780 {"defQFormat", XML_defQFormat},
3781 {"defUnhideWhenUsed", XML_defUnhideWhenUsed},
3782 {"defSemiHidden", XML_defSemiHidden},
3783 {"count", XML_count},
3784 {"defUIPriority", XML_defUIPriority},
3785 {"defLockedState", XML_defLockedState},
3786 {nullptr, 0}
3789 DocxStringTokenMap const aExceptionTokens[] = {
3790 {"name", XML_name},
3791 {"locked", XML_locked},
3792 {"uiPriority", XML_uiPriority},
3793 {"semiHidden", XML_semiHidden},
3794 {"unhideWhenUsed", XML_unhideWhenUsed},
3795 {"qFormat", XML_qFormat},
3796 {nullptr, 0}
3801 void DocxAttributeOutput::LatentStyles()
3803 // Do we have latent styles available?
3804 uno::Reference<beans::XPropertySet> xPropertySet(m_rExport.m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
3805 uno::Sequence<beans::PropertyValue> aInteropGrabBag;
3806 xPropertySet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
3807 uno::Sequence<beans::PropertyValue> aLatentStyles;
3808 for (sal_Int32 i = 0; i < aInteropGrabBag.getLength(); ++i)
3810 if (aInteropGrabBag[i].Name == "latentStyles")
3812 aInteropGrabBag[i].Value >>= aLatentStyles;
3813 break;
3816 if (!aLatentStyles.getLength())
3817 return;
3819 // Extract default attributes first.
3820 sax_fastparser::FastAttributeList* pAttributeList = FastSerializerHelper::createAttrList();
3821 uno::Sequence<beans::PropertyValue> aLsdExceptions;
3822 for (sal_Int32 i = 0; i < aLatentStyles.getLength(); ++i)
3824 if (sal_Int32 nToken = DocxStringGetToken(aDefaultTokens, aLatentStyles[i].Name))
3825 pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(aLatentStyles[i].Value.get<OUString>(), RTL_TEXTENCODING_UTF8));
3826 else if (aLatentStyles[i].Name == "lsdExceptions")
3827 aLatentStyles[i].Value >>= aLsdExceptions;
3830 XFastAttributeListRef xAttributeList(pAttributeList);
3831 m_pSerializer->startElementNS(XML_w, XML_latentStyles, xAttributeList);
3832 pAttributeList = nullptr;
3834 // Then handle the exceptions.
3835 for (sal_Int32 i = 0; i < aLsdExceptions.getLength(); ++i)
3837 pAttributeList = FastSerializerHelper::createAttrList();
3839 uno::Sequence<beans::PropertyValue> aAttributes;
3840 aLsdExceptions[i].Value >>= aAttributes;
3841 for (sal_Int32 j = 0; j < aAttributes.getLength(); ++j)
3842 if (sal_Int32 nToken = DocxStringGetToken(aExceptionTokens, aAttributes[j].Name))
3843 pAttributeList->add(FSNS(XML_w, nToken), OUStringToOString(aAttributes[j].Value.get<OUString>(), RTL_TEXTENCODING_UTF8));
3845 xAttributeList = pAttributeList;
3846 m_pSerializer->singleElementNS(XML_w, XML_lsdException, xAttributeList);
3847 pAttributeList = nullptr;
3850 m_pSerializer->endElementNS(XML_w, XML_latentStyles);
3853 namespace
3856 /// Should the font size we have written out as a default one?
3857 bool lcl_isDefaultFontSize(const SvxFontHeightItem& rFontHeight, SwDoc* pDoc)
3859 bool bRet = rFontHeight.GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default
3860 // Additionally, if the default para style has the same font size, then don't write it here.
3861 SwTextFormatColl* pDefaultStyle = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
3862 if (pDefaultStyle)
3864 const SfxPoolItem* pItem = nullptr;
3865 if (pDefaultStyle->GetAttrSet().HasItem(RES_CHRATR_FONTSIZE, &pItem))
3866 return static_cast<const SvxFontHeightItem*>(pItem)->GetHeight() != rFontHeight.GetHeight();
3868 return bRet;
3873 void DocxAttributeOutput::OutputDefaultItem(const SfxPoolItem& rHt)
3875 bool bMustWrite = true;
3876 switch (rHt.Which())
3878 case RES_CHRATR_CASEMAP:
3879 bMustWrite = static_cast< const SvxCaseMapItem& >(rHt).GetCaseMap() != SVX_CASEMAP_NOT_MAPPED;
3880 break;
3881 case RES_CHRATR_COLOR:
3882 bMustWrite = static_cast< const SvxColorItem& >(rHt).GetValue().GetColor() != COL_AUTO;
3883 break;
3884 case RES_CHRATR_CONTOUR:
3885 bMustWrite = static_cast< const SvxContourItem& >(rHt).GetValue();
3886 break;
3887 case RES_CHRATR_CROSSEDOUT:
3888 bMustWrite = static_cast< const SvxCrossedOutItem& >(rHt).GetStrikeout() != STRIKEOUT_NONE;
3889 break;
3890 case RES_CHRATR_ESCAPEMENT:
3891 bMustWrite = static_cast< const SvxEscapementItem& >(rHt).GetEscapement() != SvxEscapement::Off;
3892 break;
3893 case RES_CHRATR_FONT:
3894 bMustWrite = true;
3895 break;
3896 case RES_CHRATR_FONTSIZE:
3897 bMustWrite = lcl_isDefaultFontSize(static_cast< const SvxFontHeightItem& >(rHt), m_rExport.m_pDoc);
3898 break;
3899 case RES_CHRATR_KERNING:
3900 bMustWrite = static_cast< const SvxKerningItem& >(rHt).GetValue() != 0;
3901 break;
3902 case RES_CHRATR_LANGUAGE:
3903 bMustWrite = true;
3904 break;
3905 case RES_CHRATR_POSTURE:
3906 bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE;
3907 break;
3908 case RES_CHRATR_SHADOWED:
3909 bMustWrite = static_cast< const SvxShadowedItem& >(rHt).GetValue();
3910 break;
3911 case RES_CHRATR_UNDERLINE:
3912 bMustWrite = static_cast< const SvxUnderlineItem& >(rHt).GetLineStyle() != LINESTYLE_NONE;
3913 break;
3914 case RES_CHRATR_WEIGHT:
3915 bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL;
3916 break;
3917 case RES_CHRATR_AUTOKERN:
3918 bMustWrite = static_cast< const SvxAutoKernItem& >(rHt).GetValue();
3919 break;
3920 case RES_CHRATR_BLINK:
3921 bMustWrite = static_cast< const SvxBlinkItem& >(rHt).GetValue();
3922 break;
3923 case RES_CHRATR_BACKGROUND:
3925 const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt);
3926 bMustWrite = (rBrushItem.GetColor() != COL_AUTO ||
3927 rBrushItem.GetShadingValue() != ShadingPattern::CLEAR ||
3928 rBrushItem.GetGraphic() != nullptr ||
3929 rBrushItem.GetGraphicObject() != nullptr);
3931 break;
3933 case RES_CHRATR_CJK_FONT:
3934 bMustWrite = true;
3935 break;
3936 case RES_CHRATR_CJK_FONTSIZE:
3937 bMustWrite = false; // we have written it already as RES_CHRATR_FONTSIZE
3938 break;
3939 case RES_CHRATR_CJK_LANGUAGE:
3940 bMustWrite = true;
3941 break;
3942 case RES_CHRATR_CJK_POSTURE:
3943 bMustWrite = false; // we have written it already as RES_CHRATR_POSTURE
3944 break;
3945 case RES_CHRATR_CJK_WEIGHT:
3946 bMustWrite = false; // we have written it already as RES_CHRATR_WEIGHT
3947 break;
3949 case RES_CHRATR_CTL_FONT:
3950 bMustWrite = true;
3951 break;
3952 case RES_CHRATR_CTL_FONTSIZE:
3953 bMustWrite = static_cast< const SvxFontHeightItem& >(rHt).GetHeight() != 200; // see StyleSheetTable_Impl::StyleSheetTable_Impl() where we set this default
3954 break;
3955 case RES_CHRATR_CTL_LANGUAGE:
3956 bMustWrite = true;
3957 break;
3958 case RES_CHRATR_CTL_POSTURE:
3959 bMustWrite = static_cast< const SvxPostureItem& >(rHt).GetPosture() != ITALIC_NONE;
3960 break;
3961 case RES_CHRATR_CTL_WEIGHT:
3962 bMustWrite = static_cast< const SvxWeightItem& >(rHt).GetWeight() != WEIGHT_NORMAL;
3963 break;
3965 case RES_CHRATR_ROTATE:
3966 bMustWrite = static_cast< const SvxCharRotateItem& >(rHt).GetValue() != 0;
3967 break;
3968 case RES_CHRATR_EMPHASIS_MARK:
3969 bMustWrite = static_cast< const SvxEmphasisMarkItem& >(rHt).GetEmphasisMark() != FontEmphasisMark::NONE;
3970 break;
3971 case RES_CHRATR_TWO_LINES:
3972 bMustWrite = static_cast< const SvxTwoLinesItem& >(rHt).GetValue();
3973 break;
3974 case RES_CHRATR_SCALEW:
3975 bMustWrite = static_cast< const SvxCharScaleWidthItem& >(rHt).GetValue() != 100;
3976 break;
3977 case RES_CHRATR_RELIEF:
3978 bMustWrite = static_cast< const SvxCharReliefItem& >(rHt).GetValue() != FontRelief::NONE;
3979 break;
3980 case RES_CHRATR_HIDDEN:
3981 bMustWrite = static_cast< const SvxCharHiddenItem& >(rHt).GetValue();
3982 break;
3983 case RES_CHRATR_BOX:
3985 const SvxBoxItem& rBoxItem = static_cast< const SvxBoxItem& >(rHt);
3986 bMustWrite = rBoxItem.GetTop() || rBoxItem.GetLeft() ||
3987 rBoxItem.GetBottom() || rBoxItem.GetRight() ||
3988 rBoxItem.GetSmallestDistance();
3990 break;
3991 case RES_CHRATR_HIGHLIGHT:
3993 const SvxBrushItem& rBrushItem = static_cast< const SvxBrushItem& >(rHt);
3994 bMustWrite = (rBrushItem.GetColor() != COL_AUTO ||
3995 rBrushItem.GetShadingValue() != ShadingPattern::CLEAR ||
3996 rBrushItem.GetGraphic() != nullptr ||
3997 rBrushItem.GetGraphicObject() != nullptr);
3999 break;
4001 case RES_PARATR_LINESPACING:
4002 bMustWrite = static_cast< const SvxLineSpacingItem& >(rHt).GetInterLineSpaceRule() != SvxInterLineSpaceRule::Off;
4003 break;
4004 case RES_PARATR_ADJUST:
4005 bMustWrite = static_cast< const SvxAdjustItem& >(rHt).GetAdjust() != SVX_ADJUST_LEFT;
4006 break;
4007 case RES_PARATR_SPLIT:
4008 bMustWrite = !static_cast< const SvxFormatSplitItem& >(rHt).GetValue();
4009 break;
4010 case RES_PARATR_WIDOWS:
4011 bMustWrite = static_cast< const SvxWidowsItem& >(rHt).GetValue();
4012 break;
4013 case RES_PARATR_TABSTOP:
4014 bMustWrite = static_cast< const SvxTabStopItem& >(rHt).Count() != 0;
4015 break;
4016 case RES_PARATR_HYPHENZONE:
4017 bMustWrite = static_cast< const SvxHyphenZoneItem& >(rHt).IsHyphen();
4018 break;
4019 case RES_PARATR_NUMRULE:
4020 bMustWrite = !static_cast< const SwNumRuleItem& >(rHt).GetValue().isEmpty();
4021 break;
4022 case RES_PARATR_SCRIPTSPACE:
4023 bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4024 break;
4025 case RES_PARATR_HANGINGPUNCTUATION:
4026 bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4027 break;
4028 case RES_PARATR_FORBIDDEN_RULES:
4029 bMustWrite = !static_cast< const SfxBoolItem& >(rHt).GetValue();
4030 break;
4031 case RES_PARATR_VERTALIGN:
4032 bMustWrite = static_cast< const SvxParaVertAlignItem& >(rHt).GetValue() != SvxParaVertAlignItem::Align::Automatic;
4033 break;
4034 case RES_PARATR_SNAPTOGRID:
4035 bMustWrite = !static_cast< const SvxParaGridItem& >(rHt).GetValue();
4036 break;
4037 case RES_CHRATR_GRABBAG:
4038 bMustWrite = true;
4039 break;
4041 default:
4042 SAL_INFO("sw.ww8", "Unhandled SfxPoolItem with id " << rHt.Which() );
4043 break;
4046 if (bMustWrite)
4047 OutputItem(rHt);
4050 void DocxAttributeOutput::DocDefaults( )
4052 // Write the '<w:docDefaults>' section here
4053 m_pSerializer->startElementNS(XML_w, XML_docDefaults, FSEND);
4055 // Output the default run properties
4056 m_pSerializer->startElementNS(XML_w, XML_rPrDefault, FSEND);
4058 StartStyleProperties(false, 0);
4060 for (int i = int(RES_CHRATR_BEGIN); i < int(RES_CHRATR_END); ++i)
4061 OutputDefaultItem(m_rExport.m_pDoc->GetDefault(i));
4063 EndStyleProperties(false);
4065 m_pSerializer->endElementNS(XML_w, XML_rPrDefault);
4067 // Output the default paragraph properties
4068 m_pSerializer->startElementNS(XML_w, XML_pPrDefault, FSEND);
4070 StartStyleProperties(true, 0);
4072 for (int i = int(RES_PARATR_BEGIN); i < int(RES_PARATR_END); ++i)
4073 OutputDefaultItem(m_rExport.m_pDoc->GetDefault(i));
4075 EndStyleProperties(true);
4077 m_pSerializer->endElementNS(XML_w, XML_pPrDefault);
4079 m_pSerializer->endElementNS(XML_w, XML_docDefaults);
4082 void DocxAttributeOutput::EndStyles( sal_uInt16 nNumberOfStyles )
4084 // HACK
4085 // Ms Office seems to have an internal limitation of 4091 styles
4086 // and refuses to load .docx with more, even though the spec seems to allow that;
4087 // so simply if there are more styles, don't export those
4088 const sal_Int32 nCountStylesToWrite = MSWORD_MAX_STYLES_LIMIT - nNumberOfStyles;
4089 m_pTableStyleExport->TableStyles(nCountStylesToWrite);
4090 m_pSerializer->endElementNS( XML_w, XML_styles );
4093 void DocxAttributeOutput::DefaultStyle()
4095 // are these the values of enum ww::sti (see ../inc/wwstyles.hxx)?
4096 OSL_TRACE("TODO DocxAttributeOutput::DefaultStyle()");
4099 /* Writes <a:srcRect> tag back to document.xml if a file conatins a cropped image.
4100 * NOTE : Tested on images of type JPEG,EMF/WMF,BMP, PNG and GIF.
4102 void DocxAttributeOutput::WriteSrcRect(const SdrObject* pSdrObj, const SwFrameFormat* pFrameFormat )
4104 uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY );
4105 uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
4107 OUString sUrl;
4108 xPropSet->getPropertyValue("GraphicURL") >>= sUrl;
4109 const GraphicObject aGrafObj(GraphicObject::CreateGraphicObjectFromURL(sUrl));
4111 Size aOriginalSize(aGrafObj.GetPrefSize());
4113 const MapMode aMap100mm( MapUnit::Map100thMM );
4114 const MapMode& rMapMode = aGrafObj.GetPrefMapMode();
4115 if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
4117 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, aMap100mm);
4120 css::text::GraphicCrop aGraphicCropStruct;
4121 xPropSet->getPropertyValue( "GraphicCrop" ) >>= aGraphicCropStruct;
4122 sal_Int32 nCropL = aGraphicCropStruct.Left;
4123 sal_Int32 nCropR = aGraphicCropStruct.Right;
4124 sal_Int32 nCropT = aGraphicCropStruct.Top;
4125 sal_Int32 nCropB = aGraphicCropStruct.Bottom;
4127 // simulate border padding as a negative crop.
4128 const SfxPoolItem* pItem;
4129 if (pFrameFormat && SfxItemState::SET == pFrameFormat->GetItemState(RES_BOX, false, &pItem))
4131 const SvxBoxItem& rBox = *static_cast<const SvxBoxItem*>(pItem);
4132 nCropL -= rBox.GetDistance( SvxBoxItemLine::LEFT );
4133 nCropR -= rBox.GetDistance( SvxBoxItemLine::RIGHT );
4134 nCropT -= rBox.GetDistance( SvxBoxItemLine::TOP );
4135 nCropB -= rBox.GetDistance( SvxBoxItemLine::BOTTOM );
4138 if ( (0 != nCropL) || (0 != nCropT) || (0 != nCropR) || (0 != nCropB) )
4140 double widthMultiplier = 100000.0/aOriginalSize.Width();
4141 double heightMultiplier = 100000.0/aOriginalSize.Height();
4143 double left = nCropL * widthMultiplier;
4144 double right = nCropR * widthMultiplier;
4145 double top = nCropT * heightMultiplier;
4146 double bottom = nCropB * heightMultiplier;
4148 m_pSerializer->singleElementNS( XML_a, XML_srcRect,
4149 XML_l, I32S(left),
4150 XML_t, I32S(top),
4151 XML_r, I32S(right),
4152 XML_b, I32S(bottom),
4153 FSEND );
4157 void DocxAttributeOutput::PopRelIdCache()
4159 if (!m_aRelIdCache.empty())
4160 m_aRelIdCache.pop();
4161 if (!m_aSdrRelIdCache.empty())
4162 m_aSdrRelIdCache.pop();
4165 void DocxAttributeOutput::PushRelIdCache()
4167 m_aRelIdCache.push(std::map<const Graphic*, OString>());
4168 m_aSdrRelIdCache.push(std::map<BitmapChecksum, OUString>());
4171 OUString DocxAttributeOutput::FindRelId(BitmapChecksum nChecksum)
4173 OUString aRet;
4175 if (!m_aSdrRelIdCache.empty() && m_aSdrRelIdCache.top().find(nChecksum) != m_aSdrRelIdCache.top().end())
4176 aRet = m_aSdrRelIdCache.top()[nChecksum];
4178 return aRet;
4181 void DocxAttributeOutput::CacheRelId(BitmapChecksum nChecksum, const OUString& rRelId)
4183 if (!m_aSdrRelIdCache.empty())
4184 m_aSdrRelIdCache.top()[nChecksum] = rRelId;
4187 void DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj )
4189 OSL_TRACE( "TODO DocxAttributeOutput::FlyFrameGraphic( const SwGrfNode* pGrfNode, const Size& rSize, const SwFlyFrameFormat* pOLEFrameFormat, SwOLENode* pOLENode, const SdrObject* pSdrObj ) - some stuff still missing" );
4191 GetSdtEndBefore(pSdrObj);
4193 // detect mis-use of the API
4194 assert(pGrfNode || (pOLEFrameFormat && pOLENode));
4195 const SwFrameFormat* pFrameFormat = pGrfNode ? pGrfNode->GetFlyFormat() : pOLEFrameFormat;
4196 // create the relation ID
4197 OString aRelId;
4198 sal_Int32 nImageType;
4199 if ( pGrfNode && pGrfNode->IsLinkedFile() )
4201 // linked image, just create the relation
4202 OUString aFileName;
4203 pGrfNode->GetFileFilterNms( &aFileName, nullptr );
4205 // TODO Convert the file name to relative for better interoperability
4207 aRelId = m_rExport.AddRelation(
4208 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
4209 aFileName );
4211 nImageType = XML_link;
4213 else
4215 // inline, we also have to write the image itself
4216 const Graphic* pGraphic = nullptr;
4217 if (pGrfNode)
4218 pGraphic = &pGrfNode->GetGrf();
4219 else
4220 pGraphic = pOLENode->GetGraphic();
4222 if (!m_aRelIdCache.empty() && m_aRelIdCache.top().find(pGraphic) != m_aRelIdCache.top().end())
4223 // We already have a RelId for this Graphic.
4224 aRelId = m_aRelIdCache.top()[pGraphic];
4225 else
4227 // Not in cache, then need to write it.
4228 m_rDrawingML.SetFS( m_pSerializer ); // to be sure that we write to the right stream
4230 OUString aImageId = m_rDrawingML.WriteImage( *pGraphic );
4232 aRelId = OUStringToOString( aImageId, RTL_TEXTENCODING_UTF8 );
4233 if (!m_aRelIdCache.empty())
4234 m_aRelIdCache.top()[pGraphic] = aRelId;
4237 nImageType = XML_embed;
4240 // In case there are any grab-bag items on the graphic frame, emit them now.
4241 // These are always character grab-bags, as graphics are at-char or as-char in Word.
4242 const SfxPoolItem* pItem = nullptr;
4243 if (pFrameFormat->GetAttrSet().HasItem(RES_FRMATR_GRABBAG, &pItem))
4245 const SfxGrabBagItem* pGrabBag = static_cast<const SfxGrabBagItem*>(pItem);
4246 CharGrabBag(*pGrabBag);
4249 m_rExport.SdrExporter().startDMLAnchorInline(pFrameFormat, rSize);
4251 // picture description (used for pic:cNvPr later too)
4252 ::sax_fastparser::FastAttributeList* docPrattrList = FastSerializerHelper::createAttrList();
4253 docPrattrList->add( XML_id, OString::number( m_anchorId++).getStr());
4254 docPrattrList->add( XML_name, OUStringToOString( pFrameFormat->GetName(), RTL_TEXTENCODING_UTF8 ) );
4255 docPrattrList->add( XML_descr, OUStringToOString( pGrfNode ? pGrfNode->GetDescription() : pOLEFrameFormat->GetObjDescription(), RTL_TEXTENCODING_UTF8 ).getStr());
4256 if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT )
4257 docPrattrList->add( XML_title, OUStringToOString( pGrfNode ? pGrfNode->GetTitle() : pOLEFrameFormat->GetObjTitle(), RTL_TEXTENCODING_UTF8 ).getStr());
4258 XFastAttributeListRef docPrAttrListRef( docPrattrList );
4259 m_pSerializer->startElementNS( XML_wp, XML_docPr, docPrAttrListRef );
4260 // TODO hyperlink
4261 // m_pSerializer->singleElementNS( XML_a, XML_hlinkClick,
4262 // FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
4263 // FSNS( XML_r, XML_id ), "rId4",
4264 // FSEND );
4265 m_pSerializer->endElementNS( XML_wp, XML_docPr );
4267 m_pSerializer->startElementNS( XML_wp, XML_cNvGraphicFramePr,
4268 FSEND );
4269 // TODO change aspect?
4270 m_pSerializer->singleElementNS( XML_a, XML_graphicFrameLocks,
4271 FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
4272 XML_noChangeAspect, "1",
4273 FSEND );
4274 m_pSerializer->endElementNS( XML_wp, XML_cNvGraphicFramePr );
4276 m_pSerializer->startElementNS( XML_a, XML_graphic,
4277 FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
4278 FSEND );
4279 m_pSerializer->startElementNS( XML_a, XML_graphicData,
4280 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/picture",
4281 FSEND );
4283 m_pSerializer->startElementNS( XML_pic, XML_pic,
4284 FSNS( XML_xmlns, XML_pic ), "http://schemas.openxmlformats.org/drawingml/2006/picture",
4285 FSEND );
4287 m_pSerializer->startElementNS( XML_pic, XML_nvPicPr,
4288 FSEND );
4289 // It seems pic:cNvpr and wp:docPr are pretty much the same thing with the same attributes
4290 m_pSerializer->startElementNS( XML_pic, XML_cNvPr, docPrAttrListRef );
4292 // TODO hyperlink
4293 // m_pSerializer->singleElementNS( XML_a, XML_hlinkClick,
4294 // FSNS( XML_r, XML_id ), "rId4",
4295 // FSEND );
4296 m_pSerializer->endElementNS( XML_pic, XML_cNvPr );
4298 m_pSerializer->startElementNS( XML_pic, XML_cNvPicPr,
4299 FSEND );
4300 // TODO change aspect?
4301 m_pSerializer->singleElementNS( XML_a, XML_picLocks,
4302 XML_noChangeAspect, "1", XML_noChangeArrowheads, "1",
4303 FSEND );
4304 m_pSerializer->endElementNS( XML_pic, XML_cNvPicPr );
4305 m_pSerializer->endElementNS( XML_pic, XML_nvPicPr );
4307 // the actual picture
4308 m_pSerializer->startElementNS( XML_pic, XML_blipFill,
4309 FSEND );
4311 /* At this point we are certain that, WriteImage returns empty RelId
4312 for unhandled graphic type. Therefore we write the picture description
4313 and not the relation( coz there ain't any), so that the user knows
4314 there is a image/graphic in the doc but it is broken instead of
4315 completely discarding it.
4317 if ( aRelId.isEmpty() )
4318 m_pSerializer->startElementNS( XML_a, XML_blip,
4319 FSEND );
4320 else
4321 m_pSerializer->startElementNS( XML_a, XML_blip,
4322 FSNS( XML_r, nImageType ), aRelId.getStr(),
4323 FSEND );
4325 pItem = nullptr;
4326 sal_uInt32 nMode = GRAPHICDRAWMODE_STANDARD;
4328 if ( pGrfNode && SfxItemState::SET == pGrfNode->GetSwAttrSet().GetItemState(RES_GRFATR_DRAWMODE, true, &pItem))
4330 nMode = static_cast<const SfxEnumItem*>(pItem)->GetValue();
4331 if (nMode == GRAPHICDRAWMODE_GREYS)
4332 m_pSerializer->singleElementNS (XML_a, XML_grayscl, FSEND);
4333 else if (nMode == GRAPHICDRAWMODE_MONO) //black/white has a 0,5 threshold in LibreOffice
4334 m_pSerializer->singleElementNS (XML_a, XML_biLevel, XML_thresh, OString::number(50000), FSEND);
4335 else if (nMode == GRAPHICDRAWMODE_WATERMARK) //watermark has a brightness/luminance of 0,5 and contrast of -0.7 in LibreOffice
4336 m_pSerializer->singleElementNS( XML_a, XML_lum, XML_bright, OString::number(70000), XML_contrast, OString::number(-70000), FSEND );
4338 m_pSerializer->endElementNS( XML_a, XML_blip );
4340 if (pSdrObj){
4341 WriteSrcRect(pSdrObj, pFrameFormat);
4344 m_pSerializer->startElementNS( XML_a, XML_stretch,
4345 FSEND );
4346 m_pSerializer->singleElementNS( XML_a, XML_fillRect,
4347 FSEND );
4348 m_pSerializer->endElementNS( XML_a, XML_stretch );
4349 m_pSerializer->endElementNS( XML_pic, XML_blipFill );
4351 // TODO setup the right values below
4352 m_pSerializer->startElementNS( XML_pic, XML_spPr,
4353 XML_bwMode, "auto",
4354 FSEND );
4356 rtl::Reference<sax_fastparser::FastAttributeList> xFrameAttributes(
4357 FastSerializerHelper::createAttrList());
4359 if (pGrfNode)
4361 sal_uInt16 eMirror = pGrfNode->GetSwAttrSet().GetMirrorGrf().GetValue();
4362 if (eMirror == RES_MIRROR_GRAPH_VERT || eMirror == RES_MIRROR_GRAPH_BOTH)
4363 // Mirror on the vertical axis is a horizontal flip.
4364 xFrameAttributes->add(XML_flipH, "1");
4367 m_pSerializer->startElementNS(
4368 XML_a, XML_xfrm, uno::Reference<xml::sax::XFastAttributeList>(xFrameAttributes.get()));
4370 m_pSerializer->singleElementNS( XML_a, XML_off,
4371 XML_x, "0", XML_y, "0",
4372 FSEND );
4373 OString aWidth( OString::number( TwipsToEMU( rSize.Width() ) ) );
4374 OString aHeight( OString::number( TwipsToEMU( rSize.Height() ) ) );
4375 m_pSerializer->singleElementNS( XML_a, XML_ext,
4376 XML_cx, aWidth.getStr(),
4377 XML_cy, aHeight.getStr(),
4378 FSEND );
4379 m_pSerializer->endElementNS( XML_a, XML_xfrm );
4380 m_pSerializer->startElementNS( XML_a, XML_prstGeom,
4381 XML_prst, "rect",
4382 FSEND );
4383 m_pSerializer->singleElementNS( XML_a, XML_avLst,
4384 FSEND );
4385 m_pSerializer->endElementNS( XML_a, XML_prstGeom );
4387 const SvxBoxItem& rBoxItem = pFrameFormat->GetBox();
4388 const SvxBorderLine* pLeft = rBoxItem.GetLine(SvxBoxItemLine::LEFT);
4389 const SvxBorderLine* pRight = rBoxItem.GetLine(SvxBoxItemLine::RIGHT);
4390 const SvxBorderLine* pTop = rBoxItem.GetLine(SvxBoxItemLine::TOP);
4391 const SvxBorderLine* pBottom = rBoxItem.GetLine(SvxBoxItemLine::BOTTOM);
4392 if (pLeft || pRight || pTop || pBottom)
4393 m_rExport.SdrExporter().writeBoxItemLine(rBoxItem);
4395 m_rExport.SdrExporter().writeDMLEffectLst(*pFrameFormat);
4397 m_pSerializer->endElementNS( XML_pic, XML_spPr );
4399 m_pSerializer->endElementNS( XML_pic, XML_pic );
4401 m_pSerializer->endElementNS( XML_a, XML_graphicData );
4402 m_pSerializer->endElementNS( XML_a, XML_graphic );
4403 m_rExport.SdrExporter().endDMLAnchorInline(pFrameFormat);
4406 void DocxAttributeOutput::WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rOLENode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
4408 if( WriteOLEChart( pSdrObj, rSize ))
4409 return;
4410 if( WriteOLEMath( pSdrObj, rOLENode, rSize ))
4411 return;
4412 if( PostponeOLE( pSdrObj, rOLENode, rSize, pFlyFrameFormat ))
4413 return;
4414 // Then we fall back to just export the object as a graphic.
4415 if( !m_pPostponedGraphic )
4416 FlyFrameGraphic( nullptr, rSize, pFlyFrameFormat, &rOLENode );
4417 else
4418 // w:drawing should not be inside w:rPr, so write it out later
4419 m_pPostponedGraphic->push_back(PostponedGraphic(nullptr, rSize, pFlyFrameFormat, &rOLENode, nullptr));
4422 bool DocxAttributeOutput::WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize )
4424 uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY );
4425 if (!xShape.is())
4426 return false;
4428 uno::Reference<beans::XPropertySet> const xPropSet(xShape, uno::UNO_QUERY);
4429 if (!xPropSet.is())
4430 return false;
4432 OUString clsid; // why is the property of type string, not sequence<byte>?
4433 xPropSet->getPropertyValue("CLSID") >>= clsid;
4434 assert(!clsid.isEmpty());
4435 SvGlobalName aClassID;
4436 bool const isValid(aClassID.MakeId(clsid));
4437 assert(isValid); (void)isValid;
4439 if (!SotExchange::IsChart(aClassID))
4440 return false;
4442 m_postponedChart = pSdrObj;
4443 m_postponedChartSize = rSize;
4444 return true;
4448 * Write chart hierarchy in w:drawing after end element of w:rPr tag.
4450 void DocxAttributeOutput::WritePostponedChart()
4452 if(m_postponedChart == nullptr)
4453 return;
4454 uno::Reference< chart2::XChartDocument > xChartDoc;
4455 uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(m_postponedChart)->getUnoShape(), uno::UNO_QUERY );
4456 if( xShape.is() )
4458 uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
4459 if( xPropSet.is() )
4460 xChartDoc.set( xPropSet->getPropertyValue( "Model" ), uno::UNO_QUERY );
4463 if( xChartDoc.is() )
4465 OSL_TRACE("DocxAttributeOutput::WriteOLE2Obj: export chart ");
4466 m_pSerializer->startElementNS( XML_w, XML_drawing,
4467 FSEND );
4468 m_pSerializer->startElementNS( XML_wp, XML_inline,
4469 XML_distT, "0", XML_distB, "0", XML_distL, "0", XML_distR, "0",
4470 FSEND );
4472 OString aWidth( OString::number( TwipsToEMU( m_postponedChartSize.Width() ) ) );
4473 OString aHeight( OString::number( TwipsToEMU( m_postponedChartSize.Height() ) ) );
4474 m_pSerializer->singleElementNS( XML_wp, XML_extent,
4475 XML_cx, aWidth.getStr(),
4476 XML_cy, aHeight.getStr(),
4477 FSEND );
4478 // TODO - the right effectExtent, extent including the effect
4479 m_pSerializer->singleElementNS( XML_wp, XML_effectExtent,
4480 XML_l, "0", XML_t, "0", XML_r, "0", XML_b, "0",
4481 FSEND );
4483 OUString sName("Object 1");
4484 uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY );
4485 if( xNamed.is() )
4486 sName = xNamed->getName();
4488 /* If there is a scenario where a chart is followed by a shape
4489 which is being exported as an alternate content then, the
4490 docPr Id is being repeated, ECMA 20.4.2.5 says that the
4491 docPr Id should be unique, ensuring the same here.
4493 m_pSerializer->singleElementNS( XML_wp, XML_docPr,
4494 XML_id, I32S( m_anchorId++ ),
4495 XML_name, USS( sName ),
4496 FSEND );
4498 m_pSerializer->singleElementNS( XML_wp, XML_cNvGraphicFramePr,
4499 FSEND );
4501 m_pSerializer->startElementNS( XML_a, XML_graphic,
4502 FSNS( XML_xmlns, XML_a ), "http://schemas.openxmlformats.org/drawingml/2006/main",
4503 FSEND );
4505 m_pSerializer->startElementNS( XML_a, XML_graphicData,
4506 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart",
4507 FSEND );
4509 OString aRelId;
4510 m_nChartCount++;
4511 uno::Reference< frame::XModel > xModel( xChartDoc, uno::UNO_QUERY );
4512 aRelId = m_rExport.OutputChart( xModel, m_nChartCount, m_pSerializer );
4514 m_pSerializer->singleElementNS( XML_c, XML_chart,
4515 FSNS( XML_xmlns, XML_c ), "http://schemas.openxmlformats.org/drawingml/2006/chart",
4516 FSNS( XML_xmlns, XML_r ), "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
4517 FSNS( XML_r, XML_id ), aRelId.getStr(),
4518 FSEND );
4520 m_pSerializer->endElementNS( XML_a, XML_graphicData );
4521 m_pSerializer->endElementNS( XML_a, XML_graphic );
4522 m_pSerializer->endElementNS( XML_wp, XML_inline );
4523 m_pSerializer->endElementNS( XML_w, XML_drawing );
4526 m_postponedChart = nullptr;
4529 bool DocxAttributeOutput::WriteOLEMath( const SdrObject*, const SwOLENode& rOLENode, const Size& )
4531 uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode&>(rOLENode).GetOLEObj().GetOleRef());
4532 SvGlobalName aObjName(xObj->getClassID());
4534 if( !SotExchange::IsMath(aObjName) )
4535 return false;
4536 m_aPostponedMaths.push_back(&rOLENode);
4537 return true;
4540 void DocxAttributeOutput::WritePostponedMath(const SwOLENode* pPostponedMath)
4542 uno::Reference < embed::XEmbeddedObject > xObj(const_cast<SwOLENode*>(pPostponedMath)->GetOLEObj().GetOleRef());
4543 if (embed::EmbedStates::LOADED == xObj->getCurrentState())
4545 // must be running so there is a Component
4548 xObj->changeState(embed::EmbedStates::RUNNING);
4550 catch (const uno::Exception&)
4554 uno::Reference< uno::XInterface > xInterface( xObj->getComponent(), uno::UNO_QUERY );
4555 if (!xInterface.is())
4557 SAL_WARN("sw.ww8", "Broken math object");
4558 return;
4560 // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class,
4561 // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated
4562 // to RTLD_GLOBAL, so most probably a gcc bug.
4563 oox::FormulaExportBase* formulaexport = dynamic_cast<oox::FormulaExportBase*>(dynamic_cast<SfxBaseModel*>(xInterface.get()));
4564 assert( formulaexport != nullptr );
4565 if (formulaexport)
4566 formulaexport->writeFormulaOoxml( m_pSerializer, GetExport().GetFilter().getVersion(),
4567 oox::drawingml::DOCUMENT_DOCX);
4570 void DocxAttributeOutput::WritePostponedFormControl(const SdrObject* pObject)
4572 if (!pObject || pObject->GetObjInventor() != SdrInventor::FmForm)
4573 return;
4575 SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
4576 if (!pFormObj)
4577 return;
4579 uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
4580 uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
4581 if (!xInfo.is())
4582 return;
4584 if (xInfo->supportsService("com.sun.star.form.component.DateField"))
4586 // gather component properties
4588 Date aOriginalDate(Date::EMPTY);
4589 OUString sOriginalContent, sDateFormat, sAlias;
4590 OUString sLocale("en-US");
4591 uno::Sequence<beans::PropertyValue> aGrabBag;
4592 uno::Reference<beans::XPropertySet> xShapePropertySet(pFormObj->getUnoShape(), uno::UNO_QUERY);
4593 uno::Sequence<beans::PropertyValue> aCharFormat;
4594 if (xShapePropertySet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= aGrabBag)
4596 for (sal_Int32 i=0; i < aGrabBag.getLength(); ++i)
4598 if (aGrabBag[i].Name == "DateFormat")
4599 aGrabBag[i].Value >>= sDateFormat;
4600 else if (aGrabBag[i].Name == "Locale")
4601 aGrabBag[i].Value >>= sLocale;
4602 else if (aGrabBag[i].Name == "OriginalContent")
4603 aGrabBag[i].Value >>= sOriginalContent;
4604 else if (aGrabBag[i].Name == "OriginalDate")
4606 css::util::Date aUNODate;
4607 aGrabBag[i].Value >>= aUNODate;
4608 aOriginalDate = aUNODate;
4610 else if (aGrabBag[i].Name == "CharFormat")
4611 aGrabBag[i].Value >>= aCharFormat;
4612 else if (aGrabBag[i].Name == "ooxml:CT_SdtPr_alias")
4613 aGrabBag[i].Value >>= sAlias;
4616 uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
4618 OString sDate;
4619 OUString aContentText;
4620 bool bHasDate = false;
4621 css::util::Date aUNODate;
4622 if (xPropertySet->getPropertyValue("Date") >>= aUNODate)
4624 bHasDate = true;
4625 Date aDate(aUNODate.Day, aUNODate.Month, aUNODate.Year);
4626 sDate = DateToOString(aDate);
4628 if (aOriginalDate == aDate)
4630 aContentText = sOriginalContent;
4631 // sDateFormat was extracted from the grab bag
4633 else
4635 aContentText = OUString::createFromAscii(DateToDDMMYYYYOString(aDate).getStr());
4636 sDateFormat = "dd/MM/yyyy";
4639 else
4640 aContentText = xPropertySet->getPropertyValue("HelpText").get<OUString>();
4642 // output component
4644 m_pSerializer->startElementNS(XML_w, XML_sdt, FSEND);
4645 m_pSerializer->startElementNS(XML_w, XML_sdtPr, FSEND);
4647 if (!sAlias.isEmpty())
4648 m_pSerializer->singleElementNS(XML_w, XML_alias,
4649 FSNS(XML_w, XML_val), OUStringToOString(sAlias, RTL_TEXTENCODING_UTF8),
4650 FSEND);
4652 if (bHasDate)
4653 m_pSerializer->startElementNS(XML_w, XML_date,
4654 FSNS( XML_w, XML_fullDate ), sDate.getStr(),
4655 FSEND);
4656 else
4657 m_pSerializer->startElementNS(XML_w, XML_date, FSEND);
4659 m_pSerializer->singleElementNS(XML_w, XML_dateFormat,
4660 FSNS(XML_w, XML_val),
4661 OUStringToOString( sDateFormat, RTL_TEXTENCODING_UTF8 ).getStr(),
4662 FSEND);
4663 m_pSerializer->singleElementNS(XML_w, XML_lid,
4664 FSNS(XML_w, XML_val),
4665 OUStringToOString( sLocale, RTL_TEXTENCODING_UTF8 ).getStr(),
4666 FSEND);
4667 m_pSerializer->singleElementNS(XML_w, XML_storeMappedDataAs,
4668 FSNS(XML_w, XML_val), "dateTime",
4669 FSEND);
4670 m_pSerializer->singleElementNS(XML_w, XML_calendar,
4671 FSNS(XML_w, XML_val), "gregorian",
4672 FSEND);
4674 m_pSerializer->endElementNS(XML_w, XML_date);
4675 m_pSerializer->endElementNS(XML_w, XML_sdtPr);
4677 m_pSerializer->startElementNS(XML_w, XML_sdtContent, FSEND);
4678 m_pSerializer->startElementNS(XML_w, XML_r, FSEND);
4680 if (aCharFormat.hasElements())
4682 m_pTableStyleExport->SetSerializer(m_pSerializer);
4683 m_pTableStyleExport->CharFormat(aCharFormat);
4686 RunText(aContentText);
4687 m_pSerializer->endElementNS(XML_w, XML_r);
4688 m_pSerializer->endElementNS(XML_w, XML_sdtContent);
4690 m_pSerializer->endElementNS(XML_w, XML_sdt);
4692 else if (xInfo->supportsService("com.sun.star.form.component.ComboBox"))
4694 // gather component properties
4696 uno::Reference<beans::XPropertySet> xPropertySet(xControlModel, uno::UNO_QUERY);
4697 OUString sText = xPropertySet->getPropertyValue("Text").get<OUString>();
4698 uno::Sequence<OUString> aItems = xPropertySet->getPropertyValue("StringItemList").get< uno::Sequence<OUString> >();
4700 // output component
4702 m_pSerializer->startElementNS(XML_w, XML_sdt, FSEND);
4703 m_pSerializer->startElementNS(XML_w, XML_sdtPr, FSEND);
4705 m_pSerializer->startElementNS(XML_w, XML_dropDownList, FSEND);
4707 for (sal_Int32 i=0; i < aItems.getLength(); ++i)
4709 m_pSerializer->singleElementNS(XML_w, XML_listItem,
4710 FSNS(XML_w, XML_displayText),
4711 OUStringToOString( aItems[i], RTL_TEXTENCODING_UTF8 ).getStr(),
4712 FSNS(XML_w, XML_value),
4713 OUStringToOString( aItems[i], RTL_TEXTENCODING_UTF8 ).getStr(),
4714 FSEND);
4717 m_pSerializer->endElementNS(XML_w, XML_dropDownList);
4718 m_pSerializer->endElementNS(XML_w, XML_sdtPr);
4720 m_pSerializer->startElementNS(XML_w, XML_sdtContent, FSEND);
4721 m_pSerializer->startElementNS(XML_w, XML_r, FSEND);
4722 RunText(sText);
4723 m_pSerializer->endElementNS(XML_w, XML_r);
4724 m_pSerializer->endElementNS(XML_w, XML_sdtContent);
4726 m_pSerializer->endElementNS(XML_w, XML_sdt);
4730 void DocxAttributeOutput::WritePostponedActiveXControl(bool bInsideRun)
4732 for( std::vector<PostponedDrawing>::const_iterator it = m_aPostponedActiveXControls.begin();
4733 it != m_aPostponedActiveXControls.end(); ++it )
4735 WriteActiveXControl(it->object, *(it->frame), *(it->point), bInsideRun);
4737 m_aPostponedActiveXControls.clear();
4740 void DocxAttributeOutput::WriteActiveXControl(const SdrObject* pObject, const SwFrameFormat& rFrameFormat,const Point& rNdTopLeft, bool bInsideRun)
4742 SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
4743 if (!pFormObj)
4744 return;
4746 uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
4747 if (!xControlModel.is())
4748 return;
4750 const bool bAnchoredInline = rFrameFormat.GetAnchor().GetAnchorId() == static_cast<RndStdIds>(css::text::TextContentAnchorType_AS_CHARACTER);
4752 if(!bInsideRun)
4754 m_pSerializer->startElementNS(XML_w, XML_r, FSEND);
4757 // w:pict for floating embedded control and w:object for inline embedded control
4758 if(bAnchoredInline)
4759 m_pSerializer->startElementNS(XML_w, XML_object, FSEND);
4760 else
4761 m_pSerializer->startElementNS(XML_w, XML_pict, FSEND);
4763 // write ActiveX fragment and ActiveX binary
4764 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(), uno::UNO_QUERY);
4765 std::pair<OString,OString> sRelIdAndName = m_rExport.WriteActiveXObject(xShape, xControlModel);
4767 // VML shape definition
4768 m_rExport.VMLExporter().SetSkipwzName(true);
4769 m_rExport.VMLExporter().SetHashMarkForType(true);
4770 m_rExport.VMLExporter().OverrideShapeIDGen(true, "control_shape_");
4771 OString sShapeId;
4772 if(bAnchoredInline)
4774 sShapeId = m_rExport.VMLExporter().AddInlineSdrObject(*pObject, true);
4776 else
4778 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
4779 const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
4780 sShapeId = m_rExport.VMLExporter().AddSdrObject(*pObject,
4781 rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(),
4782 rHoriOri.GetRelationOrient(),
4783 rVertOri.GetRelationOrient(), &rNdTopLeft, true);
4785 // Restore default values
4786 m_rExport.VMLExporter().SetSkipwzName(false);
4787 m_rExport.VMLExporter().SetHashMarkForType(false);
4788 m_rExport.VMLExporter().OverrideShapeIDGen(false);
4790 // control
4791 m_pSerializer->singleElementNS(XML_w, XML_control,
4792 FSNS(XML_r, XML_id), sRelIdAndName.first.getStr(),
4793 FSNS(XML_w, XML_name), sRelIdAndName.second.getStr(),
4794 FSNS(XML_w, XML_shapeid), sShapeId.getStr(),
4795 FSEND);
4797 if(bAnchoredInline)
4798 m_pSerializer->endElementNS(XML_w, XML_object);
4799 else
4800 m_pSerializer->endElementNS(XML_w, XML_pict);
4802 if(!bInsideRun)
4804 m_pSerializer->endElementNS(XML_w, XML_r);
4808 bool DocxAttributeOutput::ExportAsActiveXControl(const SdrObject* pObject) const
4810 SdrUnoObj *pFormObj = const_cast<SdrUnoObj*>(dynamic_cast< const SdrUnoObj*>(pObject));
4811 if (!pFormObj)
4812 return false;
4814 uno::Reference<awt::XControlModel> xControlModel = pFormObj->GetUnoControlModel();
4815 if (!xControlModel.is())
4816 return false;
4818 uno::Reference< css::frame::XModel > xModel( m_rExport.m_pDoc->GetDocShell() ? m_rExport.m_pDoc->GetDocShell()->GetModel() : nullptr );
4819 if (!xModel.is())
4820 return false;
4822 uno::Reference<lang::XServiceInfo> xInfo(xControlModel, uno::UNO_QUERY);
4823 if (!xInfo.is())
4824 return false;
4826 // See WritePostponedFormControl
4827 // By now date field and combobox is handled on a different way, so let's not interfere with the other method.
4828 if(xInfo->supportsService("com.sun.star.form.component.DateField") ||
4829 xInfo->supportsService("com.sun.star.form.component.ComboBox"))
4830 return false;
4832 oox::ole::OleFormCtrlExportHelper exportHelper(comphelper::getProcessComponentContext(), xModel, xControlModel);
4833 if(!exportHelper.isValid())
4834 return false;
4836 return true;
4839 bool DocxAttributeOutput::PostponeOLE( const SdrObject*, SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* pFlyFrameFormat )
4841 if( !m_pPostponedOLEs )
4842 //cannot be postponed, try to write now
4843 WriteOLE( rNode, rSize, pFlyFrameFormat );
4844 else
4845 m_pPostponedOLEs->push_back( PostponedOLE( &rNode, rSize, pFlyFrameFormat ) );
4846 return true;
4850 * Write w:object hierarchy for embedded objects after end element of w:rPr tag.
4852 void DocxAttributeOutput::WritePostponedOLE()
4854 if( !m_pPostponedOLEs )
4855 return;
4857 for( std::list< PostponedOLE >::iterator it = m_pPostponedOLEs->begin();
4858 it != m_pPostponedOLEs->end();
4859 ++it )
4861 WriteOLE( *it->object, it->size, it->frame );
4864 // clear list of postponed objects
4865 m_pPostponedOLEs.reset(nullptr);
4868 void DocxAttributeOutput::WriteOLE( SwOLENode& rNode, const Size& rSize, const SwFlyFrameFormat* rFlyFrameFormat )
4870 // get interoperability information about embedded objects
4871 uno::Reference< beans::XPropertySet > xPropSet( m_rExport.m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
4872 uno::Sequence< beans::PropertyValue > aGrabBag, aObjectsInteropList,aObjectInteropAttributes;
4873 xPropSet->getPropertyValue( UNO_NAME_MISC_OBJ_INTEROPGRABBAG ) >>= aGrabBag;
4874 for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
4875 if ( aGrabBag[i].Name == "EmbeddedObjects" )
4877 aGrabBag[i].Value >>= aObjectsInteropList;
4878 break;
4881 SwOLEObj& aObject = rNode.GetOLEObj();
4882 uno::Reference < embed::XEmbeddedObject > xObj( aObject.GetOleRef() );
4883 comphelper::EmbeddedObjectContainer* aContainer = aObject.GetObject().GetContainer();
4884 OUString sObjectName = aContainer->GetEmbeddedObjectName( xObj );
4886 // set some attributes according to the type of the embedded object
4887 OUString sProgID, sDrawAspect = "Content";
4888 for( sal_Int32 i=0; i < aObjectsInteropList.getLength(); ++i )
4889 if ( aObjectsInteropList[i].Name == sObjectName )
4891 aObjectsInteropList[i].Value >>= aObjectInteropAttributes;
4892 break;
4895 for( sal_Int32 i=0; i < aObjectInteropAttributes.getLength(); ++i )
4897 if ( aObjectInteropAttributes[i].Name == "ProgID" )
4899 aObjectInteropAttributes[i].Value >>= sProgID;
4901 else if ( aObjectInteropAttributes[i].Name == "DrawAspect" )
4903 aObjectInteropAttributes[i].Value >>= sDrawAspect;
4907 // write embedded file
4908 OString sId = m_rExport.WriteOLEObject(aObject, sProgID);
4910 if( sId.isEmpty() )
4912 // the embedded file could not be saved
4913 // fallback: save as an image
4914 FlyFrameGraphic( nullptr, rSize, rFlyFrameFormat, &rNode );
4915 return;
4918 // write preview image
4919 const Graphic* pGraphic = rNode.GetGraphic();
4920 m_rDrawingML.SetFS(m_pSerializer);
4921 OUString sImageId = m_rDrawingML.WriteImage( *pGraphic );
4923 m_pSerializer->startElementNS( XML_w, XML_object, FSEND );
4925 OStringBuffer sShapeStyle, sShapeId;
4926 sShapeStyle.append( "width:" ).append( double( rSize.Width() ) / 20 )
4927 .append( "pt;height:" ).append( double( rSize.Height() ) / 20 )
4928 .append( "pt" ); //from VMLExport::AddRectangleDimensions(), it does: value/20
4929 sShapeId.append( "ole_" ).append( sId );
4931 // shape definition
4932 m_pSerializer->startElementNS( XML_v, XML_shape,
4933 XML_id, sShapeId.getStr(),
4934 XML_style, sShapeStyle.getStr(),
4935 FSNS( XML_o, XML_ole ), "", //compulsory, even if it's empty
4936 FSEND );
4938 // shape filled with the preview image
4939 m_pSerializer->singleElementNS( XML_v, XML_imagedata,
4940 FSNS( XML_r, XML_id ), OUStringToOString( sImageId, RTL_TEXTENCODING_UTF8 ).getStr(),
4941 FSNS( XML_o, XML_title ), "",
4942 FSEND );
4944 m_pSerializer->endElementNS( XML_v, XML_shape );
4946 // OLE object definition
4947 m_pSerializer->singleElementNS( XML_o, XML_OLEObject,
4948 XML_Type, "Embed",
4949 XML_ProgID, OUStringToOString( sProgID, RTL_TEXTENCODING_UTF8 ).getStr(),
4950 XML_ShapeID, sShapeId.getStr(),
4951 XML_DrawAspect, OUStringToOString( sDrawAspect, RTL_TEXTENCODING_UTF8 ).getStr(),
4952 XML_ObjectID, "_" + OString::number(comphelper::rng::uniform_int_distribution(0, std::numeric_limits<int>::max())),
4953 FSNS( XML_r, XML_id ), sId.getStr(),
4954 FSEND );
4956 m_pSerializer->endElementNS( XML_w, XML_object );
4960 * Write w:pict hierarchy end element of w:rPr tag.
4962 void DocxAttributeOutput::WritePostponedVMLDrawing()
4964 if (!m_pPostponedVMLDrawings)
4965 return;
4967 for( std::list< PostponedDrawing >::iterator it = m_pPostponedVMLDrawings->begin();
4968 it != m_pPostponedVMLDrawings->end();
4969 ++it )
4971 m_rExport.SdrExporter().writeVMLDrawing(it->object, *(it->frame), *(it->point));
4973 m_pPostponedVMLDrawings.reset(nullptr);
4976 void DocxAttributeOutput::WritePostponedCustomShape()
4978 if (!m_pPostponedCustomShape)
4979 return;
4981 bool bStartedParaSdt = m_bStartedParaSdt;
4982 for( std::list< PostponedDrawing >::iterator it = m_pPostponedCustomShape->begin();
4983 it != m_pPostponedCustomShape->end();
4984 ++it )
4986 if ( IsAlternateContentChoiceOpen() )
4987 m_rExport.SdrExporter().writeDMLDrawing(it->object, (it->frame), m_anchorId++);
4988 else
4989 m_rExport.SdrExporter().writeDMLAndVMLDrawing(it->object, *(it->frame), *(it->point), m_anchorId++);
4991 m_bStartedParaSdt = bStartedParaSdt;
4992 m_pPostponedCustomShape.reset(nullptr);
4995 void DocxAttributeOutput::WritePostponedDMLDrawing()
4997 if (!m_pPostponedDMLDrawings)
4998 return;
5000 // Clear the list early, this method may be called recursively.
5001 std::unique_ptr< std::list<PostponedDrawing> > pPostponedDMLDrawings(m_pPostponedDMLDrawings.release());
5002 std::unique_ptr< std::list<PostponedOLE> > pPostponedOLEs(m_pPostponedOLEs.release());
5004 bool bStartedParaSdt = m_bStartedParaSdt;
5005 for( std::list< PostponedDrawing >::iterator it = pPostponedDMLDrawings->begin();
5006 it != pPostponedDMLDrawings->end();
5007 ++it )
5009 // Avoid w:drawing within another w:drawing.
5010 if ( IsAlternateContentChoiceOpen() && !( m_rExport.SdrExporter().IsDrawingOpen()) )
5011 m_rExport.SdrExporter().writeDMLDrawing(it->object, (it->frame), m_anchorId++);
5012 else
5013 m_rExport.SdrExporter().writeDMLAndVMLDrawing(it->object, *(it->frame), *(it->point), m_anchorId++);
5015 m_bStartedParaSdt = bStartedParaSdt;
5017 m_pPostponedOLEs = std::move(pPostponedOLEs);
5020 void DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame &rFrame, const Point& rNdTopLeft )
5022 m_pSerializer->mark(Tag_OutputFlyFrame);
5024 switch ( rFrame.GetWriterType() )
5026 case ww8::Frame::eGraphic:
5028 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
5029 const SwNode *pNode = rFrame.GetContent();
5030 const SwGrfNode *pGrfNode = pNode ? pNode->GetGrfNode() : nullptr;
5031 if ( pGrfNode )
5033 if (!m_pPostponedGraphic)
5035 m_bPostponedProcessingFly = false ;
5036 FlyFrameGraphic( pGrfNode, rFrame.GetLayoutSize(), nullptr, nullptr, pSdrObj);
5038 else // we are writing out attributes, but w:drawing should not be inside w:rPr,
5039 { // so write it out later
5040 m_bPostponedProcessingFly = true ;
5041 m_pPostponedGraphic->push_back(PostponedGraphic(pGrfNode, rFrame.GetLayoutSize(), nullptr, nullptr, pSdrObj));
5045 break;
5046 case ww8::Frame::eDrawing:
5048 const SdrObject* pSdrObj = rFrame.GetFrameFormat().FindRealSdrObject();
5049 if ( pSdrObj )
5051 if ( IsDiagram( pSdrObj ) )
5053 if ( !m_pPostponedDiagrams )
5055 m_bPostponedProcessingFly = false ;
5056 m_rExport.SdrExporter().writeDiagram( pSdrObj, rFrame.GetFrameFormat(), m_anchorId++);
5058 else // we are writing out attributes, but w:drawing should not be inside w:rPr,
5059 { // so write it out later
5060 m_bPostponedProcessingFly = true ;
5061 m_pPostponedDiagrams->push_back( PostponedDiagram( pSdrObj, &(rFrame.GetFrameFormat()) ));
5064 else
5066 if (!m_pPostponedDMLDrawings)
5068 bool bStartedParaSdt = m_bStartedParaSdt;
5069 if ( IsAlternateContentChoiceOpen() )
5071 // Do not write w:drawing inside w:drawing. Instead Postpone the Inner Drawing.
5072 if( m_rExport.SdrExporter().IsDrawingOpen() )
5073 m_pPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()), &rNdTopLeft));
5074 else
5075 m_rExport.SdrExporter().writeDMLDrawing( pSdrObj, &rFrame.GetFrameFormat(), m_anchorId++);
5077 else
5078 m_rExport.SdrExporter().writeDMLAndVMLDrawing( pSdrObj, rFrame.GetFrameFormat(), rNdTopLeft, m_anchorId++);
5079 m_bStartedParaSdt = bStartedParaSdt;
5081 m_bPostponedProcessingFly = false ;
5083 // IsAlternateContentChoiceOpen(): check is to ensure that only one object is getting added. Without this check, plus one object gets added
5084 // m_bParagraphFrameOpen: check if the frame is open.
5085 else if (IsAlternateContentChoiceOpen() && m_bParagraphFrameOpen)
5086 m_pPostponedCustomShape->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()), &rNdTopLeft));
5087 else
5089 // we are writing out attributes, but w:drawing should not be inside w:rPr, so write it out later
5090 m_bPostponedProcessingFly = true ;
5091 m_pPostponedDMLDrawings->push_back(PostponedDrawing(pSdrObj, &(rFrame.GetFrameFormat()), &rNdTopLeft));
5096 break;
5097 case ww8::Frame::eTextBox:
5099 // If this is a TextBox of a shape, then ignore: it's handled in WriteTextBox().
5100 if (DocxSdrExport::isTextBox(rFrame.GetFrameFormat()))
5101 break;
5103 // The frame output is postponed to the end of the anchor paragraph
5104 bool bDuplicate = false;
5105 const OUString& rName = rFrame.GetFrameFormat().GetName();
5106 unsigned nSize = m_aFramesOfParagraph.size();
5107 for( unsigned nIndex = 0; nIndex < nSize; ++nIndex )
5109 const OUString& rNameExisting = m_aFramesOfParagraph[nIndex].GetFrameFormat().GetName();
5111 if (!rName.isEmpty() && !rNameExisting.isEmpty())
5113 if (rName == rNameExisting)
5114 bDuplicate = true;
5118 if( !bDuplicate )
5120 m_bPostponedProcessingFly = true ;
5121 m_aFramesOfParagraph.push_back(ww8::Frame(rFrame));
5124 break;
5125 case ww8::Frame::eOle:
5127 const SwFrameFormat &rFrameFormat = rFrame.GetFrameFormat();
5128 const SdrObject *pSdrObj = rFrameFormat.FindRealSdrObject();
5129 if ( pSdrObj )
5131 SwNodeIndex aIdx(*rFrameFormat.GetContent().GetContentIdx(), 1);
5132 SwOLENode& rOLENd = *aIdx.GetNode().GetOLENode();
5133 WriteOLE2Obj( pSdrObj, rOLENd, rFrame.GetLayoutSize(), dynamic_cast<const SwFlyFrameFormat*>( &rFrameFormat ));
5134 m_bPostponedProcessingFly = false ;
5137 break;
5138 case ww8::Frame::eFormControl:
5140 const SdrObject* pObject = rFrame.GetFrameFormat().FindRealSdrObject();
5141 if(ExportAsActiveXControl(pObject))
5142 m_aPostponedActiveXControls.push_back(PostponedDrawing(pObject, &(rFrame.GetFrameFormat()), &rNdTopLeft));
5143 else
5144 m_aPostponedFormControls.push_back(pObject);
5145 m_bPostponedProcessingFly = true ;
5147 break;
5148 default:
5149 OSL_TRACE( "TODO DocxAttributeOutput::OutputFlyFrame_Impl( const ww8::Frame& rFrame, const Point& rNdTopLeft ) - frame type '%s'\n",
5150 rFrame.GetWriterType() == ww8::Frame::eTextBox? "eTextBox":
5151 ( rFrame.GetWriterType() == ww8::Frame::eOle? "eOle": "???" ) );
5152 break;
5155 m_pSerializer->mergeTopMarks(Tag_OutputFlyFrame, sax_fastparser::MergeMarks::POSTPONE);
5158 bool DocxAttributeOutput::IsDiagram( const SdrObject* sdrObject )
5160 uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(sdrObject)->getUnoShape(), uno::UNO_QUERY );
5161 if ( !xShape.is() )
5162 return false;
5164 uno::Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
5165 if ( !xPropSet.is() )
5166 return false;
5168 // if the shape doesn't have the InteropGrabBag property, it's not a diagram
5169 uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo();
5170 OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
5171 if ( !xPropSetInfo->hasPropertyByName( aName ) )
5172 return false;
5174 uno::Sequence< beans::PropertyValue > propList;
5175 xPropSet->getPropertyValue( aName ) >>= propList;
5176 for ( sal_Int32 nProp=0; nProp < propList.getLength(); ++nProp )
5178 // if we find any of the diagram components, it's a diagram
5179 OUString propName = propList[nProp].Name;
5180 if ( propName == "OOXData" || propName == "OOXLayout" || propName == "OOXStyle" ||
5181 propName == "OOXColor" || propName == "OOXDrawing")
5182 return true;
5184 return false;
5187 void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj)
5189 const EditTextObject& rEditObj = rParaObj.GetTextObject();
5190 MSWord_SdrAttrIter aAttrIter( m_rExport, rEditObj, TXT_HFTXTBOX );
5192 sal_Int32 nPara = rEditObj.GetParagraphCount();
5194 m_pSerializer->startElementNS( XML_w, XML_txbxContent, FSEND );
5195 for (sal_Int32 n = 0; n < nPara; ++n)
5197 if( n )
5198 aAttrIter.NextPara( n );
5200 OUString aStr( rEditObj.GetText( n ));
5201 sal_Int32 nAktPos = 0;
5202 sal_Int32 nEnd = aStr.getLength();
5204 StartParagraph(ww8::WW8TableNodeInfo::Pointer_t());
5206 // Write paragraph properties.
5207 StartParagraphProperties();
5208 aAttrIter.OutParaAttr(false);
5209 SfxItemSet aParagraphMarkerProperties(m_rExport.m_pDoc->GetAttrPool());
5210 EndParagraphProperties(aParagraphMarkerProperties, nullptr, nullptr, nullptr);
5212 do {
5213 const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
5215 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
5217 // Write run properties.
5218 m_pSerializer->startElementNS(XML_w, XML_rPr, FSEND);
5219 aAttrIter.OutAttr(nAktPos);
5220 WriteCollectedRunProperties();
5221 m_pSerializer->endElementNS(XML_w, XML_rPr);
5223 bool bTextAtr = aAttrIter.IsTextAttr( nAktPos );
5224 if( !bTextAtr )
5226 OUString aOut( aStr.copy( nAktPos, nNextAttr - nAktPos ) );
5227 RunText(aOut);
5230 m_pSerializer->endElementNS( XML_w, XML_r );
5232 nAktPos = nNextAttr;
5233 aAttrIter.NextPos();
5235 while( nAktPos < nEnd );
5236 // Word can't handle nested text boxes, so write them on the same level.
5237 ++m_nTextFrameLevel;
5238 EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t());
5239 --m_nTextFrameLevel;
5241 m_pSerializer->endElementNS( XML_w, XML_txbxContent );
5244 void DocxAttributeOutput::pushToTableExportContext(DocxTableExportContext& rContext)
5246 rContext.m_pTableInfo = m_rExport.m_pTableInfo;
5247 m_rExport.m_pTableInfo = std::make_shared<ww8::WW8TableInfo>();
5249 rContext.m_bTableCellOpen = m_tableReference->m_bTableCellOpen;
5250 m_tableReference->m_bTableCellOpen = false;
5252 rContext.m_nTableDepth = m_tableReference->m_nTableDepth;
5253 m_tableReference->m_nTableDepth = 0;
5256 void DocxAttributeOutput::popFromTableExportContext(DocxTableExportContext& rContext)
5258 m_rExport.m_pTableInfo = rContext.m_pTableInfo;
5259 m_tableReference->m_bTableCellOpen = rContext.m_bTableCellOpen;
5260 m_tableReference->m_nTableDepth = rContext.m_nTableDepth;
5263 void DocxAttributeOutput::WriteTextBox(uno::Reference<drawing::XShape> xShape)
5265 DocxTableExportContext aTableExportContext;
5266 pushToTableExportContext(aTableExportContext);
5268 SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
5269 const SwPosition* pAnchor = pTextBox->GetAnchor().GetContentAnchor();
5270 ww8::Frame aFrame(*pTextBox, *pAnchor);
5271 m_rExport.SdrExporter().writeDMLTextFrame(&aFrame, m_anchorId++, /*bTextBoxOnly=*/true);
5273 popFromTableExportContext(aTableExportContext);
5276 void DocxAttributeOutput::WriteVMLTextBox(uno::Reference<drawing::XShape> xShape)
5278 DocxTableExportContext aTableExportContext;
5279 pushToTableExportContext(aTableExportContext);
5281 SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
5282 const SwPosition* pAnchor = pTextBox->GetAnchor().GetContentAnchor();
5283 ww8::Frame aFrame(*pTextBox, *pAnchor);
5284 m_rExport.SdrExporter().writeVMLTextFrame(&aFrame, /*bTextBoxOnly=*/true);
5286 popFromTableExportContext(aTableExportContext);
5289 oox::drawingml::DrawingML& DocxAttributeOutput::GetDrawingML()
5291 return m_rDrawingML;
5294 /// Functor to do case-insensitive ordering of OUString instances.
5295 struct OUStringIgnoreCase
5297 bool operator() (const OUString& lhs, const OUString& rhs) const
5299 return lhs.compareToIgnoreAsciiCase(rhs) < 0;
5303 /// Guesses if a style created in Writer (no grab-bag) should be qFormat or not.
5304 static bool lcl_guessQFormat(const OUString& rName, sal_uInt16 nWwId)
5306 // If the style has no dedicated STI number, then it's probably a custom style -> qFormat.
5307 if (nWwId == ww::stiUser)
5308 return true;
5310 // Allow exported built-in styles UI language neutral
5311 if ( nWwId == ww::stiNormal ||
5312 ( nWwId>= ww::stiLev1 && nWwId <= ww::stiLev9 ) ||
5313 nWwId == ww::stiCaption || nWwId == ww::stiTitle ||
5314 nWwId == ww::stiSubtitle || nWwId == ww::stiStrong ||
5315 nWwId == ww::stiEmphasis )
5316 return true;
5318 static std::set<OUString, OUStringIgnoreCase> aWhitelist;
5319 if (aWhitelist.empty())
5321 aWhitelist.insert("No Spacing");
5322 aWhitelist.insert("List Paragraph");
5323 aWhitelist.insert("Quote");
5324 aWhitelist.insert("Intense Quote");
5325 aWhitelist.insert("Subtle Emphasis,");
5326 aWhitelist.insert("Intense Emphasis");
5327 aWhitelist.insert("Subtle Reference");
5328 aWhitelist.insert("Intense Reference");
5329 aWhitelist.insert("Book Title");
5330 aWhitelist.insert("TOC Heading");
5332 // Not custom style? Then we have a list of standard styles which should be qFormat.
5333 return aWhitelist.find(rName) != aWhitelist.end();
5336 void DocxAttributeOutput::StartStyle( const OUString& rName, StyleType eType,
5337 sal_uInt16 nBase, sal_uInt16 nNext, sal_uInt16 nWwId, sal_uInt16 nId, bool bAutoUpdate )
5339 bool bQFormat = false, bUnhideWhenUsed = false, bSemiHidden = false, bLocked = false, bDefault = false, bCustomStyle = false;
5340 OUString aLink, aRsid, aUiPriority;
5341 FastAttributeList* pStyleAttributeList = FastSerializerHelper::createAttrList();
5342 uno::Any aAny;
5343 if (eType == STYLE_TYPE_PARA || eType == STYLE_TYPE_CHAR)
5345 const SwFormat* pFormat = m_rExport.m_pStyles->GetSwFormat(nId);
5346 pFormat->GetGrabBagItem(aAny);
5348 else
5350 const SwNumRule* pRule = m_rExport.m_pStyles->GetSwNumRule(nId);
5351 pRule->GetGrabBagItem(aAny);
5353 const uno::Sequence<beans::PropertyValue>& rGrabBag = aAny.get< uno::Sequence<beans::PropertyValue> >();
5355 for (sal_Int32 i = 0; i < rGrabBag.getLength(); ++i)
5357 if (rGrabBag[i].Name == "uiPriority")
5358 aUiPriority = rGrabBag[i].Value.get<OUString>();
5359 else if (rGrabBag[i].Name == "qFormat")
5360 bQFormat = true;
5361 else if (rGrabBag[i].Name == "link")
5362 aLink = rGrabBag[i].Value.get<OUString>();
5363 else if (rGrabBag[i].Name == "rsid")
5364 aRsid = rGrabBag[i].Value.get<OUString>();
5365 else if (rGrabBag[i].Name == "unhideWhenUsed")
5366 bUnhideWhenUsed = true;
5367 else if (rGrabBag[i].Name == "semiHidden")
5368 bSemiHidden = true;
5369 else if (rGrabBag[i].Name == "locked")
5370 bLocked = true;
5371 else if (rGrabBag[i].Name == "default")
5372 bDefault = rGrabBag[i].Value.get<bool>();
5373 else if (rGrabBag[i].Name == "customStyle")
5374 bCustomStyle = rGrabBag[i].Value.get<bool>();
5375 else
5376 SAL_WARN("sw.ww8", "Unhandled style property: " << rGrabBag[i].Name);
5379 // MSO exports English names and writerfilter only recognize them.
5380 const sal_Char *pEnglishName = nullptr;
5381 const char* pType = nullptr;
5382 switch (eType)
5384 case STYLE_TYPE_PARA:
5385 pType = "paragraph";
5386 if ( nWwId < ww::stiMax)
5387 pEnglishName = ww::GetEnglishNameFromSti( static_cast<ww::sti>(nWwId ) );
5388 break;
5389 case STYLE_TYPE_CHAR: pType = "character"; break;
5390 case STYLE_TYPE_LIST: pType = "numbering"; break;
5392 pStyleAttributeList->add(FSNS( XML_w, XML_type ), pType);
5393 pStyleAttributeList->add(FSNS( XML_w, XML_styleId ), m_rExport.m_pStyles->GetStyleId(nId).getStr());
5394 if (bDefault)
5395 pStyleAttributeList->add(FSNS(XML_w, XML_default), "1");
5396 if (bCustomStyle)
5397 pStyleAttributeList->add(FSNS(XML_w, XML_customStyle), "1");
5398 XFastAttributeListRef xStyleAttributeList(pStyleAttributeList);
5399 m_pSerializer->startElementNS( XML_w, XML_style, xStyleAttributeList);
5400 m_pSerializer->singleElementNS( XML_w, XML_name,
5401 FSNS( XML_w, XML_val ), pEnglishName ? pEnglishName : OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ).getStr(),
5402 FSEND );
5404 if ( nBase != 0x0FFF && eType != STYLE_TYPE_LIST)
5406 m_pSerializer->singleElementNS( XML_w, XML_basedOn,
5407 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nBase).getStr(),
5408 FSEND );
5411 if ( nNext != nId && eType != STYLE_TYPE_LIST)
5413 m_pSerializer->singleElementNS( XML_w, XML_next,
5414 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nNext).getStr(),
5415 FSEND );
5418 if (!aLink.isEmpty())
5419 m_pSerializer->singleElementNS(XML_w, XML_link,
5420 FSNS(XML_w, XML_val), OUStringToOString(aLink, RTL_TEXTENCODING_UTF8).getStr(),
5421 FSEND);
5423 if ( bAutoUpdate )
5424 m_pSerializer->singleElementNS( XML_w, XML_autoRedefine, FSEND );
5426 if (!aUiPriority.isEmpty())
5427 m_pSerializer->singleElementNS(XML_w, XML_uiPriority,
5428 FSNS(XML_w, XML_val), OUStringToOString(aUiPriority, RTL_TEXTENCODING_UTF8).getStr(),
5429 FSEND);
5430 if (bSemiHidden)
5431 m_pSerializer->singleElementNS(XML_w, XML_semiHidden, FSEND);
5432 if (bUnhideWhenUsed)
5433 m_pSerializer->singleElementNS(XML_w, XML_unhideWhenUsed, FSEND);
5435 if (bQFormat || lcl_guessQFormat(rName, nWwId))
5436 m_pSerializer->singleElementNS(XML_w, XML_qFormat, FSEND);
5437 if (bLocked)
5438 m_pSerializer->singleElementNS(XML_w, XML_locked, FSEND);
5439 if (!aRsid.isEmpty())
5440 m_pSerializer->singleElementNS(XML_w, XML_rsid,
5441 FSNS(XML_w, XML_val), OUStringToOString(aRsid, RTL_TEXTENCODING_UTF8).getStr(),
5442 FSEND);
5445 void DocxAttributeOutput::EndStyle()
5447 m_pSerializer->endElementNS( XML_w, XML_style );
5450 void DocxAttributeOutput::StartStyleProperties( bool bParProp, sal_uInt16 /*nStyle*/ )
5452 if ( bParProp )
5454 m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );
5455 InitCollectedParagraphProperties();
5457 else
5459 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
5460 InitCollectedRunProperties();
5464 void DocxAttributeOutput::EndStyleProperties( bool bParProp )
5466 if ( bParProp )
5468 WriteCollectedParagraphProperties();
5470 // Merge the marks for the ordered elements
5471 m_pSerializer->mergeTopMarks(Tag_InitCollectedParagraphProperties);
5473 m_pSerializer->endElementNS( XML_w, XML_pPr );
5475 else
5477 WriteCollectedRunProperties();
5479 // Merge the marks for the ordered elements
5480 m_pSerializer->mergeTopMarks(Tag_InitCollectedRunProperties);
5482 m_pSerializer->endElementNS( XML_w, XML_rPr );
5486 namespace
5489 void lcl_OutlineLevel(sax_fastparser::FSHelperPtr const & pSerializer, sal_uInt16 nLevel)
5491 if (nLevel >= WW8ListManager::nMaxLevel)
5492 nLevel = WW8ListManager::nMaxLevel - 1;
5494 pSerializer->singleElementNS(XML_w, XML_outlineLvl,
5495 FSNS(XML_w, XML_val), OString::number(nLevel).getStr(),
5496 FSEND);
5501 void DocxAttributeOutput::OutlineNumbering(sal_uInt8 const /*nLvl*/)
5503 // Handled by ParaOutlineLevel() instead.
5506 void DocxAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem)
5508 if (rItem.GetValue() > 0)
5509 lcl_OutlineLevel(m_pSerializer, rItem.GetValue() - 1);
5512 void DocxAttributeOutput::PageBreakBefore( bool bBreak )
5514 if ( bBreak )
5515 m_pSerializer->singleElementNS( XML_w, XML_pageBreakBefore, FSEND );
5516 else
5517 m_pSerializer->singleElementNS( XML_w, XML_pageBreakBefore,
5518 FSNS( XML_w, XML_val ), "false",
5519 FSEND );
5522 void DocxAttributeOutput::SectionBreak( sal_uInt8 nC, const WW8_SepInfo* pSectionInfo )
5524 switch ( nC )
5526 case msword::ColumnBreak:
5527 // The column break should be output in the next paragraph...
5528 m_nColBreakStatus = COLBRK_POSTPONE;
5529 break;
5530 case msword::PageBreak:
5531 if ( pSectionInfo )
5533 // don't add section properties if this will be the first
5534 // paragraph in the document
5535 if ( !m_bParagraphOpened && !m_bIsFirstParagraph)
5537 // Create a dummy paragraph if needed
5538 m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
5539 m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );
5541 m_rExport.SectionProperties( *pSectionInfo );
5543 m_pSerializer->endElementNS( XML_w, XML_pPr );
5544 m_pSerializer->endElementNS( XML_w, XML_p );
5546 else
5548 // postpone the output of this; it has to be done inside the
5549 // paragraph properties, so remember it until then
5550 m_pSectionInfo.reset( new WW8_SepInfo( *pSectionInfo ));
5553 else if ( m_bParagraphOpened )
5555 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
5556 m_pSerializer->singleElementNS( XML_w, XML_br,
5557 FSNS( XML_w, XML_type ), "page", FSEND );
5558 m_pSerializer->endElementNS( XML_w, XML_r );
5560 else
5561 m_bPostponedPageBreak = true;
5563 break;
5564 default:
5565 OSL_TRACE( "Unknown section break to write: %d", nC );
5566 break;
5570 void DocxAttributeOutput::EndParaSdtBlock()
5572 if (m_bStartedParaSdt)
5574 // Paragraph-level SDT still open? Close it now.
5575 EndSdtBlock();
5576 m_bStartedParaSdt = false;
5580 void DocxAttributeOutput::StartSection()
5582 m_pSerializer->startElementNS( XML_w, XML_sectPr, FSEND );
5583 m_bOpenedSectPr = true;
5585 // Write the elements in the spec order
5586 static const sal_Int32 aOrder[] =
5588 FSNS( XML_w, XML_headerReference ),
5589 FSNS( XML_w, XML_footerReference ),
5590 FSNS( XML_w, XML_footnotePr ),
5591 FSNS( XML_w, XML_endnotePr ),
5592 FSNS( XML_w, XML_type ),
5593 FSNS( XML_w, XML_pgSz ),
5594 FSNS( XML_w, XML_pgMar ),
5595 FSNS( XML_w, XML_paperSrc ),
5596 FSNS( XML_w, XML_pgBorders ),
5597 FSNS( XML_w, XML_lnNumType ),
5598 FSNS( XML_w, XML_pgNumType ),
5599 FSNS( XML_w, XML_cols ),
5600 FSNS( XML_w, XML_formProt ),
5601 FSNS( XML_w, XML_vAlign ),
5602 FSNS( XML_w, XML_noEndnote ),
5603 FSNS( XML_w, XML_titlePg ),
5604 FSNS( XML_w, XML_textDirection ),
5605 FSNS( XML_w, XML_bidi ),
5606 FSNS( XML_w, XML_rtlGutter ),
5607 FSNS( XML_w, XML_docGrid ),
5608 FSNS( XML_w, XML_printerSettings ),
5609 FSNS( XML_w, XML_sectPrChange )
5612 // postpone the output so that we can later [in EndParagraphProperties()]
5613 // prepend the properties before the run
5614 sal_Int32 len = sizeof ( aOrder ) / sizeof( sal_Int32 );
5615 uno::Sequence< sal_Int32 > aSeqOrder( len );
5616 for ( sal_Int32 i = 0; i < len; i++ )
5617 aSeqOrder[i] = aOrder[i];
5619 m_pSerializer->mark(Tag_StartSection, aSeqOrder);
5620 m_bHadSectPr = true;
5623 void DocxAttributeOutput::EndSection()
5625 // Write the section properties
5626 if ( m_pSectionSpacingAttrList.is() )
5628 XFastAttributeListRef xAttrList( m_pSectionSpacingAttrList.get() );
5629 m_pSectionSpacingAttrList.clear();
5631 m_pSerializer->singleElementNS( XML_w, XML_pgMar, xAttrList );
5634 // Order the elements
5635 m_pSerializer->mergeTopMarks(Tag_StartSection);
5637 m_pSerializer->endElementNS( XML_w, XML_sectPr );
5638 m_bOpenedSectPr = false;
5641 void DocxAttributeOutput::SectionFormProtection( bool bProtected )
5643 if ( bProtected )
5644 m_pSerializer->singleElementNS( XML_w, XML_formProt,
5645 FSNS( XML_w, XML_val ), "true", FSEND );
5646 else
5647 m_pSerializer->singleElementNS( XML_w, XML_formProt,
5648 FSNS( XML_w, XML_val ), "false", FSEND );
5651 void DocxAttributeOutput::SectionLineNumbering( sal_uLong nRestartNo, const SwLineNumberInfo& rLnNumInfo )
5653 FastAttributeList* pAttr = FastSerializerHelper::createAttrList();
5654 pAttr->add( FSNS( XML_w, XML_countBy ), OString::number(rLnNumInfo.GetCountBy()).getStr());
5655 pAttr->add( FSNS( XML_w, XML_restart ), rLnNumInfo.IsRestartEachPage() ? "newPage" : "continuous" );
5656 if( rLnNumInfo.GetPosFromLeft())
5657 pAttr->add( FSNS( XML_w, XML_distance ), OString::number(rLnNumInfo.GetPosFromLeft()).getStr());
5658 if( nRestartNo )
5659 pAttr->add( FSNS( XML_w, XML_start ), OString::number( nRestartNo).getStr());
5660 XFastAttributeListRef xAttrs( pAttr );
5661 m_pSerializer->singleElementNS( XML_w, XML_lnNumType, xAttrs );
5664 void DocxAttributeOutput::SectionTitlePage()
5666 m_pSerializer->singleElementNS( XML_w, XML_titlePg, FSEND );
5669 void DocxAttributeOutput::SectionPageBorders( const SwFrameFormat* pFormat, const SwFrameFormat* /*pFirstPageFormat*/ )
5671 // Output the margins
5673 const SvxBoxItem& rBox = pFormat->GetBox( );
5675 const SvxBorderLine* pLeft = rBox.GetLeft( );
5676 const SvxBorderLine* pTop = rBox.GetTop( );
5677 const SvxBorderLine* pRight = rBox.GetRight( );
5678 const SvxBorderLine* pBottom = rBox.GetBottom( );
5680 if ( pBottom || pTop || pLeft || pRight )
5682 OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions();
5684 // Check if there is a shadow item
5685 const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW );
5686 if ( pItem )
5688 const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
5689 aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation();
5692 // By top margin, impl_borders() means the distance between the top of the page and the header frame.
5693 editeng::WordPageMargins aMargins = m_pageMargins;
5694 HdFtDistanceGlue aGlue(pFormat->GetAttrSet());
5695 if (aGlue.HasHeader())
5696 aMargins.nTop = aGlue.dyaHdrTop;
5697 // Ditto for bottom margin.
5698 if (aGlue.HasFooter())
5699 aMargins.nBottom = aGlue.dyaHdrBottom;
5701 aOutputBorderOptions.pDistances = std::make_shared<editeng::WordBorderDistances>();
5702 editeng::BorderDistancesToWord(rBox, aMargins, *aOutputBorderOptions.pDistances);
5704 // All distances are relative to the text margins
5705 m_pSerializer->startElementNS(XML_w, XML_pgBorders,
5706 FSNS(XML_w, XML_display), "allPages",
5707 FSNS(XML_w, XML_offsetFrom), aOutputBorderOptions.pDistances->bFromEdge ? "page" : "text",
5708 FSEND);
5710 std::map<SvxBoxItemLine, css::table::BorderLine2> aEmptyMap; // empty styles map
5711 impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aEmptyMap );
5713 m_pSerializer->endElementNS( XML_w, XML_pgBorders );
5717 void DocxAttributeOutput::SectionBiDi( bool bBiDi )
5719 if ( bBiDi )
5720 m_pSerializer->singleElementNS( XML_w, XML_bidi, FSEND );
5723 static OString impl_NumberingType( sal_uInt16 nNumberingType )
5725 OString aType;
5727 switch ( nNumberingType )
5729 case SVX_NUM_CHARS_UPPER_LETTER:
5730 case SVX_NUM_CHARS_UPPER_LETTER_N: aType = "upperLetter"; break;
5731 case SVX_NUM_CHARS_LOWER_LETTER:
5732 case SVX_NUM_CHARS_LOWER_LETTER_N: aType = "lowerLetter"; break;
5733 case SVX_NUM_ROMAN_UPPER: aType = "upperRoman"; break;
5734 case SVX_NUM_ROMAN_LOWER: aType = "lowerRoman"; break;
5736 case SVX_NUM_ARABIC: aType = "decimal"; break;
5738 case SVX_NUM_BITMAP:
5739 case SVX_NUM_CHAR_SPECIAL: aType = "bullet"; break;
5740 case style::NumberingType::CHARS_HEBREW: aType = "hebrew1"; break;
5742 default: aType = "none"; break;
5745 return aType;
5748 // Converting Level Numbering Format Code to string
5749 static OString impl_LevelNFC( sal_uInt16 nNumberingType , const SfxItemSet *pOutSet)
5751 OString aType;
5753 switch ( nNumberingType )
5755 case style::NumberingType::CHARS_UPPER_LETTER:
5756 case style::NumberingType::CHARS_UPPER_LETTER_N:
5757 case style::NumberingType::CHARS_LOWER_LETTER:
5758 case style::NumberingType::CHARS_LOWER_LETTER_N:
5759 case style::NumberingType::ROMAN_UPPER:
5760 case style::NumberingType::ROMAN_LOWER:
5761 case style::NumberingType::ARABIC:
5762 case style::NumberingType::BITMAP:
5763 case style::NumberingType::CHAR_SPECIAL:
5764 case style::NumberingType::CHARS_HEBREW:
5765 case style::NumberingType::NUMBER_NONE:
5766 return impl_NumberingType( nNumberingType );
5767 case style::NumberingType::FULLWIDTH_ARABIC: aType="decimalFullWidth"; break;
5768 case style::NumberingType::TIAN_GAN_ZH: aType="ideographTraditional"; break;
5769 case style::NumberingType::DI_ZI_ZH: aType="ideographZodiac"; break;
5770 case style::NumberingType::NUMBER_LOWER_ZH:
5771 aType="taiwaneseCountingThousand";
5772 if (pOutSet) {
5773 const SvxLanguageItem rLang = static_cast<const SvxLanguageItem&>( pOutSet->Get( RES_CHRATR_CJK_LANGUAGE) );
5774 const LanguageType eLang = rLang.GetLanguage();
5776 if (LANGUAGE_CHINESE_SIMPLIFIED == eLang) {
5777 aType="chineseCountingThousand";
5780 break;
5781 case style::NumberingType::NUMBER_UPPER_ZH_TW: aType="ideographLegalTraditional";break;
5782 case style::NumberingType::NUMBER_UPPER_ZH: aType="chineseLegalSimplified"; break;
5783 case style::NumberingType::NUMBER_TRADITIONAL_JA: aType="japaneseLegal";break;
5784 case style::NumberingType::AIU_FULLWIDTH_JA: aType="aiueoFullWidth";break;
5785 case style::NumberingType::AIU_HALFWIDTH_JA: aType="aiueo";break;
5786 case style::NumberingType::IROHA_FULLWIDTH_JA: aType="iroha";break;
5787 case style::NumberingType::IROHA_HALFWIDTH_JA: aType="irohaFullWidth";break;
5788 case style::NumberingType::HANGUL_SYLLABLE_KO: aType="ganada";break;
5789 case style::NumberingType::HANGUL_JAMO_KO: aType="chosung";break;
5790 case style::NumberingType::NUMBER_HANGUL_KO: aType="koreanDigital";break;
5791 case style::NumberingType::NUMBER_UPPER_KO: aType="koreanLegal"; break;
5792 case style::NumberingType::CIRCLE_NUMBER: aType="decimalEnclosedCircle"; break;
5793 case style::NumberingType::CHARS_ARABIC: aType="arabicAlpha"; break;
5794 case style::NumberingType::CHARS_THAI: aType="thaiLetters"; break;
5795 case style::NumberingType::CHARS_PERSIAN: aType="hindiVowels"; break;
5797 Fallback the rest to decimal.
5798 case style::NumberingType::NATIVE_NUMBERING:
5799 case style::NumberingType::HANGUL_CIRCLED_JAMO_KO:
5800 case style::NumberingType::HANGUL_CIRCLED_SYLLABLE_KO:
5801 case style::NumberingType::CHARS_GREEK_UPPER_LETTER:
5802 case style::NumberingType::CHARS_GREEK_LOWER_LETTER:
5803 case style::NumberingType::PAGE_DESCRIPTOR:
5804 case style::NumberingType::TRANSLITERATION:
5805 case style::NumberingType::CHARS_NEPALI:
5806 case style::NumberingType::CHARS_KHMER:
5807 case style::NumberingType::CHARS_LAO:
5808 case style::NumberingType::CHARS_TIBETAN:
5809 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_BG:
5810 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_BG:
5811 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_BG:
5812 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_BG:
5813 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_RU:
5814 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_RU:
5815 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_RU:
5816 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_RU:
5817 case style::NumberingType::CHARS_MYANMAR:
5818 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_SR:
5819 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_SR:
5820 case style::NumberingType::CHARS_CYRILLIC_UPPER_LETTER_N_SR:
5821 case style::NumberingType::CHARS_CYRILLIC_LOWER_LETTER_N_SR:
5823 default:
5824 aType = "decimal"; break;
5826 return aType;
5830 void DocxAttributeOutput::SectionPageNumbering( sal_uInt16 nNumType, const ::boost::optional<sal_uInt16>& oPageRestartNumber )
5832 // FIXME Not called properly with page styles like "First Page"
5834 FastAttributeList* pAttr = FastSerializerHelper::createAttrList();
5836 // boost::none means no restart: then don't output that attribute if it is negative
5837 if ( oPageRestartNumber )
5838 pAttr->add( FSNS( XML_w, XML_start ), OString::number( oPageRestartNumber.get() ) );
5840 // nNumType corresponds to w:fmt. See WW8Export::GetNumId() for more precisions
5841 OString aFormat( impl_NumberingType( nNumType ) );
5842 if ( !aFormat.isEmpty() )
5843 pAttr->add( FSNS( XML_w, XML_fmt ), aFormat.getStr() );
5845 XFastAttributeListRef xAttrs( pAttr );
5846 m_pSerializer->singleElementNS( XML_w, XML_pgNumType, xAttrs );
5848 // see 2.6.12 pgNumType (Page Numbering Settings)
5849 OSL_TRACE( "TODO DocxAttributeOutput::SectionPageNumbering()" );
5852 void DocxAttributeOutput::SectionType( sal_uInt8 nBreakCode )
5854 /* break code: 0 No break, 1 New column
5855 2 New page, 3 Even page, 4 Odd page
5857 const char* pType = nullptr;
5858 switch ( nBreakCode )
5860 case 1: pType = "nextColumn"; break;
5861 case 2: pType = "nextPage"; break;
5862 case 3: pType = "evenPage"; break;
5863 case 4: pType = "oddPage"; break;
5864 default: pType = "continuous"; break;
5867 if ( pType )
5868 m_pSerializer->singleElementNS( XML_w, XML_type,
5869 FSNS( XML_w, XML_val ), pType,
5870 FSEND );
5873 void DocxAttributeOutput::TextVerticalAdjustment( const drawing::TextVerticalAdjust nVA )
5875 switch( nVA )
5877 case drawing::TextVerticalAdjust_CENTER:
5878 m_pSerializer->singleElementNS( XML_w, XML_vAlign,
5879 FSNS( XML_w, XML_val ), "center", FSEND );
5880 break;
5881 case drawing::TextVerticalAdjust_BOTTOM:
5882 m_pSerializer->singleElementNS( XML_w, XML_vAlign,
5883 FSNS( XML_w, XML_val ), "bottom", FSEND );
5884 break;
5885 case drawing::TextVerticalAdjust_BLOCK: //justify
5886 m_pSerializer->singleElementNS( XML_w, XML_vAlign,
5887 FSNS( XML_w, XML_val ), "both", FSEND );
5888 break;
5889 default:
5890 break;
5894 void DocxAttributeOutput::StartFont( const OUString& rFamilyName ) const
5896 m_pSerializer->startElementNS( XML_w, XML_font,
5897 FSNS( XML_w, XML_name ), OUStringToOString( rFamilyName, RTL_TEXTENCODING_UTF8 ).getStr(),
5898 FSEND );
5901 void DocxAttributeOutput::EndFont() const
5903 m_pSerializer->endElementNS( XML_w, XML_font );
5906 void DocxAttributeOutput::FontAlternateName( const OUString& rName ) const
5908 m_pSerializer->singleElementNS( XML_w, XML_altName,
5909 FSNS( XML_w, XML_val ), OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ).getStr(),
5910 FSEND );
5913 void DocxAttributeOutput::FontCharset( sal_uInt8 nCharSet, rtl_TextEncoding nEncoding ) const
5915 FastAttributeList* pAttr = FastSerializerHelper::createAttrList();
5917 OString aCharSet( OString::number( nCharSet, 16 ) );
5918 if ( aCharSet.getLength() == 1 )
5919 aCharSet = OString( "0" ) + aCharSet;
5920 pAttr->add( FSNS( XML_w, XML_val ), aCharSet.getStr());
5922 if( GetExport().GetFilter().getVersion( ) != oox::core::ECMA_DIALECT )
5924 if( const char* charset = rtl_getMimeCharsetFromTextEncoding( nEncoding ))
5925 pAttr->add( FSNS( XML_w, XML_characterSet ), charset );
5928 m_pSerializer->singleElementNS( XML_w, XML_charset, XFastAttributeListRef( pAttr ));
5931 void DocxAttributeOutput::FontFamilyType( FontFamily eFamily ) const
5933 const char *pFamily = nullptr;
5934 switch ( eFamily )
5936 case FAMILY_ROMAN: pFamily = "roman"; break;
5937 case FAMILY_SWISS: pFamily = "swiss"; break;
5938 case FAMILY_MODERN: pFamily = "modern"; break;
5939 case FAMILY_SCRIPT: pFamily = "script"; break;
5940 case FAMILY_DECORATIVE: pFamily = "decorative"; break;
5941 default: pFamily = "auto"; break; // no font family
5944 if ( pFamily )
5945 m_pSerializer->singleElementNS( XML_w, XML_family,
5946 FSNS( XML_w, XML_val ), pFamily,
5947 FSEND );
5950 void DocxAttributeOutput::FontPitchType( FontPitch ePitch ) const
5952 const char *pPitch = nullptr;
5953 switch ( ePitch )
5955 case PITCH_VARIABLE: pPitch = "variable"; break;
5956 case PITCH_FIXED: pPitch = "fixed"; break;
5957 default: pPitch = "default"; break; // no info about the pitch
5960 if ( pPitch )
5961 m_pSerializer->singleElementNS( XML_w, XML_pitch,
5962 FSNS( XML_w, XML_val ), pPitch,
5963 FSEND );
5966 void DocxAttributeOutput::EmbedFont( const OUString& name, FontFamily family, FontPitch pitch, rtl_TextEncoding encoding )
5968 if( !m_rExport.m_pDoc->getIDocumentSettingAccess().get( DocumentSettingId::EMBED_FONTS ))
5969 return; // no font embedding with this document
5970 EmbedFontStyle( name, XML_embedRegular, family, ITALIC_NONE, WEIGHT_NORMAL, pitch, encoding );
5971 EmbedFontStyle( name, XML_embedBold, family, ITALIC_NONE, WEIGHT_BOLD, pitch, encoding );
5972 EmbedFontStyle( name, XML_embedItalic, family, ITALIC_NORMAL, WEIGHT_NORMAL, pitch, encoding );
5973 EmbedFontStyle( name, XML_embedBoldItalic, family, ITALIC_NORMAL, WEIGHT_BOLD, pitch, encoding );
5976 static inline char toHexChar( int value )
5978 return value >= 10 ? value + 'A' - 10 : value + '0';
5981 void DocxAttributeOutput::EmbedFontStyle( const OUString& name, int tag, FontFamily family, FontItalic italic,
5982 FontWeight weight, FontPitch pitch, rtl_TextEncoding encoding )
5984 // Embed font if at least viewing is allowed (in which case the opening app must check
5985 // the font license rights too and open either read-only or not use the font for editing).
5986 OUString fontUrl = EmbeddedFontsHelper::fontFileUrl( name, family, italic, weight, pitch, encoding,
5987 EmbeddedFontsHelper::FontRights::ViewingAllowed );
5988 if( fontUrl.isEmpty())
5989 return;
5990 // TODO IDocumentSettingAccess::EMBED_SYSTEM_FONTS
5991 if( !fontFilesMap.count( fontUrl ))
5993 osl::File file( fontUrl );
5994 if( file.open( osl_File_OpenFlag_Read ) != osl::File::E_None )
5995 return;
5996 uno::Reference< css::io::XOutputStream > xOutStream = m_rExport.GetFilter().openFragmentStream(
5997 "word/fonts/font" + OUString::number(m_nextFontId) + ".odttf",
5998 "application/vnd.openxmlformats-officedocument.obfuscatedFont" );
5999 // Not much point in trying hard with the obfuscation key, whoever reads the spec can read the font anyway,
6000 // so just alter the first and last part of the key.
6001 char fontKeyStr[] = "{00014A78-CABC-4EF0-12AC-5CD89AEFDE00}";
6002 sal_uInt8 fontKey[ 16 ] = { 0, 0xDE, 0xEF, 0x9A, 0xD8, 0x5C, 0xAC, 0x12, 0xF0, 0x4E,
6003 0xBC, 0xCA, 0x78, 0x4A, 0x01, 0 };
6004 fontKey[ 0 ] = fontKey[ 15 ] = m_nextFontId % 256;
6005 fontKeyStr[ 1 ] = fontKeyStr[ 35 ] = toHexChar(( m_nextFontId % 256 ) / 16 );
6006 fontKeyStr[ 2 ] = fontKeyStr[ 36 ] = toHexChar(( m_nextFontId % 256 ) % 16 );
6007 char buffer[ 4096 ];
6008 sal_uInt64 readSize;
6009 file.read( buffer, 32, readSize );
6010 if( readSize < 32 )
6012 SAL_WARN( "sw.ww8", "Font file size too small (" << fontUrl << ")" );
6013 xOutStream->closeOutput();
6014 return;
6016 for( int i = 0;
6017 i < 16;
6018 ++i )
6020 buffer[ i ] ^= fontKey[ i ];
6021 buffer[ i + 16 ] ^= fontKey[ i ];
6023 xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), 32 ));
6024 for(;;)
6026 sal_Bool eof;
6027 if( file.isEndOfFile( &eof ) != osl::File::E_None )
6029 SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl );
6030 xOutStream->closeOutput();
6031 return;
6033 if( eof )
6034 break;
6035 if( file.read( buffer, 4096, readSize ) != osl::File::E_None )
6037 SAL_WARN( "sw.ww8", "Error reading font file " << fontUrl );
6038 xOutStream->closeOutput();
6039 return;
6041 if( readSize == 0 )
6042 break;
6043 xOutStream->writeBytes( uno::Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( buffer ), readSize ));
6045 xOutStream->closeOutput();
6046 OString relId = OUStringToOString( GetExport().GetFilter().addRelation( m_pSerializer->getOutputStream(),
6047 "http://schemas.openxmlformats.org/officeDocument/2006/relationships/font",
6048 "fonts/font" + OUString::number( m_nextFontId ) + ".odttf" ), RTL_TEXTENCODING_UTF8 );
6049 EmbeddedFontRef ref;
6050 ref.relId = relId;
6051 ref.fontKey = fontKeyStr;
6052 fontFilesMap[ fontUrl ] = ref;
6053 ++m_nextFontId;
6055 m_pSerializer->singleElementNS( XML_w, tag,
6056 FSNS( XML_r, XML_id ), fontFilesMap[ fontUrl ].relId,
6057 FSNS( XML_w, XML_fontKey ), fontFilesMap[ fontUrl ].fontKey,
6058 FSEND );
6061 OString DocxAttributeOutput::TransHighlightColor( sal_uInt8 nIco )
6063 switch (nIco)
6065 case 1: return OString("black"); break;
6066 case 2: return OString("blue"); break;
6067 case 3: return OString("cyan"); break;
6068 case 4: return OString("green"); break;
6069 case 5: return OString("magenta"); break;
6070 case 6: return OString("red"); break;
6071 case 7: return OString("yellow"); break;
6072 case 8: return OString("white"); break;
6073 case 9: return OString("darkBlue"); break;
6074 case 10: return OString("darkCyan"); break;
6075 case 11: return OString("darkGreen"); break;
6076 case 12: return OString("darkMagenta"); break;
6077 case 13: return OString("darkRed"); break;
6078 case 14: return OString("darkYellow"); break;
6079 case 15: return OString("darkGray"); break;
6080 case 16: return OString("lightGray"); break;
6081 default: return OString(); break;
6085 void DocxAttributeOutput::NumberingDefinition( sal_uInt16 nId, const SwNumRule &rRule )
6087 // nId is the same both for abstract numbering definition as well as the
6088 // numbering definition itself
6089 // TODO check that this is actually true & fix if not ;-)
6090 OString aId( OString::number( nId ) );
6092 m_pSerializer->startElementNS( XML_w, XML_num,
6093 FSNS( XML_w, XML_numId ), aId.getStr(),
6094 FSEND );
6096 m_pSerializer->singleElementNS( XML_w, XML_abstractNumId,
6097 FSNS( XML_w, XML_val ), aId.getStr(),
6098 FSEND );
6100 #if OSL_DEBUG_LEVEL > 1
6101 // TODO ww8 version writes this, anything to do about it here?
6102 if ( rRule.IsContinusNum() )
6103 OSL_TRACE( "TODO DocxAttributeOutput::NumberingDefinition()" );
6104 #else
6105 (void) rRule; // to quiet the warning...
6106 #endif
6108 m_pSerializer->endElementNS( XML_w, XML_num );
6111 void DocxAttributeOutput::StartAbstractNumbering( sal_uInt16 nId )
6113 const SwNumRule* pRule = (*m_rExport.m_pUsedNumTable)[nId - 1];
6114 m_bExportingOutline = pRule && pRule->IsOutlineRule();
6115 m_pSerializer->startElementNS( XML_w, XML_abstractNum,
6116 FSNS( XML_w, XML_abstractNumId ), OString::number( nId ).getStr(),
6117 FSEND );
6120 void DocxAttributeOutput::EndAbstractNumbering()
6122 m_pSerializer->endElementNS( XML_w, XML_abstractNum );
6125 void DocxAttributeOutput::NumberingLevel( sal_uInt8 nLevel,
6126 sal_uInt16 nStart,
6127 sal_uInt16 nNumberingType,
6128 SvxAdjust eAdjust,
6129 const sal_uInt8 * /*pNumLvlPos*/,
6130 sal_uInt8 nFollow,
6131 const wwFont *pFont,
6132 const SfxItemSet *pOutSet,
6133 sal_Int16 nIndentAt,
6134 sal_Int16 nFirstLineIndex,
6135 sal_Int16 nListTabPos,
6136 const OUString &rNumberingString,
6137 const SvxBrushItem* pBrush)
6139 m_pSerializer->startElementNS( XML_w, XML_lvl,
6140 FSNS( XML_w, XML_ilvl ), OString::number( nLevel ).getStr(),
6141 FSEND );
6143 // start with the nStart value. Do not write w:start if Numbered Lists
6144 // starts from zero.As it's an optional parameter.
6145 // refer ECMA 376 Second edition Part-1
6146 if(!(0 == nLevel && 0 == nStart))
6148 m_pSerializer->singleElementNS( XML_w, XML_start,
6149 FSNS( XML_w, XML_val ), OString::number( nStart ).getStr(),
6150 FSEND );
6153 if (m_bExportingOutline)
6155 sal_uInt16 nId = m_rExport.m_pStyles->GetHeadingParagraphStyleId( nLevel );
6156 if ( nId != SAL_MAX_UINT16 )
6157 m_pSerializer->singleElementNS( XML_w, XML_pStyle ,
6158 FSNS( XML_w, XML_val ), m_rExport.m_pStyles->GetStyleId(nId).getStr(),
6159 FSEND );
6161 // format
6162 OString aFormat( impl_LevelNFC( nNumberingType ,pOutSet) );
6164 if ( !aFormat.isEmpty() )
6165 m_pSerializer->singleElementNS( XML_w, XML_numFmt,
6166 FSNS( XML_w, XML_val ), aFormat.getStr(),
6167 FSEND );
6169 // suffix
6170 const char *pSuffix = nullptr;
6171 switch ( nFollow )
6173 case 1: pSuffix = "space"; break;
6174 case 2: pSuffix = "nothing"; break;
6175 default: /*pSuffix = "tab";*/ break;
6177 if ( pSuffix )
6178 m_pSerializer->singleElementNS( XML_w, XML_suff,
6179 FSNS( XML_w, XML_val ), pSuffix,
6180 FSEND );
6182 // text
6183 OUStringBuffer aBuffer( rNumberingString.getLength() + WW8ListManager::nMaxLevel );
6185 const sal_Unicode *pPrev = rNumberingString.getStr();
6186 const sal_Unicode *pIt = rNumberingString.getStr();
6187 while ( pIt < rNumberingString.getStr() + rNumberingString.getLength() )
6189 // convert the level values to %NUMBER form
6190 // (we don't use pNumLvlPos at all)
6191 // FIXME so far we support the ww8 limit of levels only
6192 if ( *pIt < sal_Unicode( WW8ListManager::nMaxLevel ) )
6194 aBuffer.append( pPrev, pIt - pPrev );
6195 aBuffer.append( '%' );
6196 aBuffer.append( OUString::number( sal_Int32( *pIt ) + 1 ) );
6198 pPrev = pIt + 1;
6200 ++pIt;
6202 if ( pPrev < pIt )
6203 aBuffer.append( pPrev, pIt - pPrev );
6205 // If bullet char is empty, set lvlText as empty
6206 if ( rNumberingString == OUStringLiteral1(0) && nNumberingType == SVX_NUM_CHAR_SPECIAL )
6208 m_pSerializer->singleElementNS( XML_w, XML_lvlText, FSNS( XML_w, XML_val ), "", FSEND );
6210 else
6212 // Writer's "zero width space" suffix is necessary, so that LabelFollowedBy shows up, but Word doesn't require that.
6213 OUString aLevelText = aBuffer.makeStringAndClear();
6214 static OUString aZeroWidthSpace(static_cast<sal_Unicode>(0x200B));
6215 if (aLevelText == aZeroWidthSpace)
6216 aLevelText.clear();
6217 m_pSerializer->singleElementNS(XML_w, XML_lvlText, FSNS(XML_w, XML_val), aLevelText.toUtf8(), FSEND);
6220 // bullet
6221 if (nNumberingType == SVX_NUM_BITMAP && pBrush)
6223 int nIndex = m_rExport.GetGrfIndex(*pBrush);
6224 if (nIndex != -1)
6226 m_pSerializer->singleElementNS(XML_w, XML_lvlPicBulletId,
6227 FSNS(XML_w, XML_val), OString::number(nIndex).getStr(),
6228 FSEND);
6232 // justification
6233 const char *pJc;
6234 bool ecmaDialect = ( m_rExport.GetFilter().getVersion() == oox::core::ECMA_DIALECT );
6235 switch ( eAdjust )
6237 case SVX_ADJUST_CENTER: pJc = "center"; break;
6238 case SVX_ADJUST_RIGHT: pJc = !ecmaDialect ? "end" : "right"; break;
6239 default: pJc = !ecmaDialect ? "start" : "left"; break;
6241 m_pSerializer->singleElementNS( XML_w, XML_lvlJc,
6242 FSNS( XML_w, XML_val ), pJc,
6243 FSEND );
6245 // indentation
6246 m_pSerializer->startElementNS( XML_w, XML_pPr, FSEND );
6247 if( nListTabPos != 0 )
6249 m_pSerializer->startElementNS( XML_w, XML_tabs, FSEND );
6250 m_pSerializer->singleElementNS( XML_w, XML_tab,
6251 FSNS( XML_w, XML_val ), "num",
6252 FSNS( XML_w, XML_pos ), OString::number( nListTabPos ).getStr(),
6253 FSEND );
6254 m_pSerializer->endElementNS( XML_w, XML_tabs );
6257 sal_Int32 nToken = ecmaDialect ? XML_left : XML_start;
6258 m_pSerializer->singleElementNS( XML_w, XML_ind,
6259 FSNS( XML_w, nToken ), OString::number( nIndentAt ).getStr(),
6260 FSNS( XML_w, XML_hanging ), OString::number( -nFirstLineIndex ).getStr(),
6261 FSEND );
6262 m_pSerializer->endElementNS( XML_w, XML_pPr );
6264 // font
6265 if ( pOutSet )
6267 m_pSerializer->startElementNS( XML_w, XML_rPr, FSEND );
6269 if ( pFont )
6271 GetExport().GetId( *pFont ); // ensure font info is written to fontTable.xml
6272 OString aFamilyName( OUStringToOString( OUString( pFont->GetFamilyName() ), RTL_TEXTENCODING_UTF8 ) );
6273 m_pSerializer->singleElementNS( XML_w, XML_rFonts,
6274 FSNS( XML_w, XML_ascii ), aFamilyName.getStr(),
6275 FSNS( XML_w, XML_hAnsi ), aFamilyName.getStr(),
6276 FSNS( XML_w, XML_cs ), aFamilyName.getStr(),
6277 FSNS( XML_w, XML_hint ), "default",
6278 FSEND );
6280 m_rExport.OutputItemSet( *pOutSet, false, true, i18n::ScriptType::LATIN, m_rExport.m_bExportModeRTF );
6282 WriteCollectedRunProperties();
6284 m_pSerializer->endElementNS( XML_w, XML_rPr );
6287 // TODO anything to do about nListTabPos?
6289 m_pSerializer->endElementNS( XML_w, XML_lvl );
6292 void DocxAttributeOutput::CharCaseMap( const SvxCaseMapItem& rCaseMap )
6294 switch ( rCaseMap.GetValue() )
6296 case SVX_CASEMAP_KAPITAELCHEN:
6297 m_pSerializer->singleElementNS( XML_w, XML_smallCaps, FSEND );
6298 break;
6299 case SVX_CASEMAP_VERSALIEN:
6300 m_pSerializer->singleElementNS( XML_w, XML_caps, FSEND );
6301 break;
6302 default: // Something that ooxml does not support
6303 m_pSerializer->singleElementNS( XML_w, XML_smallCaps, FSNS( XML_w, XML_val ), "false", FSEND );
6304 m_pSerializer->singleElementNS( XML_w, XML_caps, FSNS( XML_w, XML_val ), "false", FSEND );
6305 break;
6309 void DocxAttributeOutput::CharColor( const SvxColorItem& rColor )
6311 const Color aColor( rColor.GetValue() );
6312 OString aColorString;
6314 aColorString = msfilter::util::ConvertColor( aColor );
6316 const char* pExistingValue(nullptr);
6317 if (m_pColorAttrList.is() && m_pColorAttrList->getAsChar(FSNS(XML_w, XML_val), pExistingValue))
6319 assert(aColorString.equalsL(pExistingValue, rtl_str_getLength(pExistingValue)));
6320 return;
6323 AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_val ), aColorString.getStr() );
6326 void DocxAttributeOutput::CharContour( const SvxContourItem& rContour )
6328 if ( rContour.GetValue() )
6329 m_pSerializer->singleElementNS( XML_w, XML_outline, FSEND );
6330 else
6331 m_pSerializer->singleElementNS( XML_w, XML_outline, FSNS( XML_w, XML_val ), "false", FSEND );
6334 void DocxAttributeOutput::CharCrossedOut( const SvxCrossedOutItem& rCrossedOut )
6336 switch ( rCrossedOut.GetStrikeout() )
6338 case STRIKEOUT_DOUBLE:
6339 m_pSerializer->singleElementNS( XML_w, XML_dstrike, FSEND );
6340 break;
6341 case STRIKEOUT_NONE:
6342 m_pSerializer->singleElementNS( XML_w, XML_dstrike, FSNS( XML_w, XML_val ), "false", FSEND );
6343 m_pSerializer->singleElementNS( XML_w, XML_strike, FSNS( XML_w, XML_val ), "false", FSEND );
6344 break;
6345 default:
6346 m_pSerializer->singleElementNS( XML_w, XML_strike, FSEND );
6347 break;
6351 void DocxAttributeOutput::CharEscapement( const SvxEscapementItem& rEscapement )
6353 OString sIss;
6354 short nEsc = rEscapement.GetEsc(), nProp = rEscapement.GetProportionalHeight();
6355 if ( !nEsc )
6357 sIss = OString( "baseline" );
6358 nEsc = 0;
6359 nProp = 100;
6361 else if ( DFLT_ESC_PROP == nProp )
6363 if ( DFLT_ESC_SUB == nEsc || DFLT_ESC_AUTO_SUB == nEsc )
6364 sIss = OString( "subscript" );
6365 else if ( DFLT_ESC_SUPER == nEsc || DFLT_ESC_AUTO_SUPER == nEsc )
6366 sIss = OString( "superscript" );
6369 if ( !sIss.isEmpty() )
6370 m_pSerializer->singleElementNS( XML_w, XML_vertAlign,
6371 FSNS( XML_w, XML_val ), sIss.getStr(), FSEND );
6373 const SvxFontHeightItem& rItem = static_cast<const SvxFontHeightItem&>(m_rExport.GetItem(RES_CHRATR_FONTSIZE));
6374 if (sIss.isEmpty() || sIss.match(OString("baseline")))
6376 long nHeight = rItem.GetHeight();
6377 OString sPos = OString::number( ( nHeight * nEsc + 500 ) / 1000 );
6378 m_pSerializer->singleElementNS( XML_w, XML_position,
6379 FSNS( XML_w, XML_val ), sPos.getStr( ), FSEND );
6381 if( ( 100 != nProp || sIss.match( OString( "baseline" ) ) ) && !m_rExport.m_bFontSizeWritten )
6383 OString sSize = OString::number( ( nHeight * nProp + 500 ) / 1000 );
6384 m_pSerializer->singleElementNS( XML_w, XML_sz,
6385 FSNS( XML_w, XML_val ), sSize.getStr( ), FSEND );
6390 void DocxAttributeOutput::CharFont( const SvxFontItem& rFont)
6392 GetExport().GetId( rFont ); // ensure font info is written to fontTable.xml
6393 const OUString& sFontName(rFont.GetFamilyName());
6394 OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
6395 if (!sFontNameUtf8.isEmpty())
6396 AddToAttrList( m_pFontsAttrList, 2,
6397 FSNS( XML_w, XML_ascii ), sFontNameUtf8.getStr(),
6398 FSNS( XML_w, XML_hAnsi ), sFontNameUtf8.getStr() );
6401 void DocxAttributeOutput::CharFontSize( const SvxFontHeightItem& rFontSize)
6403 OString fontSize = OString::number( ( rFontSize.GetHeight() + 5 ) / 10 );
6405 switch ( rFontSize.Which() )
6407 case RES_CHRATR_FONTSIZE:
6408 case RES_CHRATR_CJK_FONTSIZE:
6409 m_pSerializer->singleElementNS( XML_w, XML_sz, FSNS( XML_w, XML_val ), fontSize.getStr(), FSEND );
6410 break;
6411 case RES_CHRATR_CTL_FONTSIZE:
6412 m_pSerializer->singleElementNS( XML_w, XML_szCs, FSNS( XML_w, XML_val ), fontSize.getStr(), FSEND );
6413 break;
6417 void DocxAttributeOutput::CharKerning( const SvxKerningItem& rKerning )
6419 OString aKerning = OString::number( rKerning.GetValue() );
6420 m_pSerializer->singleElementNS( XML_w, XML_spacing, FSNS(XML_w, XML_val), aKerning.getStr(), FSEND );
6423 void DocxAttributeOutput::CharLanguage( const SvxLanguageItem& rLanguage )
6425 OString aLanguageCode( OUStringToOString(
6426 LanguageTag( rLanguage.GetLanguage()).getBcp47(),
6427 RTL_TEXTENCODING_UTF8));
6429 switch ( rLanguage.Which() )
6431 case RES_CHRATR_LANGUAGE:
6432 AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_val ), aLanguageCode.getStr() );
6433 break;
6434 case RES_CHRATR_CJK_LANGUAGE:
6435 AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_eastAsia ), aLanguageCode.getStr() );
6436 break;
6437 case RES_CHRATR_CTL_LANGUAGE:
6438 AddToAttrList( m_pCharLangAttrList, FSNS( XML_w, XML_bidi ), aLanguageCode.getStr() );
6439 break;
6443 void DocxAttributeOutput::CharPosture( const SvxPostureItem& rPosture )
6445 if ( rPosture.GetPosture() != ITALIC_NONE )
6446 m_pSerializer->singleElementNS( XML_w, XML_i, FSEND );
6447 else
6448 m_pSerializer->singleElementNS( XML_w, XML_i, FSNS( XML_w, XML_val ), "false", FSEND );
6451 void DocxAttributeOutput::CharShadow( const SvxShadowedItem& rShadow )
6453 if ( rShadow.GetValue() )
6454 m_pSerializer->singleElementNS( XML_w, XML_shadow, FSEND );
6455 else
6456 m_pSerializer->singleElementNS( XML_w, XML_shadow, FSNS( XML_w, XML_val ), "false", FSEND );
6459 void DocxAttributeOutput::CharUnderline( const SvxUnderlineItem& rUnderline )
6461 const char *pUnderlineValue;
6463 switch ( rUnderline.GetLineStyle() )
6465 case LINESTYLE_SINGLE: pUnderlineValue = "single"; break;
6466 case LINESTYLE_BOLD: pUnderlineValue = "thick"; break;
6467 case LINESTYLE_DOUBLE: pUnderlineValue = "double"; break;
6468 case LINESTYLE_DOTTED: pUnderlineValue = "dotted"; break;
6469 case LINESTYLE_DASH: pUnderlineValue = "dash"; break;
6470 case LINESTYLE_DASHDOT: pUnderlineValue = "dotDash"; break;
6471 case LINESTYLE_DASHDOTDOT: pUnderlineValue = "dotDotDash"; break;
6472 case LINESTYLE_WAVE: pUnderlineValue = "wave"; break;
6473 case LINESTYLE_BOLDDOTTED: pUnderlineValue = "dottedHeavy"; break;
6474 case LINESTYLE_BOLDDASH: pUnderlineValue = "dashedHeavy"; break;
6475 case LINESTYLE_LONGDASH: pUnderlineValue = "dashLongHeavy"; break;
6476 case LINESTYLE_BOLDLONGDASH: pUnderlineValue = "dashLongHeavy"; break;
6477 case LINESTYLE_BOLDDASHDOT: pUnderlineValue = "dashDotHeavy"; break;
6478 case LINESTYLE_BOLDDASHDOTDOT: pUnderlineValue = "dashDotDotHeavy"; break;
6479 case LINESTYLE_BOLDWAVE: pUnderlineValue = "wavyHeavy"; break;
6480 case LINESTYLE_DOUBLEWAVE: pUnderlineValue = "wavyDouble"; break;
6481 case LINESTYLE_NONE: // fall through
6482 default: pUnderlineValue = "none"; break;
6485 Color aUnderlineColor = rUnderline.GetColor();
6486 bool bUnderlineHasColor = aUnderlineColor.GetTransparency() == 0;
6487 if (bUnderlineHasColor)
6489 // Underline has a color
6490 m_pSerializer->singleElementNS( XML_w, XML_u,
6491 FSNS( XML_w, XML_val ), pUnderlineValue,
6492 FSNS( XML_w, XML_color ), msfilter::util::ConvertColor( aUnderlineColor ).getStr(),
6493 FSEND );
6495 else
6497 // Underline has no color
6498 m_pSerializer->singleElementNS( XML_w, XML_u, FSNS( XML_w, XML_val ), pUnderlineValue, FSEND );
6502 void DocxAttributeOutput::CharWeight( const SvxWeightItem& rWeight )
6504 if ( rWeight.GetWeight() == WEIGHT_BOLD )
6505 m_pSerializer->singleElementNS( XML_w, XML_b, FSEND );
6506 else
6507 m_pSerializer->singleElementNS( XML_w, XML_b, FSNS( XML_w, XML_val ), "false", FSEND );
6510 void DocxAttributeOutput::CharAutoKern( const SvxAutoKernItem& )
6512 OSL_TRACE( "TODO DocxAttributeOutput::CharAutoKern()" );
6515 void DocxAttributeOutput::CharAnimatedText( const SvxBlinkItem& rBlink )
6517 if ( rBlink.GetValue() )
6518 m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS( XML_w, XML_val ), "blinkBackground", FSEND );
6519 else
6520 m_pSerializer->singleElementNS(XML_w, XML_effect, FSNS( XML_w, XML_val ), "none", FSEND );
6523 #define MSWORD_CH_SHADING_FILL "FFFFFF" // The attribute w:fill of w:shd, for MS-Word's character shading,
6524 #define MSWORD_CH_SHADING_COLOR "auto" // The attribute w:color of w:shd, for MS-Word's character shading,
6525 #define MSWORD_CH_SHADING_VAL "pct15" // The attribute w:value of w:shd, for MS-Word's character shading,
6527 void DocxAttributeOutput::CharBackground( const SvxBrushItem& rBrush )
6529 // Check if the brush shading pattern is 'PCT15'. If so - write it back to the DOCX
6530 if (rBrush.GetShadingValue() == ShadingPattern::PCT15)
6532 m_pSerializer->singleElementNS( XML_w, XML_shd,
6533 FSNS( XML_w, XML_val ), MSWORD_CH_SHADING_VAL,
6534 FSNS( XML_w, XML_color ), MSWORD_CH_SHADING_COLOR,
6535 FSNS( XML_w, XML_fill ), MSWORD_CH_SHADING_FILL,
6536 FSEND );
6538 else
6540 m_pSerializer->singleElementNS( XML_w, XML_shd,
6541 FSNS( XML_w, XML_fill ), msfilter::util::ConvertColor( rBrush.GetColor() ).getStr(),
6542 FSNS( XML_w, XML_val ), "clear",
6543 FSEND );
6547 void DocxAttributeOutput::CharFontCJK( const SvxFontItem& rFont )
6549 const OUString& sFontName(rFont.GetFamilyName());
6550 OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
6551 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsia ), sFontNameUtf8.getStr() );
6554 void DocxAttributeOutput::CharPostureCJK( const SvxPostureItem& rPosture )
6556 if ( rPosture.GetPosture() != ITALIC_NONE )
6557 m_pSerializer->singleElementNS( XML_w, XML_i, FSEND );
6558 else
6559 m_pSerializer->singleElementNS( XML_w, XML_i, FSNS( XML_w, XML_val ), "false", FSEND );
6562 void DocxAttributeOutput::CharWeightCJK( const SvxWeightItem& rWeight )
6564 if ( rWeight.GetWeight() == WEIGHT_BOLD )
6565 m_pSerializer->singleElementNS( XML_w, XML_b, FSEND );
6566 else
6567 m_pSerializer->singleElementNS( XML_w, XML_b, FSNS( XML_w, XML_val ), "false", FSEND );
6570 void DocxAttributeOutput::CharFontCTL( const SvxFontItem& rFont )
6572 const OUString& sFontName(rFont.GetFamilyName());
6573 OString sFontNameUtf8 = OUStringToOString(sFontName, RTL_TEXTENCODING_UTF8);
6574 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cs ), sFontNameUtf8.getStr() );
6577 void DocxAttributeOutput::CharPostureCTL( const SvxPostureItem& rPosture)
6579 if ( rPosture.GetPosture() != ITALIC_NONE )
6580 m_pSerializer->singleElementNS( XML_w, XML_iCs, FSEND );
6581 else
6582 m_pSerializer->singleElementNS( XML_w, XML_iCs, FSNS( XML_w, XML_val ), "false", FSEND );
6585 void DocxAttributeOutput::CharWeightCTL( const SvxWeightItem& rWeight )
6587 if ( rWeight.GetWeight() == WEIGHT_BOLD )
6588 m_pSerializer->singleElementNS( XML_w, XML_bCs, FSEND );
6589 else
6590 m_pSerializer->singleElementNS( XML_w, XML_bCs, FSNS( XML_w, XML_val ), "false", FSEND );
6593 void DocxAttributeOutput::CharBidiRTL( const SfxPoolItem& )
6597 void DocxAttributeOutput::CharIdctHint( const SfxPoolItem& )
6601 void DocxAttributeOutput::CharRotate( const SvxCharRotateItem& rRotate)
6603 // Not rotated or we the rotation already handled?
6604 if ( !rRotate.GetValue() || m_bBtLr || m_rExport.SdrExporter().getFrameBtLr())
6605 return;
6607 AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vert ), "true" );
6609 if (rRotate.IsFitToLine())
6610 AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_vertCompress ), "true" );
6613 void DocxAttributeOutput::CharEmphasisMark( const SvxEmphasisMarkItem& rEmphasisMark )
6615 const char *pEmphasis;
6616 const FontEmphasisMark v = rEmphasisMark.GetEmphasisMark();
6618 if (v == (FontEmphasisMark::Dot | FontEmphasisMark::PosAbove))
6619 pEmphasis = "dot";
6620 else if (v == (FontEmphasisMark::Accent | FontEmphasisMark::PosAbove))
6621 pEmphasis = "comma";
6622 else if (v == (FontEmphasisMark::Circle | FontEmphasisMark::PosAbove))
6623 pEmphasis = "circle";
6624 else if (v == (FontEmphasisMark::Dot|FontEmphasisMark::PosBelow))
6625 pEmphasis = "underDot";
6626 else
6627 pEmphasis = "none";
6629 m_pSerializer->singleElementNS( XML_w, XML_em, FSNS( XML_w, XML_val ), pEmphasis, FSEND );
6632 void DocxAttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
6634 if ( !rTwoLines.GetValue() )
6635 return;
6637 AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combine ), "true" );
6639 sal_Unicode cStart = rTwoLines.GetStartBracket();
6640 sal_Unicode cEnd = rTwoLines.GetEndBracket();
6642 if (!cStart && !cEnd)
6643 return;
6645 OString sBracket;
6646 if ((cStart == '{') || (cEnd == '}'))
6647 sBracket = const_cast<sal_Char *>("curly");
6648 else if ((cStart == '<') || (cEnd == '>'))
6649 sBracket = const_cast<sal_Char *>("angle");
6650 else if ((cStart == '[') || (cEnd == ']'))
6651 sBracket = const_cast<sal_Char *>("square");
6652 else
6653 sBracket = const_cast<sal_Char *>("round");
6654 AddToAttrList( m_pEastAsianLayoutAttrList, FSNS( XML_w, XML_combineBrackets ), sBracket.getStr() );
6657 void DocxAttributeOutput::CharScaleWidth( const SvxCharScaleWidthItem& rScaleWidth )
6659 // Clamp CharScaleWidth to OOXML limits ([1..600])
6660 const sal_Int16 nScaleWidth( std::max<sal_Int16>( 1,
6661 std::min<sal_Int16>( rScaleWidth.GetValue(), 600 ) ) );
6662 m_pSerializer->singleElementNS( XML_w, XML_w,
6663 FSNS( XML_w, XML_val ), OString::number( nScaleWidth ).getStr(), FSEND );
6666 void DocxAttributeOutput::CharRelief( const SvxCharReliefItem& rRelief )
6668 switch ( rRelief.GetValue() )
6670 case FontRelief::Embossed:
6671 m_pSerializer->singleElementNS( XML_w, XML_emboss, FSEND );
6672 break;
6673 case FontRelief::Engraved:
6674 m_pSerializer->singleElementNS( XML_w, XML_imprint, FSEND );
6675 break;
6676 default:
6677 m_pSerializer->singleElementNS( XML_w, XML_emboss, FSNS( XML_w, XML_val ), "false", FSEND );
6678 m_pSerializer->singleElementNS( XML_w, XML_imprint, FSNS( XML_w, XML_val ), "false", FSEND );
6679 break;
6683 void DocxAttributeOutput::CharHidden( const SvxCharHiddenItem& rHidden )
6685 if ( rHidden.GetValue() )
6686 m_pSerializer->singleElementNS( XML_w, XML_vanish, FSEND );
6687 else
6688 m_pSerializer->singleElementNS( XML_w, XML_vanish, FSNS( XML_w, XML_val ), "false", FSEND );
6691 void DocxAttributeOutput::CharBorder(
6692 const SvxBorderLine* pAllBorder, const sal_uInt16 nDist, const bool bShadow )
6694 impl_borderLine( m_pSerializer, XML_bdr, pAllBorder, nDist, bShadow );
6697 void DocxAttributeOutput::CharHighlight( const SvxBrushItem& rHighlight )
6699 const OString sColor = TransHighlightColor( msfilter::util::TransColToIco(rHighlight.GetColor()) );
6700 if ( !sColor.isEmpty() )
6702 m_pSerializer->singleElementNS( XML_w, XML_highlight,
6703 FSNS( XML_w, XML_val ), sColor.getStr(), FSEND );
6707 void DocxAttributeOutput::TextINetFormat( const SwFormatINetFormat& rLink )
6709 const SwTextINetFormat* pINetFormat = rLink.GetTextINetFormat();
6710 const SwCharFormat* pCharFormat = pINetFormat->GetCharFormat();
6712 OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat)));
6714 m_pSerializer->singleElementNS( XML_w, XML_rStyle, FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
6717 void DocxAttributeOutput::TextCharFormat( const SwFormatCharFormat& rCharFormat )
6719 OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(rCharFormat.GetCharFormat())));
6721 m_pSerializer->singleElementNS( XML_w, XML_rStyle, FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
6724 void DocxAttributeOutput::RefField( const SwField& rField, const OUString& rRef )
6726 sal_uInt16 nType = rField.GetTyp( )->Which( );
6727 if ( nType == RES_GETEXPFLD )
6729 OUString sCmd = FieldString( ww::eREF );
6730 sCmd += "\"" + rRef + "\" ";
6732 m_rExport.OutputField( &rField, ww::eREF, sCmd );
6735 // There is nothing to do here for the set fields
6738 void DocxAttributeOutput::HiddenField( const SwField& /*rField*/ )
6740 OSL_TRACE( "TODO DocxAttributeOutput::HiddenField()" );
6743 void DocxAttributeOutput::PostitField( const SwField* pField )
6745 assert( dynamic_cast< const SwPostItField* >( pField ));
6746 const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField);
6747 OString aName = OUStringToOString(pPostItField->GetName(), RTL_TEXTENCODING_UTF8);
6748 sal_Int32 nId = 0;
6749 std::map< OString, sal_Int32 >::iterator it = m_rOpenedAnnotationMarksIds.find(aName);
6750 if (it != m_rOpenedAnnotationMarksIds.end())
6751 // If the postit field has an annotation mark associated, we already have an id.
6752 nId = it->second;
6753 else
6754 // Otherwise get a new one.
6755 nId = m_nNextAnnotationMarkId++;
6756 m_postitFields.push_back(std::make_pair(pPostItField, nId));
6759 void DocxAttributeOutput::WritePostitFieldReference()
6761 while( m_postitFieldsMaxId < m_postitFields.size())
6763 OString idstr = OString::number(m_postitFields[m_postitFieldsMaxId].second);
6765 // In case this file is inside annotation marks, we want to write the
6766 // comment reference after the annotation mark is closed, not here.
6767 OString idname = OUStringToOString(m_postitFields[m_postitFieldsMaxId].first->GetName(), RTL_TEXTENCODING_UTF8);
6768 std::map< OString, sal_Int32 >::iterator it = m_rOpenedAnnotationMarksIds.find( idname );
6769 if ( it == m_rOpenedAnnotationMarksIds.end( ) )
6770 m_pSerializer->singleElementNS( XML_w, XML_commentReference, FSNS( XML_w, XML_id ), idstr.getStr(), FSEND );
6771 ++m_postitFieldsMaxId;
6775 void DocxAttributeOutput::WritePostitFields()
6777 for (const std::pair<const SwPostItField*,int> & rPair : m_postitFields)
6779 OString idstr = OString::number( rPair.second);
6780 const SwPostItField* f = rPair.first;
6781 m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr.getStr(),
6782 FSNS( XML_w, XML_author ), OUStringToOString( f->GetPar1(), RTL_TEXTENCODING_UTF8 ).getStr(),
6783 FSNS( XML_w, XML_date ), DateTimeToOString(f->GetDateTime()).getStr(),
6784 FSNS( XML_w, XML_initials ), OUStringToOString( f->GetInitials(), RTL_TEXTENCODING_UTF8 ).getStr(), FSEND );
6786 if (f->GetTextObject() != nullptr)
6788 // richtext
6789 GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN);
6791 else
6793 // just plain text - eg. when the field was created via the
6794 // .uno:InsertAnnotation API
6795 m_pSerializer->startElementNS(XML_w, XML_p, FSEND);
6796 m_pSerializer->startElementNS(XML_w, XML_r, FSEND);
6797 RunText(f->GetText());
6798 m_pSerializer->endElementNS(XML_w, XML_r);
6799 m_pSerializer->endElementNS(XML_w, XML_p);
6802 m_pSerializer->endElementNS( XML_w, XML_comment );
6806 bool DocxAttributeOutput::DropdownField( const SwField* pField )
6808 bool bExpand = false;
6810 ww::eField eType = ww::eFORMDROPDOWN;
6811 OUString sCmd = FieldString( eType );
6812 GetExport( ).OutputField( pField, eType, sCmd );
6814 return bExpand;
6817 bool DocxAttributeOutput::PlaceholderField( const SwField* pField )
6819 assert( pendingPlaceholder == nullptr );
6820 pendingPlaceholder = pField;
6821 return false; // do not expand
6824 void DocxAttributeOutput::WritePendingPlaceholder()
6826 if( pendingPlaceholder == nullptr )
6827 return;
6828 const SwField* pField = pendingPlaceholder;
6829 pendingPlaceholder = nullptr;
6830 m_pSerializer->startElementNS( XML_w, XML_sdt, FSEND );
6831 m_pSerializer->startElementNS( XML_w, XML_sdtPr, FSEND );
6832 if( !pField->GetPar2().isEmpty())
6833 m_pSerializer->singleElementNS( XML_w, XML_alias,
6834 FSNS( XML_w, XML_val ), OUStringToOString( pField->GetPar2(), RTL_TEXTENCODING_UTF8 ), FSEND );
6835 m_pSerializer->singleElementNS( XML_w, XML_temporary, FSEND );
6836 m_pSerializer->singleElementNS( XML_w, XML_showingPlcHdr, FSEND );
6837 m_pSerializer->singleElementNS( XML_w, XML_text, FSEND );
6838 m_pSerializer->endElementNS( XML_w, XML_sdtPr );
6839 m_pSerializer->startElementNS( XML_w, XML_sdtContent, FSEND );
6840 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
6841 RunText( pField->GetPar1());
6842 m_pSerializer->endElementNS( XML_w, XML_r );
6843 m_pSerializer->endElementNS( XML_w, XML_sdtContent );
6844 m_pSerializer->endElementNS( XML_w, XML_sdt );
6847 void DocxAttributeOutput::SetField( const SwField& rField, ww::eField eType, const OUString& rCmd )
6849 // field bookmarks are handled in the EndRun method
6850 GetExport().OutputField(&rField, eType, rCmd );
6853 void DocxAttributeOutput::WriteExpand( const SwField* pField )
6855 // Will be written in the next End Run
6856 OUString sCmd;
6857 m_rExport.OutputField( pField, ww::eUNKNOWN, sCmd );
6860 void DocxAttributeOutput::WriteField_Impl( const SwField* pField, ww::eField eType, const OUString& rFieldCmd, sal_uInt8 nMode )
6862 struct FieldInfos infos;
6863 if (pField)
6864 infos.pField.reset(pField->CopyField());
6865 infos.sCmd = rFieldCmd;
6866 infos.eType = eType;
6867 infos.bClose = WRITEFIELD_CLOSE & nMode;
6868 infos.bOpen = WRITEFIELD_START & nMode;
6869 m_Fields.push_back( infos );
6871 if ( pField )
6873 sal_uInt16 nType = pField->GetTyp( )->Which( );
6874 sal_uInt16 nSubType = pField->GetSubType();
6876 // TODO Any other field types here ?
6877 if ( ( nType == RES_SETEXPFLD ) && ( nSubType & nsSwGetSetExpType::GSE_STRING ) )
6879 const SwSetExpField *pSet = static_cast<const SwSetExpField*>( pField );
6880 m_sFieldBkm = pSet->GetPar1( );
6882 else if ( nType == RES_DROPDOWN )
6884 const SwDropDownField* pDropDown = static_cast<const SwDropDownField*>( pField );
6885 m_sFieldBkm = pDropDown->GetName( );
6890 void DocxAttributeOutput::WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark )
6892 if ( !m_Fields.empty() )
6893 m_Fields.begin()->pFieldmark = &rFieldmark;
6896 void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts,
6897 std::vector< OUString >& rEnds )
6899 for ( std::vector< OUString >::const_iterator it = rStarts.begin(), end = rStarts.end(); it != end; ++it )
6901 OString rName = OUStringToOString( *it, RTL_TEXTENCODING_UTF8 ).getStr( );
6902 m_rBookmarksStart.push_back( rName );
6904 rStarts.clear();
6906 for ( std::vector< OUString >::const_iterator it = rEnds.begin(), end = rEnds.end(); it != end; ++it )
6908 OString rName = OUStringToOString( *it, RTL_TEXTENCODING_UTF8 ).getStr( );
6909 m_rBookmarksEnd.push_back( rName );
6911 rEnds.clear();
6914 void DocxAttributeOutput::WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts,
6915 std::vector< OUString >& rEnds )
6917 for ( std::vector< OUString >::const_iterator it = rStarts.begin(), end = rStarts.end(); it != end; ++it )
6919 OString rName = OUStringToOString( *it, RTL_TEXTENCODING_UTF8 ).getStr( );
6920 m_rAnnotationMarksStart.push_back( rName );
6922 rStarts.clear();
6924 for ( std::vector< OUString >::const_iterator it = rEnds.begin(), end = rEnds.end(); it != end; ++it )
6926 OString rName = OUStringToOString( *it, RTL_TEXTENCODING_UTF8 ).getStr( );
6927 m_rAnnotationMarksEnd.push_back( rName );
6929 rEnds.clear();
6932 void DocxAttributeOutput::TextFootnote_Impl( const SwFormatFootnote& rFootnote )
6934 const SwEndNoteInfo& rInfo = rFootnote.IsEndNote()?
6935 m_rExport.m_pDoc->GetEndNoteInfo(): m_rExport.m_pDoc->GetFootnoteInfo();
6937 // footnote/endnote run properties
6938 const SwCharFormat* pCharFormat = rInfo.GetAnchorCharFormat( *m_rExport.m_pDoc );
6940 OString aStyleId(m_rExport.m_pStyles->GetStyleId(m_rExport.GetId(pCharFormat)));
6942 m_pSerializer->singleElementNS( XML_w, XML_rStyle, FSNS( XML_w, XML_val ), aStyleId.getStr(), FSEND );
6944 // remember the footnote/endnote to
6945 // 1) write the footnoteReference/endnoteReference in EndRunProperties()
6946 // 2) be able to dump them all to footnotes.xml/endnotes.xml
6947 if ( !rFootnote.IsEndNote() )
6948 m_pFootnotesList->add( rFootnote );
6949 else
6950 m_pEndnotesList->add( rFootnote );
6953 void DocxAttributeOutput::FootnoteEndnoteReference()
6955 sal_Int32 nId;
6956 const SwFormatFootnote *pFootnote = m_pFootnotesList->getCurrent( nId );
6958 // both cannot be set at the same time - if they are, it's a bug
6959 if ( !pFootnote )
6960 pFootnote = m_pEndnotesList->getCurrent( nId );
6962 if ( !pFootnote )
6963 return;
6965 sal_Int32 nToken = pFootnote->IsEndNote()? XML_endnoteReference: XML_footnoteReference;
6967 // write it
6968 if ( pFootnote->GetNumStr().isEmpty() )
6970 // autonumbered
6971 m_pSerializer->singleElementNS( XML_w, nToken,
6972 FSNS( XML_w, XML_id ), OString::number( nId ).getStr(),
6973 FSEND );
6975 else
6977 // not autonumbered
6978 m_pSerializer->singleElementNS( XML_w, nToken,
6979 FSNS( XML_w, XML_customMarkFollows ), "1",
6980 FSNS( XML_w, XML_id ), OString::number( nId ).getStr(),
6981 FSEND );
6983 RunText( pFootnote->GetNumStr() );
6987 void DocxAttributeOutput::FootnotesEndnotes( bool bFootnotes )
6989 m_setFootnote = true;
6990 const FootnotesVector& rVector = bFootnotes? m_pFootnotesList->getVector(): m_pEndnotesList->getVector();
6992 sal_Int32 nBody = bFootnotes? XML_footnotes: XML_endnotes;
6993 sal_Int32 nItem = bFootnotes? XML_footnote: XML_endnote;
6995 m_pSerializer->startElementNS( XML_w, nBody, DocxExport::MainXmlNamespaces() );
6997 sal_Int32 nIndex = 0;
6999 // separator
7000 m_pSerializer->startElementNS( XML_w, nItem,
7001 FSNS( XML_w, XML_id ), OString::number( nIndex++ ).getStr(),
7002 FSNS( XML_w, XML_type ), "separator",
7003 FSEND );
7004 m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
7005 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
7007 bool bSeparator = true;
7008 if (bFootnotes)
7010 const SwPageFootnoteInfo& rFootnoteInfo = m_rExport.m_pDoc->GetPageDesc(0).GetFootnoteInfo();
7011 // Request a separator only in case the width is larger than zero.
7012 bSeparator = double(rFootnoteInfo.GetWidth()) > 0;
7015 if (bSeparator)
7016 m_pSerializer->singleElementNS( XML_w, XML_separator, FSEND );
7017 m_pSerializer->endElementNS( XML_w, XML_r );
7018 m_pSerializer->endElementNS( XML_w, XML_p );
7019 m_pSerializer->endElementNS( XML_w, nItem );
7021 // separator
7022 m_pSerializer->startElementNS( XML_w, nItem,
7023 FSNS( XML_w, XML_id ), OString::number( nIndex++ ).getStr(),
7024 FSNS( XML_w, XML_type ), "continuationSeparator",
7025 FSEND );
7026 m_pSerializer->startElementNS( XML_w, XML_p, FSEND );
7027 m_pSerializer->startElementNS( XML_w, XML_r, FSEND );
7028 m_pSerializer->singleElementNS( XML_w, XML_continuationSeparator, FSEND );
7029 m_pSerializer->endElementNS( XML_w, XML_r );
7030 m_pSerializer->endElementNS( XML_w, XML_p );
7031 m_pSerializer->endElementNS( XML_w, nItem );
7033 // if new special ones are added, update also WriteFootnoteEndnotePr()
7035 // footnotes/endnotes themselves
7036 for ( FootnotesVector::const_iterator i = rVector.begin(); i != rVector.end(); ++i, ++nIndex )
7038 m_pSerializer->startElementNS( XML_w, nItem,
7039 FSNS( XML_w, XML_id ), OString::number( nIndex ).getStr(),
7040 FSEND );
7042 const SwNodeIndex* pIndex = (*i)->GetTextFootnote()->GetStartNode();
7043 // tag required at the start of each footnote/endnote
7044 m_footnoteEndnoteRefTag = bFootnotes ? XML_footnoteRef : XML_endnoteRef;
7046 m_rExport.WriteSpecialText( pIndex->GetIndex() + 1,
7047 pIndex->GetNode().EndOfSectionIndex(),
7048 bFootnotes? TXT_FTN: TXT_EDN );
7050 m_pSerializer->endElementNS( XML_w, nItem );
7053 m_pSerializer->endElementNS( XML_w, nBody );
7057 void DocxAttributeOutput::WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & fs, int tag,
7058 const SwEndNoteInfo& info, int listtag )
7060 fs->startElementNS( XML_w, tag, FSEND );
7061 const char* fmt = nullptr;
7062 switch( info.aFormat.GetNumberingType())
7064 case SVX_NUM_CHARS_UPPER_LETTER_N: // fall through, map to upper letters
7065 case SVX_NUM_CHARS_UPPER_LETTER:
7066 fmt = "upperLetter";
7067 break;
7068 case SVX_NUM_CHARS_LOWER_LETTER_N: // fall through, map to lower letters
7069 case SVX_NUM_CHARS_LOWER_LETTER:
7070 fmt = "lowerLetter";
7071 break;
7072 case SVX_NUM_ROMAN_UPPER:
7073 fmt = "upperRoman";
7074 break;
7075 case SVX_NUM_ROMAN_LOWER:
7076 fmt = "lowerRoman";
7077 break;
7078 case SVX_NUM_ARABIC:
7079 fmt = "decimal";
7080 break;
7081 case SVX_NUM_NUMBER_NONE:
7082 fmt = "none";
7083 break;
7084 case SVX_NUM_CHAR_SPECIAL:
7085 fmt = "bullet";
7086 break;
7087 case SVX_NUM_PAGEDESC:
7088 case SVX_NUM_BITMAP:
7089 default:
7090 break; // no format
7092 if( fmt != nullptr )
7093 fs->singleElementNS( XML_w, XML_numFmt, FSNS( XML_w, XML_val ), fmt, FSEND );
7094 if( info.nFootnoteOffset != 0 )
7095 fs->singleElementNS( XML_w, XML_numStart, FSNS( XML_w, XML_val ),
7096 OString::number( info.nFootnoteOffset + 1).getStr(), FSEND );
7098 const SwFootnoteInfo* pFootnoteInfo = dynamic_cast<const SwFootnoteInfo*>(&info);
7099 if( pFootnoteInfo )
7101 switch( pFootnoteInfo->eNum )
7103 case FTNNUM_PAGE: fmt = "eachPage"; break;
7104 case FTNNUM_CHAPTER: fmt = "eachSect"; break;
7105 default: fmt = nullptr; break;
7107 if( fmt != nullptr )
7108 fs->singleElementNS( XML_w, XML_numRestart, FSNS( XML_w, XML_val ), fmt, FSEND );
7111 if( listtag != 0 ) // we are writing to settings.xml, write also special footnote/endnote list
7112 { // there are currently only two hardcoded ones ( see FootnotesEndnotes())
7113 fs->singleElementNS( XML_w, listtag, FSNS( XML_w, XML_id ), "0", FSEND );
7114 fs->singleElementNS( XML_w, listtag, FSNS( XML_w, XML_id ), "1", FSEND );
7116 fs->endElementNS( XML_w, tag );
7119 void DocxAttributeOutput::SectFootnoteEndnotePr()
7121 if( HasFootnotes())
7122 WriteFootnoteEndnotePr( m_pSerializer, XML_footnotePr, m_rExport.m_pDoc->GetFootnoteInfo(), 0 );
7123 if( HasEndnotes())
7124 WriteFootnoteEndnotePr( m_pSerializer, XML_endnotePr, m_rExport.m_pDoc->GetEndNoteInfo(), 0 );
7127 void DocxAttributeOutput::ParaLineSpacing_Impl( short nSpace, short nMulti )
7129 if ( nSpace < 0 )
7131 AddToAttrList( m_pParagraphSpacingAttrList, 2,
7132 FSNS( XML_w, XML_lineRule ), "exact",
7133 FSNS( XML_w, XML_line ), OString::number( -nSpace ).getStr() );
7135 else if( nMulti )
7137 AddToAttrList( m_pParagraphSpacingAttrList, 2,
7138 FSNS( XML_w, XML_lineRule ), "auto",
7139 FSNS( XML_w, XML_line ), OString::number( nSpace ).getStr() );
7141 else if ( nSpace > 0 )
7143 AddToAttrList( m_pParagraphSpacingAttrList, 2,
7144 FSNS( XML_w, XML_lineRule ), "atLeast",
7145 FSNS( XML_w, XML_line ), OString::number( nSpace ).getStr() );
7147 else
7148 AddToAttrList( m_pParagraphSpacingAttrList, FSNS( XML_w, XML_lineRule ), "auto" );
7151 void DocxAttributeOutput::ParaAdjust( const SvxAdjustItem& rAdjust )
7153 const char *pAdjustString;
7155 bool bEcma = GetExport().GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
7157 const SfxItemSet* pItems = GetExport().GetCurItemSet();
7158 const SvxFrameDirectionItem* rFrameDir = pItems?
7159 static_cast< const SvxFrameDirectionItem* >( pItems->GetItem( RES_FRAMEDIR ) ): nullptr;
7161 short nDir = FRMDIR_ENVIRONMENT;
7162 if( rFrameDir != nullptr )
7163 nDir = rFrameDir->GetValue();
7164 if ( nDir == FRMDIR_ENVIRONMENT )
7165 nDir = GetExport( ).GetDefaultFrameDirection( );
7166 bool bRtl = ( nDir == FRMDIR_HORI_RIGHT_TOP );
7168 switch ( rAdjust.GetAdjust() )
7170 case SVX_ADJUST_LEFT:
7171 if ( bEcma )
7173 if ( bRtl )
7174 pAdjustString = "right";
7175 else
7176 pAdjustString = "left";
7178 else if ( bRtl )
7179 pAdjustString = "end";
7180 else
7181 pAdjustString = "start";
7182 break;
7183 case SVX_ADJUST_RIGHT:
7184 if ( bEcma )
7186 if ( bRtl )
7187 pAdjustString = "left";
7188 else
7189 pAdjustString = "right";
7191 else if ( bRtl )
7192 pAdjustString = "start";
7193 else
7194 pAdjustString = "end";
7195 break;
7196 case SVX_ADJUST_BLOCKLINE:
7197 case SVX_ADJUST_BLOCK:
7198 pAdjustString = "both";
7199 break;
7200 case SVX_ADJUST_CENTER:
7201 pAdjustString = "center";
7202 break;
7203 default:
7204 return; // not supported attribute
7206 m_pSerializer->singleElementNS( XML_w, XML_jc, FSNS( XML_w, XML_val ), pAdjustString, FSEND );
7209 void DocxAttributeOutput::ParaSplit( const SvxFormatSplitItem& rSplit )
7211 if (rSplit.GetValue())
7212 m_pSerializer->singleElementNS( XML_w, XML_keepLines, FSNS( XML_w, XML_val ), "false", FSEND );
7213 else
7214 m_pSerializer->singleElementNS( XML_w, XML_keepLines, FSEND );
7217 void DocxAttributeOutput::ParaWidows( const SvxWidowsItem& rWidows )
7219 if (rWidows.GetValue())
7220 m_pSerializer->singleElementNS( XML_w, XML_widowControl, FSEND );
7221 else
7222 m_pSerializer->singleElementNS( XML_w, XML_widowControl, FSNS( XML_w, XML_val ), "false", FSEND );
7225 static void impl_WriteTabElement( FSHelperPtr const & pSerializer,
7226 const SvxTabStop& rTab, long /* nCurrentLeft */ )
7228 FastAttributeList *pTabElementAttrList = FastSerializerHelper::createAttrList();
7230 switch (rTab.GetAdjustment())
7232 case SvxTabAdjust::Right:
7233 pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "right" ) );
7234 break;
7235 case SvxTabAdjust::Decimal:
7236 pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "decimal" ) );
7237 break;
7238 case SvxTabAdjust::Center:
7239 pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "center" ) );
7240 break;
7241 case SvxTabAdjust::Default:
7242 case SvxTabAdjust::Left:
7243 default:
7244 pTabElementAttrList->add( FSNS( XML_w, XML_val ), OString( "left" ) );
7245 break;
7248 // Because GetTabPos already includes indent, we don't need to add nCurrentLeft (CurrentLeft is indentation information)
7249 //pTabElementAttrList->add( FSNS( XML_w, XML_pos ), OString::valueOf( rTab.GetTabPos() + nCurrentLeft ) );
7250 pTabElementAttrList->add( FSNS( XML_w, XML_pos ), OString::number( rTab.GetTabPos() ) );
7252 sal_Unicode cFillChar = rTab.GetFill();
7254 if ('.' == cFillChar )
7255 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "dot" ) );
7256 else if ( '-' == cFillChar )
7257 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "hyphen" ) );
7258 else if ( sal_Unicode(0xB7) == cFillChar ) // middle dot
7259 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "middleDot" ) );
7260 else if ( '_' == cFillChar )
7261 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "underscore" ) );
7262 else
7263 pTabElementAttrList->add( FSNS( XML_w, XML_leader ), OString( "none" ) );
7265 pSerializer->singleElementNS( XML_w, XML_tab, pTabElementAttrList );
7268 void DocxAttributeOutput::ParaTabStop( const SvxTabStopItem& rTabStop )
7270 const SfxPoolItem* pLR = m_rExport.HasItem( RES_LR_SPACE );
7271 long nCurrentLeft = pLR ? static_cast<const SvxLRSpaceItem*>(pLR)->GetTextLeft() : 0;
7273 sal_uInt16 nCount = rTabStop.Count();
7275 // <w:tabs> must contain at least one <w:tab>, so don't write it empty
7276 if( nCount == 0 )
7277 return;
7278 if( nCount == 1 && rTabStop[ 0 ].GetAdjustment() == SvxTabAdjust::Default )
7280 GetExport().setDefaultTabStop( rTabStop[ 0 ].GetTabPos());
7281 return;
7284 m_pSerializer->startElementNS( XML_w, XML_tabs, FSEND );
7286 for (sal_uInt16 i = 0; i < nCount; i++ )
7288 if( rTabStop[i].GetAdjustment() != SvxTabAdjust::Default )
7289 impl_WriteTabElement( m_pSerializer, rTabStop[i], nCurrentLeft );
7290 else
7291 GetExport().setDefaultTabStop( rTabStop[i].GetTabPos());
7294 m_pSerializer->endElementNS( XML_w, XML_tabs );
7297 void DocxAttributeOutput::ParaHyphenZone( const SvxHyphenZoneItem& rHyphenZone )
7299 m_pSerializer->singleElementNS( XML_w, XML_suppressAutoHyphens,
7300 FSNS( XML_w, XML_val ), OString::boolean( !rHyphenZone.IsHyphen() ),
7301 FSEND );
7304 void DocxAttributeOutput::ParaNumRule_Impl( const SwTextNode* /*pTextNd*/, sal_Int32 nLvl, sal_Int32 nNumId )
7306 if ( USHRT_MAX != nNumId )
7308 m_pSerializer->startElementNS( XML_w, XML_numPr, FSEND );
7309 m_pSerializer->singleElementNS( XML_w, XML_ilvl, FSNS( XML_w, XML_val ), OString::number( nLvl).getStr(), FSEND );
7310 m_pSerializer->singleElementNS( XML_w, XML_numId, FSNS( XML_w, XML_val ), OString::number( nNumId).getStr(), FSEND );
7311 m_pSerializer->endElementNS( XML_w, XML_numPr );
7315 void DocxAttributeOutput::ParaScriptSpace( const SfxBoolItem& rScriptSpace )
7317 m_pSerializer->singleElementNS( XML_w, XML_autoSpaceDE,
7318 FSNS( XML_w, XML_val ), OString::boolean( rScriptSpace.GetValue() ),
7319 FSEND );
7322 void DocxAttributeOutput::ParaHangingPunctuation( const SfxBoolItem& rItem )
7324 m_pSerializer->singleElementNS( XML_w, XML_overflowPunct,
7325 FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ),
7326 FSEND );
7329 void DocxAttributeOutput::ParaForbiddenRules( const SfxBoolItem& rItem )
7331 m_pSerializer->singleElementNS( XML_w, XML_kinsoku,
7332 FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ),
7333 FSEND );
7336 void DocxAttributeOutput::ParaVerticalAlign( const SvxParaVertAlignItem& rAlign )
7338 const char *pAlignString;
7340 switch ( rAlign.GetValue() )
7342 case SvxParaVertAlignItem::Align::Baseline:
7343 pAlignString = "baseline";
7344 break;
7345 case SvxParaVertAlignItem::Align::Top:
7346 pAlignString = "top";
7347 break;
7348 case SvxParaVertAlignItem::Align::Center:
7349 pAlignString = "center";
7350 break;
7351 case SvxParaVertAlignItem::Align::Bottom:
7352 pAlignString = "bottom";
7353 break;
7354 case SvxParaVertAlignItem::Align::Automatic:
7355 pAlignString = "auto";
7356 break;
7357 default:
7358 return; // not supported attribute
7360 m_pSerializer->singleElementNS( XML_w, XML_textAlignment, FSNS( XML_w, XML_val ), pAlignString, FSEND );
7363 void DocxAttributeOutput::ParaSnapToGrid( const SvxParaGridItem& rGrid )
7365 m_pSerializer->singleElementNS( XML_w, XML_snapToGrid,
7366 FSNS( XML_w, XML_val ), OString::boolean( rGrid.GetValue() ),
7367 FSEND );
7370 void DocxAttributeOutput::FormatFrameSize( const SwFormatFrameSize& rSize )
7372 if (m_rExport.SdrExporter().getTextFrameSyntax() && m_rExport.SdrExporter().getFlyFrameSize())
7374 const Size* pSize = m_rExport.SdrExporter().getFlyFrameSize();
7375 m_rExport.SdrExporter().getTextFrameStyle().append(";width:").append(double(pSize->Width()) / 20);
7376 m_rExport.SdrExporter().getTextFrameStyle().append("pt;height:").append(double(pSize->Height()) / 20).append("pt");
7378 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7381 else if ( m_rExport.m_bOutFlyFrameAttrs )
7383 if ( rSize.GetWidth() && rSize.GetWidthSizeType() == ATT_FIX_SIZE )
7384 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(),
7385 FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ).getStr() );
7387 if ( rSize.GetHeight() )
7389 OString sRule( "exact" );
7390 if ( rSize.GetHeightSizeType() == ATT_MIN_SIZE )
7391 sRule = OString( "atLeast" );
7392 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2,
7393 FSNS( XML_w, XML_hRule ), sRule.getStr(),
7394 FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ).getStr() );
7397 else if ( m_rExport.m_bOutPageDescs )
7399 FastAttributeList *attrList = FastSerializerHelper::createAttrList( );
7400 if ( m_rExport.m_pAktPageDesc->GetLandscape( ) )
7401 attrList->add( FSNS( XML_w, XML_orient ), "landscape" );
7403 attrList->add( FSNS( XML_w, XML_w ), OString::number( rSize.GetWidth( ) ) );
7404 attrList->add( FSNS( XML_w, XML_h ), OString::number( rSize.GetHeight( ) ) );
7406 XFastAttributeListRef xAttrList( attrList );
7407 attrList = nullptr;
7409 m_pSerializer->singleElementNS( XML_w, XML_pgSz, xAttrList );
7413 void DocxAttributeOutput::FormatPaperBin( const SvxPaperBinItem& )
7415 OSL_TRACE( "TODO DocxAttributeOutput::FormatPaperBin()" );
7418 void DocxAttributeOutput::FormatLRSpace( const SvxLRSpaceItem& rLRSpace )
7420 bool bEcma = m_rExport.GetFilter().getVersion( ) == oox::core::ECMA_DIALECT;
7422 if (m_rExport.SdrExporter().getTextFrameSyntax())
7424 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-left:").append(double(rLRSpace.GetLeft()) / 20).append("pt");
7425 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-right:").append(double(rLRSpace.GetRight()) / 20).append("pt");
7427 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7430 else if ( m_rExport.m_bOutFlyFrameAttrs )
7432 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hSpace ),
7433 OString::number(
7434 ( rLRSpace.GetLeft() + rLRSpace.GetRight() ) / 2 ).getStr() );
7436 else if ( m_rExport.m_bOutPageDescs )
7438 m_pageMargins.nLeft = 0;
7439 m_pageMargins.nRight = 0;
7441 if ( auto pBoxItem = static_cast<const SvxBoxItem*>(m_rExport.HasItem( RES_BOX )) )
7443 m_pageMargins.nLeft = pBoxItem->CalcLineSpace( SvxBoxItemLine::LEFT, /*bEvenIfNoLine*/true );
7444 m_pageMargins.nRight = pBoxItem->CalcLineSpace( SvxBoxItemLine::RIGHT, /*bEvenIfNoLine*/true );
7447 m_pageMargins.nLeft += sal::static_int_cast<sal_uInt16>(rLRSpace.GetLeft());
7448 m_pageMargins.nRight += sal::static_int_cast<sal_uInt16>(rLRSpace.GetRight());
7450 AddToAttrList( m_pSectionSpacingAttrList, 2,
7451 FSNS( XML_w, XML_left ), OString::number( m_pageMargins.nLeft ).getStr(),
7452 FSNS( XML_w, XML_right ), OString::number( m_pageMargins.nRight ).getStr() );
7454 else
7456 FastAttributeList *pLRSpaceAttrList = FastSerializerHelper::createAttrList();
7457 if((0 != rLRSpace.GetTextLeft()) || (rLRSpace.IsExplicitZeroMarginValLeft()))
7459 pLRSpaceAttrList->add( FSNS( XML_w, ( bEcma ? XML_left : XML_start ) ), OString::number( rLRSpace.GetTextLeft() ) );
7461 if((0 != rLRSpace.GetRight()) || (rLRSpace.IsExplicitZeroMarginValRight()))
7463 pLRSpaceAttrList->add( FSNS( XML_w, ( bEcma ? XML_right : XML_end ) ), OString::number( rLRSpace.GetRight() ) );
7465 sal_Int32 nFirstLineAdjustment = rLRSpace.GetTextFirstLineOfst();
7466 if (nFirstLineAdjustment > 0)
7467 pLRSpaceAttrList->add( FSNS( XML_w, XML_firstLine ), OString::number( nFirstLineAdjustment ) );
7468 else
7469 pLRSpaceAttrList->add( FSNS( XML_w, XML_hanging ), OString::number( - nFirstLineAdjustment ) );
7470 m_pSerializer->singleElementNS( XML_w, XML_ind, pLRSpaceAttrList );
7474 void DocxAttributeOutput::FormatULSpace( const SvxULSpaceItem& rULSpace )
7477 if (m_rExport.SdrExporter().getTextFrameSyntax())
7479 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-top:").append(double(rULSpace.GetUpper()) / 20).append("pt");
7480 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-wrap-distance-bottom:").append(double(rULSpace.GetLower()) / 20).append("pt");
7482 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7485 else if ( m_rExport.m_bOutFlyFrameAttrs )
7487 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vSpace ),
7488 OString::number(
7489 ( rULSpace.GetLower() + rULSpace.GetUpper() ) / 2 ).getStr() );
7491 else if (m_rExport.m_bOutPageDescs )
7493 OSL_ENSURE( m_rExport.GetCurItemSet(), "Impossible" );
7494 if ( !m_rExport.GetCurItemSet() )
7495 return;
7497 HdFtDistanceGlue aDistances( *m_rExport.GetCurItemSet() );
7499 sal_Int32 nHeader = 0;
7500 if ( aDistances.HasHeader() )
7501 nHeader = sal_Int32( aDistances.dyaHdrTop );
7503 // Page top
7504 m_pageMargins.nTop = aDistances.dyaTop;
7506 sal_Int32 nFooter = 0;
7507 if ( aDistances.HasFooter() )
7508 nFooter = sal_Int32( aDistances.dyaHdrBottom );
7510 // Page Bottom
7511 m_pageMargins.nBottom = aDistances.dyaBottom;
7513 AddToAttrList( m_pSectionSpacingAttrList, 5,
7514 FSNS( XML_w, XML_header ), OString::number( nHeader ).getStr(),
7515 FSNS( XML_w, XML_top ), OString::number( m_pageMargins.nTop ).getStr(),
7516 FSNS( XML_w, XML_footer ), OString::number( nFooter ).getStr(),
7517 FSNS( XML_w, XML_bottom ), OString::number( m_pageMargins.nBottom ).getStr(),
7518 // FIXME Page Gutter is not handled ATM, setting to 0 as it's mandatory for OOXML
7519 FSNS( XML_w, XML_gutter ), "0" );
7521 else
7523 SAL_INFO("sw.ww8", "DocxAttributeOutput::FormatULSpace: setting spacing" << rULSpace.GetUpper() );
7524 // check if before auto spacing was set during import and spacing we get from actual object is same
7525 // that we set in import. If yes just write beforeAutoSpacing tag.
7526 if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == rULSpace.GetUpper())
7528 AddToAttrList( m_pParagraphSpacingAttrList,
7529 FSNS( XML_w, XML_beforeAutospacing ), "1" );
7531 else if (m_bParaBeforeAutoSpacing && m_nParaBeforeSpacing == -1)
7533 AddToAttrList( m_pParagraphSpacingAttrList,
7534 FSNS( XML_w, XML_beforeAutospacing ), "0" );
7535 AddToAttrList( m_pParagraphSpacingAttrList,
7536 FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ).getStr() );
7538 else
7540 AddToAttrList( m_pParagraphSpacingAttrList,
7541 FSNS( XML_w, XML_before ), OString::number( rULSpace.GetUpper() ).getStr() );
7543 m_bParaBeforeAutoSpacing = false;
7544 // check if after auto spacing was set during import and spacing we get from actual object is same
7545 // that we set in import. If yes just write afterAutoSpacing tag.
7546 if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == rULSpace.GetLower())
7548 AddToAttrList( m_pParagraphSpacingAttrList,
7549 FSNS( XML_w, XML_afterAutospacing ), "1" );
7551 else if (m_bParaAfterAutoSpacing && m_nParaAfterSpacing == -1)
7553 AddToAttrList( m_pParagraphSpacingAttrList,
7554 FSNS( XML_w, XML_afterAutospacing ), "0" );
7555 AddToAttrList( m_pParagraphSpacingAttrList,
7556 FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()).getStr() );
7558 else
7560 AddToAttrList( m_pParagraphSpacingAttrList,
7561 FSNS( XML_w, XML_after ), OString::number( rULSpace.GetLower()).getStr() );
7563 m_bParaAfterAutoSpacing = false;
7565 if (rULSpace.GetContext())
7566 m_pSerializer->singleElementNS( XML_w, XML_contextualSpacing, FSEND );
7570 void DocxAttributeOutput::FormatSurround( const SwFormatSurround& rSurround )
7572 if (m_rExport.SdrExporter().getTextFrameSyntax())
7574 OString sType, sSide;
7575 switch (rSurround.GetSurround())
7577 case SURROUND_NONE:
7578 sType = "topAndBottom";
7579 break;
7580 case SURROUND_PARALLEL:
7581 sType = "square";
7582 break;
7583 case SURROUND_IDEAL:
7584 sType = "square";
7585 sSide = "largest";
7586 break;
7587 case SURROUND_LEFT:
7588 sType = "square";
7589 sSide = "left";
7590 break;
7591 case SURROUND_RIGHT:
7592 sType = "square";
7593 sSide = "right";
7594 break;
7595 case SURROUND_THROUGHT:
7596 /* empty type and side means throught */
7597 default:
7598 break;
7600 if (!sType.isEmpty() || !sSide.isEmpty())
7602 m_rExport.SdrExporter().setFlyWrapAttrList(FastSerializerHelper::createAttrList());
7603 if (!sType.isEmpty())
7604 m_rExport.SdrExporter().getFlyWrapAttrList()->add(XML_type, sType);
7605 if (!sSide.isEmpty())
7606 m_rExport.SdrExporter().getFlyWrapAttrList()->add(XML_side, sSide);
7609 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7612 else if ( m_rExport.m_bOutFlyFrameAttrs )
7614 OString sWrap( "auto" );
7615 switch ( rSurround.GetSurround( ) )
7617 case SURROUND_NONE:
7618 sWrap = OString( "none" );
7619 break;
7620 case SURROUND_THROUGHT:
7621 sWrap = OString( "through" );
7622 break;
7623 case SURROUND_IDEAL:
7624 case SURROUND_PARALLEL:
7625 case SURROUND_LEFT:
7626 case SURROUND_RIGHT:
7627 default:
7628 sWrap = OString( "around" );
7631 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_wrap ), sWrap.getStr() );
7635 void DocxAttributeOutput::FormatVertOrientation( const SwFormatVertOrient& rFlyVert )
7637 OString sAlign;
7638 switch( rFlyVert.GetVertOrient() )
7640 case text::VertOrientation::NONE:
7641 break;
7642 case text::VertOrientation::CENTER:
7643 case text::VertOrientation::LINE_CENTER:
7644 sAlign = OString( "center" );
7645 break;
7646 case text::VertOrientation::BOTTOM:
7647 sAlign = OString( "bottom" );
7648 break;
7649 case text::VertOrientation::LINE_BOTTOM:
7650 sAlign = OString( "outside" );
7651 break;
7652 case text::VertOrientation::TOP:
7653 sAlign = OString( "top" );
7654 break;
7655 case text::VertOrientation::LINE_TOP:
7656 default:
7657 sAlign = OString( "inside" );
7658 break;
7660 OString sVAnchor( "page" );
7661 switch ( rFlyVert.GetRelationOrient( ) )
7663 case text::RelOrientation::CHAR:
7664 case text::RelOrientation::PRINT_AREA:
7665 case text::RelOrientation::TEXT_LINE:
7666 case text::RelOrientation::FRAME:
7667 sVAnchor = OString( "text" );
7668 break;
7669 case text::RelOrientation::PAGE_LEFT:
7670 case text::RelOrientation::PAGE_RIGHT:
7671 case text::RelOrientation::FRAME_LEFT:
7672 case text::RelOrientation::FRAME_RIGHT:
7673 case text::RelOrientation::PAGE_PRINT_AREA:
7674 sVAnchor = OString( "margin" );
7675 break;
7676 case text::RelOrientation::PAGE_FRAME:
7677 default:
7678 break;
7681 if (m_rExport.SdrExporter().getTextFrameSyntax())
7683 m_rExport.SdrExporter().getTextFrameStyle().append(";margin-top:").append(double(rFlyVert.GetPos()) / 20).append("pt");
7684 if ( !sAlign.isEmpty() )
7685 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical:").append(sAlign);
7686 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-vertical-relative:").append(sVAnchor);
7688 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7691 else if ( m_rExport.m_bOutFlyFrameAttrs )
7693 if ( !sAlign.isEmpty() )
7694 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_yAlign ), sAlign.getStr() );
7695 else
7696 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_y ),
7697 OString::number( rFlyVert.GetPos() ).getStr() );
7698 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_vAnchor ), sVAnchor.getStr() );
7702 void DocxAttributeOutput::FormatHorizOrientation( const SwFormatHoriOrient& rFlyHori )
7704 OString sAlign;
7705 switch( rFlyHori.GetHoriOrient() )
7707 case text::HoriOrientation::NONE:
7708 break;
7709 case text::HoriOrientation::LEFT:
7710 sAlign = OString( rFlyHori.IsPosToggle( ) ? "inside" : "left" );
7711 break;
7712 case text::HoriOrientation::RIGHT:
7713 sAlign = OString( rFlyHori.IsPosToggle( ) ? "outside" : "right" );
7714 break;
7715 case text::HoriOrientation::CENTER:
7716 case text::HoriOrientation::FULL: // FULL only for tables
7717 default:
7718 sAlign = OString( "center" );
7719 break;
7721 OString sHAnchor( "page" );
7722 switch ( rFlyHori.GetRelationOrient( ) )
7724 case text::RelOrientation::CHAR:
7725 case text::RelOrientation::PRINT_AREA:
7726 case text::RelOrientation::FRAME:
7727 sHAnchor = OString( "text" );
7728 break;
7729 case text::RelOrientation::PAGE_LEFT:
7730 case text::RelOrientation::PAGE_RIGHT:
7731 case text::RelOrientation::FRAME_LEFT:
7732 case text::RelOrientation::FRAME_RIGHT:
7733 case text::RelOrientation::PAGE_PRINT_AREA:
7734 sHAnchor = OString( "margin" );
7735 break;
7736 case text::RelOrientation::PAGE_FRAME:
7737 default:
7738 break;
7741 if (m_rExport.SdrExporter().getTextFrameSyntax())
7743 m_rExport.SdrExporter().getTextFrameStyle().append(";margin-left:").append(double(rFlyHori.GetPos()) / 20).append("pt");
7744 if ( !sAlign.isEmpty() )
7745 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal:").append(sAlign);
7746 m_rExport.SdrExporter().getTextFrameStyle().append(";mso-position-horizontal-relative:").append(sHAnchor);
7748 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7751 else if ( m_rExport.m_bOutFlyFrameAttrs )
7753 if ( !sAlign.isEmpty() )
7754 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_xAlign ), sAlign.getStr() );
7755 else
7756 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_x ),
7757 OString::number( rFlyHori.GetPos() ).getStr() );
7758 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), FSNS( XML_w, XML_hAnchor ), sHAnchor.getStr() );
7762 void DocxAttributeOutput::FormatAnchor( const SwFormatAnchor& )
7764 // Fly frames: anchors here aren't matching the anchors in docx
7767 boost::optional<sal_Int32> lcl_getDmlAlpha(const SvxBrushItem& rBrush)
7769 boost::optional<sal_Int32> oRet;
7770 sal_Int32 nTransparency = rBrush.GetColor().GetTransparency();
7771 if (nTransparency)
7773 // Convert transparency to percent
7774 sal_Int8 nTransparencyPercent = SvxBrushItem::TransparencyToPercent(nTransparency);
7776 // Calculate alpha value
7777 // Consider oox/source/drawingml/color.cxx : getTransparency() function.
7778 sal_Int32 nAlpha = (::oox::drawingml::MAX_PERCENT - ( ::oox::drawingml::PER_PERCENT * nTransparencyPercent ) );
7779 oRet = nAlpha;
7781 return oRet;
7784 void DocxAttributeOutput::FormatBackground( const SvxBrushItem& rBrush )
7786 OString sColor = msfilter::util::ConvertColor( rBrush.GetColor().GetRGBColor() );
7787 boost::optional<sal_Int32> oAlpha = lcl_getDmlAlpha(rBrush);
7788 if (m_rExport.SdrExporter().getTextFrameSyntax())
7790 // Handle 'Opacity'
7791 if (oAlpha)
7793 // Calculate opacity value
7794 // Consider oox/source/vml/vmlformatting.cxx : decodeColor() function.
7795 double fOpacity = (double)(*oAlpha) * 65535 / ::oox::drawingml::MAX_PERCENT;
7796 OUString sOpacity = OUString::number(fOpacity) + "f";
7798 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_opacity, OUStringToOString(sOpacity, RTL_TEXTENCODING_UTF8).getStr() );
7801 sColor = "#" + sColor;
7802 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, sColor.getStr() );
7804 else if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7806 bool bImageBackground = false;
7807 const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE);
7808 if (pItem)
7810 const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem);
7811 if(pFillStyle->GetValue() == drawing::FillStyle_BITMAP)
7813 bImageBackground = true;
7816 if (!bImageBackground)
7818 m_pSerializer->startElementNS(XML_a, XML_solidFill, FSEND);
7819 m_pSerializer->startElementNS(XML_a, XML_srgbClr,
7820 XML_val, sColor,
7821 FSEND);
7822 if (oAlpha)
7823 m_pSerializer->singleElementNS(XML_a, XML_alpha,
7824 XML_val, OString::number(*oAlpha),
7825 FSEND);
7826 m_pSerializer->endElementNS(XML_a, XML_srgbClr);
7827 m_pSerializer->endElementNS(XML_a, XML_solidFill);
7830 else if ( !m_rExport.m_bOutPageDescs )
7832 // compare fill color with the original fill color
7833 OString sOriginalFill = OUStringToOString(
7834 m_sOriginalBackgroundColor, RTL_TEXTENCODING_UTF8 );
7836 if( !m_pBackgroundAttrList.is() )
7838 m_pBackgroundAttrList = FastSerializerHelper::createAttrList();
7839 m_pBackgroundAttrList->add( FSNS( XML_w, XML_fill ), sColor.getStr() );
7840 m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" );
7842 else if ( sOriginalFill != sColor )
7844 // fill was modified during edition, theme fill attribute must be dropped
7845 m_pBackgroundAttrList = FastSerializerHelper::createAttrList();
7846 m_pBackgroundAttrList->add( FSNS( XML_w, XML_fill ), sColor.getStr() );
7847 m_pBackgroundAttrList->add( FSNS( XML_w, XML_val ), "clear" );
7849 m_sOriginalBackgroundColor.clear();
7853 void DocxAttributeOutput::FormatFillStyle( const XFillStyleItem& rFillStyle )
7855 if (!m_bIgnoreNextFill)
7856 m_oFillStyle.reset(rFillStyle.GetValue());
7857 else
7858 m_bIgnoreNextFill = false;
7861 void DocxAttributeOutput::FormatFillGradient( const XFillGradientItem& rFillGradient )
7863 if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && !m_rExport.SdrExporter().getDMLTextFrameSyntax())
7865 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_type, "gradient" );
7867 const XGradient& rGradient = rFillGradient.GetGradientValue();
7868 OString sStartColor = msfilter::util::ConvertColor(rGradient.GetStartColor());
7869 OString sEndColor = msfilter::util::ConvertColor(rGradient.GetEndColor());
7871 // Calculate the angle that was originally in the imported DOCX file
7872 // (reverse calculate the angle that was converted in the file
7873 // /oox/source/vml/vmlformatting.cxx :: FillModel::pushToPropMap
7874 // and also in
7875 // /oox/source/drawingml/fillproperties.cxx :: FillProperties::pushToPropMap
7876 sal_Int32 nReverseAngle = 4500 - rGradient.GetAngle();
7877 nReverseAngle = nReverseAngle / 10;
7878 nReverseAngle = (270 - nReverseAngle) % 360;
7879 if (nReverseAngle != 0)
7880 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(),
7881 XML_angle, OString::number( nReverseAngle ).getStr() );
7883 OString sColor1 = sStartColor;
7884 OString sColor2 = sEndColor;
7886 switch (rGradient.GetGradientStyle())
7888 case css::awt::GradientStyle_AXIAL:
7889 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_focus, "50%" );
7890 // If it is an 'axial' gradient - swap the colors
7891 // (because in the import process they were imported swapped)
7892 sColor1 = sEndColor;
7893 sColor2 = sStartColor;
7894 break;
7895 case css::awt::GradientStyle_LINEAR: break;
7896 case css::awt::GradientStyle_RADIAL: break;
7897 case css::awt::GradientStyle_ELLIPTICAL: break;
7898 case css::awt::GradientStyle_SQUARE: break;
7899 case css::awt::GradientStyle_RECT: break;
7900 default:
7901 break;
7904 sColor1 = "#" + sColor1;
7905 sColor2 = "#" + sColor2;
7906 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), XML_fillcolor, sColor1.getStr() );
7907 AddToAttrList( m_rExport.SdrExporter().getFlyFillAttrList(), XML_color2, sColor2.getStr() );
7909 else if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_GRADIENT && m_rExport.SdrExporter().getDMLTextFrameSyntax())
7911 SwFrameFormat & rFormat(
7912 const_cast<SwFrameFormat&>(m_rExport.m_pParentFrame->GetFrameFormat()));
7913 uno::Reference<beans::XPropertySet> const xPropertySet(
7914 SwXTextFrame::CreateXTextFrame(*rFormat.GetDoc(), &rFormat),
7915 uno::UNO_QUERY);
7916 m_rDrawingML.SetFS(m_pSerializer);
7917 m_rDrawingML.WriteGradientFill(xPropertySet);
7919 m_oFillStyle.reset();
7922 void DocxAttributeOutput::FormatBox( const SvxBoxItem& rBox )
7924 if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7926 // <a:gradFill> should be before <a:ln>.
7927 const SfxPoolItem* pItem = GetExport().HasItem(XATTR_FILLSTYLE);
7928 if (pItem)
7930 const XFillStyleItem* pFillStyle = static_cast<const XFillStyleItem*>(pItem);
7931 FormatFillStyle(*pFillStyle);
7932 if (m_oFillStyle && *m_oFillStyle == drawing::FillStyle_BITMAP)
7934 const SdrObject* pSdrObj = m_rExport.m_pParentFrame->GetFrameFormat().FindRealSdrObject();
7935 if (pSdrObj)
7937 uno::Reference< drawing::XShape > xShape( const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY );
7938 uno::Reference< beans::XPropertySet > xPropertySet( xShape, uno::UNO_QUERY );
7939 m_rDrawingML.SetFS(m_pSerializer);
7940 m_rDrawingML.WriteBlipFill( xPropertySet, "BackGraphicURL" );
7945 pItem = GetExport().HasItem(XATTR_FILLGRADIENT);
7946 if (pItem)
7948 const XFillGradientItem* pFillGradient = static_cast<const XFillGradientItem*>(pItem);
7949 FormatFillGradient(*pFillGradient);
7951 m_bIgnoreNextFill = true;
7953 if (m_rExport.SdrExporter().getTextFrameSyntax() || m_rExport.SdrExporter().getDMLTextFrameSyntax())
7955 const SvxBorderLine* pLeft = rBox.GetLeft( );
7956 const SvxBorderLine* pTop = rBox.GetTop( );
7957 const SvxBorderLine* pRight = rBox.GetRight( );
7958 const SvxBorderLine* pBottom = rBox.GetBottom( );
7960 if (pLeft && pRight && pTop && pBottom &&
7961 *pLeft == *pRight && *pLeft == *pTop && *pLeft == *pBottom)
7963 // Check border style
7964 editeng::SvxBorderStyle eBorderStyle = pTop->GetBorderLineStyle();
7965 if (eBorderStyle == table::BorderLineStyle::NONE)
7967 if (m_rExport.SdrExporter().getTextFrameSyntax())
7969 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2,
7970 XML_stroked, "f", XML_strokeweight, "0pt" );
7973 else
7975 OString sColor(msfilter::util::ConvertColor(pTop->GetColor()));
7976 double const fConverted(editeng::ConvertBorderWidthToWord(pTop->GetBorderLineStyle(), pTop->GetWidth()));
7978 if (m_rExport.SdrExporter().getTextFrameSyntax())
7980 sColor = "#" + sColor;
7981 sal_Int32 nWidth = sal_Int32(fConverted / 20);
7982 OString sWidth = OString::number(nWidth) + "pt";
7983 AddToAttrList( m_rExport.SdrExporter().getFlyAttrList(), 2,
7984 XML_strokecolor, sColor.getStr(),
7985 XML_strokeweight, sWidth.getStr() );
7986 if( drawing::LineStyle_DASH == pTop->GetBorderLineStyle() ) // Line Style is Dash type
7987 AddToAttrList( m_rExport.SdrExporter().getDashLineStyle(),
7988 XML_dashstyle, "dash" );
7990 else
7991 m_rExport.SdrExporter().writeBoxItemLine(rBox);
7995 if (m_rExport.SdrExporter().getDMLTextFrameSyntax())
7997 m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_lIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::LEFT))));
7998 m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_tIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::TOP))));
7999 m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_rIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::RIGHT))));
8000 m_rExport.SdrExporter().getBodyPrAttrList()->add(XML_bIns, OString::number(TwipsToEMU(rBox.GetDistance(SvxBoxItemLine::BOTTOM))));
8001 return;
8004 // v:textbox's inset attribute: inner margin values for textbox text - write only non-default values
8005 double fDistanceLeftTwips = double(rBox.GetDistance(SvxBoxItemLine::LEFT));
8006 double fDistanceTopTwips = double(rBox.GetDistance(SvxBoxItemLine::TOP));
8007 double fDistanceRightTwips = double(rBox.GetDistance(SvxBoxItemLine::RIGHT));
8008 double fDistanceBottomTwips = double(rBox.GetDistance(SvxBoxItemLine::BOTTOM));
8010 // Convert 'TWIPS' to 'INCH' (because in Word the default values are in Inches)
8011 double fDistanceLeftInch = fDistanceLeftTwips / 1440;
8012 double fDistanceTopInch = fDistanceTopTwips / 1440;
8013 double fDistanceRightInch = fDistanceRightTwips / 1440;
8014 double fDistanceBottomInch = fDistanceBottomTwips / 1440;
8016 // This code will write ONLY the non-default values. The values are in 'left','top','right','bottom' order.
8017 // so 'bottom' is checked if it is default and if it is non-default - all the values will be written
8018 // otherwise - 'right' is checked if it is default and if it is non-default - all the values except for 'bottom' will be written
8019 // and so on.
8020 OStringBuffer aInset;
8021 if(!aInset.isEmpty() || fDistanceBottomInch != double(0.05))
8022 aInset.insert(0, "," + OString::number(fDistanceBottomInch) + "in");
8024 if(!aInset.isEmpty() || fDistanceRightInch != double(0.1))
8025 aInset.insert(0, "," + OString::number(fDistanceRightInch) + "in");
8027 if(!aInset.isEmpty() || fDistanceTopInch != double(0.05))
8028 aInset.insert(0, "," + OString::number(fDistanceTopInch) + "in");
8030 if(!aInset.isEmpty() || fDistanceLeftInch != double(0.1))
8031 aInset.insert(0, OString::number(fDistanceLeftInch) + "in");
8033 if (!aInset.isEmpty())
8034 m_rExport.SdrExporter().getTextboxAttrList()->add(XML_inset, aInset.makeStringAndClear());
8036 return;
8039 OutputBorderOptions aOutputBorderOptions = lcl_getBoxBorderOptions();
8040 // Check if there is a shadow item
8041 const SfxPoolItem* pItem = GetExport().HasItem( RES_SHADOW );
8042 if ( pItem )
8044 const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
8045 aOutputBorderOptions.aShadowLocation = pShadowItem->GetLocation();
8048 if ( !m_bOpenedSectPr || GetWritingHeaderFooter())
8050 // Not inside a section
8052 // Open the paragraph's borders tag
8053 m_pSerializer->startElementNS( XML_w, XML_pBdr, FSEND );
8055 std::map<SvxBoxItemLine, css::table::BorderLine2> aEmptyMap; // empty styles map
8056 impl_borders( m_pSerializer, rBox, aOutputBorderOptions, aEmptyMap );
8058 // Close the paragraph's borders tag
8059 m_pSerializer->endElementNS( XML_w, XML_pBdr );
8063 void DocxAttributeOutput::FormatColumns_Impl( sal_uInt16 nCols, const SwFormatCol& rCol, bool bEven, SwTwips nPageSize )
8065 // Get the columns attributes
8066 FastAttributeList *pColsAttrList = FastSerializerHelper::createAttrList();
8068 pColsAttrList->add( FSNS( XML_w, XML_num ),
8069 OString::number( nCols ). getStr( ) );
8071 const char* pEquals = "false";
8072 if ( bEven )
8074 sal_uInt16 nWidth = rCol.GetGutterWidth( true );
8075 pColsAttrList->add( FSNS( XML_w, XML_space ),
8076 OString::number( nWidth ).getStr( ) );
8078 pEquals = "true";
8081 pColsAttrList->add( FSNS( XML_w, XML_equalWidth ), pEquals );
8083 bool bHasSep = (COLADJ_NONE != rCol.GetLineAdj());
8085 pColsAttrList->add( FSNS( XML_w, XML_sep ), OString::boolean( bHasSep ) );
8087 // Write the element
8088 m_pSerializer->startElementNS( XML_w, XML_cols, pColsAttrList );
8090 // Write the columns width if non-equals
8091 const SwColumns & rColumns = rCol.GetColumns( );
8092 if ( !bEven )
8094 for ( sal_uInt16 n = 0; n < nCols; ++n )
8096 FastAttributeList *pColAttrList = FastSerializerHelper::createAttrList();
8097 sal_uInt16 nWidth = rCol.CalcPrtColWidth( n, ( sal_uInt16 ) nPageSize );
8098 pColAttrList->add( FSNS( XML_w, XML_w ),
8099 OString::number( nWidth ).getStr( ) );
8101 if ( n + 1 != nCols )
8103 sal_uInt16 nSpacing = rColumns[n].GetRight( ) + rColumns[n + 1].GetLeft( );
8104 pColAttrList->add( FSNS( XML_w, XML_space ),
8105 OString::number( nSpacing ).getStr( ) );
8108 m_pSerializer->singleElementNS( XML_w, XML_col, pColAttrList );
8112 m_pSerializer->endElementNS( XML_w, XML_cols );
8115 void DocxAttributeOutput::FormatKeep( const SvxFormatKeepItem& rItem )
8117 m_pSerializer->singleElementNS( XML_w, XML_keepNext,
8118 FSNS( XML_w, XML_val ), OString::boolean( rItem.GetValue() ),
8119 FSEND );
8122 void DocxAttributeOutput::FormatTextGrid( const SwTextGridItem& rGrid )
8124 FastAttributeList *pGridAttrList = FastSerializerHelper::createAttrList();
8126 OString sGridType;
8127 switch ( rGrid.GetGridType( ) )
8129 default:
8130 case GRID_NONE:
8131 sGridType = OString( "default" );
8132 break;
8133 case GRID_LINES_ONLY:
8134 sGridType = OString( "lines" );
8135 break;
8136 case GRID_LINES_CHARS:
8137 if ( rGrid.IsSnapToChars( ) )
8138 sGridType = OString( "snapToChars" );
8139 else
8140 sGridType = OString( "linesAndChars" );
8141 break;
8143 pGridAttrList->add( FSNS( XML_w, XML_type ), sGridType.getStr( ) );
8145 sal_uInt16 nHeight = rGrid.GetBaseHeight() + rGrid.GetRubyHeight();
8146 pGridAttrList->add( FSNS( XML_w, XML_linePitch ),
8147 OString::number( nHeight ).getStr( ) );
8149 pGridAttrList->add( FSNS( XML_w, XML_charSpace ),
8150 OString::number( GridCharacterPitch( rGrid ) ).getStr( ) );
8152 m_pSerializer->singleElementNS( XML_w, XML_docGrid, pGridAttrList );
8155 void DocxAttributeOutput::FormatLineNumbering( const SwFormatLineNumber& rNumbering )
8157 if ( !rNumbering.IsCount( ) )
8158 m_pSerializer->singleElementNS( XML_w, XML_suppressLineNumbers, FSEND );
8161 void DocxAttributeOutput::FormatFrameDirection( const SvxFrameDirectionItem& rDirection )
8163 OString sTextFlow;
8164 bool bBiDi = false;
8165 short nDir = rDirection.GetValue();
8167 if ( nDir == FRMDIR_ENVIRONMENT )
8168 nDir = GetExport( ).GetDefaultFrameDirection( );
8170 switch ( nDir )
8172 default:
8173 case FRMDIR_HORI_LEFT_TOP:
8174 sTextFlow = OString( "lrTb" );
8175 break;
8176 case FRMDIR_HORI_RIGHT_TOP:
8177 sTextFlow = OString( "lrTb" );
8178 bBiDi = true;
8179 break;
8180 case FRMDIR_VERT_TOP_LEFT: // many things but not this one
8181 case FRMDIR_VERT_TOP_RIGHT:
8182 sTextFlow = OString( "tbRl" );
8183 break;
8186 if ( m_rExport.m_bOutPageDescs )
8188 m_pSerializer->singleElementNS( XML_w, XML_textDirection,
8189 FSNS( XML_w, XML_val ), sTextFlow.getStr( ),
8190 FSEND );
8191 if ( bBiDi )
8192 m_pSerializer->singleElementNS( XML_w, XML_bidi, FSEND );
8194 else if ( !m_rExport.m_bOutFlyFrameAttrs )
8196 if ( bBiDi )
8197 m_pSerializer->singleElementNS( XML_w, XML_bidi, FSNS( XML_w, XML_val ), "1", FSEND );
8198 else
8199 m_pSerializer->singleElementNS( XML_w, XML_bidi, FSNS( XML_w, XML_val ), "0", FSEND );
8203 void DocxAttributeOutput::ParaGrabBag(const SfxGrabBagItem& rItem)
8205 const std::map<OUString, css::uno::Any>& rMap = rItem.GetGrabBag();
8206 for (std::map<OUString, css::uno::Any>::const_iterator i = rMap.begin(); i != rMap.end(); ++i)
8208 if (i->first == "MirrorIndents")
8209 m_pSerializer->singleElementNS(XML_w, XML_mirrorIndents, FSEND);
8210 else if (i->first == "ParaTopMarginBeforeAutoSpacing")
8212 m_bParaBeforeAutoSpacing = true;
8213 // get fixed value which was set during import
8214 i->second >>= m_nParaBeforeSpacing;
8215 m_nParaBeforeSpacing = convertMm100ToTwip(m_nParaBeforeSpacing);
8216 SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << i->first << " : m_nParaBeforeSpacing= " << m_nParaBeforeSpacing);
8218 else if (i->first == "ParaBottomMarginAfterAutoSpacing")
8220 m_bParaAfterAutoSpacing = true;
8221 // get fixed value which was set during import
8222 i->second >>= m_nParaAfterSpacing;
8223 m_nParaAfterSpacing = convertMm100ToTwip(m_nParaAfterSpacing);
8224 SAL_INFO("sw.ww8", "DocxAttributeOutput::ParaGrabBag: property =" << i->first << " : m_nParaBeforeSpacing= " << m_nParaAfterSpacing);
8226 else if (i->first == "CharThemeFill")
8228 uno::Sequence<beans::PropertyValue> aGrabBagSeq;
8229 i->second >>= aGrabBagSeq;
8231 for (sal_Int32 j=0; j < aGrabBagSeq.getLength(); ++j)
8233 OString sVal = OUStringToOString(aGrabBagSeq[j].Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
8235 if (sVal.isEmpty())
8236 continue;
8238 if (aGrabBagSeq[j].Name == "val")
8239 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_val), sVal.getStr());
8240 else if (aGrabBagSeq[j].Name == "color")
8241 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_color), sVal.getStr());
8242 else if (aGrabBagSeq[j].Name == "themeColor")
8243 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeColor), sVal.getStr());
8244 else if (aGrabBagSeq[j].Name == "themeTint")
8245 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeTint), sVal.getStr());
8246 else if (aGrabBagSeq[j].Name == "themeShade")
8247 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeShade), sVal.getStr());
8248 else if (aGrabBagSeq[j].Name == "fill")
8249 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_fill), sVal.getStr());
8250 else if (aGrabBagSeq[j].Name == "themeFill")
8251 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFill), sVal.getStr());
8252 else if (aGrabBagSeq[j].Name == "themeFillTint")
8253 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillTint), sVal.getStr());
8254 else if (aGrabBagSeq[j].Name == "themeFillShade")
8255 AddToAttrList(m_pBackgroundAttrList, FSNS(XML_w, XML_themeFillShade), sVal.getStr());
8256 else if (aGrabBagSeq[j].Name == "originalColor")
8257 aGrabBagSeq[j].Value >>= m_sOriginalBackgroundColor;
8260 else if (i->first == "SdtPr")
8262 uno::Sequence<beans::PropertyValue> aGrabBagSdt =
8263 i->second.get< uno::Sequence<beans::PropertyValue> >();
8264 for (sal_Int32 k=0; k < aGrabBagSdt.getLength(); ++k)
8266 beans::PropertyValue aPropertyValue = aGrabBagSdt[k];
8267 if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj" ||
8268 aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
8270 if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartObj")
8271 m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartObj );
8272 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_docPartList")
8273 m_nParagraphSdtPrToken = FSNS( XML_w, XML_docPartList );
8275 uno::Sequence<beans::PropertyValue> aGrabBag;
8276 aPropertyValue.Value >>= aGrabBag;
8277 for (sal_Int32 j=0; j < aGrabBag.getLength(); ++j)
8279 OUString sValue = aGrabBag[j].Value.get<OUString>();
8280 if (aGrabBag[j].Name == "ooxml:CT_SdtDocPart_docPartGallery")
8281 AddToAttrList( m_pParagraphSdtPrTokenChildren,
8282 FSNS( XML_w, XML_docPartGallery ),
8283 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8284 else if (aGrabBag[j].Name == "ooxml:CT_SdtDocPart_docPartCategory")
8285 AddToAttrList( m_pParagraphSdtPrTokenChildren,
8286 FSNS( XML_w, XML_docPartCategory ),
8287 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8288 else if (aGrabBag[j].Name == "ooxml:CT_SdtDocPart_docPartUnique")
8290 if (sValue.isEmpty())
8291 sValue = "true";
8292 AddToAttrList( m_pParagraphSdtPrTokenChildren, FSNS( XML_w, XML_docPartUnique ),
8293 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8297 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_equation")
8298 m_nParagraphSdtPrToken = FSNS( XML_w, XML_equation );
8299 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_picture")
8300 m_nParagraphSdtPrToken = FSNS( XML_w, XML_picture );
8301 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
8302 m_nParagraphSdtPrToken = FSNS( XML_w, XML_citation );
8303 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_group")
8304 m_nParagraphSdtPrToken = FSNS( XML_w, XML_group );
8305 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text")
8306 m_nParagraphSdtPrToken = FSNS(XML_w, XML_text);
8307 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pParagraphSdtPrDataBindingAttrs.is())
8309 uno::Sequence<beans::PropertyValue> aGrabBag;
8310 aPropertyValue.Value >>= aGrabBag;
8311 for (sal_Int32 j = 0; j < aGrabBag.getLength(); ++j)
8313 OUString sValue = aGrabBag[j].Value.get<OUString>();
8314 if (aGrabBag[j].Name == "ooxml:CT_DataBinding_prefixMappings")
8315 AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
8316 FSNS( XML_w, XML_prefixMappings ),
8317 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8318 else if (aGrabBag[j].Name == "ooxml:CT_DataBinding_xpath")
8319 AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
8320 FSNS( XML_w, XML_xpath ),
8321 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8322 else if (aGrabBag[j].Name == "ooxml:CT_DataBinding_storeItemID")
8323 AddToAttrList( m_pParagraphSdtPrDataBindingAttrs,
8324 FSNS( XML_w, XML_storeItemID ),
8325 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8328 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aParagraphSdtPrAlias.isEmpty())
8330 if (!(aPropertyValue.Value >>= m_aParagraphSdtPrAlias))
8331 SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unexpected sdt alias value");
8332 m_aStartedParagraphSdtPrAlias = m_aParagraphSdtPrAlias;
8334 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
8336 m_nParagraphSdtPrToken = FSNS( XML_w14, XML_checkbox );
8337 uno::Sequence<beans::PropertyValue> aGrabBag;
8338 aPropertyValue.Value >>= aGrabBag;
8339 for (sal_Int32 j=0; j < aGrabBag.getLength(); ++j)
8341 OUString sValue = aGrabBag[j].Value.get<OUString>();
8342 if (aGrabBag[j].Name == "ooxml:CT_SdtCheckbox_checked")
8343 AddToAttrList( m_pParagraphSdtPrTokenChildren,
8344 FSNS( XML_w14, XML_checked ),
8345 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8346 else if (aGrabBag[j].Name == "ooxml:CT_SdtCheckbox_checkedState")
8347 AddToAttrList( m_pParagraphSdtPrTokenChildren,
8348 FSNS( XML_w14, XML_checkedState ),
8349 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8350 else if (aGrabBag[j].Name == "ooxml:CT_SdtCheckbox_uncheckedState")
8351 AddToAttrList( m_pParagraphSdtPrTokenChildren,
8352 FSNS( XML_w14, XML_uncheckedState ),
8353 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8356 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id")
8357 m_bParagraphSdtHasId = true;
8358 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_date")
8360 m_nParagraphSdtPrToken = FSNS(XML_w, XML_date);
8361 uno::Sequence<beans::PropertyValue> aGrabBag = aPropertyValue.Value.get< uno::Sequence<beans::PropertyValue> >();
8362 for (sal_Int32 j=0; j < aGrabBag.getLength(); ++j)
8364 OString sValue = OUStringToOString(aGrabBag[j].Value.get<OUString>(), RTL_TEXTENCODING_UTF8);
8366 if (aGrabBag[j].Name == "ooxml:CT_SdtDate_fullDate")
8367 AddToAttrList(m_pParagraphSdtPrTokenAttributes, FSNS(XML_w, XML_fullDate), sValue.getStr());
8368 else if (aGrabBag[j].Name == "ooxml:CT_SdtDate_dateFormat")
8369 AddToAttrList(m_pParagraphSdtPrTokenChildren, FSNS(XML_w, XML_dateFormat), sValue.getStr());
8370 else if (aGrabBag[j].Name == "ooxml:CT_SdtDate_lid")
8371 AddToAttrList(m_pParagraphSdtPrTokenChildren, FSNS(XML_w, XML_lid), sValue.getStr());
8372 else if (aGrabBag[j].Name == "ooxml:CT_SdtDate_storeMappedDataAs")
8373 AddToAttrList(m_pParagraphSdtPrTokenChildren, FSNS(XML_w, XML_storeMappedDataAs), sValue.getStr());
8374 else if (aGrabBag[j].Name == "ooxml:CT_SdtDate_calendar")
8375 AddToAttrList(m_pParagraphSdtPrTokenChildren, FSNS(XML_w, XML_calendar), sValue.getStr());
8376 else
8377 SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled SdtPr / ooxml:CT_SdtPr_date grab bag property " << aGrabBag[j].Name);
8380 else
8381 SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled SdtPr grab bag property " << aPropertyValue.Name);
8384 else if (i->first == "ParaCnfStyle")
8386 uno::Sequence<beans::PropertyValue> aAttributes = i->second.get< uno::Sequence<beans::PropertyValue> >();
8387 m_pTableStyleExport->CnfStyle(aAttributes);
8389 else if (i->first == "ParaSdtEndBefore")
8391 // Handled already in StartParagraph().
8393 else
8394 SAL_WARN("sw.ww8", "DocxAttributeOutput::ParaGrabBag: unhandled grab bag property " << i->first );
8398 void DocxAttributeOutput::CharGrabBag( const SfxGrabBagItem& rItem )
8400 const std::map< OUString, css::uno::Any >& rMap = rItem.GetGrabBag();
8402 // get original values of theme-derived properties to check if they have changed during the edition
8403 bool bWriteCSTheme = true;
8404 bool bWriteAsciiTheme = true;
8405 bool bWriteEastAsiaTheme = true;
8406 bool bWriteThemeFontColor = true;
8407 OUString sOriginalValue;
8408 for ( std::map< OUString, css::uno::Any >::const_iterator i = rMap.begin(); i != rMap.end(); ++i )
8410 if ( m_pFontsAttrList.is() && i->first == "CharThemeFontNameCs" )
8412 if ( i->second >>= sOriginalValue )
8413 bWriteCSTheme =
8414 ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_cs ) ) == sOriginalValue );
8416 else if ( m_pFontsAttrList.is() && i->first == "CharThemeFontNameAscii" )
8418 if ( i->second >>= sOriginalValue )
8419 bWriteAsciiTheme =
8420 ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_ascii ) ) == sOriginalValue );
8422 else if ( m_pFontsAttrList.is() && i->first == "CharThemeFontNameEastAsia" )
8424 if ( i->second >>= sOriginalValue )
8425 bWriteEastAsiaTheme =
8426 ( m_pFontsAttrList->getOptionalValue( FSNS( XML_w, XML_eastAsia ) ) == sOriginalValue );
8428 else if ( m_pColorAttrList.is() && i->first == "CharThemeOriginalColor" )
8430 if ( i->second >>= sOriginalValue )
8431 bWriteThemeFontColor =
8432 ( m_pColorAttrList->getOptionalValue( FSNS( XML_w, XML_val ) ) == sOriginalValue );
8436 // save theme attributes back to the run properties
8437 OUString str;
8438 for ( std::map< OUString, css::uno::Any >::const_iterator i = rMap.begin(); i != rMap.end(); ++i )
8440 if ( i->first == "CharThemeNameAscii" && bWriteAsciiTheme )
8442 i->second >>= str;
8443 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_asciiTheme ),
8444 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
8446 else if ( i->first == "CharThemeNameCs" && bWriteCSTheme )
8448 i->second >>= str;
8449 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_cstheme ),
8450 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
8452 else if ( i->first == "CharThemeNameEastAsia" && bWriteEastAsiaTheme )
8454 i->second >>= str;
8455 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_eastAsiaTheme ),
8456 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
8458 else if ( i->first == "CharThemeNameHAnsi" && bWriteAsciiTheme )
8459 // this is not a mistake: in LibO we don't directly support the hAnsi family
8460 // of attributes so we save the same value from ascii attributes instead
8462 i->second >>= str;
8463 AddToAttrList( m_pFontsAttrList, FSNS( XML_w, XML_hAnsiTheme ),
8464 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
8466 else if ( i->first == "CharThemeColor" && bWriteThemeFontColor )
8468 i->second >>= str;
8469 AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeColor ),
8470 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
8472 else if ( i->first == "CharThemeColorShade" )
8474 i->second >>= str;
8475 AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeShade ),
8476 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
8478 else if ( i->first == "CharThemeColorTint" )
8480 i->second >>= str;
8481 AddToAttrList( m_pColorAttrList, FSNS( XML_w, XML_themeTint ),
8482 OUStringToOString( str, RTL_TEXTENCODING_UTF8 ).getStr() );
8484 else if( i->first == "CharThemeFontNameCs" ||
8485 i->first == "CharThemeFontNameAscii" ||
8486 i->first == "CharThemeFontNameEastAsia" ||
8487 i->first == "CharThemeOriginalColor" )
8489 // just skip these, they were processed before
8491 else if(i->first == "CharGlowTextEffect" ||
8492 i->first == "CharShadowTextEffect" ||
8493 i->first == "CharReflectionTextEffect" ||
8494 i->first == "CharTextOutlineTextEffect" ||
8495 i->first == "CharTextFillTextEffect" ||
8496 i->first == "CharScene3DTextEffect" ||
8497 i->first == "CharProps3DTextEffect" ||
8498 i->first == "CharLigaturesTextEffect" ||
8499 i->first == "CharNumFormTextEffect" ||
8500 i->first == "CharNumSpacingTextEffect" ||
8501 i->first == "CharStylisticSetsTextEffect" ||
8502 i->first == "CharCntxtAltsTextEffect")
8504 beans::PropertyValue aPropertyValue;
8505 i->second >>= aPropertyValue;
8506 m_aTextEffectsGrabBag.push_back(aPropertyValue);
8508 else if (i->first == "SdtEndBefore")
8510 if (m_bStartedCharSdt)
8511 m_bEndCharSdt = true;
8513 else if (i->first == "SdtPr" && FLY_NOT_PROCESSED != m_nStateOfFlyFrame )
8515 uno::Sequence<beans::PropertyValue> aGrabBagSdt =
8516 i->second.get< uno::Sequence<beans::PropertyValue> >();
8517 for (sal_Int32 k=0; k < aGrabBagSdt.getLength(); ++k)
8519 beans::PropertyValue aPropertyValue = aGrabBagSdt[k];
8520 if (aPropertyValue.Name == "ooxml:CT_SdtPr_checkbox")
8522 m_nRunSdtPrToken = FSNS( XML_w14, XML_checkbox );
8523 uno::Sequence<beans::PropertyValue> aGrabBag;
8524 aPropertyValue.Value >>= aGrabBag;
8525 for (sal_Int32 j=0; j < aGrabBag.getLength(); ++j)
8527 OUString sValue = aGrabBag[j].Value.get<OUString>();
8528 if (aGrabBag[j].Name == "ooxml:CT_SdtCheckbox_checked")
8529 AddToAttrList( m_pRunSdtPrTokenChildren,
8530 FSNS( XML_w14, XML_checked ),
8531 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8532 else if (aGrabBag[j].Name == "ooxml:CT_SdtCheckbox_checkedState")
8533 AddToAttrList( m_pRunSdtPrTokenChildren,
8534 FSNS( XML_w14, XML_checkedState ),
8535 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8536 else if (aGrabBag[j].Name == "ooxml:CT_SdtCheckbox_uncheckedState")
8537 AddToAttrList( m_pRunSdtPrTokenChildren,
8538 FSNS( XML_w14, XML_uncheckedState ),
8539 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8542 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_dataBinding" && !m_pRunSdtPrDataBindingAttrs.is())
8544 uno::Sequence<beans::PropertyValue> aGrabBag;
8545 aPropertyValue.Value >>= aGrabBag;
8546 for (sal_Int32 j=0; j < aGrabBag.getLength(); ++j)
8548 OUString sValue = aGrabBag[j].Value.get<OUString>();
8549 if (aGrabBag[j].Name == "ooxml:CT_DataBinding_prefixMappings")
8550 AddToAttrList( m_pRunSdtPrDataBindingAttrs,
8551 FSNS( XML_w, XML_prefixMappings ),
8552 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8553 else if (aGrabBag[j].Name == "ooxml:CT_DataBinding_xpath")
8554 AddToAttrList( m_pRunSdtPrDataBindingAttrs,
8555 FSNS( XML_w, XML_xpath ),
8556 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8557 else if (aGrabBag[j].Name == "ooxml:CT_DataBinding_storeItemID")
8558 AddToAttrList( m_pRunSdtPrDataBindingAttrs,
8559 FSNS( XML_w, XML_storeItemID ),
8560 OUStringToOString( sValue, RTL_TEXTENCODING_UTF8 ).getStr() );
8563 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_alias" && m_aRunSdtPrAlias.isEmpty())
8565 if (!(aPropertyValue.Value >>= m_aRunSdtPrAlias))
8566 SAL_WARN("sw.ww8", "DocxAttributeOutput::CharGrabBag: unexpected sdt alias value");
8568 //do not overwrite the parent node.
8569 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_text" && !m_pRunSdtPrTokenChildren.is())
8570 m_nRunSdtPrToken = FSNS( XML_w, XML_text );
8571 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_id" && m_nRunSdtPrToken == 0)
8572 // only write id token as a marker if no other exist
8573 m_nRunSdtPrToken = FSNS( XML_w, XML_id );
8574 else if (aPropertyValue.Name == "ooxml:CT_SdtPr_citation")
8575 m_nRunSdtPrToken = FSNS( XML_w, XML_citation );
8578 else
8579 SAL_INFO("sw.ww8", "DocxAttributeOutput::CharGrabBag: unhandled grab bag property " << i->first);
8583 DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, FSHelperPtr pSerializer, oox::drawingml::DrawingML* pDrawingML )
8584 : m_rExport( rExport ),
8585 m_pSerializer( pSerializer ),
8586 m_rDrawingML( *pDrawingML ),
8587 m_bEndCharSdt(false),
8588 m_bStartedCharSdt(false),
8589 m_bStartedParaSdt(false),
8590 m_endPageRef( false ),
8591 m_pFootnotesList( new ::docx::FootnotesList() ),
8592 m_pEndnotesList( new ::docx::FootnotesList() ),
8593 m_footnoteEndnoteRefTag( 0 ),
8594 m_pRedlineData( nullptr ),
8595 m_nRedlineId( 0 ),
8596 m_bOpenedSectPr( false ),
8597 m_bHadSectPr(false),
8598 m_bRunTextIsOn( false ),
8599 m_bWritingHeaderFooter( false ),
8600 m_bAnchorLinkedToNode(false),
8601 m_sFieldBkm( ),
8602 m_nNextBookmarkId( 0 ),
8603 m_nNextAnnotationMarkId( 0 ),
8604 m_pCurrentFrame( nullptr ),
8605 m_bParagraphOpened( false ),
8606 m_bParagraphFrameOpen( false ),
8607 m_bIsFirstParagraph( true ),
8608 m_bAlternateContentChoiceOpen( false ),
8609 m_bPostponedProcessingFly( false ),
8610 m_nColBreakStatus( COLBRK_NONE ),
8611 m_bPostponedPageBreak( false ),
8612 m_nTextFrameLevel( 0 ),
8613 m_closeHyperlinkInThisRun( false ),
8614 m_closeHyperlinkInPreviousRun( false ),
8615 m_startedHyperlink( false ),
8616 m_nHyperLinkCount(0),
8617 m_nFieldsInHyperlink( 0 ),
8618 m_bExportingOutline(false),
8619 m_nChartCount(0),
8620 m_postponedChart( nullptr ),
8621 pendingPlaceholder( nullptr ),
8622 m_postitFieldsMaxId( 0 ),
8623 m_anchorId( 1 ),
8624 m_nextFontId( 1 ),
8625 m_tableReference(new TableReference()),
8626 m_bIgnoreNextFill(false),
8627 m_bBtLr(false),
8628 m_pTableStyleExport(new DocxTableStyleExport(rExport.m_pDoc, pSerializer)),
8629 m_bParaBeforeAutoSpacing(false),
8630 m_bParaAfterAutoSpacing(false),
8631 m_nParaBeforeSpacing(0),
8632 m_nParaAfterSpacing(0),
8633 m_setFootnote(false)
8634 , m_nParagraphSdtPrToken(0)
8635 , m_nRunSdtPrToken(0)
8636 , m_nStateOfFlyFrame( FLY_NOT_PROCESSED )
8637 , m_bParagraphSdtHasId(false)
8639 // Push initial items to the RelId cache. In case the document contains no
8640 // special streams (headers, footers, etc.) then these items are used
8641 // during the full export.
8642 PushRelIdCache();
8645 DocxAttributeOutput::~DocxAttributeOutput()
8649 DocxExport& DocxAttributeOutput::GetExport()
8651 return m_rExport;
8654 void DocxAttributeOutput::SetSerializer( ::sax_fastparser::FSHelperPtr const & pSerializer )
8656 m_pSerializer = pSerializer;
8657 m_pTableStyleExport->SetSerializer(pSerializer);
8660 bool DocxAttributeOutput::HasFootnotes() const
8662 return !m_pFootnotesList->isEmpty();
8665 bool DocxAttributeOutput::HasEndnotes() const
8667 return !m_pEndnotesList->isEmpty();
8670 bool DocxAttributeOutput::HasPostitFields() const
8672 return !m_postitFields.empty();
8675 void DocxAttributeOutput::BulletDefinition(int nId, const Graphic& rGraphic, Size aSize)
8677 m_pSerializer->startElementNS(XML_w, XML_numPicBullet,
8678 FSNS(XML_w, XML_numPicBulletId), OString::number(nId).getStr(),
8679 FSEND);
8681 OStringBuffer aStyle;
8682 // Size is in twips, we need it in points.
8683 aStyle.append("width:").append(double(aSize.Width()) / 20);
8684 aStyle.append("pt;height:").append(double(aSize.Height()) / 20).append("pt");
8685 m_pSerializer->startElementNS( XML_w, XML_pict, FSEND);
8686 m_pSerializer->startElementNS( XML_v, XML_shape,
8687 XML_style, aStyle.getStr(),
8688 FSNS(XML_o, XML_bullet), "t",
8689 FSEND);
8691 m_rDrawingML.SetFS(m_pSerializer);
8692 OUString aRelId = m_rDrawingML.WriteImage(rGraphic);
8693 m_pSerializer->singleElementNS( XML_v, XML_imagedata,
8694 FSNS(XML_r, XML_id), OUStringToOString(aRelId, RTL_TEXTENCODING_UTF8),
8695 FSNS(XML_o, XML_title), "",
8696 FSEND);
8698 m_pSerializer->endElementNS(XML_v, XML_shape);
8699 m_pSerializer->endElementNS(XML_w, XML_pict);
8701 m_pSerializer->endElementNS(XML_w, XML_numPicBullet);
8704 void DocxAttributeOutput::AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrName, const sal_Char* sAttrValue )
8706 AddToAttrList( pAttrList, 1, nAttrName, sAttrValue );
8709 void DocxAttributeOutput::AddToAttrList( rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList, sal_Int32 nAttrs, ... )
8711 if( !pAttrList.is() )
8712 pAttrList = FastSerializerHelper::createAttrList();
8714 va_list args;
8715 va_start( args, nAttrs );
8716 for( sal_Int32 i = 0; i<nAttrs; i++)
8718 sal_Int32 nName = va_arg( args, sal_Int32 );
8719 const char* pValue = va_arg( args, const char* );
8720 if( pValue )
8721 pAttrList->add( nName, pValue );
8723 va_end( args );
8726 void DocxAttributeOutput::SetStartedParaSdt(bool bStartedParaSdt)
8728 m_bStartedParaSdt = bStartedParaSdt;
8731 bool DocxAttributeOutput::IsStartedParaSdt()
8733 return m_bStartedParaSdt;
8736 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */