1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9
10 #include "docxsdrexport.hxx"
11 #include <com/sun/star/beans/XPropertySet.hpp>
12 #include <com/sun/star/drawing/PointSequenceSequence.hpp>
13 #include <editeng/lrspitem.hxx>
14 #include <editeng/ulspitem.hxx>
15 #include <editeng/shaditem.hxx>
16 #include <editeng/opaqitem.hxx>
17 #include <editeng/boxitem.hxx>
18 #include <svx/svdogrp.hxx>
19 #include <oox/token/namespaces.hxx>
20 #include <textboxhelper.hxx>
21 #include <fmtanchr.hxx>
22 #include <fmtsrnd.hxx>
23 #include <fmtcntnt.hxx>
24 #include <fmtornt.hxx>
25 #include <fmtfsize.hxx>
26 #include <frmatr.hxx>
27 #include <fmtwrapinfluenceonobjpos.hxx>
28 #include "docxattributeoutput.hxx"
29 #include "docxexportfilter.hxx"
30 #include <comphelper/flagguard.hxx>
31 #include <comphelper/sequence.hxx>
32 #include <comphelper/sequenceashashmap.hxx>
33 #include <sal/log.hxx>
34
35 #include <IDocumentDrawModelAccess.hxx>
36
37 using namespace com::sun::star;
38 using namespace oox;
39
40 namespace
41 {
lclGetProperty(const uno::Reference<drawing::XShape> & rShape,const OUString & rPropName)42 uno::Sequence<beans::PropertyValue> lclGetProperty(const uno::Reference<drawing::XShape>& rShape,
43 const OUString& rPropName)
44 {
45 uno::Sequence<beans::PropertyValue> aResult;
46 uno::Reference<beans::XPropertySet> xPropertySet(rShape, uno::UNO_QUERY);
47 uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
48
49 if (!xPropertySet.is())
50 return aResult;
51
52 xPropSetInfo = xPropertySet->getPropertySetInfo();
53 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(rPropName))
54 {
55 xPropertySet->getPropertyValue(rPropName) >>= aResult;
56 }
57 return aResult;
58 }
59
lclGetAnchorIdFromGrabBag(const SdrObject * pObj)60 OUString lclGetAnchorIdFromGrabBag(const SdrObject* pObj)
61 {
62 OUString aResult;
63 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObj)->getUnoShape(),
64 uno::UNO_QUERY);
65 OUString aGrabBagName;
66 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY);
67 if (xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
68 aGrabBagName = "FrameInteropGrabBag";
69 else
70 aGrabBagName = "InteropGrabBag";
71 uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, aGrabBagName);
72 auto pProp
73 = std::find_if(propList.begin(), propList.end(),
74 [](const beans::PropertyValue& rProp) { return rProp.Name == "AnchorId"; });
75 if (pProp != propList.end())
76 pProp->Value >>= aResult;
77 return aResult;
78 }
79
lclMovePositionWithRotation(awt::Point & aPos,const Size & rSize,sal_Int64 nRotation)80 void lclMovePositionWithRotation(awt::Point& aPos, const Size& rSize, sal_Int64 nRotation)
81 {
82 // code from ImplEESdrWriter::ImplFlipBoundingBox (filter/source/msfilter/eschesdo.cxx)
83 // TODO: refactor
84
85 if (nRotation == 0)
86 return;
87
88 if (nRotation < 0)
89 nRotation = (36000 + nRotation) % 36000;
90 if (nRotation % 18000 == 0)
91 nRotation = 0;
92 while (nRotation > 9000)
93 nRotation = (18000 - (nRotation % 18000));
94
95 double fVal = static_cast<double>(nRotation) * F_PI18000;
96 double fCos = cos(fVal);
97 double fSin = sin(fVal);
98
99 double nWidthHalf = static_cast<double>(rSize.Width()) / 2;
100 double nHeightHalf = static_cast<double>(rSize.Height()) / 2;
101
102 double nXDiff = fSin * nHeightHalf + fCos * nWidthHalf - nWidthHalf;
103 double nYDiff = fSin * nWidthHalf + fCos * nHeightHalf - nHeightHalf;
104
105 aPos.X += nXDiff;
106 aPos.Y += nYDiff;
107 }
108
109 /// Determines if the anchor is inside a paragraph.
IsAnchorTypeInsideParagraph(const ww8::Frame * pFrame)110 bool IsAnchorTypeInsideParagraph(const ww8::Frame* pFrame)
111 {
112 const SwFormatAnchor& rAnchor = pFrame->GetFrameFormat().GetAttrSet().GetAnchor();
113 return rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE;
114 }
115 }
116
ExportDataSaveRestore(DocxExport & rExport,sal_uLong nStt,sal_uLong nEnd,ww8::Frame const * pParentFrame)117 ExportDataSaveRestore::ExportDataSaveRestore(DocxExport& rExport, sal_uLong nStt, sal_uLong nEnd,
118 ww8::Frame const* pParentFrame)
119 : m_rExport(rExport)
120 {
121 m_rExport.SaveData(nStt, nEnd);
122 m_rExport.m_pParentFrame = pParentFrame;
123 }
124
~ExportDataSaveRestore()125 ExportDataSaveRestore::~ExportDataSaveRestore() { m_rExport.RestoreData(); }
126
127 /// Holds data used by DocxSdrExport only.
128 struct DocxSdrExport::Impl
129 {
130 private:
131 DocxExport& m_rExport;
132 sax_fastparser::FSHelperPtr m_pSerializer;
133 oox::drawingml::DrawingML* m_pDrawingML;
134 const Size* m_pFlyFrameSize;
135 bool m_bTextFrameSyntax;
136 bool m_bDMLTextFrameSyntax;
137 rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyAttrList;
138 rtl::Reference<sax_fastparser::FastAttributeList> m_pTextboxAttrList;
139 OStringBuffer m_aTextFrameStyle;
140 bool m_bDrawingOpen;
141 bool m_bParagraphSdtOpen;
142 bool m_bParagraphHasDrawing; ///Flag for checking drawing in a paragraph.
143 rtl::Reference<sax_fastparser::FastAttributeList> m_pFlyFillAttrList;
144 sax_fastparser::FastAttributeList* m_pFlyWrapAttrList;
145 sax_fastparser::FastAttributeList* m_pBodyPrAttrList;
146 rtl::Reference<sax_fastparser::FastAttributeList> m_pDashLineStyleAttr;
147 bool m_bDMLAndVMLDrawingOpen;
148 /// List of TextBoxes in this document: they are exported as part of their shape, never alone.
149 /// Preserved rotation for TextFrames.
150 sal_Int32 m_nDMLandVMLTextFrameRotation;
151
152 public:
153 bool m_bFlyFrameGraphic = false;
154
ImplDocxSdrExport::Impl155 Impl(DocxExport& rExport, sax_fastparser::FSHelperPtr pSerializer,
156 oox::drawingml::DrawingML* pDrawingML)
157 : m_rExport(rExport)
158 , m_pSerializer(std::move(pSerializer))
159 , m_pDrawingML(pDrawingML)
160 , m_pFlyFrameSize(nullptr)
161 , m_bTextFrameSyntax(false)
162 , m_bDMLTextFrameSyntax(false)
163 , m_bDrawingOpen(false)
164 , m_bParagraphSdtOpen(false)
165 , m_bParagraphHasDrawing(false)
166 , m_pFlyWrapAttrList(nullptr)
167 , m_pBodyPrAttrList(nullptr)
168 , m_bDMLAndVMLDrawingOpen(false)
169 , m_nDMLandVMLTextFrameRotation(0)
170 {
171 }
172
173 /// Writes wp wrapper code around an SdrObject, which itself is written using drawingML syntax.
174
175 void textFrameShadow(const SwFrameFormat& rFrameFormat);
176 static bool isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape);
177
setSerializerDocxSdrExport::Impl178 void setSerializer(const sax_fastparser::FSHelperPtr& pSerializer)
179 {
180 m_pSerializer = pSerializer;
181 }
182
getSerializerDocxSdrExport::Impl183 const sax_fastparser::FSHelperPtr& getSerializer() const { return m_pSerializer; }
184
setFlyFrameSizeDocxSdrExport::Impl185 void setFlyFrameSize(const Size* pFlyFrameSize) { m_pFlyFrameSize = pFlyFrameSize; }
186
getFlyFrameSizeDocxSdrExport::Impl187 const Size* getFlyFrameSize() const { return m_pFlyFrameSize; }
188
setTextFrameSyntaxDocxSdrExport::Impl189 void setTextFrameSyntax(bool bTextFrameSyntax) { m_bTextFrameSyntax = bTextFrameSyntax; }
190
getTextFrameSyntaxDocxSdrExport::Impl191 bool getTextFrameSyntax() const { return m_bTextFrameSyntax; }
192
setDMLTextFrameSyntaxDocxSdrExport::Impl193 void setDMLTextFrameSyntax(bool bDMLTextFrameSyntax)
194 {
195 m_bDMLTextFrameSyntax = bDMLTextFrameSyntax;
196 }
197
getDMLTextFrameSyntaxDocxSdrExport::Impl198 bool getDMLTextFrameSyntax() const { return m_bDMLTextFrameSyntax; }
199
setFlyAttrListDocxSdrExport::Impl200 void setFlyAttrList(const rtl::Reference<sax_fastparser::FastAttributeList>& pFlyAttrList)
201 {
202 m_pFlyAttrList = pFlyAttrList;
203 }
204
getFlyAttrListDocxSdrExport::Impl205 rtl::Reference<sax_fastparser::FastAttributeList>& getFlyAttrList() { return m_pFlyAttrList; }
206
207 void
setTextboxAttrListDocxSdrExport::Impl208 setTextboxAttrList(const rtl::Reference<sax_fastparser::FastAttributeList>& pTextboxAttrList)
209 {
210 m_pTextboxAttrList = pTextboxAttrList;
211 }
212
getTextboxAttrListDocxSdrExport::Impl213 rtl::Reference<sax_fastparser::FastAttributeList>& getTextboxAttrList()
214 {
215 return m_pTextboxAttrList;
216 }
217
getTextFrameStyleDocxSdrExport::Impl218 OStringBuffer& getTextFrameStyle() { return m_aTextFrameStyle; }
219
setDrawingOpenDocxSdrExport::Impl220 void setDrawingOpen(bool bDrawingOpen) { m_bDrawingOpen = bDrawingOpen; }
221
getDrawingOpenDocxSdrExport::Impl222 bool getDrawingOpen() const { return m_bDrawingOpen; }
223
setParagraphSdtOpenDocxSdrExport::Impl224 void setParagraphSdtOpen(bool bParagraphSdtOpen) { m_bParagraphSdtOpen = bParagraphSdtOpen; }
225
getParagraphSdtOpenDocxSdrExport::Impl226 bool getParagraphSdtOpen() const { return m_bParagraphSdtOpen; }
227
setDMLAndVMLDrawingOpenDocxSdrExport::Impl228 void setDMLAndVMLDrawingOpen(bool bDMLAndVMLDrawingOpen)
229 {
230 m_bDMLAndVMLDrawingOpen = bDMLAndVMLDrawingOpen;
231 }
232
getDMLAndVMLDrawingOpenDocxSdrExport::Impl233 bool getDMLAndVMLDrawingOpen() const { return m_bDMLAndVMLDrawingOpen; }
234
setParagraphHasDrawingDocxSdrExport::Impl235 void setParagraphHasDrawing(bool bParagraphHasDrawing)
236 {
237 m_bParagraphHasDrawing = bParagraphHasDrawing;
238 }
239
getParagraphHasDrawingDocxSdrExport::Impl240 bool getParagraphHasDrawing() const { return m_bParagraphHasDrawing; }
241
getFlyFillAttrListDocxSdrExport::Impl242 rtl::Reference<sax_fastparser::FastAttributeList>& getFlyFillAttrList()
243 {
244 return m_pFlyFillAttrList;
245 }
246
setFlyWrapAttrListDocxSdrExport::Impl247 void setFlyWrapAttrList(sax_fastparser::FastAttributeList* pFlyWrapAttrList)
248 {
249 m_pFlyWrapAttrList = pFlyWrapAttrList;
250 }
251
getFlyWrapAttrListDocxSdrExport::Impl252 sax_fastparser::FastAttributeList* getFlyWrapAttrList() const { return m_pFlyWrapAttrList; }
253
setBodyPrAttrListDocxSdrExport::Impl254 void setBodyPrAttrList(sax_fastparser::FastAttributeList* pBodyPrAttrList)
255 {
256 m_pBodyPrAttrList = pBodyPrAttrList;
257 }
258
getBodyPrAttrListDocxSdrExport::Impl259 sax_fastparser::FastAttributeList* getBodyPrAttrList() const { return m_pBodyPrAttrList; }
260
getDashLineStyleAttrDocxSdrExport::Impl261 rtl::Reference<sax_fastparser::FastAttributeList>& getDashLineStyleAttr()
262 {
263 return m_pDashLineStyleAttr;
264 }
265
getFlyFrameGraphicDocxSdrExport::Impl266 bool getFlyFrameGraphic() const { return m_bFlyFrameGraphic; }
267
getDrawingMLDocxSdrExport::Impl268 oox::drawingml::DrawingML* getDrawingML() const { return m_pDrawingML; }
269
getExportDocxSdrExport::Impl270 DocxExport& getExport() const { return m_rExport; }
271
setDMLandVMLTextFrameRotationDocxSdrExport::Impl272 void setDMLandVMLTextFrameRotation(sal_Int32 nDMLandVMLTextFrameRotation)
273 {
274 m_nDMLandVMLTextFrameRotation = nDMLandVMLTextFrameRotation;
275 }
276
getDMLandVMLTextFrameRotationDocxSdrExport::Impl277 sal_Int32& getDMLandVMLTextFrameRotation() { return m_nDMLandVMLTextFrameRotation; }
278 };
279
DocxSdrExport(DocxExport & rExport,const sax_fastparser::FSHelperPtr & pSerializer,oox::drawingml::DrawingML * pDrawingML)280 DocxSdrExport::DocxSdrExport(DocxExport& rExport, const sax_fastparser::FSHelperPtr& pSerializer,
281 oox::drawingml::DrawingML* pDrawingML)
282 : m_pImpl(std::make_unique<Impl>(rExport, pSerializer, pDrawingML))
283 {
284 }
285
286 DocxSdrExport::~DocxSdrExport() = default;
287
setSerializer(const sax_fastparser::FSHelperPtr & pSerializer)288 void DocxSdrExport::setSerializer(const sax_fastparser::FSHelperPtr& pSerializer)
289 {
290 m_pImpl->setSerializer(pSerializer);
291 }
292
getFlyFrameSize() const293 const Size* DocxSdrExport::getFlyFrameSize() const { return m_pImpl->getFlyFrameSize(); }
294
getTextFrameSyntax() const295 bool DocxSdrExport::getTextFrameSyntax() const { return m_pImpl->getTextFrameSyntax(); }
296
getDMLTextFrameSyntax() const297 bool DocxSdrExport::getDMLTextFrameSyntax() const { return m_pImpl->getDMLTextFrameSyntax(); }
298
getFlyAttrList()299 rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getFlyAttrList()
300 {
301 return m_pImpl->getFlyAttrList();
302 }
303
getTextboxAttrList()304 rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getTextboxAttrList()
305 {
306 return m_pImpl->getTextboxAttrList();
307 }
308
getTextFrameStyle()309 OStringBuffer& DocxSdrExport::getTextFrameStyle() { return m_pImpl->getTextFrameStyle(); }
310
IsDrawingOpen() const311 bool DocxSdrExport::IsDrawingOpen() const { return m_pImpl->getDrawingOpen(); }
312
setParagraphSdtOpen(bool bParagraphSdtOpen)313 void DocxSdrExport::setParagraphSdtOpen(bool bParagraphSdtOpen)
314 {
315 m_pImpl->setParagraphSdtOpen(bParagraphSdtOpen);
316 }
317
IsDMLAndVMLDrawingOpen() const318 bool DocxSdrExport::IsDMLAndVMLDrawingOpen() const { return m_pImpl->getDMLAndVMLDrawingOpen(); }
319
IsParagraphHasDrawing() const320 bool DocxSdrExport::IsParagraphHasDrawing() const { return m_pImpl->getParagraphHasDrawing(); }
321
setParagraphHasDrawing(bool bParagraphHasDrawing)322 void DocxSdrExport::setParagraphHasDrawing(bool bParagraphHasDrawing)
323 {
324 m_pImpl->setParagraphHasDrawing(bParagraphHasDrawing);
325 }
326
getFlyFillAttrList()327 rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getFlyFillAttrList()
328 {
329 return m_pImpl->getFlyFillAttrList();
330 }
331
getFlyWrapAttrList()332 sax_fastparser::FastAttributeList* DocxSdrExport::getFlyWrapAttrList()
333 {
334 return m_pImpl->getFlyWrapAttrList();
335 }
336
getBodyPrAttrList()337 sax_fastparser::FastAttributeList* DocxSdrExport::getBodyPrAttrList()
338 {
339 return m_pImpl->getBodyPrAttrList();
340 }
341
getDashLineStyle()342 rtl::Reference<sax_fastparser::FastAttributeList>& DocxSdrExport::getDashLineStyle()
343 {
344 return m_pImpl->getDashLineStyleAttr();
345 }
346
setFlyWrapAttrList(sax_fastparser::FastAttributeList * pAttrList)347 void DocxSdrExport::setFlyWrapAttrList(sax_fastparser::FastAttributeList* pAttrList)
348 {
349 m_pImpl->setFlyWrapAttrList(pAttrList);
350 }
351
startDMLAnchorInline(const SwFrameFormat * pFrameFormat,const Size & rSize)352 void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize)
353 {
354 m_pImpl->setDrawingOpen(true);
355 m_pImpl->setParagraphHasDrawing(true);
356 m_pImpl->getSerializer()->startElementNS(XML_w, XML_drawing);
357
358 const SvxLRSpaceItem aLRSpaceItem = pFrameFormat->GetLRSpace(false);
359 const SvxULSpaceItem aULSpaceItem = pFrameFormat->GetULSpace(false);
360
361 bool isAnchor;
362
363 if (m_pImpl->getFlyFrameGraphic())
364 {
365 isAnchor = false; // make Graphic object inside DMLTextFrame & VMLTextFrame as Inline
366 }
367 else
368 {
369 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
370 }
371
372 // Count effectExtent values, their value is needed before dist{T,B,L,R} is written.
373 SvxShadowItem aShadowItem = pFrameFormat->GetShadow();
374 sal_Int32 nLeftExt = 0;
375 sal_Int32 nRightExt = 0;
376 sal_Int32 nTopExt = 0;
377 sal_Int32 nBottomExt = 0;
378 if (aShadowItem.GetLocation() != SvxShadowLocation::NONE)
379 {
380 sal_Int32 nShadowWidth(TwipsToEMU(aShadowItem.GetWidth()));
381 switch (aShadowItem.GetLocation())
382 {
383 case SvxShadowLocation::TopLeft:
384 nTopExt = nLeftExt = nShadowWidth;
385 break;
386 case SvxShadowLocation::TopRight:
387 nTopExt = nRightExt = nShadowWidth;
388 break;
389 case SvxShadowLocation::BottomLeft:
390 nBottomExt = nLeftExt = nShadowWidth;
391 break;
392 case SvxShadowLocation::BottomRight:
393 nBottomExt = nRightExt = nShadowWidth;
394 break;
395 case SvxShadowLocation::NONE:
396 case SvxShadowLocation::End:
397 break;
398 }
399 }
400 else if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject())
401 {
402 // No shadow, but we have an idea what was the original effectExtent.
403 uno::Any aAny;
404 pObject->GetGrabBagItem(aAny);
405 comphelper::SequenceAsHashMap aGrabBag(aAny);
406 auto it = aGrabBag.find("CT_EffectExtent");
407 if (it != aGrabBag.end())
408 {
409 comphelper::SequenceAsHashMap aEffectExtent(it->second);
410 for (const std::pair<const OUString, uno::Any>& rDirection : aEffectExtent)
411 {
412 if (rDirection.first == "l" && rDirection.second.has<sal_Int32>())
413 nLeftExt = rDirection.second.get<sal_Int32>();
414 else if (rDirection.first == "t" && rDirection.second.has<sal_Int32>())
415 nTopExt = rDirection.second.get<sal_Int32>();
416 else if (rDirection.first == "r" && rDirection.second.has<sal_Int32>())
417 nRightExt = rDirection.second.get<sal_Int32>();
418 else if (rDirection.first == "b" && rDirection.second.has<sal_Int32>())
419 nBottomExt = rDirection.second.get<sal_Int32>();
420 }
421 }
422 }
423
424 if (isAnchor)
425 {
426 sax_fastparser::FastAttributeList* attrList
427 = sax_fastparser::FastSerializerHelper::createAttrList();
428 bool bOpaque = pFrameFormat->GetOpaque().GetValue();
429 awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(),
430 pFrameFormat->GetVertOrient().GetPos());
431 const SdrObject* pObj = pFrameFormat->FindRealSdrObject();
432 long nRotation = 0;
433 if (pObj != nullptr)
434 {
435 // SdrObjects know their layer, consider that instead of the frame format.
436 bOpaque = pObj->GetLayer()
437 != pFrameFormat->GetDoc()->getIDocumentDrawModelAccess().GetHellId()
438 && pObj->GetLayer()
439 != pFrameFormat->GetDoc()
440 ->getIDocumentDrawModelAccess()
441 .GetInvisibleHellId();
442
443 // Do not do this with lines.
444 if (pObj->GetObjIdentifier() != OBJ_LINE)
445 {
446 nRotation = pObj->GetRotateAngle();
447 lclMovePositionWithRotation(aPos, rSize, nRotation);
448 }
449 }
450 attrList->add(XML_behindDoc, bOpaque ? "0" : "1");
451 // Extend distance with the effect extent if the shape is not rotated, which is the opposite
452 // of the mapping done at import time.
453 // The type of dist* attributes is unsigned, so make sure no negative value is written.
454 sal_Int64 nTopExtDist = nRotation ? 0 : nTopExt;
455 sal_Int64 nDistT = std::max(static_cast<sal_Int64>(0),
456 TwipsToEMU(aULSpaceItem.GetUpper()) - nTopExtDist);
457 attrList->add(XML_distT, OString::number(nDistT).getStr());
458 sal_Int64 nBottomExtDist = nRotation ? 0 : nBottomExt;
459 sal_Int64 nDistB = std::max(static_cast<sal_Int64>(0),
460 TwipsToEMU(aULSpaceItem.GetLower()) - nBottomExtDist);
461 attrList->add(XML_distB, OString::number(nDistB).getStr());
462 sal_Int64 nLeftExtDist = nRotation ? 0 : nLeftExt;
463 sal_Int64 nDistL = std::max(static_cast<sal_Int64>(0),
464 TwipsToEMU(aLRSpaceItem.GetLeft()) - nLeftExtDist);
465 attrList->add(XML_distL, OString::number(nDistL).getStr());
466 sal_Int64 nRightExtDist = nRotation ? 0 : nRightExt;
467 sal_Int64 nDistR = std::max(static_cast<sal_Int64>(0),
468 TwipsToEMU(aLRSpaceItem.GetRight()) - nRightExtDist);
469 attrList->add(XML_distR, OString::number(nDistR).getStr());
470 attrList->add(XML_simplePos, "0");
471 attrList->add(XML_locked, "0");
472 attrList->add(XML_layoutInCell, "1");
473 bool bAllowOverlap = pFrameFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap();
474 attrList->add(XML_allowOverlap, bAllowOverlap ? "1" : "0");
475 if (pObj != nullptr)
476 // It seems 0 and 1 have special meaning: just start counting from 2 to avoid issues with that.
477 attrList->add(XML_relativeHeight, OString::number(pObj->GetOrdNum() + 2));
478 else
479 // relativeHeight is mandatory attribute, if value is not present, we must write default value
480 attrList->add(XML_relativeHeight, "0");
481 if (pObj != nullptr)
482 {
483 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
484 if (!sAnchorId.isEmpty())
485 attrList->addNS(XML_wp14, XML_anchorId,
486 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
487 }
488 sax_fastparser::XFastAttributeListRef xAttrList(attrList);
489 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_anchor, xAttrList);
490 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_simplePos, XML_x, "0", XML_y,
491 "0"); // required, unused
492 const char* relativeFromH;
493 const char* relativeFromV;
494 const char* alignH = nullptr;
495 const char* alignV = nullptr;
496 switch (pFrameFormat->GetVertOrient().GetRelationOrient())
497 {
498 case text::RelOrientation::PAGE_PRINT_AREA:
499 relativeFromV = "margin";
500 break;
501 case text::RelOrientation::PAGE_FRAME:
502 relativeFromV = "page";
503 break;
504 case text::RelOrientation::FRAME:
505 relativeFromV = "paragraph";
506 break;
507 case text::RelOrientation::TEXT_LINE:
508 default:
509 relativeFromV = "line";
510 break;
511 }
512 switch (pFrameFormat->GetVertOrient().GetVertOrient())
513 {
514 case text::VertOrientation::TOP:
515 case text::VertOrientation::CHAR_TOP:
516 case text::VertOrientation::LINE_TOP:
517 if (pFrameFormat->GetVertOrient().GetRelationOrient()
518 == text::RelOrientation::TEXT_LINE)
519 alignV = "bottom";
520 else
521 alignV = "top";
522 break;
523 case text::VertOrientation::BOTTOM:
524 case text::VertOrientation::CHAR_BOTTOM:
525 case text::VertOrientation::LINE_BOTTOM:
526 if (pFrameFormat->GetVertOrient().GetRelationOrient()
527 == text::RelOrientation::TEXT_LINE)
528 alignV = "top";
529 else
530 alignV = "bottom";
531 break;
532 case text::VertOrientation::CENTER:
533 case text::VertOrientation::CHAR_CENTER:
534 case text::VertOrientation::LINE_CENTER:
535 alignV = "center";
536 break;
537 default:
538 break;
539 }
540 switch (pFrameFormat->GetHoriOrient().GetRelationOrient())
541 {
542 case text::RelOrientation::PAGE_PRINT_AREA:
543 relativeFromH = "margin";
544 break;
545 case text::RelOrientation::PAGE_FRAME:
546 relativeFromH = "page";
547 break;
548 case text::RelOrientation::CHAR:
549 relativeFromH = "character";
550 break;
551 case text::RelOrientation::PAGE_RIGHT:
552 relativeFromH = "rightMargin";
553 break;
554 case text::RelOrientation::PAGE_LEFT:
555 relativeFromH = "leftMargin";
556 break;
557 case text::RelOrientation::FRAME:
558 default:
559 relativeFromH = "column";
560 break;
561 }
562 switch (pFrameFormat->GetHoriOrient().GetHoriOrient())
563 {
564 case text::HoriOrientation::LEFT:
565 alignH = "left";
566 break;
567 case text::HoriOrientation::RIGHT:
568 alignH = "right";
569 break;
570 case text::HoriOrientation::CENTER:
571 alignH = "center";
572 break;
573 case text::HoriOrientation::INSIDE:
574 alignH = "inside";
575 break;
576 case text::HoriOrientation::OUTSIDE:
577 alignH = "outside";
578 break;
579 default:
580 break;
581 }
582 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionH, XML_relativeFrom,
583 relativeFromH);
584 /**
585 * Sizes of integral types
586 * climits header defines constants with the limits of integral types for the specific system and compiler implementation used.
587 * Use of this might cause platform dependent problem like posOffset exceed the limit.
588 **/
589 const sal_Int64 MAX_INTEGER_VALUE = SAL_MAX_INT32;
590 const sal_Int64 MIN_INTEGER_VALUE = SAL_MIN_INT32;
591 if (alignH != nullptr)
592 {
593 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
594 m_pImpl->getSerializer()->write(alignH);
595 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
596 }
597 else
598 {
599 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
600 sal_Int64 nPosXEMU = TwipsToEMU(aPos.X);
601
602 /* Absolute Position Offset Value is of type Int. Hence it should not be greater than
603 * Maximum value for Int OR Less than the Minimum value for Int.
604 * - Maximum value for Int = 2147483647
605 * - Minimum value for Int = -2147483648
606 *
607 * As per ECMA Specification : ECMA-376, Second Edition,
608 * Part 1 - Fundamentals And Markup Language Reference[20.4.3.3 ST_PositionOffset (Absolute Position Offset Value)]
609 *
610 * Please refer : http://www.schemacentral.com/sc/xsd/t-xsd_int.html
611 */
612
613 if (nPosXEMU > MAX_INTEGER_VALUE)
614 {
615 nPosXEMU = MAX_INTEGER_VALUE;
616 }
617 else if (nPosXEMU < MIN_INTEGER_VALUE)
618 {
619 nPosXEMU = MIN_INTEGER_VALUE;
620 }
621 m_pImpl->getSerializer()->write(nPosXEMU);
622 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
623 }
624 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionH);
625 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionV, XML_relativeFrom,
626 relativeFromV);
627
628 sal_Int64 nPosYEMU = TwipsToEMU(aPos.Y);
629
630 // tdf#93675, 0 below line/paragraph and/or top line/paragraph with
631 // wrap top+bottom or other wraps is affecting the line directly
632 // above the anchor line, which seems odd, but a tiny adjustment
633 // here to bring the top down convinces msoffice to wrap like us
634 if (nPosYEMU == 0
635 && (strcmp(relativeFromV, "line") == 0 || strcmp(relativeFromV, "paragraph") == 0)
636 && (!alignV || strcmp(alignV, "top") == 0))
637 {
638 alignV = nullptr;
639 nPosYEMU = 635;
640 }
641
642 if (alignV != nullptr)
643 {
644 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
645 m_pImpl->getSerializer()->write(alignV);
646 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
647 }
648 else
649 {
650 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
651 if (nPosYEMU > MAX_INTEGER_VALUE)
652 {
653 nPosYEMU = MAX_INTEGER_VALUE;
654 }
655 else if (nPosYEMU < MIN_INTEGER_VALUE)
656 {
657 nPosYEMU = MIN_INTEGER_VALUE;
658 }
659 m_pImpl->getSerializer()->write(nPosYEMU);
660 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
661 }
662 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionV);
663 }
664 else
665 {
666 sax_fastparser::FastAttributeList* aAttrList
667 = sax_fastparser::FastSerializerHelper::createAttrList();
668 aAttrList->add(XML_distT, OString::number(TwipsToEMU(aULSpaceItem.GetUpper())).getStr());
669 aAttrList->add(XML_distB, OString::number(TwipsToEMU(aULSpaceItem.GetLower())).getStr());
670 aAttrList->add(XML_distL, OString::number(TwipsToEMU(aLRSpaceItem.GetLeft())).getStr());
671 aAttrList->add(XML_distR, OString::number(TwipsToEMU(aLRSpaceItem.GetRight())).getStr());
672 const SdrObject* pObj = pFrameFormat->FindRealSdrObject();
673 if (pObj != nullptr)
674 {
675 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
676 if (!sAnchorId.isEmpty())
677 aAttrList->addNS(XML_wp14, XML_anchorId,
678 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
679 }
680 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_inline, aAttrList);
681 }
682
683 // now the common parts
684 // extent of the image
685 /**
686 * Extent width is of type long ( i.e cx & cy ) as
687 *
688 * per ECMA-376, Second Edition, Part 1 - Fundamentals And Markup Language Reference
689 * [ 20.4.2.7 extent (Drawing Object Size)]
690 *
691 * cy is of type a:ST_PositiveCoordinate.
692 * Minimum inclusive: 0
693 * Maximum inclusive: 27273042316900
694 *
695 * reference : http://www.schemacentral.com/sc/ooxml/e-wp_extent-1.html
696 *
697 * Though ECMA mentions the max value as aforementioned. It appears that MSO does not
698 * handle for the same, in fact it actually can handle a max value of int32 i.e
699 * 2147483647( MAX_INTEGER_VALUE ).
700 * Therefore changing the following accordingly so that LO sync's up with MSO.
701 **/
702 sal_uInt64 cx = TwipsToEMU(std::clamp(rSize.Width(), 0L, long(SAL_MAX_INT32)));
703 OString aWidth(OString::number(std::min(cx, sal_uInt64(SAL_MAX_INT32))));
704 sal_uInt64 cy = TwipsToEMU(std::clamp(rSize.Height(), 0L, long(SAL_MAX_INT32)));
705 OString aHeight(OString::number(std::min(cy, sal_uInt64(SAL_MAX_INT32))));
706
707 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_extent, XML_cx, aWidth, XML_cy, aHeight);
708
709 // effectExtent, extent including the effect (shadow only for now)
710 m_pImpl->getSerializer()->singleElementNS(
711 XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExt), XML_t, OString::number(nTopExt),
712 XML_r, OString::number(nRightExt), XML_b, OString::number(nBottomExt));
713
714 // See if we know the exact wrap type from grab-bag.
715 sal_Int32 nWrapToken = 0;
716 if (const SdrObject* pObject = pFrameFormat->FindRealSdrObject())
717 {
718 uno::Any aAny;
719 pObject->GetGrabBagItem(aAny);
720 comphelper::SequenceAsHashMap aGrabBag(aAny);
721 auto it = aGrabBag.find("EG_WrapType");
722 if (it != aGrabBag.end())
723 {
724 auto sType = it->second.get<OUString>();
725 if (sType == "wrapTight")
726 nWrapToken = XML_wrapTight;
727 else if (sType == "wrapThrough")
728 nWrapToken = XML_wrapThrough;
729 else
730 SAL_WARN("sw.ww8",
731 "DocxSdrExport::startDMLAnchorInline: unexpected EG_WrapType value");
732
733 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, "bothSides");
734
735 it = aGrabBag.find("CT_WrapPath");
736 if (it != aGrabBag.end())
737 {
738 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
739 auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>();
740 auto aPoints(comphelper::sequenceToContainer<std::vector<awt::Point>>(aSeqSeq[0]));
741 for (auto i = aPoints.begin(); i != aPoints.end(); ++i)
742 {
743 awt::Point& rPoint = *i;
744 m_pImpl->getSerializer()->singleElementNS(
745 XML_wp, (i == aPoints.begin() ? XML_start : XML_lineTo), XML_x,
746 OString::number(rPoint.X), XML_y, OString::number(rPoint.Y));
747 }
748 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
749 }
750
751 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
752 }
753 }
754
755 // Or if we have a contour.
756 if (!nWrapToken && pFrameFormat->GetSurround().IsContour())
757 {
758 if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat))
759 {
760 const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
761 if (pPolyPoly && pPolyPoly->Count())
762 {
763 nWrapToken = XML_wrapTight;
764 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText,
765 "bothSides");
766
767 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
768 tools::Polygon aPoly = sw::util::CorrectWordWrapPolygonForExport(*pPolyPoly, pNd);
769 for (sal_uInt16 i = 0; i < aPoly.GetSize(); ++i)
770 m_pImpl->getSerializer()->singleElementNS(
771 XML_wp, (i == 0 ? XML_start : XML_lineTo), XML_x,
772 OString::number(aPoly[i].X()), XML_y, OString::number(aPoly[i].Y()));
773 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
774
775 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
776 }
777 }
778 }
779
780 // No? Then just approximate based on what we have.
781 if (isAnchor && !nWrapToken)
782 {
783 switch (pFrameFormat->GetSurround().GetValue())
784 {
785 case css::text::WrapTextMode_NONE:
786 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom);
787 break;
788 case css::text::WrapTextMode_THROUGH:
789 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone);
790 break;
791 case css::text::WrapTextMode_PARALLEL:
792 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText,
793 "bothSides");
794 break;
795 case css::text::WrapTextMode_DYNAMIC:
796 default:
797 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText,
798 "largest");
799 break;
800 }
801 }
802 }
803
endDMLAnchorInline(const SwFrameFormat * pFrameFormat)804 void DocxSdrExport::endDMLAnchorInline(const SwFrameFormat* pFrameFormat)
805 {
806 bool isAnchor;
807 if (m_pImpl->getFlyFrameGraphic())
808 {
809 isAnchor = false; // end Inline Graphic object inside DMLTextFrame
810 }
811 else
812 {
813 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
814 }
815 m_pImpl->getSerializer()->endElementNS(XML_wp, isAnchor ? XML_anchor : XML_inline);
816
817 m_pImpl->getSerializer()->endElementNS(XML_w, XML_drawing);
818 m_pImpl->setDrawingOpen(false);
819 }
820
writeVMLDrawing(const SdrObject * sdrObj,const SwFrameFormat & rFrameFormat)821 void DocxSdrExport::writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat)
822 {
823 m_pImpl->getSerializer()->startElementNS(XML_w, XML_pict);
824 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
825 // See WinwordAnchoring::SetAnchoring(), these are not part of the SdrObject, have to be passed around manually.
826
827 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
828 const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
829 m_pImpl->getExport().VMLExporter().AddSdrObject(
830 *sdrObj, rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(), rHoriOri.GetRelationOrient(),
831 rVertOri.GetRelationOrient(), true);
832 m_pImpl->getSerializer()->endElementNS(XML_w, XML_pict);
833 }
834
lcl_isLockedCanvas(const uno::Reference<drawing::XShape> & xShape)835 static bool lcl_isLockedCanvas(const uno::Reference<drawing::XShape>& xShape)
836 {
837 uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, "InteropGrabBag");
838 /*
839 * Export as Locked Canvas only if the property
840 * is in the PropertySet
841 */
842 return std::any_of(propList.begin(), propList.end(), [](const beans::PropertyValue& rProp) {
843 return rProp.Name == "LockedCanvas";
844 });
845 }
846
writeDMLDrawing(const SdrObject * pSdrObject,const SwFrameFormat * pFrameFormat,int nAnchorId)847 void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat,
848 int nAnchorId)
849 {
850 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape(),
851 uno::UNO_QUERY_THROW);
852 if (!Impl::isSupportedDMLShape(xShape))
853 return;
854
855 m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject);
856
857 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
858 Size aSize(pSdrObject->GetLogicRect().GetWidth(), pSdrObject->GetLogicRect().GetHeight());
859 startDMLAnchorInline(pFrameFormat, aSize);
860
861 sax_fastparser::FastAttributeList* pDocPrAttrList
862 = sax_fastparser::FastSerializerHelper::createAttrList();
863 pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr());
864 pDocPrAttrList->add(XML_name,
865 OUStringToOString(pSdrObject->GetName(), RTL_TEXTENCODING_UTF8).getStr());
866 if (!pSdrObject->GetTitle().isEmpty())
867 pDocPrAttrList->add(XML_title,
868 OUStringToOString(pSdrObject->GetTitle(), RTL_TEXTENCODING_UTF8));
869 if (!pSdrObject->GetDescription().isEmpty())
870 pDocPrAttrList->add(XML_descr,
871 OUStringToOString(pSdrObject->GetDescription(), RTL_TEXTENCODING_UTF8));
872 if (!pSdrObject->IsVisible()
873 && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
874
875 pDocPrAttrList->add(XML_hidden, OString::number(1).getStr());
876 sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList);
877 pFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef);
878
879 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
880 const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
881 if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"))
882 pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
883 else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
884 pNamespace = "http://schemas.openxmlformats.org/drawingml/2006/picture";
885 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
886 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8());
887 pFS->startElementNS(XML_a, XML_graphicData, XML_uri, pNamespace);
888
889 bool bLockedCanvas = lcl_isLockedCanvas(xShape);
890 if (bLockedCanvas)
891 pFS->startElementNS(
892 XML_lc, XML_lockedCanvas, FSNS(XML_xmlns, XML_lc),
893 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlLockedCanvas)).toUtf8());
894
895 m_pImpl->getExport().OutputDML(xShape);
896
897 if (bLockedCanvas)
898 pFS->endElementNS(XML_lc, XML_lockedCanvas);
899 pFS->endElementNS(XML_a, XML_graphicData);
900 pFS->endElementNS(XML_a, XML_graphic);
901
902 // Relative size of the drawing.
903 if (pSdrObject->GetRelativeWidth())
904 {
905 // At the moment drawinglayer objects are always relative from page.
906 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom,
907 (pSdrObject->GetRelativeWidthRelation() == text::RelOrientation::FRAME
908 ? "margin"
909 : "page"));
910 pFS->startElementNS(XML_wp14, XML_pctWidth);
911 pFS->writeEscaped(
912 OUString::number(*pSdrObject->GetRelativeWidth() * 100 * oox::drawingml::PER_PERCENT));
913 pFS->endElementNS(XML_wp14, XML_pctWidth);
914 pFS->endElementNS(XML_wp14, XML_sizeRelH);
915 }
916 if (pSdrObject->GetRelativeHeight())
917 {
918 pFS->startElementNS(XML_wp14, XML_sizeRelV, XML_relativeFrom,
919 (pSdrObject->GetRelativeHeightRelation() == text::RelOrientation::FRAME
920 ? "margin"
921 : "page"));
922 pFS->startElementNS(XML_wp14, XML_pctHeight);
923 pFS->writeEscaped(
924 OUString::number(*pSdrObject->GetRelativeHeight() * 100 * oox::drawingml::PER_PERCENT));
925 pFS->endElementNS(XML_wp14, XML_pctHeight);
926 pFS->endElementNS(XML_wp14, XML_sizeRelV);
927 }
928
929 endDMLAnchorInline(pFrameFormat);
930 }
931
textFrameShadow(const SwFrameFormat & rFrameFormat)932 void DocxSdrExport::Impl::textFrameShadow(const SwFrameFormat& rFrameFormat)
933 {
934 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
935 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
936 return;
937
938 OString aShadowWidth(OString::number(double(aShadowItem.GetWidth()) / 20) + "pt");
939 OString aOffset;
940 switch (aShadowItem.GetLocation())
941 {
942 case SvxShadowLocation::TopLeft:
943 aOffset = "-" + aShadowWidth + ",-" + aShadowWidth;
944 break;
945 case SvxShadowLocation::TopRight:
946 aOffset = aShadowWidth + ",-" + aShadowWidth;
947 break;
948 case SvxShadowLocation::BottomLeft:
949 aOffset = "-" + aShadowWidth + "," + aShadowWidth;
950 break;
951 case SvxShadowLocation::BottomRight:
952 aOffset = aShadowWidth + "," + aShadowWidth;
953 break;
954 case SvxShadowLocation::NONE:
955 case SvxShadowLocation::End:
956 break;
957 }
958 if (aOffset.isEmpty())
959 return;
960
961 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
962 m_pSerializer->singleElementNS(XML_v, XML_shadow, XML_on, "t", XML_color, "#" + aShadowColor,
963 XML_offset, aOffset);
964 }
965
isSupportedDMLShape(const uno::Reference<drawing::XShape> & xShape)966 bool DocxSdrExport::Impl::isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape)
967 {
968 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
969 if (xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonShape")
970 || xServiceInfo->supportsService("com.sun.star.drawing.PolyLineShape"))
971 return false;
972
973 // For signature line shapes, we don't want DML, just the VML shape.
974 bool bIsSignatureLineShape = false;
975 if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
976 {
977 uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
978 xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLineShape;
979 if (bIsSignatureLineShape)
980 return false;
981 }
982
983 return true;
984 }
985
writeDMLAndVMLDrawing(const SdrObject * sdrObj,const SwFrameFormat & rFrameFormat,int nAnchorId)986 void DocxSdrExport::writeDMLAndVMLDrawing(const SdrObject* sdrObj,
987 const SwFrameFormat& rFrameFormat, int nAnchorId)
988 {
989 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
990 m_pImpl->setDMLAndVMLDrawingOpen(true);
991
992 // Depending on the shape type, we actually don't write the shape as DML.
993 OUString sShapeType;
994 ShapeFlag nMirrorFlags = ShapeFlag::NONE;
995 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObj)->getUnoShape(),
996 uno::UNO_QUERY_THROW);
997
998 MSO_SPT eShapeType
999 = EscherPropertyContainer::GetCustomShapeType(xShape, nMirrorFlags, sShapeType);
1000
1001 // In case we are already inside a DML block, then write the shape only as VML, turn out that's allowed to do.
1002 // A common service created in util to check for VML shapes which are allowed to have textbox in content
1003 if ((msfilter::util::HasTextBoxContent(eShapeType)) && Impl::isSupportedDMLShape(xShape)
1004 && (!bDMLAndVMLDrawingOpen || lcl_isLockedCanvas(xShape))) // Locked canvas is OK inside DML
1005 {
1006 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_AlternateContent);
1007
1008 auto pObjGroup = dynamic_cast<const SdrObjGroup*>(sdrObj);
1009 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Choice, XML_Requires,
1010 (pObjGroup ? "wpg" : "wps"));
1011 writeDMLDrawing(sdrObj, &rFrameFormat, nAnchorId);
1012 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Choice);
1013
1014 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Fallback);
1015 writeVMLDrawing(sdrObj, rFrameFormat);
1016 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Fallback);
1017
1018 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_AlternateContent);
1019 }
1020 else
1021 writeVMLDrawing(sdrObj, rFrameFormat);
1022
1023 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
1024 }
1025
1026 // Converts ARGB transparency (0..255) to drawingml alpha (opposite, and 0..100000)
lcl_ConvertTransparency(const Color & rColor)1027 static OString lcl_ConvertTransparency(const Color& rColor)
1028 {
1029 if (rColor.GetTransparency() > 0)
1030 {
1031 sal_Int32 nTransparencyPercent = 100 - float(rColor.GetTransparency()) / 2.55;
1032 return OString::number(nTransparencyPercent * oox::drawingml::PER_PERCENT);
1033 }
1034
1035 return OString();
1036 }
1037
writeDMLEffectLst(const SwFrameFormat & rFrameFormat)1038 void DocxSdrExport::writeDMLEffectLst(const SwFrameFormat& rFrameFormat)
1039 {
1040 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1041
1042 // Output effects
1043 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1044 return;
1045
1046 // Distance is measured diagonally from corner
1047 double nShadowDist
1048 = sqrt(static_cast<double>(aShadowItem.GetWidth()) * aShadowItem.GetWidth() * 2.0);
1049 OString aShadowDist(OString::number(TwipsToEMU(nShadowDist)));
1050 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1051 OString aShadowAlpha = lcl_ConvertTransparency(aShadowItem.GetColor());
1052 sal_uInt32 nShadowDir = 0;
1053 switch (aShadowItem.GetLocation())
1054 {
1055 case SvxShadowLocation::TopLeft:
1056 nShadowDir = 13500000;
1057 break;
1058 case SvxShadowLocation::TopRight:
1059 nShadowDir = 18900000;
1060 break;
1061 case SvxShadowLocation::BottomLeft:
1062 nShadowDir = 8100000;
1063 break;
1064 case SvxShadowLocation::BottomRight:
1065 nShadowDir = 2700000;
1066 break;
1067 case SvxShadowLocation::NONE:
1068 case SvxShadowLocation::End:
1069 break;
1070 }
1071 OString aShadowDir(OString::number(nShadowDir));
1072
1073 m_pImpl->getSerializer()->startElementNS(XML_a, XML_effectLst);
1074 m_pImpl->getSerializer()->startElementNS(XML_a, XML_outerShdw, XML_dist, aShadowDist, XML_dir,
1075 aShadowDir);
1076 if (aShadowAlpha.isEmpty())
1077 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1078 else
1079 {
1080 m_pImpl->getSerializer()->startElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1081 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_alpha, XML_val, aShadowAlpha);
1082 m_pImpl->getSerializer()->endElementNS(XML_a, XML_srgbClr);
1083 }
1084 m_pImpl->getSerializer()->endElementNS(XML_a, XML_outerShdw);
1085 m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst);
1086 }
1087
writeDiagram(const SdrObject * sdrObject,const SwFrameFormat & rFrameFormat,int nDiagramId)1088 void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat,
1089 int nDiagramId)
1090 {
1091 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(),
1092 uno::UNO_QUERY);
1093
1094 // write necessary tags to document.xml
1095 Size aSize(sdrObject->GetSnapRect().GetWidth(), sdrObject->GetSnapRect().GetHeight());
1096 startDMLAnchorInline(&rFrameFormat, aSize);
1097
1098 m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId);
1099
1100 endDMLAnchorInline(&rFrameFormat);
1101 }
1102
writeOnlyTextOfFrame(ww8::Frame const * pParentFrame)1103 void DocxSdrExport::writeOnlyTextOfFrame(ww8::Frame const* pParentFrame)
1104 {
1105 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1106 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1107 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1108
1109 sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0;
1110 sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0;
1111
1112 //Save data here and restore when out of scope
1113 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1114
1115 m_pImpl->setBodyPrAttrList(sax_fastparser::FastSerializerHelper::createAttrList());
1116 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1117 auto const nTextTyp(m_pImpl->getExport().m_nTextTyp);
1118 m_pImpl->getExport().m_nTextTyp = TXT_TXTBOX;
1119 ::comphelper::ScopeGuard const sg(
1120 [this, nTextTyp]() { m_pImpl->getExport().m_nTextTyp = nTextTyp; });
1121 m_pImpl->getExport().WriteText();
1122 }
1123
writeBoxItemLine(const SvxBoxItem & rBox)1124 void DocxSdrExport::writeBoxItemLine(const SvxBoxItem& rBox)
1125 {
1126 const editeng::SvxBorderLine* pBorderLine = nullptr;
1127
1128 if (rBox.GetTop())
1129 {
1130 pBorderLine = rBox.GetTop();
1131 }
1132 else if (rBox.GetLeft())
1133 {
1134 pBorderLine = rBox.GetLeft();
1135 }
1136 else if (rBox.GetBottom())
1137 {
1138 pBorderLine = rBox.GetBottom();
1139 }
1140 else if (rBox.GetRight())
1141 {
1142 pBorderLine = rBox.GetRight();
1143 }
1144
1145 if (!pBorderLine)
1146 {
1147 return;
1148 }
1149
1150 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1151 double fConverted(editeng::ConvertBorderWidthToWord(pBorderLine->GetBorderLineStyle(),
1152 pBorderLine->GetWidth()));
1153 OString sWidth(OString::number(TwipsToEMU(fConverted)));
1154 pFS->startElementNS(XML_a, XML_ln, XML_w, sWidth);
1155
1156 pFS->startElementNS(XML_a, XML_solidFill);
1157 OString sColor(msfilter::util::ConvertColor(pBorderLine->GetColor()));
1158 pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
1159 pFS->endElementNS(XML_a, XML_solidFill);
1160
1161 if (SvxBorderLineStyle::DASHED == pBorderLine->GetBorderLineStyle()) // Line Style is Dash type
1162 pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
1163
1164 pFS->endElementNS(XML_a, XML_ln);
1165 }
1166
writeDMLTextFrame(ww8::Frame const * pParentFrame,int nAnchorId,bool bTextBoxOnly)1167 void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId,
1168 bool bTextBoxOnly)
1169 {
1170 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1171 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
1172
1173 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1174 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1175 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1176
1177 sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0;
1178 sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0;
1179
1180 //Save data here and restore when out of scope
1181 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1182
1183 // When a frame has some low height, but automatically expanded due
1184 // to lots of contents, this size contains the real size.
1185 const Size aSize = pParentFrame->GetSize();
1186
1187 uno::Reference<drawing::XShape> xShape;
1188 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
1189 if (pSdrObj)
1190 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
1191 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1192 uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
1193 if (xPropertySet.is())
1194 xPropSetInfo = xPropertySet->getPropertySetInfo();
1195
1196 m_pImpl->setBodyPrAttrList(sax_fastparser::FastSerializerHelper::createAttrList());
1197 {
1198 drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP;
1199 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("TextVerticalAdjust"))
1200 xPropertySet->getPropertyValue("TextVerticalAdjust") >>= eAdjust;
1201 m_pImpl->getBodyPrAttrList()->add(XML_anchor,
1202 oox::drawingml::GetTextVerticalAdjust(eAdjust));
1203 }
1204
1205 if (!bTextBoxOnly)
1206 {
1207 startDMLAnchorInline(&rFrameFormat, aSize);
1208
1209 sax_fastparser::FastAttributeList* pDocPrAttrList
1210 = sax_fastparser::FastSerializerHelper::createAttrList();
1211 pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr());
1212 pDocPrAttrList->add(
1213 XML_name, OUStringToOString(rFrameFormat.GetName(), RTL_TEXTENCODING_UTF8).getStr());
1214 sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList);
1215 pFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef);
1216
1217 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1218 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)).toUtf8());
1219 pFS->startElementNS(XML_a, XML_graphicData, XML_uri,
1220 "http://schemas.microsoft.com/office/word/2010/wordprocessingShape");
1221 pFS->startElementNS(XML_wps, XML_wsp);
1222 pFS->singleElementNS(XML_wps, XML_cNvSpPr, XML_txBox, "1");
1223
1224 uno::Any aRotation;
1225 m_pImpl->setDMLandVMLTextFrameRotation(0);
1226 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1227 {
1228 uno::Sequence<beans::PropertyValue> propList;
1229 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1230 auto pProp = std::find_if(propList.begin(), propList.end(),
1231 [](const beans::PropertyValue& rProp) {
1232 return rProp.Name == "mso-rotation-angle";
1233 });
1234 if (pProp != propList.end())
1235 aRotation = pProp->Value;
1236 }
1237 aRotation >>= m_pImpl->getDMLandVMLTextFrameRotation();
1238 OString sRotation(OString::number(
1239 oox::drawingml::ExportRotateClockwisify(m_pImpl->getDMLandVMLTextFrameRotation())));
1240 // Shape properties
1241 pFS->startElementNS(XML_wps, XML_spPr);
1242 if (m_pImpl->getDMLandVMLTextFrameRotation())
1243 {
1244 pFS->startElementNS(XML_a, XML_xfrm, XML_rot, sRotation);
1245 }
1246 else
1247 {
1248 pFS->startElementNS(XML_a, XML_xfrm);
1249 }
1250 pFS->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
1251 OString aWidth(OString::number(TwipsToEMU(aSize.Width())));
1252 OString aHeight(OString::number(TwipsToEMU(aSize.Height())));
1253 pFS->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
1254 pFS->endElementNS(XML_a, XML_xfrm);
1255 OUString shapeType = "rect";
1256 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1257 {
1258 uno::Sequence<beans::PropertyValue> propList;
1259 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1260 auto pProp = std::find_if(propList.begin(), propList.end(),
1261 [](const beans::PropertyValue& rProp) {
1262 return rProp.Name == "mso-orig-shape-type";
1263 });
1264 if (pProp != propList.end())
1265 pProp->Value >>= shapeType;
1266 }
1267 //Empty shapeType will lead to corruption so to avoid that shapeType is set to default i.e. "rect"
1268 if (shapeType.isEmpty())
1269 shapeType = "rect";
1270
1271 pFS->singleElementNS(XML_a, XML_prstGeom, XML_prst, shapeType.toUtf8());
1272 m_pImpl->setDMLTextFrameSyntax(true);
1273 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
1274 m_pImpl->setDMLTextFrameSyntax(false);
1275 writeDMLEffectLst(rFrameFormat);
1276 pFS->endElementNS(XML_wps, XML_spPr);
1277 }
1278
1279 //first, loop through ALL of the chained textboxes to identify a unique ID for each chain, and sequence number for each textbox in that chain.
1280 if (!m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized)
1281 {
1282 sal_Int32 nSeq = 0;
1283 for (auto& rEntry : m_pImpl->getExport().m_aLinkedTextboxesHelper)
1284 {
1285 //find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link
1286 if (rEntry.second.sPrevChain.isEmpty() && !rEntry.second.sNextChain.isEmpty())
1287 {
1288 //assign this chain a unique ID and start a new sequence
1289 nSeq = 0;
1290 rEntry.second.nId = ++m_pImpl->getExport().m_nLinkedTextboxesChainId;
1291 rEntry.second.nSeq = nSeq;
1292
1293 OUString sCheckForBrokenChains = rEntry.first;
1294
1295 //follow the chain and assign the same id, and incremental sequence numbers.
1296 auto followChainIter
1297 = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(rEntry.second.sNextChain);
1298 while (followChainIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1299 {
1300 //verify that the NEXT textbox also points to me as the PREVIOUS.
1301 // A broken link indicates a leftover remnant that can be ignored.
1302 if (followChainIter->second.sPrevChain != sCheckForBrokenChains)
1303 break;
1304
1305 followChainIter->second.nId = m_pImpl->getExport().m_nLinkedTextboxesChainId;
1306 followChainIter->second.nSeq = ++nSeq;
1307
1308 //empty next chain indicates the end of the linked chain.
1309 if (followChainIter->second.sNextChain.isEmpty())
1310 break;
1311
1312 sCheckForBrokenChains = followChainIter->first;
1313 followChainIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(
1314 followChainIter->second.sNextChain);
1315 }
1316 }
1317 }
1318 m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized = true;
1319 }
1320
1321 m_pImpl->getExport().m_pParentFrame = nullptr;
1322 bool skipTxBxContent = false;
1323 bool isTxbxLinked = false;
1324
1325 OUString sLinkChainName;
1326 if (xPropSetInfo.is())
1327 {
1328 if (xPropSetInfo->hasPropertyByName("LinkDisplayName"))
1329 xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
1330 else if (xPropSetInfo->hasPropertyByName("ChainName"))
1331 xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
1332 }
1333
1334 // second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx
1335 auto linkedTextboxesIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(sLinkChainName);
1336 if (linkedTextboxesIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1337 {
1338 if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq != 0))
1339 {
1340 //not the first in the chain, so write the tag as linkedTxbx
1341 pFS->singleElementNS(XML_wps, XML_linkedTxbx, XML_id,
1342 OString::number(linkedTextboxesIter->second.nId), XML_seq,
1343 OString::number(linkedTextboxesIter->second.nSeq));
1344 /* no text content should be added to this tag,
1345 since the textbox is linked, the entire content
1346 is written in txbx block
1347 */
1348 skipTxBxContent = true;
1349 }
1350 else if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0))
1351 {
1352 /* this is the first textbox in the chaining, we add the text content
1353 to this block*/
1354 //since the text box is linked, it needs an id.
1355 pFS->startElementNS(XML_wps, XML_txbx, XML_id,
1356 OString::number(linkedTextboxesIter->second.nId));
1357 isTxbxLinked = true;
1358 }
1359 }
1360
1361 if (!skipTxBxContent)
1362 {
1363 if (!isTxbxLinked)
1364 pFS->startElementNS(XML_wps, XML_txbx); //text box is not linked, therefore no id.
1365
1366 pFS->startElementNS(XML_w, XML_txbxContent);
1367
1368 const SvxFrameDirectionItem& rDirection = rFrameFormat.GetFrameDir();
1369 if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB)
1370 m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert");
1371 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
1372 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270");
1373
1374 {
1375 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1376 auto const nTextTyp(m_pImpl->getExport().m_nTextTyp);
1377 m_pImpl->getExport().m_nTextTyp = TXT_TXTBOX;
1378 ::comphelper::ScopeGuard const sg(
1379 [this, nTextTyp]() { m_pImpl->getExport().m_nTextTyp = nTextTyp; });
1380 m_pImpl->getExport().WriteText();
1381 if (m_pImpl->getParagraphSdtOpen())
1382 {
1383 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
1384 m_pImpl->setParagraphSdtOpen(false);
1385 }
1386 }
1387
1388 pFS->endElementNS(XML_w, XML_txbxContent);
1389 pFS->endElementNS(XML_wps, XML_txbx);
1390 }
1391
1392 // We need to init padding to 0, if it's not set.
1393 // In LO the default is 0 and so ins attributes are not set when padding is 0
1394 // but in MSO the default is 254 / 127, so we need to set 0 padding explicitly
1395 if (m_pImpl->getBodyPrAttrList())
1396 {
1397 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_lIns))
1398 m_pImpl->getBodyPrAttrList()->add(XML_lIns, OString::number(0));
1399 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_tIns))
1400 m_pImpl->getBodyPrAttrList()->add(XML_tIns, OString::number(0));
1401 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_rIns))
1402 m_pImpl->getBodyPrAttrList()->add(XML_rIns, OString::number(0));
1403 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_bIns))
1404 m_pImpl->getBodyPrAttrList()->add(XML_bIns, OString::number(0));
1405 }
1406
1407 sax_fastparser::XFastAttributeListRef xBodyPrAttrList(m_pImpl->getBodyPrAttrList());
1408 m_pImpl->setBodyPrAttrList(nullptr);
1409 if (!bTextBoxOnly)
1410 {
1411 pFS->startElementNS(XML_wps, XML_bodyPr, xBodyPrAttrList);
1412 // AutoSize of the Text Frame.
1413 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
1414 pFS->singleElementNS(
1415 XML_a, (rSize.GetHeightSizeType() == ATT_VAR_SIZE ? XML_spAutoFit : XML_noAutofit));
1416 pFS->endElementNS(XML_wps, XML_bodyPr);
1417
1418 pFS->endElementNS(XML_wps, XML_wsp);
1419 pFS->endElementNS(XML_a, XML_graphicData);
1420 pFS->endElementNS(XML_a, XML_graphic);
1421
1422 // Relative size of the Text Frame.
1423 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
1424 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
1425 {
1426 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom,
1427 (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME
1428 ? "page"
1429 : "margin"));
1430 pFS->startElementNS(XML_wp14, XML_pctWidth);
1431 pFS->writeEscaped(OUString::number(nWidthPercent * oox::drawingml::PER_PERCENT));
1432 pFS->endElementNS(XML_wp14, XML_pctWidth);
1433 pFS->endElementNS(XML_wp14, XML_sizeRelH);
1434 }
1435 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
1436 if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED)
1437 {
1438 pFS->startElementNS(
1439 XML_wp14, XML_sizeRelV, XML_relativeFrom,
1440 (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME ? "page"
1441 : "margin"));
1442 pFS->startElementNS(XML_wp14, XML_pctHeight);
1443 pFS->writeEscaped(OUString::number(nHeightPercent * oox::drawingml::PER_PERCENT));
1444 pFS->endElementNS(XML_wp14, XML_pctHeight);
1445 pFS->endElementNS(XML_wp14, XML_sizeRelV);
1446 }
1447
1448 endDMLAnchorInline(&rFrameFormat);
1449 }
1450 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
1451 }
1452
writeVMLTextFrame(ww8::Frame const * pParentFrame,bool bTextBoxOnly)1453 void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly)
1454 {
1455 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1456 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
1457
1458 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1459 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1460 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1461
1462 sal_uLong nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : 0;
1463 sal_uLong nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : 0;
1464
1465 //Save data here and restore when out of scope
1466 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1467
1468 // When a frame has some low height, but automatically expanded due
1469 // to lots of contents, this size contains the real size.
1470 const Size aSize = pParentFrame->GetSize();
1471 m_pImpl->setFlyFrameSize(&aSize);
1472
1473 m_pImpl->setTextFrameSyntax(true);
1474 m_pImpl->setFlyAttrList(sax_fastparser::FastSerializerHelper::createAttrList());
1475 m_pImpl->setTextboxAttrList(sax_fastparser::FastSerializerHelper::createAttrList());
1476 m_pImpl->getTextFrameStyle() = "position:absolute";
1477 if (!bTextBoxOnly)
1478 {
1479 OString sRotation(OString::number(m_pImpl->getDMLandVMLTextFrameRotation() / -100));
1480 m_pImpl->getExport()
1481 .SdrExporter()
1482 .getTextFrameStyle()
1483 .append(";rotation:")
1484 .append(sRotation);
1485 }
1486 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
1487 m_pImpl->getFlyAttrList()->add(XML_style, m_pImpl->getTextFrameStyle().makeStringAndClear());
1488
1489 const SdrObject* pObject = pParentFrame->GetFrameFormat().FindRealSdrObject();
1490 if (pObject != nullptr)
1491 {
1492 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObject);
1493 if (!sAnchorId.isEmpty())
1494 m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId,
1495 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
1496 }
1497 sax_fastparser::XFastAttributeListRef xFlyAttrList(m_pImpl->getFlyAttrList().get());
1498 m_pImpl->getFlyAttrList().clear();
1499 sax_fastparser::XFastAttributeListRef xTextboxAttrList(m_pImpl->getTextboxAttrList().get());
1500 m_pImpl->getTextboxAttrList().clear();
1501 m_pImpl->setTextFrameSyntax(false);
1502 m_pImpl->setFlyFrameSize(nullptr);
1503 m_pImpl->getExport().m_pParentFrame = nullptr;
1504
1505 if (!bTextBoxOnly)
1506 {
1507 pFS->startElementNS(XML_w, XML_pict);
1508 pFS->startElementNS(XML_v, XML_rect, xFlyAttrList);
1509 m_pImpl->textFrameShadow(rFrameFormat);
1510 if (m_pImpl->getFlyFillAttrList().is())
1511 {
1512 sax_fastparser::XFastAttributeListRef xFlyFillAttrList(
1513 m_pImpl->getFlyFillAttrList().get());
1514 m_pImpl->getFlyFillAttrList().clear();
1515 pFS->singleElementNS(XML_v, XML_fill, xFlyFillAttrList);
1516 }
1517 if (m_pImpl->getDashLineStyleAttr().is())
1518 {
1519 sax_fastparser::XFastAttributeListRef xDashLineStyleAttr(
1520 m_pImpl->getDashLineStyleAttr().get());
1521 m_pImpl->getDashLineStyleAttr().clear();
1522 pFS->singleElementNS(XML_v, XML_stroke, xDashLineStyleAttr);
1523 }
1524 pFS->startElementNS(XML_v, XML_textbox, xTextboxAttrList);
1525 }
1526 pFS->startElementNS(XML_w, XML_txbxContent);
1527 {
1528 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1529 auto const nTextTyp(m_pImpl->getExport().m_nTextTyp);
1530 m_pImpl->getExport().m_nTextTyp = TXT_TXTBOX;
1531 ::comphelper::ScopeGuard const sg(
1532 [this, nTextTyp]() { m_pImpl->getExport().m_nTextTyp = nTextTyp; });
1533 m_pImpl->getExport().WriteText();
1534 if (m_pImpl->getParagraphSdtOpen())
1535 {
1536 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
1537 m_pImpl->setParagraphSdtOpen(false);
1538 }
1539 }
1540 pFS->endElementNS(XML_w, XML_txbxContent);
1541 if (!bTextBoxOnly)
1542 {
1543 pFS->endElementNS(XML_v, XML_textbox);
1544
1545 if (m_pImpl->getFlyWrapAttrList())
1546 {
1547 sax_fastparser::XFastAttributeListRef xFlyWrapAttrList(m_pImpl->getFlyWrapAttrList());
1548 m_pImpl->setFlyWrapAttrList(nullptr);
1549 pFS->singleElementNS(XML_w10, XML_wrap, xFlyWrapAttrList);
1550 }
1551
1552 pFS->endElementNS(XML_v, XML_rect);
1553 pFS->endElementNS(XML_w, XML_pict);
1554 }
1555
1556 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
1557 }
1558
isTextBox(const SwFrameFormat & rFrameFormat)1559 bool DocxSdrExport::isTextBox(const SwFrameFormat& rFrameFormat)
1560 {
1561 return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT);
1562 }
1563
1564 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1565