LibreOffice Module sw (master) 1
docxsdrexport.cxx
Go to the documentation of this file.
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/svdoashp.hxx>
19#include <svx/svdogrp.hxx>
20#include <svx/svdopath.hxx>
21#include <svx/svdobjkind.hxx>
22#include <svx/svditer.hxx>
24#include <oox/token/namespaces.hxx>
26#include <textboxhelper.hxx>
27#include <fmtanchr.hxx>
28#include <fmtsrnd.hxx>
29#include <fmtcntnt.hxx>
30#include <fmtornt.hxx>
31#include <fmtfsize.hxx>
32#include <fmtfollowtextflow.hxx>
33#include <frmatr.hxx>
36#include "docxexportfilter.hxx"
40#include <frmfmt.hxx>
42
43#include <svx/svdtrans.hxx>
45
46using namespace com::sun::star;
47using namespace oox;
48using namespace sax_fastparser;
49
50namespace
51{
52uno::Sequence<beans::PropertyValue> lclGetProperty(const uno::Reference<drawing::XShape>& rShape,
53 const OUString& rPropName)
54{
55 uno::Sequence<beans::PropertyValue> aResult;
56 uno::Reference<beans::XPropertySet> xPropertySet(rShape, uno::UNO_QUERY);
57 uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
58
59 if (!xPropertySet.is())
60 return aResult;
61
62 xPropSetInfo = xPropertySet->getPropertySetInfo();
63 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(rPropName))
64 {
65 xPropertySet->getPropertyValue(rPropName) >>= aResult;
66 }
67 return aResult;
68}
69
70OUString lclGetAnchorIdFromGrabBag(const SdrObject* pObj)
71{
72 OUString aResult;
73 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObj)->getUnoShape(),
74 uno::UNO_QUERY);
75 OUString aGrabBagName;
76 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY);
77 if (xServiceInfo->supportsService("com.sun.star.text.TextFrame"))
78 aGrabBagName = "FrameInteropGrabBag";
79 else
80 aGrabBagName = "InteropGrabBag";
81 const uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, aGrabBagName);
82 auto pProp
83 = std::find_if(propList.begin(), propList.end(),
84 [](const beans::PropertyValue& rProp) { return rProp.Name == "AnchorId"; });
85 if (pProp != propList.end())
86 pProp->Value >>= aResult;
87 return aResult;
88}
89
90void lclMovePositionWithRotation(awt::Point& aPos, const Size& rSize, Degree100 nRotation100)
91{
92 // code from ImplEESdrWriter::ImplFlipBoundingBox (filter/source/msfilter/eschesdo.cxx)
93 // TODO: refactor
94 // MSO uses left|top of the unrotated object rectangle as position. When you rotate that rectangle
95 // around its center and build a snap rectangle S from it, then left|top of S has to be the
96 // position used in LO. This method converts LOs aPos to the position used by MSO.
97
98 // rSize has to be size of the logicRect of the object. For calculating the diff, we build a
99 // rectangle with left|top A = (-fWidthHalf | -fHeightHalf) and
100 // right|top B = (fWidthHalf | -fHeightHalf). The rotation matrix R is here
101 // fcos fsin
102 // -fsin fcos
103 // Left of rectangle S = X-coord of R * A, Top of rectangle S = Y-coord of R * B
104
105 // Use nRotation in [0;9000], for to have only one and not four cases.
106 if (nRotation100 == 0_deg100)
107 return;
108 if (nRotation100 < 0_deg100)
109 nRotation100 = (36000_deg100 + nRotation100) % 36000_deg100;
110 if (nRotation100 % 18000_deg100 == 0_deg100)
111 nRotation100 = 0_deg100; // prevents endless loop
112 while (nRotation100 > 9000_deg100)
113 nRotation100 = 18000_deg100 - (nRotation100 % 18000_deg100);
114
115 double fVal = toRadians(nRotation100);
116 double fCos = (nRotation100 == 9000_deg100) ? 0.0 : cos(fVal);
117 double fSin = sin(fVal);
118
119 double fWidthHalf = static_cast<double>(rSize.Width()) / 2.0;
120 double fHeightHalf = static_cast<double>(rSize.Height()) / 2.0;
121
122 double fXDiff = fSin * fHeightHalf + fCos * fWidthHalf - fWidthHalf;
123 double fYDiff = fSin * fWidthHalf + fCos * fHeightHalf - fHeightHalf;
124
125 aPos.X += fXDiff + 0.5;
126 aPos.Y += fYDiff + 0.5;
127}
128
130bool IsAnchorTypeInsideParagraph(const ww8::Frame* pFrame)
131{
132 const SwFormatAnchor& rAnchor = pFrame->GetFrameFormat().GetAttrSet().GetAnchor();
133 return rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE;
134}
135
136bool lcl_IsRotateAngleValid(const SdrObject& rObj)
137{
138 // Some shape types report a rotation angle but are not actually rotated, because all rotation
139 // have been incorporated.
140 switch (rObj.GetObjIdentifier())
141 {
142 case SdrObjKind::Group:
143 case SdrObjKind::Line:
144 case SdrObjKind::PolyLine:
145 case SdrObjKind::PathLine:
146 case SdrObjKind::PathFill:
147 return false;
148 default:
149 return true;
150 }
151}
152
153void lcl_calculateMSOBaseRectangle(const SdrObject& rObj, double& rfMSOLeft, double& rfMSORight,
154 double& rfMSOTop, double& rfMSOBottom,
155 const bool bIsWord2007Image)
156{
157 // Word rotates around shape center, LO around left/top. Thus logic rectangle of LO is not
158 // directly usable as 'base rectangle'.
159 double fCenterX = (rObj.GetSnapRect().Left() + rObj.GetSnapRect().Right()) / 2.0;
160 double fCenterY = (rObj.GetSnapRect().Top() + rObj.GetSnapRect().Bottom()) / 2.0;
161 double fHalfWidth = rObj.GetLogicRect().getOpenWidth() / 2.0;
162 double fHalfHeight = rObj.GetLogicRect().getOpenHeight() / 2.0;
163
164 // MSO swaps width and height depending on rotation angle; exception: Word 2007 (vers 12) never
165 // swaps width and height for images.
166 double fRotation
167 = lcl_IsRotateAngleValid(rObj) ? toDegrees(NormAngle36000(rObj.GetRotateAngle())) : 0.0;
168 if (((fRotation > 45.0 && fRotation <= 135.0) || (fRotation > 225.0 && fRotation <= 315.0))
169 && !bIsWord2007Image)
170 {
171 rfMSOLeft = fCenterX - fHalfHeight;
172 rfMSORight = fCenterX + fHalfHeight;
173 rfMSOTop = fCenterY - fHalfWidth;
174 rfMSOBottom = fCenterY + fHalfWidth;
175 }
176 else
177 {
178 rfMSOLeft = fCenterX - fHalfWidth;
179 rfMSORight = fCenterX + fHalfWidth;
180 rfMSOTop = fCenterY - fHalfHeight;
181 rfMSOBottom = fCenterY + fHalfHeight;
182 }
183}
184
185void lcl_calculateRawEffectExtent(sal_Int32& rLeft, sal_Int32& rTop, sal_Int32& rRight,
186 sal_Int32& rBottom, const SdrObject& rObj,
187 const bool bUseBoundRect, const bool bIsWord2007Image)
188{
189 // This method calculates the extent needed, to let Word use the same outer area for the object
190 // as LO. Word uses as 'base rectangle' the unrotated shape rectangle, maybe having swapped width
191 // and height depending on rotation angle and version of Word.
192 double fMSOLeft;
193 double fMSORight;
194 double fMSOTop;
195 double fMSOBottom;
196 lcl_calculateMSOBaseRectangle(rObj, fMSOLeft, fMSORight, fMSOTop, fMSOBottom, bIsWord2007Image);
197
198 tools::Rectangle aLORect = bUseBoundRect ? rObj.GetCurrentBoundRect() : rObj.GetSnapRect();
199 rLeft = fMSOLeft - aLORect.Left();
200 rRight = aLORect.Right() - fMSORight;
201 rTop = fMSOTop - aLORect.Top();
202 rBottom = aLORect.Bottom() - fMSOBottom;
203 // Result values might be negative, e.g for a custom shape 'Arc'.
204 return;
205}
206
207bool lcl_makeSingleDistAndEffectExtentNonNegative(sal_Int64& rDist, sal_Int32& rExt)
208{
209 // A negative effectExtent is allowed in OOXML, but Word cannot handle it (bug in Word). It
210 // might occur, if the BoundRect in LO is smaller than the base rect in Word.
211 // A negative wrap distance from text is allowed in ODF. LO can currently only handle left and
212 // right negative values, see bug tdf#141880. Dist must be non-negative in OOXML.
213 // We try to compensate Dist vs effectExtent to get similar visual appearance.
214 if (rExt >= 0 && rDist >= 0)
215 return true;
216 if (rExt < 0 && rDist < 0)
217 {
218 rExt = 0;
219 rDist = 0;
220 return false;
221 }
222 if (rDist + static_cast<sal_Int64>(rExt) < 0) // different sign, so no overflow
223 {
224 rExt = 0;
225 rDist = 0;
226 return false;
227 }
228 // rDist + rExt >= 0
229 if (rDist < 0)
230 {
231 rExt += rDist;
232 rDist = 0;
233 }
234 else // rExt < 0
235 {
236 rDist += rExt;
237 rExt = 0;
238 }
239 return true;
240}
241
242bool lcl_makeDistAndExtentNonNegative(sal_Int64& rDistT, sal_Int64& rDistB, sal_Int64& rDistL,
243 sal_Int64& rDistR, sal_Int32& rLeftExt, sal_Int32& rTopExt,
244 sal_Int32& rRightExt, sal_Int32& rBottomExt)
245{
246 bool bLeft = lcl_makeSingleDistAndEffectExtentNonNegative(rDistL, rLeftExt);
247 bool bTop = lcl_makeSingleDistAndEffectExtentNonNegative(rDistT, rTopExt);
248 bool bRight = lcl_makeSingleDistAndEffectExtentNonNegative(rDistR, rRightExt);
249 bool bBottom = lcl_makeSingleDistAndEffectExtentNonNegative(rDistB, rBottomExt);
250 return bLeft && bTop && bRight && bBottom;
251}
252
253void lcl_makeSingleDistZeroAndExtentNonNegative(sal_Int64& rDist, sal_Int32& rExt)
254{
255 if (static_cast<double>(rDist) + static_cast<double>(rExt)
256 >= static_cast<double>(SAL_MAX_INT32))
257 rExt = SAL_MAX_INT32;
258 else if (static_cast<double>(rDist) + static_cast<double>(rExt) <= 0)
259 rExt = 0;
260 else // 0 < rDist + rExt < SAL_MAX_INT32
261 {
262 rExt = static_cast<sal_Int32>(rDist + rExt);
263 if (rExt < 0)
264 rExt = 0;
265 }
266 rDist = 0;
267}
268
269void lcl_makeDistZeroAndExtentNonNegative(sal_Int64& rDistT, sal_Int64& rDistB, sal_Int64& rDistL,
270 sal_Int64& rDistR, sal_Int32& rLeftExt,
271 sal_Int32& rTopExt, sal_Int32& rRightExt,
272 sal_Int32& rBottomExt)
273{
274 lcl_makeSingleDistZeroAndExtentNonNegative(rDistL, rLeftExt);
275 lcl_makeSingleDistZeroAndExtentNonNegative(rDistT, rTopExt);
276 lcl_makeSingleDistZeroAndExtentNonNegative(rDistR, rRightExt);
277 lcl_makeSingleDistZeroAndExtentNonNegative(rDistB, rBottomExt);
278}
279
280tools::Polygon lcl_CreateContourPolygon(SdrObject* pSdrObj)
281{
282 tools::Polygon aContour;
283 if (!pSdrObj)
284 {
285 // use rectangular default
286 aContour.Insert(0, Point(0, 0));
287 aContour.Insert(1, Point(21600, 0));
288 aContour.Insert(2, Point(21600, 21600));
289 aContour.Insert(3, Point(0, 21600));
290 aContour.Insert(4, Point(0, 0));
291 return aContour;
292 }
293
294 // Simple version for now: Use ready PolygonFromPolyPolygon().
295 // For that we first create a B2DPolyPolygon from the shape, that ideally contains
296 // the outline of the shape.
297 basegfx::B2DPolyPolygon aPolyPolygon;
298 switch (pSdrObj->GetObjIdentifier())
299 {
300 case SdrObjKind::CustomShape:
301 {
302 // EnhancedCustomShapeEngine::GetLineGeometry() is not directly usable, because the wrap
303 // polygon acts on the untransformed shape in Word. We do here similar as in
304 // GetLineGeometry(), but without transformations.
305 EnhancedCustomShape2d aCustomShape2d(*static_cast<SdrObjCustomShape*>(pSdrObj));
306 rtl::Reference<SdrObject> pLineGeometryObj = aCustomShape2d.CreateLineGeometry();
307 if (!pLineGeometryObj)
308 break;
309
310 // We might have got other object kinds than SdrPathObj, even groups.
311 SdrObjListIter aIter(*pLineGeometryObj, SdrIterMode::DeepWithGroups);
312 while (aIter.IsMore())
313 {
315 const SdrObject* pNext = aIter.Next();
316 if (auto pPathObj = dynamic_cast<const SdrPathObj*>(pNext))
317 aPP = pPathObj->GetPathPoly();
318 else
319 {
321 = pLineGeometryObj->ConvertToPolyObj(false, false);
322 SdrPathObj* pPath = dynamic_cast<SdrPathObj*>(pNewObj.get());
323 if (pPath)
324 aPP = pPath->GetPathPoly();
325 }
326 if (aPP.count())
327 aPolyPolygon.append(aPP);
328 }
329
330 if (!aPolyPolygon.count())
331 break;
332
333 // Make relative to range 0..21600, 0..21600
334 Point aCenter(pSdrObj->GetSnapRect().Center());
335 basegfx::B2DHomMatrix aTranslateToOrigin(
336 basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
337 aPolyPolygon.transform(aTranslateToOrigin);
338 const double fWidth(pSdrObj->GetLogicRect().getOpenWidth());
339 double fScaleX = fWidth == 0.0 ? 1.0 : 21600.0 / fWidth;
340 const double fHeight(pSdrObj->GetLogicRect().getOpenHeight());
341 double fScaleY = fHeight == 0.0 ? 1.0 : 21600.0 / fHeight;
343 aPolyPolygon.transform(aScale);
344
345 basegfx::B2DHomMatrix aTranslateToCenter(
347 aPolyPolygon.transform(aTranslateToCenter);
348 break;
349 } // end case OBJ_CUSTOMSHAPE
350 case SdrObjKind::Line:
351 {
352 aContour.Insert(0, Point(0, 0));
353 aContour.Insert(1, Point(21600, 21600));
354 aContour.Insert(2, Point(0, 0));
355 return aContour;
356 }
357 case SdrObjKind::PathFill:
358 case SdrObjKind::PathLine:
359 case SdrObjKind::FreehandFill:
360 case SdrObjKind::FreehandLine:
361 case SdrObjKind::PathPoly:
362 case SdrObjKind::PathPolyLine:
363 // case OBJ_POLY: FixMe: Creating wrap polygon would work, but export to DML is currently
364 // case OBJ_PLIN: disabled for unknown reason; related bug 75254.
365 {
366 // Includes removing any control points
367 rtl::Reference<SdrObject> pNewObj = pSdrObj->ConvertToPolyObj(false, false);
368 SdrPathObj* pConverted = dynamic_cast<SdrPathObj*>(pNewObj.get());
369 if (!pConverted)
370 break;
371 aPolyPolygon = pConverted->GetPathPoly();
372 pNewObj.clear();
373
374 // Word adds a line from last to first point. That will cut of indentations from being
375 // filled. To prevent this, the wrap polygon is lead along the path back to the first
376 // point and so indentation is kept.
377 if (!aPolyPolygon.isClosed())
378 {
379 basegfx::B2DPolyPolygon aReverse(aPolyPolygon);
380 aReverse.flip();
381 aPolyPolygon.append(aReverse);
382 }
383
384 // Make relative to range 0..21600, 0..21600
385 Point aCenter(pSdrObj->GetSnapRect().Center());
386 basegfx::B2DHomMatrix aTranslateToOrigin(
387 basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y()));
388 aPolyPolygon.transform(aTranslateToOrigin);
389
390 const double fWidth(pSdrObj->GetLogicRect().getOpenWidth());
391 double fScaleX = fWidth == 0.0 ? 1.0 : 21600.0 / fWidth;
392 const double fHeight(pSdrObj->GetLogicRect().getOpenHeight());
393 double fScaleY = fHeight == 0.0 ? 1.0 : 21600.0 / fHeight;
396 aPolyPolygon.transform(aScale);
397
398 basegfx::B2DHomMatrix aTranslateToCenter(
400 aPolyPolygon.transform(aTranslateToCenter);
401 break;
402 }
403 case SdrObjKind::NONE:
404 default:
405 break;
406 }
407
408 // Simple version for now: Use ready PolygonFromPolyPolygon()
409 const tools::PolyPolygon aToolsPolyPoly(aPolyPolygon);
410 aContour = sw::util::PolygonFromPolyPolygon(aToolsPolyPoly);
411
412 // The wrap polygon needs at least two points in OOXML and three points in Word.
413 switch (aContour.GetSize())
414 {
415 case 0:
416 // use rectangular default
417 aContour.Insert(0, Point(0, 0));
418 aContour.Insert(1, Point(21600, 0));
419 aContour.Insert(2, Point(21600, 21600));
420 aContour.Insert(3, Point(0, 21600));
421 aContour.Insert(4, Point(0, 0));
422 break;
423 case 1:
424 aContour.Insert(1, aContour.GetPoint(0));
425 aContour.Insert(2, aContour.GetPoint(0));
426 break;
427 case 2:
428 aContour.Insert(2, aContour.GetPoint(0));
429 break;
430 default:
431 break;
432 }
433 return aContour;
434}
435} // end anonymous namespace
436
438 SwNodeOffset nEnd, ww8::Frame const* pParentFrame)
439 : m_rExport(rExport)
440{
441 m_rExport.SaveData(nStt, nEnd);
442 m_rExport.m_pParentFrame = pParentFrame;
443}
444
446
449{
450private:
459 OStringBuffer m_aTextFrameStyle;
471
472public:
473 bool m_bFlyFrameGraphic = false;
474
476 oox::drawingml::DrawingML* pDrawingML)
477 : m_rExport(rExport)
478 , m_pSerializer(std::move(pSerializer))
479 , m_pDrawingML(pDrawingML)
480 , m_pFlyFrameSize(nullptr)
481 , m_bTextFrameSyntax(false)
482 , m_bDMLTextFrameSyntax(false)
483 , m_bDrawingOpen(false)
484 , m_bParagraphSdtOpen(false)
487 {
488 }
489
491
492 void textFrameShadow(const SwFrameFormat& rFrameFormat);
493 static bool isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape);
494
496 {
497 m_pSerializer = pSerializer;
498 }
499
501
502 void setFlyFrameSize(const Size* pFlyFrameSize) { m_pFlyFrameSize = pFlyFrameSize; }
503
504 const Size* getFlyFrameSize() const { return m_pFlyFrameSize; }
505
506 void setTextFrameSyntax(bool bTextFrameSyntax) { m_bTextFrameSyntax = bTextFrameSyntax; }
507
508 bool getTextFrameSyntax() const { return m_bTextFrameSyntax; }
509
510 void setDMLTextFrameSyntax(bool bDMLTextFrameSyntax)
511 {
512 m_bDMLTextFrameSyntax = bDMLTextFrameSyntax;
513 }
514
516
518 {
519 m_pFlyAttrList = pFlyAttrList;
520 }
521
523
524 void
526 {
527 m_pTextboxAttrList = pTextboxAttrList;
528 }
529
531 {
532 return m_pTextboxAttrList;
533 }
534
535 OStringBuffer& getTextFrameStyle() { return m_aTextFrameStyle; }
536
537 void setDrawingOpen(bool bDrawingOpen) { m_bDrawingOpen = bDrawingOpen; }
538
539 bool getDrawingOpen() const { return m_bDrawingOpen; }
540
541 void setParagraphSdtOpen(bool bParagraphSdtOpen) { m_bParagraphSdtOpen = bParagraphSdtOpen; }
542
544
545 void setDMLAndVMLDrawingOpen(bool bDMLAndVMLDrawingOpen)
546 {
547 m_bDMLAndVMLDrawingOpen = bDMLAndVMLDrawingOpen;
548 }
549
551
552 void setParagraphHasDrawing(bool bParagraphHasDrawing)
553 {
554 m_bParagraphHasDrawing = bParagraphHasDrawing;
555 }
556
558
560 {
561 return m_pFlyFillAttrList;
562 }
563
564 void
566 {
567 m_pFlyWrapAttrList = pFlyWrapAttrList;
568 }
569
571 {
572 return m_pFlyWrapAttrList.get();
573 }
574
576 {
577 m_pBodyPrAttrList = pBodyPrAttrList;
578 }
579
581
583 {
585 }
586
587 bool getFlyFrameGraphic() const { return m_bFlyFrameGraphic; }
588
590
591 DocxExport& getExport() const { return m_rExport; }
592
593 void setDMLandVMLTextFrameRotation(Degree100 nDMLandVMLTextFrameRotation)
594 {
595 m_nDMLandVMLTextFrameRotation = nDMLandVMLTextFrameRotation;
596 }
597
599};
600
602 oox::drawingml::DrawingML* pDrawingML)
603 : m_pImpl(std::make_unique<Impl>(rExport, pSerializer, pDrawingML))
604{
605}
606
608
610{
611 m_pImpl->setSerializer(pSerializer);
612}
613
614const Size* DocxSdrExport::getFlyFrameSize() const { return m_pImpl->getFlyFrameSize(); }
615
616bool DocxSdrExport::getTextFrameSyntax() const { return m_pImpl->getTextFrameSyntax(); }
617
618bool DocxSdrExport::getDMLTextFrameSyntax() const { return m_pImpl->getDMLTextFrameSyntax(); }
619
621{
622 return m_pImpl->getFlyAttrList();
623}
624
626{
627 return m_pImpl->getTextboxAttrList();
628}
629
630OStringBuffer& DocxSdrExport::getTextFrameStyle() { return m_pImpl->getTextFrameStyle(); }
631
632bool DocxSdrExport::IsDrawingOpen() const { return m_pImpl->getDrawingOpen(); }
633
634void DocxSdrExport::setParagraphSdtOpen(bool bParagraphSdtOpen)
635{
636 m_pImpl->setParagraphSdtOpen(bParagraphSdtOpen);
637}
638
639bool DocxSdrExport::IsDMLAndVMLDrawingOpen() const { return m_pImpl->getDMLAndVMLDrawingOpen(); }
640
641bool DocxSdrExport::IsParagraphHasDrawing() const { return m_pImpl->getParagraphHasDrawing(); }
642
643void DocxSdrExport::setParagraphHasDrawing(bool bParagraphHasDrawing)
644{
645 m_pImpl->setParagraphHasDrawing(bParagraphHasDrawing);
646}
647
649{
650 return m_pImpl->getFlyFillAttrList();
651}
652
654{
655 return m_pImpl->getBodyPrAttrList();
656}
657
659{
660 return m_pImpl->getDashLineStyleAttr();
661}
662
665{
666 m_pImpl->setFlyWrapAttrList(pAttrList);
667}
668
669void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize)
670{
671 // Word uses size excluding right edge. Caller writeDMLDrawing and writeDiagram are changed for
672 // now. ToDo: Look whether the other callers give the size this way.
673 m_pImpl->setDrawingOpen(true);
674 m_pImpl->setParagraphHasDrawing(true);
675 m_pImpl->getSerializer()->startElementNS(XML_w, XML_drawing);
676 const SdrObject* pObj = pFrameFormat->FindRealSdrObject();
677
678 // LO determines the place needed for the object from wrap type, wrap margin ('distance to text'),
679 // object type and anchor type. Word uses dist* for user set margins and effectExtent for place
680 // needed for effects like shadow and glow, for fat stroke and for rotation. We map the LO values
681 // to values needed by Word so that the appearance is the same as far as possible.
682 // All values in Twips, change to EMU is done immediately before writing out.
683
684 bool isAnchor; // true XML_anchor, false XML_inline
685 if (m_pImpl->getFlyFrameGraphic())
686 {
687 isAnchor = false; // make Graphic object inside DMLTextFrame & VMLTextFrame as Inline
688 }
689 else
690 {
691 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
692 }
693
694 // tdf#135047: It must be allowed to find in parents too, but default value of bInP parameter
695 // for GetLRSpace() and GetULSpace() is true, so no direct setting is required.
696 const SvxLRSpaceItem& aLRSpaceItem = pFrameFormat->GetLRSpace();
697 const SvxULSpaceItem& aULSpaceItem = pFrameFormat->GetULSpace();
698 sal_Int64 nDistT = aULSpaceItem.GetUpper();
699 sal_Int64 nDistB = aULSpaceItem.GetLower();
700 sal_Int64 nDistL = aLRSpaceItem.GetLeft();
701 sal_Int64 nDistR = aLRSpaceItem.GetRight();
702
703 // LibreOffice behaves different for frames and drawing objects, but MS Office treats frames
704 // as drawing objects too. Therefore we transform the values from frame so as if they come
705 // from a drawing object.
706 sal_Int32 nWidthDiff(0);
707 sal_Int32 nHeightDiff(0);
708 sal_Int32 nPosXDiff(0);
709 sal_Int32 nPosYDiff(0);
710 sal_Int32 nLeftExt(0);
711 sal_Int32 nRightExt(0);
712 sal_Int32 nTopExt(0);
713 sal_Int32 nBottomExt(0);
714
715 if ((!pObj) || (pObj && (pObj->GetObjIdentifier() == SdrObjKind::SwFlyDrawObjIdentifier)))
716 {
717 // Frame objects have a restricted shadow and no further effects. They have border instead of
718 // stroke. LO includes shadow and border in the object size, but Word not.
719 SvxShadowItem aShadowItem = pFrameFormat->GetShadow();
720 if (aShadowItem.GetLocation() != SvxShadowLocation::NONE)
721 {
722 sal_Int32 nShadowWidth(aShadowItem.GetWidth());
723 switch (aShadowItem.GetLocation())
724 {
725 case SvxShadowLocation::TopLeft:
726 nTopExt = nLeftExt = nShadowWidth;
727 nPosXDiff = nLeftExt; // actual move is postponed
728 nPosYDiff = nTopExt;
729 nWidthDiff = -nLeftExt; // actual size extent is postponed
730 nHeightDiff = -nTopExt;
731 break;
732 case SvxShadowLocation::TopRight:
733 nTopExt = nRightExt = nShadowWidth;
734 nPosYDiff = nTopExt;
735 nWidthDiff = -nRightExt;
736 nHeightDiff = -nTopExt;
737 break;
738 case SvxShadowLocation::BottomLeft:
739 nBottomExt = nLeftExt = nShadowWidth;
740 nPosXDiff = nLeftExt;
741 nWidthDiff = -nLeftExt;
742 nHeightDiff = -nBottomExt;
743 break;
744 case SvxShadowLocation::BottomRight:
745 nBottomExt = nRightExt = nShadowWidth;
746 nWidthDiff = -nRightExt;
747 nHeightDiff = -nBottomExt;
748 break;
749 case SvxShadowLocation::NONE:
750 case SvxShadowLocation::End:
751 break;
752 }
753 }
754 // ToDo: Position refers to outer edge of border in LO, but to center of border in Word.
755 // Adaption is missing here. Frames in LO have no stroke but border. The current conversion
756 // from border to line treats borders like table borders. That might give wrong values
757 // for drawing frames.
758
759 if (pObj && pObj->GetRotateAngle() != 0_deg100)
760 {
761 Degree100 nRotation = pObj->GetRotateAngle();
762 const SwRect aBoundRect(pFrameFormat->FindLayoutRect());
763 tools::Long nMSOWidth = rSize.Width();
764 tools::Long nMSOHeight = rSize.Height();
765 if ((nRotation > 4500_deg100 && nRotation <= 13500_deg100)
766 || (nRotation > 22500_deg100 && nRotation <= 31500_deg100))
767 std::swap(nMSOWidth, nMSOHeight);
768 nBottomExt += (aBoundRect.Height() - 1 - nMSOHeight) / 2;
769 nTopExt += (aBoundRect.Height() - 1 - nMSOHeight) / 2;
770 nLeftExt += (aBoundRect.Width() - nMSOWidth) / 2;
771 nRightExt += (aBoundRect.Width() - nMSOWidth) / 2;
772 }
773 lcl_makeDistAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
774 nRightExt, nBottomExt);
775
776 // ToDo: Inline rotated image fails because it would need wrapTight, what is not possible.
777 // ToDo: Image plus shadow fails because of wrong shadow direction.
778 }
779 else // other objects than frames. pObj exists.
780 {
781 // Word 2007 makes no width-height-swap for images. Detect this situation.
782 sal_Int32 nMode = m_pImpl->getExport().getWordCompatibilityModeFromGrabBag();
783 bool bIsWord2007Image(nMode > 0 && nMode < 14
784 && pObj->GetObjIdentifier() == SdrObjKind::Graphic);
785
786 // Word cannot handle negative EffectExtent although allowed in OOXML, the 'dist' attributes
787 // may not be negative. Take care of that.
788 if (isAnchor)
789 {
790 lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, true,
791 bIsWord2007Image);
792 // We have calculated the effectExtent from boundRect, therefore half stroke width is
793 // already contained.
794 // ToDo: The other half of the stroke width needs to be subtracted from padding.
795 // Where is that?
796
797 // The import has added a difference to dist* in case of contour wrap for to give a
798 // rendering nearer to Word. In that case, we need to subtract it on export.
799 uno::Any aAny;
800 pObj->GetGrabBagItem(aAny);
801 comphelper::SequenceAsHashMap aGrabBag(aAny);
802 auto it = aGrabBag.find("AnchorDistDiff");
803 if (it != aGrabBag.end())
804 {
805 comphelper::SequenceAsHashMap aAnchorDistDiff(it->second);
806 for (const std::pair<const comphelper::OUStringAndHashCode, uno::Any>& rDiff :
807 aAnchorDistDiff)
808 {
809 const OUString& rName = rDiff.first.maString;
810 if (rName == "distTDiff" && rDiff.second.has<sal_Int32>())
811 nDistT -= round(rDiff.second.get<sal_Int32>());
812 else if (rName == "distBDiff" && rDiff.second.has<sal_Int32>())
813 nDistB -= round(rDiff.second.get<sal_Int32>());
814 else if (rName == "distLDiff" && rDiff.second.has<sal_Int32>())
815 nDistL -= rDiff.second.get<sal_Int32>();
816 else if (rName == "distRDiff" && rDiff.second.has<sal_Int32>())
817 nDistR -= rDiff.second.get<sal_Int32>();
818 }
819 }
820 // ToDo: bool bCompansated = ... to be later able to switch from wrapSquare to wrapTight,
821 // if wrapSquare would require negative effectExtent.
822 lcl_makeDistAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
823 nRightExt, nBottomExt);
824 }
825 else
826 {
827 lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, false,
828 bIsWord2007Image);
829 // nDistT,... contain the needed distances from import or set by user. But Word
830 // ignores Dist attributes of inline shapes. So we move all needed distances to
831 // effectExtent and force effectExtent to non-negative.
832 lcl_makeDistZeroAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
833 nRightExt, nBottomExt);
834 }
835 }
836
837 if (isAnchor)
838 {
841
842 bool bOpaque = pFrameFormat->GetOpaque().GetValue();
843 if (pObj)
844 {
845 // SdrObjects know their layer, consider that instead of the frame format.
846 bOpaque = pObj->GetLayer()
847 != pFrameFormat->GetDoc()->getIDocumentDrawModelAccess().GetHellId()
848 && pObj->GetLayer()
849 != pFrameFormat->GetDoc()
852 }
853 attrList->add(XML_behindDoc, bOpaque ? "0" : "1");
854
855 attrList->add(XML_distT, OString::number(TwipsToEMU(nDistT)).getStr());
856 attrList->add(XML_distB, OString::number(TwipsToEMU(nDistB)).getStr());
857 attrList->add(XML_distL, OString::number(TwipsToEMU(nDistL)).getStr());
858 attrList->add(XML_distR, OString::number(TwipsToEMU(nDistR)).getStr());
859
860 attrList->add(XML_simplePos, "0");
861 attrList->add(XML_locked, "0");
862
863 bool bLclInTabCell = true;
864 if (pObj)
865 {
866 uno::Reference<drawing::XShape> xShape((const_cast<SdrObject*>(pObj)->getUnoShape()),
867 uno::UNO_QUERY);
868 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
869 if (xShapeProps.is())
870 xShapeProps->getPropertyValue("IsFollowingTextFlow") >>= bLclInTabCell;
871 }
872 if (bLclInTabCell)
873 attrList->add(XML_layoutInCell, "1");
874 else
875 attrList->add(XML_layoutInCell, "0");
876
877 bool bAllowOverlap = pFrameFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap();
878 attrList->add(XML_allowOverlap, bAllowOverlap ? "1" : "0");
879
880 if (pObj)
881 // It seems 0 and 1 have special meaning: just start counting from 2 to avoid issues with that.
882 attrList->add(XML_relativeHeight, OString::number(pObj->GetOrdNum() + 2));
883 else
884 // relativeHeight is mandatory attribute, if value is not present, we must write default value
885 attrList->add(XML_relativeHeight, "0");
886
887 if (pObj)
888 {
889 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
890 if (!sAnchorId.isEmpty())
891 attrList->addNS(XML_wp14, XML_anchorId,
892 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
893 }
894
895 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_anchor, attrList);
896
897 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_simplePos, XML_x, "0", XML_y,
898 "0"); // required, unused
899
900 // Position is either determined by coordinates aPos or alignment keywords like 'center'.
901 // First prepare them.
902 awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(),
903 pFrameFormat->GetVertOrient().GetPos());
904
905 aPos.X += nPosXDiff; // Make the postponed position move of frames.
906 aPos.Y += nPosYDiff;
907 if (pObj && lcl_IsRotateAngleValid(*pObj)
908 && pObj->GetObjIdentifier() != SdrObjKind::SwFlyDrawObjIdentifier)
909 lclMovePositionWithRotation(aPos, rSize, pObj->GetRotateAngle());
910
911 const char* relativeFromH;
912 const char* relativeFromV;
913 const char* alignH = nullptr;
914 const char* alignV = nullptr;
915 switch (pFrameFormat->GetVertOrient().GetRelationOrient())
916 {
917 case text::RelOrientation::PAGE_PRINT_AREA:
918 relativeFromV = "margin";
919 break;
920 case text::RelOrientation::PAGE_PRINT_AREA_TOP:
921 relativeFromV = "topMargin";
922 break;
923 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
924 relativeFromV = "bottomMargin";
925 break;
926 case text::RelOrientation::PAGE_FRAME:
927 relativeFromV = "page";
928 break;
929 case text::RelOrientation::FRAME:
930 relativeFromV = "paragraph";
931 break;
932 case text::RelOrientation::TEXT_LINE:
933 default:
934 relativeFromV = "line";
935 break;
936 }
937 switch (pFrameFormat->GetVertOrient().GetVertOrient())
938 {
939 case text::VertOrientation::TOP:
940 case text::VertOrientation::CHAR_TOP:
941 case text::VertOrientation::LINE_TOP:
942 if (pFrameFormat->GetVertOrient().GetRelationOrient()
943 == text::RelOrientation::TEXT_LINE)
944 alignV = "bottom";
945 else
946 alignV = "top";
947 break;
948 case text::VertOrientation::BOTTOM:
949 case text::VertOrientation::CHAR_BOTTOM:
950 case text::VertOrientation::LINE_BOTTOM:
951 if (pFrameFormat->GetVertOrient().GetRelationOrient()
952 == text::RelOrientation::TEXT_LINE)
953 alignV = "top";
954 else
955 alignV = "bottom";
956 break;
957 case text::VertOrientation::CENTER:
958 case text::VertOrientation::CHAR_CENTER:
959 case text::VertOrientation::LINE_CENTER:
960 alignV = "center";
961 break;
962 default:
963 break;
964 }
965 switch (pFrameFormat->GetHoriOrient().GetRelationOrient())
966 {
967 case text::RelOrientation::PAGE_PRINT_AREA:
968 relativeFromH = "margin";
969 break;
970 case text::RelOrientation::PAGE_FRAME:
971 relativeFromH = "page";
972 break;
973 case text::RelOrientation::CHAR:
974 relativeFromH = "character";
975 break;
976 case text::RelOrientation::PAGE_RIGHT:
977 relativeFromH = "rightMargin";
978 break;
979 case text::RelOrientation::PAGE_LEFT:
980 relativeFromH = "leftMargin";
981 break;
982 case text::RelOrientation::FRAME:
983 default:
984 relativeFromH = "column";
985 break;
986 }
987 switch (pFrameFormat->GetHoriOrient().GetHoriOrient())
988 {
989 case text::HoriOrientation::LEFT:
990 alignH = "left";
991 break;
992 case text::HoriOrientation::RIGHT:
993 alignH = "right";
994 break;
995 case text::HoriOrientation::CENTER:
996 alignH = "center";
997 break;
998 case text::HoriOrientation::INSIDE:
999 alignH = "inside";
1000 break;
1001 case text::HoriOrientation::OUTSIDE:
1002 alignH = "outside";
1003 break;
1004 default:
1005 break;
1006 }
1007
1008 // write out horizontal position
1009 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionH, XML_relativeFrom,
1010 relativeFromH);
1011
1017 const sal_Int64 MAX_INTEGER_VALUE = SAL_MAX_INT32;
1018 const sal_Int64 MIN_INTEGER_VALUE = SAL_MIN_INT32;
1019
1020 if (alignH != nullptr)
1021 {
1022 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
1023 m_pImpl->getSerializer()->write(alignH);
1024 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
1025 }
1026 else
1027 {
1028 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
1029 sal_Int64 nPosXEMU = TwipsToEMU(aPos.X);
1030
1031 /* Absolute Position Offset Value is of type Int. Hence it should not be greater than
1032 * Maximum value for Int OR Less than the Minimum value for Int.
1033 * - Maximum value for Int = 2147483647
1034 * - Minimum value for Int = -2147483648
1035 *
1036 * As per ECMA Specification : ECMA-376, Second Edition,
1037 * Part 1 - Fundamentals And Markup Language Reference[20.4.3.3 ST_PositionOffset (Absolute Position Offset Value)]
1038 *
1039 * Please refer : http://www.schemacentral.com/sc/xsd/t-xsd_int.html
1040 */
1041
1042 if (nPosXEMU > MAX_INTEGER_VALUE)
1043 {
1044 nPosXEMU = MAX_INTEGER_VALUE;
1045 }
1046 else if (nPosXEMU < MIN_INTEGER_VALUE)
1047 {
1048 nPosXEMU = MIN_INTEGER_VALUE;
1049 }
1050 m_pImpl->getSerializer()->write(nPosXEMU);
1051 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
1052 }
1053 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionH);
1054
1055 // write out vertical position
1056 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionV, XML_relativeFrom,
1057 relativeFromV);
1058 sal_Int64 nPosYEMU = TwipsToEMU(aPos.Y);
1059
1060 // tdf#93675, 0 below line/paragraph and/or top line/paragraph with
1061 // wrap top+bottom or other wraps is affecting the line directly
1062 // above the anchor line, which seems odd, but a tiny adjustment
1063 // here to bring the top down convinces msoffice to wrap like us
1064 if (nPosYEMU == 0
1065 && (strcmp(relativeFromV, "line") == 0 || strcmp(relativeFromV, "paragraph") == 0)
1066 && (!alignV || strcmp(alignV, "top") == 0))
1067 {
1068 alignV = nullptr;
1069 nPosYEMU = TwipsToEMU(1);
1070 }
1071
1072 if (alignV != nullptr)
1073 {
1074 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
1075 m_pImpl->getSerializer()->write(alignV);
1076 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
1077 }
1078 else
1079 {
1080 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
1081 if (nPosYEMU > MAX_INTEGER_VALUE)
1082 {
1083 nPosYEMU = MAX_INTEGER_VALUE;
1084 }
1085 else if (nPosYEMU < MIN_INTEGER_VALUE)
1086 {
1087 nPosYEMU = MIN_INTEGER_VALUE;
1088 }
1089 m_pImpl->getSerializer()->write(nPosYEMU);
1090 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
1091 }
1092 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionV);
1093 }
1094 else // inline
1095 {
1096 // nDist is forced to zero above and ignored by Word anyway, so write 0 directly.
1099 aAttrList->add(XML_distT, OString::number(0).getStr());
1100 aAttrList->add(XML_distB, OString::number(0).getStr());
1101 aAttrList->add(XML_distL, OString::number(0).getStr());
1102 aAttrList->add(XML_distR, OString::number(0).getStr());
1103 if (pObj)
1104 {
1105 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
1106 if (!sAnchorId.isEmpty())
1107 aAttrList->addNS(XML_wp14, XML_anchorId,
1108 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
1109 }
1110 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_inline, aAttrList);
1111 }
1112
1113 // now the common parts 'extent' and 'effectExtent'
1131 sal_uInt64 cx = TwipsToEMU(
1132 std::clamp(rSize.Width() + nWidthDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
1133 OString aWidth(OString::number(std::min(cx, sal_uInt64(SAL_MAX_INT32))));
1134 sal_uInt64 cy = TwipsToEMU(
1135 std::clamp(rSize.Height() + nHeightDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
1136 OString aHeight(OString::number(std::min(cy, sal_uInt64(SAL_MAX_INT32))));
1137
1138 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_extent, XML_cx, aWidth, XML_cy, aHeight);
1139
1140 // XML_effectExtent, includes effects, fat stroke and rotation
1141 // FixMe: tdf141880. Because LibreOffice currently cannot handle negative vertical margins, they
1142 // were forced to zero on import. Especially bottom margin of inline anchored rotated objects are
1143 // affected. If the object was not changed, it would be better to export the original values
1144 // from grab-Bag. Unfortunately there exists no marker for "not changed", so a heuristic is used
1145 // here: If current left, top and right margins do not differ more than 1Hmm = 635EMU from the
1146 // values in grab-Bag, it is likely, that the object was not changed and we restore the values
1147 // from grab-Bag.
1148 sal_Int64 nLeftExtEMU = TwipsToEMU(nLeftExt);
1149 sal_Int64 nTopExtEMU = TwipsToEMU(nTopExt);
1150 sal_Int64 nRightExtEMU = TwipsToEMU(nRightExt);
1151 sal_Int64 nBottomExtEMU = TwipsToEMU(nBottomExt);
1152 if (pObj)
1153 {
1154 uno::Any aAny;
1155 pObj->GetGrabBagItem(aAny);
1156 comphelper::SequenceAsHashMap aGrabBag(aAny);
1157 auto it = aGrabBag.find("CT_EffectExtent");
1158 if (it != aGrabBag.end())
1159 {
1160 comphelper::SequenceAsHashMap aEffectExtent(it->second);
1161 sal_Int64 nLeftExtGrabBag(0);
1162 sal_Int64 nTopExtGrabBag(0);
1163 sal_Int64 nRightExtGrabBag(0);
1164 sal_Int64 nBottomExtGrabBag(0);
1165 for (const std::pair<const comphelper::OUStringAndHashCode, uno::Any>& rDirection :
1166 aEffectExtent)
1167 {
1168 const OUString& rName = rDirection.first.maString;
1169 if (rName == "l" && rDirection.second.has<sal_Int32>())
1170 nLeftExtGrabBag = rDirection.second.get<sal_Int32>();
1171 else if (rName == "t" && rDirection.second.has<sal_Int32>())
1172 nTopExtGrabBag = rDirection.second.get<sal_Int32>();
1173 else if (rName == "r" && rDirection.second.has<sal_Int32>())
1174 nRightExtGrabBag = rDirection.second.get<sal_Int32>();
1175 else if (rName == "b" && rDirection.second.has<sal_Int32>())
1176 nBottomExtGrabBag = rDirection.second.get<sal_Int32>();
1177 }
1178 if (abs(nLeftExtEMU - nLeftExtGrabBag) <= 635 && abs(nTopExtEMU - nTopExtGrabBag) <= 635
1179 && abs(nRightExtEMU - nRightExtGrabBag) <= 635)
1180 {
1181 nLeftExtEMU = nLeftExtGrabBag;
1182 nTopExtEMU = nTopExtGrabBag;
1183 nRightExtEMU = nRightExtGrabBag;
1184 nBottomExtEMU = nBottomExtGrabBag;
1185 }
1186 }
1187 }
1188 m_pImpl->getSerializer()->singleElementNS(
1189 XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExtEMU), XML_t,
1190 OString::number(nTopExtEMU), XML_r, OString::number(nRightExtEMU), XML_b,
1191 OString::number(nBottomExtEMU));
1192
1193 if (!isAnchor)
1194 return; // OOXML 'inline' has not wrap type at all
1195
1196 // XML_anchor has exact one of types wrapNone, wrapSquare, wrapTight, wrapThrough and
1197 // WrapTopAndBottom. Map our own types to them as far as possible.
1198
1199 if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGH
1200 || pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGHT)
1201 {
1202 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone);
1203 return;
1204 }
1205
1206 if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_NONE)
1207 {
1208 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom);
1209 return;
1210 }
1211
1212 // All remaining cases need attribute XML_wrapText
1213 OUString sWrapType;
1214 switch (pFrameFormat->GetSurround().GetSurround())
1215 {
1216 case text::WrapTextMode_DYNAMIC:
1217 sWrapType = OUString("largest");
1218 break;
1219 case text::WrapTextMode_LEFT:
1220 sWrapType = OUString("left");
1221 break;
1222 case text::WrapTextMode_RIGHT:
1223 sWrapType = OUString("right");
1224 break;
1225 case text::WrapTextMode_PARALLEL:
1226 default:
1227 sWrapType = OUString("bothSides");
1228 break;
1229 }
1230
1231 // ToDo: Exclude cases where LibreOffice wrap without contour is different
1232 // from Word XML_wrapSquare or where direct use of distances not possible and workaround
1233 // will be done using wrapPolygon.
1234 // ToDo: handle case Writer frame, where contour can be set in LibreOffice but is not rendered.
1235
1236 // This case needs no wrapPolygon
1237 if (!pFrameFormat->GetSurround().IsContour())
1238 {
1239 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, sWrapType);
1240 return;
1241 }
1242
1243 // Contour wrap.
1244 sal_Int32 nWrapToken
1245 = pFrameFormat->GetSurround().IsOutside() ? XML_wrapTight : XML_wrapThrough;
1246
1247 // ToDo: cases where wrapPolygon is used as workaround.
1248
1249 // Own wrap polygon exists only for TextGraphicObject and TextEmbeddedObject. It might be edited
1250 // by user. If such exists, we use it and we are done.
1251 if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat))
1252 {
1253 const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
1254 if (pPolyPoly && pPolyPoly->Count())
1255 {
1256 tools::Polygon aPoly
1257 = sw::util::CorrectWordWrapPolygonForExport(*pPolyPoly, pNd, /*bCorrectCrop=*/true);
1258 if (aPoly.GetSize() >= 3)
1259 {
1260 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText,
1261 sWrapType);
1262 // ToDo: Test whether XML_edited true or false gives better results.
1263 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1264 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
1265 OString::number(aPoly[0].X()), XML_y,
1266 OString::number(aPoly[0].Y()));
1267 for (sal_uInt16 i = 1; i < aPoly.GetSize(); ++i)
1268 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_lineTo, XML_x,
1269 OString::number(aPoly[i].X()), XML_y,
1270 OString::number(aPoly[i].Y()));
1271 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1272
1273 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1274 return;
1275 }
1276 }
1277 }
1278
1279 // If this shape comes from ooxml import, there might be a wrap polygon in InteropGrabBag.
1280 // Wrap polygons can be edited by users in Word. They are independent from changing shape size or
1281 // rotation. So it is likely, that it is still usable.
1282 if (pObj)
1283 {
1284 uno::Any aAny;
1285 pObj->GetGrabBagItem(aAny);
1286 comphelper::SequenceAsHashMap aGrabBag(aAny);
1287 auto it = aGrabBag.find("CT_WrapPath");
1288 if (it != aGrabBag.end())
1289 {
1290 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
1291
1292 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1293 auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>();
1294 const auto& rPoints = aSeqSeq[0];
1295 for (auto i = rPoints.begin(); i != rPoints.end(); ++i)
1296 {
1297 const awt::Point& rPoint = *i;
1298 m_pImpl->getSerializer()->singleElementNS(
1299 XML_wp, (i == rPoints.begin() ? XML_start : XML_lineTo), XML_x,
1300 OString::number(rPoint.X), XML_y, OString::number(rPoint.Y));
1301 }
1302 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1303
1304 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1305 return;
1306 }
1307 }
1308
1309 // In this case we likely had an odt document to be exported to docx. ODF does not know the
1310 // concept of a wrap polygon and LibreOffice has no one internally. So as a workaround, we
1311 // generate a wrap polygon from the shape geometry.
1312 tools::Polygon aContour = lcl_CreateContourPolygon(const_cast<SdrObject*>(pObj));
1313
1314 // lcl_CreateContourPolygon() ensures at least three points
1315 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
1316
1317 // ToDo: Test whether XML_edited true or false gives better results.
1318 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1319 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
1320 OString::number(aContour.GetPoint(0).getX()), XML_y,
1321 OString::number(aContour.GetPoint(0).getY()));
1322 for (sal_uInt32 i = 1; i < aContour.GetSize(); i++)
1323 m_pImpl->getSerializer()->singleElementNS(
1324 XML_wp, XML_lineTo, XML_x, OString::number(aContour.GetPoint(i).getX()), XML_y,
1325 OString::number(aContour.GetPoint(i).getY()));
1326 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1327
1328 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1329}
1330
1332{
1333 bool isAnchor;
1334 if (m_pImpl->getFlyFrameGraphic())
1335 {
1336 isAnchor = false; // end Inline Graphic object inside DMLTextFrame
1337 }
1338 else
1339 {
1340 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
1341 }
1342 m_pImpl->getSerializer()->endElementNS(XML_wp, isAnchor ? XML_anchor : XML_inline);
1343
1344 m_pImpl->getSerializer()->endElementNS(XML_w, XML_drawing);
1345 m_pImpl->setDrawingOpen(false);
1346}
1347
1348void DocxSdrExport::writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat)
1349{
1350 m_pImpl->getSerializer()->startElementNS(XML_w, XML_pict);
1351 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
1352 // See WinwordAnchoring::SetAnchoring(), these are not part of the SdrObject, have to be passed around manually.
1353
1354 SwFormatFollowTextFlow const& rFlow(rFrameFormat.GetFollowTextFlow());
1355 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
1356 const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
1357 SwFormatSurround const& rSurround(rFrameFormat.GetSurround());
1358
1360 m_pImpl->getExport().VMLExporter().AddSdrObject(
1361 *sdrObj, rFlow.GetValue(), rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(),
1362 rHoriOri.GetRelationOrient(), rVertOri.GetRelationOrient(), pAttrList.get(), true);
1363 m_pImpl->getSerializer()->endElementNS(XML_w, XML_pict);
1364}
1365
1366static bool lcl_isLockedCanvas(const uno::Reference<drawing::XShape>& xShape)
1367{
1368 const uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, "InteropGrabBag");
1369 /*
1370 * Export as Locked Canvas only if the property
1371 * is in the PropertySet
1372 */
1373 return std::any_of(propList.begin(), propList.end(), [](const beans::PropertyValue& rProp) {
1374 return rProp.Name == "LockedCanvas";
1375 });
1376}
1377
1378void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat,
1379 int nAnchorId)
1380{
1381 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape());
1382 if (!Impl::isSupportedDMLShape(xShape))
1383 return;
1384
1385 m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject);
1386
1387 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1388 Size aSize(pSdrObject->GetLogicRect().getOpenWidth(),
1389 pSdrObject->GetLogicRect().getOpenHeight());
1390 startDMLAnchorInline(pFrameFormat, aSize);
1391
1394 pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr());
1395 pDocPrAttrList->add(XML_name, OUStringToOString(pSdrObject->GetName(), RTL_TEXTENCODING_UTF8));
1396 if (!pSdrObject->GetTitle().isEmpty())
1397 pDocPrAttrList->add(XML_title,
1398 OUStringToOString(pSdrObject->GetTitle(), RTL_TEXTENCODING_UTF8));
1399 if (!pSdrObject->GetDescription().isEmpty())
1400 pDocPrAttrList->add(XML_descr,
1401 OUStringToOString(pSdrObject->GetDescription(), RTL_TEXTENCODING_UTF8));
1402 if (!pSdrObject->IsVisible()
1403 && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
1404
1405 pDocPrAttrList->add(XML_hidden, OString::number(1).getStr());
1406
1407 pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
1408 OUString sHyperlink = pSdrObject->getHyperlink();
1409 if (!sHyperlink.isEmpty())
1410 {
1411 OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
1412 pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1415 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1416 FSNS(XML_xmlns, XML_a),
1417 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1418 }
1419 pFS->endElementNS(XML_wp, XML_docPr);
1420
1421 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
1422 const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
1423 if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"))
1424 pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
1425 else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1426 pNamespace = "http://schemas.openxmlformats.org/drawingml/2006/picture";
1427 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1428 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1429 pFS->startElementNS(XML_a, XML_graphicData, XML_uri, pNamespace);
1430
1431 bool bLockedCanvas = lcl_isLockedCanvas(xShape);
1432 if (bLockedCanvas)
1433 pFS->startElementNS(
1434 XML_lc, XML_lockedCanvas, FSNS(XML_xmlns, XML_lc),
1435 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlLockedCanvas)));
1436
1437 m_pImpl->getExport().OutputDML(xShape);
1438
1439 if (bLockedCanvas)
1440 pFS->endElementNS(XML_lc, XML_lockedCanvas);
1441 pFS->endElementNS(XML_a, XML_graphicData);
1442 pFS->endElementNS(XML_a, XML_graphic);
1443
1444 // Relative size of the drawing.
1445 if (pSdrObject->GetRelativeWidth())
1446 {
1447 // At the moment drawinglayer objects are always relative from page.
1448 OUString sValue;
1449 switch (pSdrObject->GetRelativeWidthRelation())
1450 {
1451 case text::RelOrientation::FRAME:
1452 sValue = "margin";
1453 break;
1454 case text::RelOrientation::PAGE_LEFT:
1455 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
1456 sValue = "outsideMargin";
1457 else
1458 sValue = "leftMargin";
1459 break;
1460 case text::RelOrientation::PAGE_RIGHT:
1461 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
1462 sValue = "insideMargin";
1463 else
1464 sValue = "rightMargin";
1465 break;
1466 case text::RelOrientation::PAGE_FRAME:
1467 default:
1468 sValue = "page";
1469 break;
1470 }
1471 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, sValue);
1472 pFS->startElementNS(XML_wp14, XML_pctWidth);
1473 pFS->writeEscaped(
1474 OUString::number(*pSdrObject->GetRelativeWidth() * 100 * oox::drawingml::PER_PERCENT));
1475 pFS->endElementNS(XML_wp14, XML_pctWidth);
1476 pFS->endElementNS(XML_wp14, XML_sizeRelH);
1477 }
1478 if (pSdrObject->GetRelativeHeight())
1479 {
1480 OUString sValue;
1481 switch (pSdrObject->GetRelativeHeightRelation())
1482 {
1483 case text::RelOrientation::FRAME:
1484 sValue = "margin";
1485 break;
1486 case text::RelOrientation::PAGE_PRINT_AREA:
1487 sValue = "topMargin";
1488 break;
1489 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
1490 sValue = "bottomMargin";
1491 break;
1492 case text::RelOrientation::PAGE_FRAME:
1493 default:
1494 sValue = "page";
1495 break;
1496 }
1497 pFS->startElementNS(XML_wp14, XML_sizeRelV, XML_relativeFrom, sValue);
1498 pFS->startElementNS(XML_wp14, XML_pctHeight);
1499 pFS->writeEscaped(
1500 OUString::number(*pSdrObject->GetRelativeHeight() * 100 * oox::drawingml::PER_PERCENT));
1501 pFS->endElementNS(XML_wp14, XML_pctHeight);
1502 pFS->endElementNS(XML_wp14, XML_sizeRelV);
1503 }
1504
1505 endDMLAnchorInline(pFrameFormat);
1506}
1507
1509{
1510 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1511 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1512 return;
1513
1514 OString aShadowWidth(OString::number(double(aShadowItem.GetWidth()) / 20) + "pt");
1515 OString aOffset;
1516 switch (aShadowItem.GetLocation())
1517 {
1518 case SvxShadowLocation::TopLeft:
1519 aOffset = "-" + aShadowWidth + ",-" + aShadowWidth;
1520 break;
1521 case SvxShadowLocation::TopRight:
1522 aOffset = aShadowWidth + ",-" + aShadowWidth;
1523 break;
1524 case SvxShadowLocation::BottomLeft:
1525 aOffset = "-" + aShadowWidth + "," + aShadowWidth;
1526 break;
1527 case SvxShadowLocation::BottomRight:
1528 aOffset = aShadowWidth + "," + aShadowWidth;
1529 break;
1530 case SvxShadowLocation::NONE:
1531 case SvxShadowLocation::End:
1532 break;
1533 }
1534 if (aOffset.isEmpty())
1535 return;
1536
1537 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1538 m_pSerializer->singleElementNS(XML_v, XML_shadow, XML_on, "t", XML_color, "#" + aShadowColor,
1539 XML_offset, aOffset);
1540}
1541
1542bool DocxSdrExport::Impl::isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape)
1543{
1544 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
1545 if (xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonShape")
1546 || xServiceInfo->supportsService("com.sun.star.drawing.PolyLineShape"))
1547 return false;
1548
1549 // For signature line shapes, we don't want DML, just the VML shape.
1550 if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1551 {
1552 uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
1553 bool bIsSignatureLineShape = false;
1554 xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLineShape;
1555 if (bIsSignatureLineShape)
1556 return false;
1557 }
1558
1559 return true;
1560}
1561
1563 const SwFrameFormat& rFrameFormat, int nAnchorId)
1564{
1565 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1566 m_pImpl->setDMLAndVMLDrawingOpen(true);
1567
1568 // Depending on the shape type, we actually don't write the shape as DML.
1569 OUString sShapeType;
1570 ShapeFlag nMirrorFlags = ShapeFlag::NONE;
1571 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObj)->getUnoShape());
1572
1573 MSO_SPT eShapeType
1574 = EscherPropertyContainer::GetCustomShapeType(xShape, nMirrorFlags, sShapeType);
1575
1576 // In case we are already inside a DML block, then write the shape only as VML, turn out that's allowed to do.
1577 // A common service created in util to check for VML shapes which are allowed to have textbox in content
1579 && (!bDMLAndVMLDrawingOpen || lcl_isLockedCanvas(xShape))) // Locked canvas is OK inside DML
1580 {
1581 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_AlternateContent);
1582
1583 auto pObjGroup = dynamic_cast<const SdrObjGroup*>(sdrObj);
1584 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Choice, XML_Requires,
1585 (pObjGroup ? "wpg" : "wps"));
1586 writeDMLDrawing(sdrObj, &rFrameFormat, nAnchorId);
1587 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Choice);
1588
1589 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Fallback);
1590 writeVMLDrawing(sdrObj, rFrameFormat);
1591 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Fallback);
1592
1593 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_AlternateContent);
1594 }
1595 else
1596 writeVMLDrawing(sdrObj, rFrameFormat);
1597
1598 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
1599}
1600
1601// Converts ARGB transparency (0..255) to drawingml alpha (opposite, and 0..100000)
1602static OString lcl_TransparencyToDrawingMlAlpha(const Color& rColor)
1603{
1604 if (rColor.IsTransparent())
1605 {
1606 sal_Int32 nAlphaPercent = float(rColor.GetAlpha()) / 2.55;
1607 return OString::number(nAlphaPercent * oox::drawingml::PER_PERCENT);
1608 }
1609
1610 return OString();
1611}
1612
1614{
1615 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1616
1617 // Output effects
1618 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1619 return;
1620
1621 // Distance is measured diagonally from corner
1622 double nShadowDist
1623 = sqrt(static_cast<double>(aShadowItem.GetWidth()) * aShadowItem.GetWidth() * 2.0);
1624 OString aShadowDist(OString::number(TwipsToEMU(nShadowDist)));
1625 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1626 OString aShadowAlpha = lcl_TransparencyToDrawingMlAlpha(aShadowItem.GetColor());
1627 sal_uInt32 nShadowDir = 0;
1628 switch (aShadowItem.GetLocation())
1629 {
1630 case SvxShadowLocation::TopLeft:
1631 nShadowDir = 13500000;
1632 break;
1633 case SvxShadowLocation::TopRight:
1634 nShadowDir = 18900000;
1635 break;
1636 case SvxShadowLocation::BottomLeft:
1637 nShadowDir = 8100000;
1638 break;
1639 case SvxShadowLocation::BottomRight:
1640 nShadowDir = 2700000;
1641 break;
1642 case SvxShadowLocation::NONE:
1643 case SvxShadowLocation::End:
1644 break;
1645 }
1646 OString aShadowDir(OString::number(nShadowDir));
1647
1648 m_pImpl->getSerializer()->startElementNS(XML_a, XML_effectLst);
1649 m_pImpl->getSerializer()->startElementNS(XML_a, XML_outerShdw, XML_dist, aShadowDist, XML_dir,
1650 aShadowDir);
1651 if (aShadowAlpha.isEmpty())
1652 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1653 else
1654 {
1655 m_pImpl->getSerializer()->startElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1656 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_alpha, XML_val, aShadowAlpha);
1657 m_pImpl->getSerializer()->endElementNS(XML_a, XML_srgbClr);
1658 }
1659 m_pImpl->getSerializer()->endElementNS(XML_a, XML_outerShdw);
1660 m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst);
1661}
1662
1663void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat,
1664 int nDiagramId)
1665{
1666 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(),
1667 uno::UNO_QUERY);
1668
1669 // write necessary tags to document.xml
1670 Size aSize(sdrObject->GetSnapRect().getOpenWidth(), sdrObject->GetSnapRect().getOpenHeight());
1671 startDMLAnchorInline(&rFrameFormat, aSize);
1672
1673 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
1674 m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId);
1675
1676 endDMLAnchorInline(&rFrameFormat);
1677}
1678
1680{
1681 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1682 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1683
1684 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1685 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1686
1687 //Save data here and restore when out of scope
1688 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1689
1691 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1692 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
1693 m_pImpl->getExport().WriteText();
1694}
1695
1697{
1698 const editeng::SvxBorderLine* pBorderLine = nullptr;
1699
1700 if (rBox.GetTop())
1701 {
1702 pBorderLine = rBox.GetTop();
1703 }
1704 else if (rBox.GetLeft())
1705 {
1706 pBorderLine = rBox.GetLeft();
1707 }
1708 else if (rBox.GetBottom())
1709 {
1710 pBorderLine = rBox.GetBottom();
1711 }
1712 else if (rBox.GetRight())
1713 {
1714 pBorderLine = rBox.GetRight();
1715 }
1716
1717 if (!pBorderLine)
1718 {
1719 return;
1720 }
1721
1722 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1723 if (pBorderLine->GetWidth() == SvxBorderLineWidth::Hairline)
1724 pFS->startElementNS(XML_a, XML_ln);
1725 else
1726 {
1727 double fConverted(editeng::ConvertBorderWidthToWord(pBorderLine->GetBorderLineStyle(),
1728 pBorderLine->GetWidth()));
1729 OString sWidth(OString::number(TwipsToEMU(fConverted)));
1730 pFS->startElementNS(XML_a, XML_ln, XML_w, sWidth);
1731 }
1732
1733 pFS->startElementNS(XML_a, XML_solidFill);
1734 OString sColor(msfilter::util::ConvertColor(pBorderLine->GetColor()));
1735 pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
1736 pFS->endElementNS(XML_a, XML_solidFill);
1737
1738 if (SvxBorderLineStyle::DASHED == pBorderLine->GetBorderLineStyle()) // Line Style is Dash type
1739 pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
1740
1741 pFS->endElementNS(XML_a, XML_ln);
1742}
1743
1744void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId,
1745 bool bTextBoxOnly)
1746{
1747 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1748 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
1749
1750 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1751 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1752 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1753
1754 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1755 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1756
1757 //Save data here and restore when out of scope
1758 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1759
1760 // When a frame has some low height, but automatically expanded due
1761 // to lots of contents, this size contains the real size.
1762 const Size aSize = pParentFrame->GetSize();
1763
1764 uno::Reference<drawing::XShape> xShape;
1765 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
1766 if (pSdrObj)
1767 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
1768 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1769 uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
1770 if (xPropertySet.is())
1771 xPropSetInfo = xPropertySet->getPropertySetInfo();
1772
1774 {
1775 drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP;
1776 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("TextVerticalAdjust"))
1777 xPropertySet->getPropertyValue("TextVerticalAdjust") >>= eAdjust;
1778 m_pImpl->getBodyPrAttrList()->add(XML_anchor,
1780 }
1781
1782 if (!bTextBoxOnly)
1783 {
1784 startDMLAnchorInline(&rFrameFormat, aSize);
1785
1788 pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr());
1789 pDocPrAttrList->add(XML_name,
1790 OUStringToOString(rFrameFormat.GetName(), RTL_TEXTENCODING_UTF8));
1791
1792 pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
1793
1794 OUString sHyperlink;
1795 if (xPropertySet.is())
1796 xPropertySet->getPropertyValue("HyperLinkURL") >>= sHyperlink;
1797 if (!sHyperlink.isEmpty())
1798 {
1799 OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
1800 pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1803 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1804 FSNS(XML_xmlns, XML_a),
1805 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1806 }
1807
1808 pFS->endElementNS(XML_wp, XML_docPr);
1809
1810 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1811 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1812 pFS->startElementNS(XML_a, XML_graphicData, XML_uri,
1813 "http://schemas.microsoft.com/office/word/2010/wordprocessingShape");
1814 pFS->startElementNS(XML_wps, XML_wsp);
1815 pFS->singleElementNS(XML_wps, XML_cNvSpPr, XML_txBox, "1");
1816
1817 uno::Any aRotation;
1818 m_pImpl->setDMLandVMLTextFrameRotation(0_deg100);
1819 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1820 {
1821 uno::Sequence<beans::PropertyValue> propList;
1822 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1823 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
1824 [](const beans::PropertyValue& rProp) {
1825 return rProp.Name == "mso-rotation-angle";
1826 });
1827 if (pProp != std::cend(propList))
1828 aRotation = pProp->Value;
1829 }
1830 sal_Int32 nTmp;
1831 if (aRotation >>= nTmp)
1832 m_pImpl->getDMLandVMLTextFrameRotation() = Degree100(nTmp);
1833 OString sRotation(OString::number(
1834 oox::drawingml::ExportRotateClockwisify(m_pImpl->getDMLandVMLTextFrameRotation())));
1835 // Shape properties
1836 pFS->startElementNS(XML_wps, XML_spPr);
1837 if (m_pImpl->getDMLandVMLTextFrameRotation())
1838 {
1839 pFS->startElementNS(XML_a, XML_xfrm, XML_rot, sRotation);
1840 }
1841 else
1842 {
1843 pFS->startElementNS(XML_a, XML_xfrm);
1844 }
1845 pFS->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
1846 OString aWidth(OString::number(TwipsToEMU(aSize.Width())));
1847 OString aHeight(OString::number(TwipsToEMU(aSize.Height())));
1848 pFS->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
1849 pFS->endElementNS(XML_a, XML_xfrm);
1850 OUString shapeType = "rect";
1851 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1852 {
1853 uno::Sequence<beans::PropertyValue> propList;
1854 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1855 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
1856 [](const beans::PropertyValue& rProp) {
1857 return rProp.Name == "mso-orig-shape-type";
1858 });
1859 if (pProp != std::cend(propList))
1860 pProp->Value >>= shapeType;
1861 }
1862 //Empty shapeType will lead to corruption so to avoid that shapeType is set to default i.e. "rect"
1863 if (shapeType.isEmpty())
1864 shapeType = "rect";
1865
1866 pFS->singleElementNS(XML_a, XML_prstGeom, XML_prst, shapeType);
1867 m_pImpl->setDMLTextFrameSyntax(true);
1868 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
1869 m_pImpl->setDMLTextFrameSyntax(false);
1870 writeDMLEffectLst(rFrameFormat);
1871 pFS->endElementNS(XML_wps, XML_spPr);
1872 }
1873
1874 //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.
1875 if (!m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized)
1876 {
1877 sal_Int32 nSeq = 0;
1878 for (auto& rEntry : m_pImpl->getExport().m_aLinkedTextboxesHelper)
1879 {
1880 //find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link
1881 if (rEntry.second.sPrevChain.isEmpty() && !rEntry.second.sNextChain.isEmpty())
1882 {
1883 //assign this chain a unique ID and start a new sequence
1884 nSeq = 0;
1885 rEntry.second.nId = ++m_pImpl->getExport().m_nLinkedTextboxesChainId;
1886 rEntry.second.nSeq = nSeq;
1887
1888 OUString sCheckForBrokenChains = rEntry.first;
1889
1890 //follow the chain and assign the same id, and incremental sequence numbers.
1891 auto followChainIter
1892 = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(rEntry.second.sNextChain);
1893 while (followChainIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1894 {
1895 //verify that the NEXT textbox also points to me as the PREVIOUS.
1896 // A broken link indicates a leftover remnant that can be ignored.
1897 if (followChainIter->second.sPrevChain != sCheckForBrokenChains)
1898 break;
1899
1900 followChainIter->second.nId = m_pImpl->getExport().m_nLinkedTextboxesChainId;
1901 followChainIter->second.nSeq = ++nSeq;
1902
1903 //empty next chain indicates the end of the linked chain.
1904 if (followChainIter->second.sNextChain.isEmpty())
1905 break;
1906
1907 sCheckForBrokenChains = followChainIter->first;
1908 followChainIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(
1909 followChainIter->second.sNextChain);
1910 }
1911 }
1912 }
1913 m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized = true;
1914 }
1915
1916 m_pImpl->getExport().m_pParentFrame = nullptr;
1917 bool skipTxBxContent = false;
1918 bool isTxbxLinked = false;
1919
1920 OUString sLinkChainName;
1921 if (xPropSetInfo.is())
1922 {
1923 if (xPropSetInfo->hasPropertyByName("LinkDisplayName"))
1924 xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
1925 else if (xPropSetInfo->hasPropertyByName("ChainName"))
1926 xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
1927 }
1928
1929 // second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx
1930 auto linkedTextboxesIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(sLinkChainName);
1931 if (linkedTextboxesIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1932 {
1933 if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq != 0))
1934 {
1935 //not the first in the chain, so write the tag as linkedTxbx
1936 pFS->singleElementNS(XML_wps, XML_linkedTxbx, XML_id,
1937 OString::number(linkedTextboxesIter->second.nId), XML_seq,
1938 OString::number(linkedTextboxesIter->second.nSeq));
1939 /* no text content should be added to this tag,
1940 since the textbox is linked, the entire content
1941 is written in txbx block
1942 */
1943 skipTxBxContent = true;
1944 }
1945 else if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0))
1946 {
1947 /* this is the first textbox in the chaining, we add the text content
1948 to this block*/
1949 //since the text box is linked, it needs an id.
1950 pFS->startElementNS(XML_wps, XML_txbx, XML_id,
1951 OString::number(linkedTextboxesIter->second.nId));
1952 isTxbxLinked = true;
1953 }
1954 }
1955
1956 if (!skipTxBxContent)
1957 {
1958 if (!isTxbxLinked)
1959 pFS->startElementNS(XML_wps, XML_txbx); //text box is not linked, therefore no id.
1960
1961 pFS->startElementNS(XML_w, XML_txbxContent);
1962
1963 const SvxFrameDirectionItem& rDirection = rFrameFormat.GetFrameDir();
1964 if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB)
1965 m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert");
1966 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
1967 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270");
1968 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB)
1969 m_pImpl->getBodyPrAttrList()->add(XML_vert, "mongolianVert");
1970 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB90)
1971 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert");
1972 {
1973 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1974 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
1975 m_pImpl->getExport().WriteText();
1976 if (m_pImpl->getParagraphSdtOpen())
1977 {
1978 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
1979 m_pImpl->setParagraphSdtOpen(false);
1980 }
1981 }
1982
1983 pFS->endElementNS(XML_w, XML_txbxContent);
1984 pFS->endElementNS(XML_wps, XML_txbx);
1985 }
1986
1987 // We need to init padding to 0, if it's not set.
1988 // In LO the default is 0 and so ins attributes are not set when padding is 0
1989 // but in MSO the default is 254 / 127, so we need to set 0 padding explicitly
1990 if (m_pImpl->getBodyPrAttrList())
1991 {
1992 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_lIns))
1993 m_pImpl->getBodyPrAttrList()->add(XML_lIns, OString::number(0));
1994 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_tIns))
1995 m_pImpl->getBodyPrAttrList()->add(XML_tIns, OString::number(0));
1996 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_rIns))
1997 m_pImpl->getBodyPrAttrList()->add(XML_rIns, OString::number(0));
1998 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_bIns))
1999 m_pImpl->getBodyPrAttrList()->add(XML_bIns, OString::number(0));
2000 }
2001
2002 rtl::Reference<FastAttributeList> xBodyPrAttrList(m_pImpl->getBodyPrAttrList());
2003 m_pImpl->setBodyPrAttrList(nullptr);
2004 if (!bTextBoxOnly)
2005 {
2006 pFS->startElementNS(XML_wps, XML_bodyPr, xBodyPrAttrList);
2007 // AutoSize of the Text Frame.
2008 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
2009 pFS->singleElementNS(
2010 XML_a,
2011 (rSize.GetHeightSizeType() == SwFrameSize::Variable ? XML_spAutoFit : XML_noAutofit));
2012 pFS->endElementNS(XML_wps, XML_bodyPr);
2013
2014 pFS->endElementNS(XML_wps, XML_wsp);
2015 pFS->endElementNS(XML_a, XML_graphicData);
2016 pFS->endElementNS(XML_a, XML_graphic);
2017
2018 // Relative size of the Text Frame.
2019 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
2020 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
2021 {
2022 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom,
2023 (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME
2024 ? "page"
2025 : "margin"));
2026 pFS->startElementNS(XML_wp14, XML_pctWidth);
2027 pFS->writeEscaped(OUString::number(nWidthPercent * oox::drawingml::PER_PERCENT));
2028 pFS->endElementNS(XML_wp14, XML_pctWidth);
2029 pFS->endElementNS(XML_wp14, XML_sizeRelH);
2030 }
2031 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
2032 if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED)
2033 {
2034 pFS->startElementNS(
2035 XML_wp14, XML_sizeRelV, XML_relativeFrom,
2036 (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME ? "page"
2037 : "margin"));
2038 pFS->startElementNS(XML_wp14, XML_pctHeight);
2039 pFS->writeEscaped(OUString::number(nHeightPercent * oox::drawingml::PER_PERCENT));
2040 pFS->endElementNS(XML_wp14, XML_pctHeight);
2041 pFS->endElementNS(XML_wp14, XML_sizeRelV);
2042 }
2043
2044 endDMLAnchorInline(&rFrameFormat);
2045 }
2046 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
2047}
2048
2049void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly)
2050{
2051 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
2052 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
2053
2054 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
2055 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
2056 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
2057
2058 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
2059 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
2060
2061 //Save data here and restore when out of scope
2062 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
2063
2064 // When a frame has some low height, but automatically expanded due
2065 // to lots of contents, this size contains the real size.
2066 const Size aSize = pParentFrame->GetSize();
2067 m_pImpl->setFlyFrameSize(&aSize);
2068
2069 m_pImpl->setTextFrameSyntax(true);
2072 m_pImpl->getTextFrameStyle() = "position:absolute";
2073 if (!bTextBoxOnly)
2074 {
2075 OString sRotation(OString::number(-toDegrees(m_pImpl->getDMLandVMLTextFrameRotation())));
2076 m_pImpl->getExport().SdrExporter().getTextFrameStyle().append(";rotation:" + sRotation);
2077 }
2078 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
2079 m_pImpl->getFlyAttrList()->add(XML_style, m_pImpl->getTextFrameStyle().makeStringAndClear());
2080
2081 const SdrObject* pObject = pParentFrame->GetFrameFormat().FindRealSdrObject();
2082 if (pObject != nullptr)
2083 {
2084 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObject);
2085 if (!sAnchorId.isEmpty())
2086 m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId,
2087 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
2088
2089 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(),
2090 uno::UNO_QUERY);
2091 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
2092 OUString sHyperlink;
2093 if (xShapeProps.is())
2094 xShapeProps->getPropertyValue("HyperLinkURL") >>= sHyperlink;
2095 if (!sHyperlink.isEmpty())
2096 m_pImpl->getFlyAttrList()->add(XML_href,
2097 OUStringToOString(sHyperlink, RTL_TEXTENCODING_UTF8));
2098 }
2099 rtl::Reference<FastAttributeList> xFlyAttrList(m_pImpl->getFlyAttrList());
2100 m_pImpl->getFlyAttrList().clear();
2101 rtl::Reference<FastAttributeList> xTextboxAttrList(m_pImpl->getTextboxAttrList());
2102 m_pImpl->getTextboxAttrList().clear();
2103 m_pImpl->setTextFrameSyntax(false);
2104 m_pImpl->setFlyFrameSize(nullptr);
2105 m_pImpl->getExport().m_pParentFrame = nullptr;
2106
2107 if (!bTextBoxOnly)
2108 {
2109 pFS->startElementNS(XML_w, XML_pict);
2110 pFS->startElementNS(XML_v, XML_rect, xFlyAttrList);
2111 m_pImpl->textFrameShadow(rFrameFormat);
2112 if (m_pImpl->getFlyFillAttrList().is())
2113 {
2114 rtl::Reference<FastAttributeList> xFlyFillAttrList(m_pImpl->getFlyFillAttrList());
2115 pFS->singleElementNS(XML_v, XML_fill, xFlyFillAttrList);
2116 }
2117 if (m_pImpl->getDashLineStyleAttr().is())
2118 {
2119 rtl::Reference<FastAttributeList> xDashLineStyleAttr(m_pImpl->getDashLineStyleAttr());
2120 pFS->singleElementNS(XML_v, XML_stroke, xDashLineStyleAttr);
2121 }
2122 pFS->startElementNS(XML_v, XML_textbox, xTextboxAttrList);
2123 }
2124 m_pImpl->getFlyFillAttrList().clear();
2125 m_pImpl->getDashLineStyleAttr().clear();
2126
2127 pFS->startElementNS(XML_w, XML_txbxContent);
2128 {
2129 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
2130 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
2131 m_pImpl->getExport().WriteText();
2132 if (m_pImpl->getParagraphSdtOpen())
2133 {
2134 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
2135 m_pImpl->setParagraphSdtOpen(false);
2136 }
2137 }
2138 pFS->endElementNS(XML_w, XML_txbxContent);
2139 if (!bTextBoxOnly)
2140 {
2141 pFS->endElementNS(XML_v, XML_textbox);
2142
2143 if (m_pImpl->getFlyWrapAttrList())
2144 {
2145 rtl::Reference<FastAttributeList> xFlyWrapAttrList(m_pImpl->getFlyWrapAttrList());
2146 m_pImpl->setFlyWrapAttrList(nullptr);
2147 pFS->singleElementNS(XML_w10, XML_wrap, xFlyWrapAttrList);
2148 }
2149
2150 pFS->endElementNS(XML_v, XML_rect);
2151 pFS->endElementNS(XML_w, XML_pict);
2152 }
2153
2154 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
2155}
2156
2158{
2159 return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT);
2160}
2161
2162/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
sal_uInt8 GetAlpha() const
bool IsTransparent() const
The class that does all the actual DOCX export-related work.
Definition: docxexport.hxx:75
void writeVMLDrawing(const SdrObject *sdrObj, const SwFrameFormat &rFrameFormat)
Writes a drawing as VML data.
void endDMLAnchorInline(const SwFrameFormat *pFrameFormat)
void setParagraphSdtOpen(bool bParagraphSdtOpen)
Set if paragraph sdt open in the current drawing.
void writeDMLEffectLst(const SwFrameFormat &rFrameFormat)
Write <a:effectLst>, the effect list.
static bool isTextBox(const SwFrameFormat &rFrameFormat)
Is this a standalone TextFrame, or used as a TextBox of a shape?
rtl::Reference< sax_fastparser::FastAttributeList > & getFlyAttrList()
bool getTextFrameSyntax() const
void writeDMLDrawing(const SdrObject *pSdrObject, const SwFrameFormat *pFrameFormat, int nAnchorId)
Writes a drawing as DML.
bool IsDrawingOpen() const
void writeBoxItemLine(const SvxBoxItem &rBox)
Writes the drawingML <a:ln> markup of a box item.
rtl::Reference< sax_fastparser::FastAttributeList > & getFlyFillAttrList()
bool getDMLTextFrameSyntax() const
void writeVMLTextFrame(ww8::Frame const *pParentFrame, bool bTextBoxOnly=false)
Writes text frame in VML format.
rtl::Reference< sax_fastparser::FastAttributeList > & getTextboxAttrList()
Attributes of the next v:textbox element.
void setFlyWrapAttrList(rtl::Reference< sax_fastparser::FastAttributeList > const &pAttrList)
DocxSdrExport(DocxExport &rExport, const sax_fastparser::FSHelperPtr &pSerializer, oox::drawingml::DrawingML *pDrawingML)
bool IsDMLAndVMLDrawingOpen() const
void writeDiagram(const SdrObject *sdrObject, const SwFrameFormat &rFrameFormat, int nDiagramId)
Writes a diagram (smartart).
void writeOnlyTextOfFrame(ww8::Frame const *pParentFrame)
Writes text from Textbox for <w:framePr>
void writeDMLTextFrame(ww8::Frame const *pParentFrame, int nAnchorId, bool bTextBoxOnly=false)
Writes text frame in DML format.
const Size * getFlyFrameSize() const
When exporting fly frames, this holds the real size of the frame.
bool IsParagraphHasDrawing() const
void setParagraphHasDrawing(bool bParagraphHasDrawing)
sax_fastparser::FastAttributeList * getBodyPrAttrList()
Attributes of <wps:bodyPr>, used during DML export of text frames.
void startDMLAnchorInline(const SwFrameFormat *pFrameFormat, const Size &rSize)
void writeDMLAndVMLDrawing(const SdrObject *sdrObj, const SwFrameFormat &rFrameFormat, int nAnchorId)
Writes shape in both DML and VML format.
rtl::Reference< sax_fastparser::FastAttributeList > & getDashLineStyle()
OStringBuffer & getTextFrameStyle()
std::unique_ptr< Impl > m_pImpl
void setSerializer(const sax_fastparser::FSHelperPtr &pSerializer)
static MSO_SPT GetCustomShapeType(const css::uno::Reference< css::drawing::XShape > &rXShape, ShapeFlag &nMirrorFlags, OUString &rShapeType, bool bOOXML=false)
Helper class, so that the DocxExport::RestoreData() call will always happen.
ExportDataSaveRestore(DocxExport &rExport, SwNodeOffset nStt, SwNodeOffset nEnd, ww8::Frame const *pParentFrame)
virtual SdrLayerID GetHellId() const =0
virtual SdrLayerID GetInvisibleHellId() const =0
virtual void SaveData(SwNodeOffset nStt, SwNodeOffset nEnd)
Remember some of the members so that we can recurse in WriteText().
Definition: wrtww8.cxx:1893
virtual void RestoreData()
Restore what was saved in SaveData().
Definition: wrtww8.cxx:1925
const ww8::Frame * m_pParentFrame
Definition: wrtww8.hxx:520
virtual Degree100 GetRotateAngle() const
const OUString & getHyperlink() const
const double * GetRelativeHeight() const
OUString GetTitle() const
sal_uInt32 GetOrdNum() const
rtl::Reference< SdrObject > ConvertToPolyObj(bool bBezier, bool bLineToArea) const
virtual const tools::Rectangle & GetCurrentBoundRect() const
OUString GetDescription() const
const double * GetRelativeWidth() const
virtual const tools::Rectangle & GetSnapRect() const
const OUString & GetName() const
sal_Int16 GetRelativeHeightRelation() const
virtual SdrObjKind GetObjIdentifier() const
bool IsVisible() const
virtual SdrLayerID GetLayer() const
sal_Int16 GetRelativeWidthRelation() const
void GetGrabBagItem(css::uno::Any &rVal) const
virtual const tools::Rectangle & GetLogicRect() const
const basegfx::B2DPolyPolygon & GetPathPoly() const
bool GetValue() const
EnumT GetValue() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
static const sal_Int16 Hairline
const editeng::SvxBorderLine * GetTop() const
const editeng::SvxBorderLine * GetRight() const
const editeng::SvxBorderLine * GetLeft() const
const editeng::SvxBorderLine * GetBottom() const
tools::Long GetRight() const
tools::Long GetLeft() const
sal_uInt16 GetWidth() const
const Color & GetColor() const
SvxShadowLocation GetLocation() const
sal_uInt16 GetUpper() const
sal_uInt16 GetLower() const
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:80
IDocumentDrawModelAccess const & getIDocumentDrawModelAccess() const
Definition: doc.cxx:156
const SwPageDesc & GetPageDesc(const size_t i) const
Definition: doc.hxx:885
FlyAnchors.
Definition: fmtanchr.hxx:37
RndStdIds GetAnchorId() const
Definition: fmtanchr.hxx:67
const SwNodeIndex * GetContentIdx() const
Definition: fmtcntnt.hxx:46
sal_Int16 GetWidthPercentRelation() const
Definition: fmtfsize.hxx:92
sal_Int16 GetHeightPercentRelation() const
Definition: fmtfsize.hxx:89
sal_uInt8 GetWidthPercent() const
Definition: fmtfsize.hxx:91
SwFrameSize GetHeightSizeType() const
Definition: fmtfsize.hxx:80
sal_uInt8 GetHeightPercent() const
Definition: fmtfsize.hxx:88
sal_Int16 GetHoriOrient() const
Definition: fmtornt.hxx:87
SwTwips GetPos() const
Definition: fmtornt.hxx:92
sal_Int16 GetRelationOrient() const
Definition: fmtornt.hxx:88
bool IsOutside() const
Definition: fmtsrnd.hxx:54
bool IsContour() const
Definition: fmtsrnd.hxx:53
css::text::WrapTextMode GetSurround() const
Definition: fmtsrnd.hxx:51
sal_Int16 GetRelationOrient() const
Definition: fmtornt.hxx:55
SwTwips GetPos() const
Definition: fmtornt.hxx:59
sal_Int16 GetVertOrient() const
Definition: fmtornt.hxx:54
const SvxFrameDirectionItem & GetFrameDir(bool=true) const
Definition: frmatr.hxx:94
const SwDoc * GetDoc() const
The document is set in SwAttrPool now, therefore you always can access it.
Definition: format.hxx:139
const SwFormatWrapInfluenceOnObjPos & GetWrapInfluenceOnObjPos(bool=true) const
const SvxOpaqueItem & GetOpaque(bool=true) const
Definition: frmatr.hxx:80
const SwFormatFrameSize & GetFrameSize(bool=true) const
Definition: fmtfsize.hxx:104
const SvxLRSpaceItem & GetLRSpace(bool=true) const
Definition: frmatr.hxx:74
const OUString & GetName() const
Definition: format.hxx:131
const SwFormatVertOrient & GetVertOrient(bool=true) const
Definition: fmtornt.hxx:106
const SvxShadowItem & GetShadow(bool=true) const
Definition: frmatr.hxx:88
const SwFormatFollowTextFlow & GetFollowTextFlow(bool=true) const
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:83
const SwAttrSet & GetAttrSet() const
For querying the attribute array.
Definition: format.hxx:136
const SwFormatSurround & GetSurround(bool=true) const
Definition: fmtsrnd.hxx:66
const SwFormatHoriOrient & GetHoriOrient(bool=true) const
Definition: fmtornt.hxx:108
const SvxULSpaceItem & GetULSpace(bool=true) const
Definition: frmatr.hxx:76
const SwFormatContent & GetContent(bool=true) const
Definition: fmtcntnt.hxx:55
Style of a layout element.
Definition: frmfmt.hxx:62
SwRect FindLayoutRect(const bool bPrtArea=false, const Point *pPoint=nullptr) const
Definition: atrfrm.cxx:2718
SdrObject * FindRealSdrObject()
Definition: atrfrm.cxx:2771
Layout frame for SwNoTextNode, i.e. graphics and OLE nodes (including charts).
Definition: ndnotxt.hxx:30
Marks a node in the document model.
Definition: ndindex.hxx:31
SwNode & GetNode() const
Definition: ndindex.hxx:136
SwNodeOffset GetIndex() const
Definition: ndindex.hxx:171
SwNodeOffset EndOfSectionIndex() const
Definition: node.hxx:705
UseOnPage GetUseOn() const
Definition: pagedesc.hxx:353
Of course Writer needs its own rectangles.
Definition: swrect.hxx:35
void Height(tools::Long nNew)
Definition: swrect.hxx:193
void Width(tools::Long nNew)
Definition: swrect.hxx:189
static bool isTextBox(const SwFrameFormat *pFormat, sal_uInt16 nType, const SdrObject *pObject=nullptr)
Is the frame format a text box?
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void transform(const basegfx::B2DHomMatrix &rMatrix)
sal_uInt32 count() const
iterator find(const OUString &rKey)
const Color & GetColor() const
tools::Long GetWidth() const
SvxBorderLineStyle GetBorderLineStyle() const
virtual bool isExternalURL(const OUString &rURL) const
virtual OUString getTransformedString(const OUString &rURL) const
static rtl::Reference< FastAttributeList > createAttrList()
sal_uInt16 Count() const
void Insert(sal_uInt16 nPos, const Point &rPt)
sal_uInt16 GetSize() const
const Point & GetPoint(sal_uInt16 nPos) const
constexpr Point Center() const
constexpr tools::Long Top() const
tools::Long getOpenHeight() const
constexpr tools::Long Right() const
tools::Long getOpenWidth() const
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
Make exporting a Writer Frame easy.
const Size & GetSize() const
The Size of the contained element.
const SwFrameFormat & GetFrameFormat() const
Get the writer SwFrameFormat that this object describes.
const OUStringLiteral sColor
double toRadians(D x)
double toDegrees(D x)
static bool lcl_isLockedCanvas(const uno::Reference< drawing::XShape > &xShape)
static OString lcl_TransparencyToDrawingMlAlpha(const Color &rColor)
EmbeddedObjectRef * pObject
ShapeFlag
@ Variable
Frame is variable in Var-direction.
constexpr sal_Int32 FSNS(sal_Int32 namespc, sal_Int32 element)
constexpr TypedWhichId< SwFlyFrameFormat > RES_FLYFRMFMT(156)
MSO_SPT
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
rtl::Reference< FastAttributeList > SurroundToVMLWrap(SwFormatSurround const &rSurround)
double ConvertBorderWidthToWord(SvxBorderLineStyle, double)
int i
OString ConvertColor(const Color &rColor)
bool HasTextBoxContent(sal_uInt32 nShapeType)
TextVerticalAdjust GetTextVerticalAdjust(sal_Int32 nToken)
sal_Int32 ExportRotateClockwisify(Degree100 input)
const sal_Int32 PER_PERCENT
OUString getRelationship(Relationship eRelationship)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
std::shared_ptr< FastSerializerHelper > FSHelperPtr
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly)
Make a best fit Polygon from a PolyPolygon.
SwNoTextNode * GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat)
Get the SwNoTextNode associated with a SwFrameFormat if here is one.
tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon &rPolyPoly, const SwNoTextNode *pNd, bool bCorrectCrop)
Undo all scaling / move tricks of the wrap polygon done during import.
long Long
o3tl::strong_int< sal_Int32, struct Tag_SwNodeOffset > SwNodeOffset
Definition: nodeoffset.hxx:16
SwNodeOffset min(const SwNodeOffset &a, const SwNodeOffset &b)
Definition: nodeoffset.hxx:35
SwNodeOffset abs(const SwNodeOffset &a)
Definition: nodeoffset.hxx:34
#define Y
Holds data used by DocxSdrExport only.
rtl::Reference< sax_fastparser::FastAttributeList > m_pFlyFillAttrList
Flag for checking drawing in a paragraph.
void setDMLTextFrameSyntax(bool bDMLTextFrameSyntax)
Degree100 & getDMLandVMLTextFrameRotation()
Degree100 m_nDMLandVMLTextFrameRotation
List of TextBoxes in this document: they are exported as part of their shape, never alone.
void setFlyAttrList(const rtl::Reference< sax_fastparser::FastAttributeList > &pFlyAttrList)
bool getDrawingOpen() const
void setTextFrameSyntax(bool bTextFrameSyntax)
bool getDMLAndVMLDrawingOpen() const
rtl::Reference< sax_fastparser::FastAttributeList > m_pFlyAttrList
sax_fastparser::FastAttributeList * getFlyWrapAttrList() const
const Size * m_pFlyFrameSize
OStringBuffer m_aTextFrameStyle
bool getDMLTextFrameSyntax() const
rtl::Reference< sax_fastparser::FastAttributeList > m_pTextboxAttrList
bool getParagraphSdtOpen() const
rtl::Reference< sax_fastparser::FastAttributeList > m_pDashLineStyleAttr
oox::drawingml::DrawingML * m_pDrawingML
DocxExport & getExport() const
const sax_fastparser::FSHelperPtr & getSerializer() const
DocxExport & m_rExport
void setDrawingOpen(bool bDrawingOpen)
void setDMLAndVMLDrawingOpen(bool bDMLAndVMLDrawingOpen)
rtl::Reference< sax_fastparser::FastAttributeList > & getDashLineStyleAttr()
const Size * getFlyFrameSize() const
Impl(DocxExport &rExport, sax_fastparser::FSHelperPtr pSerializer, oox::drawingml::DrawingML *pDrawingML)
bool getTextFrameSyntax() const
void setSerializer(const sax_fastparser::FSHelperPtr &pSerializer)
OStringBuffer & getTextFrameStyle()
void setDMLandVMLTextFrameRotation(Degree100 nDMLandVMLTextFrameRotation)
rtl::Reference< sax_fastparser::FastAttributeList > m_pFlyWrapAttrList
void textFrameShadow(const SwFrameFormat &rFrameFormat)
Writes wp wrapper code around an SdrObject, which itself is written using drawingML syntax.
bool getFlyFrameGraphic() const
rtl::Reference< sax_fastparser::FastAttributeList > m_pBodyPrAttrList
bool getParagraphHasDrawing() const
rtl::Reference< sax_fastparser::FastAttributeList > & getFlyFillAttrList()
void setParagraphSdtOpen(bool bParagraphSdtOpen)
void setFlyWrapAttrList(rtl::Reference< sax_fastparser::FastAttributeList > const &pFlyWrapAttrList)
void setParagraphHasDrawing(bool bParagraphHasDrawing)
oox::drawingml::DrawingML * getDrawingML() const
void setFlyFrameSize(const Size *pFlyFrameSize)
void setBodyPrAttrList(sax_fastparser::FastAttributeList *pBodyPrAttrList)
rtl::Reference< sax_fastparser::FastAttributeList > & getTextboxAttrList()
rtl::Reference< sax_fastparser::FastAttributeList > & getFlyAttrList()
static bool isSupportedDMLShape(const uno::Reference< drawing::XShape > &xShape)
sax_fastparser::FastAttributeList * getBodyPrAttrList() const
sax_fastparser::FSHelperPtr m_pSerializer
void setTextboxAttrList(const rtl::Reference< sax_fastparser::FastAttributeList > &pTextboxAttrList)
Object Value
SVXCORE_DLLPUBLIC Degree100 NormAngle36000(Degree100 a)
unsigned char sal_uInt8
#define SAL_MAX_INT32
#define SAL_MIN_INT32
@ TXT_TXTBOX
Definition: wrtww8.hxx:159