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