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