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 const SdrObject* pSdrObject);
495
497 {
498 m_pSerializer = pSerializer;
499 }
500
502
503 void setFlyFrameSize(const Size* pFlyFrameSize) { m_pFlyFrameSize = pFlyFrameSize; }
504
505 const Size* getFlyFrameSize() const { return m_pFlyFrameSize; }
506
507 void setTextFrameSyntax(bool bTextFrameSyntax) { m_bTextFrameSyntax = bTextFrameSyntax; }
508
509 bool getTextFrameSyntax() const { return m_bTextFrameSyntax; }
510
511 void setDMLTextFrameSyntax(bool bDMLTextFrameSyntax)
512 {
513 m_bDMLTextFrameSyntax = bDMLTextFrameSyntax;
514 }
515
517
519 {
520 m_pFlyAttrList = pFlyAttrList;
521 }
522
524
525 void
527 {
528 m_pTextboxAttrList = pTextboxAttrList;
529 }
530
532 {
533 return m_pTextboxAttrList;
534 }
535
536 OStringBuffer& getTextFrameStyle() { return m_aTextFrameStyle; }
537
538 void setDrawingOpen(bool bDrawingOpen) { m_bDrawingOpen = bDrawingOpen; }
539
540 bool getDrawingOpen() const { return m_bDrawingOpen; }
541
542 void setParagraphSdtOpen(bool bParagraphSdtOpen) { m_bParagraphSdtOpen = bParagraphSdtOpen; }
543
545
546 void setDMLAndVMLDrawingOpen(bool bDMLAndVMLDrawingOpen)
547 {
548 m_bDMLAndVMLDrawingOpen = bDMLAndVMLDrawingOpen;
549 }
550
552
553 void setParagraphHasDrawing(bool bParagraphHasDrawing)
554 {
555 m_bParagraphHasDrawing = bParagraphHasDrawing;
556 }
557
559
561 {
562 return m_pFlyFillAttrList;
563 }
564
565 void
567 {
568 m_pFlyWrapAttrList = pFlyWrapAttrList;
569 }
570
572 {
573 return m_pFlyWrapAttrList.get();
574 }
575
577 {
578 m_pBodyPrAttrList = pBodyPrAttrList;
579 }
580
582
584 {
586 }
587
588 bool getFlyFrameGraphic() const { return m_bFlyFrameGraphic; }
589
591
592 DocxExport& getExport() const { return m_rExport; }
593
594 void setDMLandVMLTextFrameRotation(Degree100 nDMLandVMLTextFrameRotation)
595 {
596 m_nDMLandVMLTextFrameRotation = nDMLandVMLTextFrameRotation;
597 }
598
600};
601
603 oox::drawingml::DrawingML* pDrawingML)
604 : m_pImpl(std::make_unique<Impl>(rExport, pSerializer, pDrawingML))
605{
606}
607
609
611{
612 m_pImpl->setSerializer(pSerializer);
613}
614
615const Size* DocxSdrExport::getFlyFrameSize() const { return m_pImpl->getFlyFrameSize(); }
616
617bool DocxSdrExport::getTextFrameSyntax() const { return m_pImpl->getTextFrameSyntax(); }
618
619bool DocxSdrExport::getDMLTextFrameSyntax() const { return m_pImpl->getDMLTextFrameSyntax(); }
620
622{
623 return m_pImpl->getFlyAttrList();
624}
625
627{
628 return m_pImpl->getTextboxAttrList();
629}
630
631OStringBuffer& DocxSdrExport::getTextFrameStyle() { return m_pImpl->getTextFrameStyle(); }
632
633bool DocxSdrExport::IsDrawingOpen() const { return m_pImpl->getDrawingOpen(); }
634
635void DocxSdrExport::setParagraphSdtOpen(bool bParagraphSdtOpen)
636{
637 m_pImpl->setParagraphSdtOpen(bParagraphSdtOpen);
638}
639
640bool DocxSdrExport::IsDMLAndVMLDrawingOpen() const { return m_pImpl->getDMLAndVMLDrawingOpen(); }
641
642bool DocxSdrExport::IsParagraphHasDrawing() const { return m_pImpl->getParagraphHasDrawing(); }
643
644void DocxSdrExport::setParagraphHasDrawing(bool bParagraphHasDrawing)
645{
646 m_pImpl->setParagraphHasDrawing(bParagraphHasDrawing);
647}
648
650{
651 return m_pImpl->getFlyFillAttrList();
652}
653
655{
656 return m_pImpl->getBodyPrAttrList();
657}
658
660{
661 return m_pImpl->getDashLineStyleAttr();
662}
663
666{
667 m_pImpl->setFlyWrapAttrList(pAttrList);
668}
669
670void DocxSdrExport::startDMLAnchorInline(const SwFrameFormat* pFrameFormat, const Size& rSize)
671{
672 // Word uses size excluding right edge. Caller writeDMLDrawing and writeDiagram are changed for
673 // now. ToDo: Look whether the other callers give the size this way.
674 m_pImpl->setDrawingOpen(true);
675 m_pImpl->setParagraphHasDrawing(true);
676 m_pImpl->getSerializer()->startElementNS(XML_w, XML_drawing);
677 const SdrObject* pObj = pFrameFormat->FindRealSdrObject();
678
679 // LO determines the place needed for the object from wrap type, wrap margin ('distance to text'),
680 // object type and anchor type. Word uses dist* for user set margins and effectExtent for place
681 // needed for effects like shadow and glow, for fat stroke and for rotation. We map the LO values
682 // to values needed by Word so that the appearance is the same as far as possible.
683 // All values in Twips, change to EMU is done immediately before writing out.
684
685 bool isAnchor; // true XML_anchor, false XML_inline
686 if (m_pImpl->getFlyFrameGraphic())
687 {
688 isAnchor = false; // make Graphic object inside DMLTextFrame & VMLTextFrame as Inline
689 }
690 else
691 {
692 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
693 }
694
695 // tdf#135047: It must be allowed to find in parents too, but default value of bInP parameter
696 // for GetLRSpace() and GetULSpace() is true, so no direct setting is required.
697 const SvxLRSpaceItem& aLRSpaceItem = pFrameFormat->GetLRSpace();
698 const SvxULSpaceItem& aULSpaceItem = pFrameFormat->GetULSpace();
699 sal_Int64 nDistT = aULSpaceItem.GetUpper();
700 sal_Int64 nDistB = aULSpaceItem.GetLower();
701 sal_Int64 nDistL = aLRSpaceItem.GetLeft();
702 sal_Int64 nDistR = aLRSpaceItem.GetRight();
703
704 // LibreOffice behaves different for frames and drawing objects, but MS Office treats frames
705 // as drawing objects too. Therefore we transform the values from frame so as if they come
706 // from a drawing object.
707 sal_Int32 nWidthDiff(0);
708 sal_Int32 nHeightDiff(0);
709 sal_Int32 nPosXDiff(0);
710 sal_Int32 nPosYDiff(0);
711 sal_Int32 nLeftExt(0);
712 sal_Int32 nRightExt(0);
713 sal_Int32 nTopExt(0);
714 sal_Int32 nBottomExt(0);
715
716 if ((!pObj) || (pObj && (pObj->GetObjIdentifier() == SdrObjKind::SwFlyDrawObjIdentifier)))
717 {
718 // Frame objects have a restricted shadow and no further effects. They have border instead of
719 // stroke. LO includes shadow and border in the object size, but Word not.
720 SvxShadowItem aShadowItem = pFrameFormat->GetShadow();
721 if (aShadowItem.GetLocation() != SvxShadowLocation::NONE)
722 {
723 sal_Int32 nShadowWidth(aShadowItem.GetWidth());
724 switch (aShadowItem.GetLocation())
725 {
726 case SvxShadowLocation::TopLeft:
727 nTopExt = nLeftExt = nShadowWidth;
728 nPosXDiff = nLeftExt; // actual move is postponed
729 nPosYDiff = nTopExt;
730 nWidthDiff = -nLeftExt; // actual size extent is postponed
731 nHeightDiff = -nTopExt;
732 break;
733 case SvxShadowLocation::TopRight:
734 nTopExt = nRightExt = nShadowWidth;
735 nPosYDiff = nTopExt;
736 nWidthDiff = -nRightExt;
737 nHeightDiff = -nTopExt;
738 break;
739 case SvxShadowLocation::BottomLeft:
740 nBottomExt = nLeftExt = nShadowWidth;
741 nPosXDiff = nLeftExt;
742 nWidthDiff = -nLeftExt;
743 nHeightDiff = -nBottomExt;
744 break;
745 case SvxShadowLocation::BottomRight:
746 nBottomExt = nRightExt = nShadowWidth;
747 nWidthDiff = -nRightExt;
748 nHeightDiff = -nBottomExt;
749 break;
750 case SvxShadowLocation::NONE:
751 case SvxShadowLocation::End:
752 break;
753 }
754 }
755 // ToDo: Position refers to outer edge of border in LO, but to center of border in Word.
756 // Adaption is missing here. Frames in LO have no stroke but border. The current conversion
757 // from border to line treats borders like table borders. That might give wrong values
758 // for drawing frames.
759
760 if (pObj && pObj->GetRotateAngle() != 0_deg100)
761 {
762 Degree100 nRotation = pObj->GetRotateAngle();
763 const SwRect aBoundRect(pFrameFormat->FindLayoutRect());
764 tools::Long nMSOWidth = rSize.Width();
765 tools::Long nMSOHeight = rSize.Height();
766 if ((nRotation > 4500_deg100 && nRotation <= 13500_deg100)
767 || (nRotation > 22500_deg100 && nRotation <= 31500_deg100))
768 std::swap(nMSOWidth, nMSOHeight);
769 nBottomExt += (aBoundRect.Height() - 1 - nMSOHeight) / 2;
770 nTopExt += (aBoundRect.Height() - 1 - nMSOHeight) / 2;
771 nLeftExt += (aBoundRect.Width() - nMSOWidth) / 2;
772 nRightExt += (aBoundRect.Width() - nMSOWidth) / 2;
773 }
774 lcl_makeDistAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
775 nRightExt, nBottomExt);
776
777 // ToDo: Inline rotated image fails because it would need wrapTight, what is not possible.
778 // ToDo: Image plus shadow fails because of wrong shadow direction.
779 }
780 else // other objects than frames. pObj exists.
781 {
782 // Word 2007 makes no width-height-swap for images. Detect this situation.
783 sal_Int32 nMode = m_pImpl->getExport().getWordCompatibilityModeFromGrabBag();
784 bool bIsWord2007Image(nMode > 0 && nMode < 14
785 && pObj->GetObjIdentifier() == SdrObjKind::Graphic);
786
787 // Word cannot handle negative EffectExtent although allowed in OOXML, the 'dist' attributes
788 // may not be negative. Take care of that.
789 if (isAnchor)
790 {
791 lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, true,
792 bIsWord2007Image);
793 // We have calculated the effectExtent from boundRect, therefore half stroke width is
794 // already contained.
795 // ToDo: The other half of the stroke width needs to be subtracted from padding.
796 // Where is that?
797
798 // The import has added a difference to dist* in case of contour wrap for to give a
799 // rendering nearer to Word. In that case, we need to subtract it on export.
800 uno::Any aAny;
801 pObj->GetGrabBagItem(aAny);
802 comphelper::SequenceAsHashMap aGrabBag(aAny);
803 auto it = aGrabBag.find("AnchorDistDiff");
804 if (it != aGrabBag.end())
805 {
806 comphelper::SequenceAsHashMap aAnchorDistDiff(it->second);
807 for (const std::pair<const comphelper::OUStringAndHashCode, uno::Any>& rDiff :
808 aAnchorDistDiff)
809 {
810 const OUString& rName = rDiff.first.maString;
811 if (rName == "distTDiff" && rDiff.second.has<sal_Int32>())
812 nDistT -= round(rDiff.second.get<sal_Int32>());
813 else if (rName == "distBDiff" && rDiff.second.has<sal_Int32>())
814 nDistB -= round(rDiff.second.get<sal_Int32>());
815 else if (rName == "distLDiff" && rDiff.second.has<sal_Int32>())
816 nDistL -= rDiff.second.get<sal_Int32>();
817 else if (rName == "distRDiff" && rDiff.second.has<sal_Int32>())
818 nDistR -= rDiff.second.get<sal_Int32>();
819 }
820 }
821 // ToDo: bool bCompansated = ... to be later able to switch from wrapSquare to wrapTight,
822 // if wrapSquare would require negative effectExtent.
823 lcl_makeDistAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
824 nRightExt, nBottomExt);
825 }
826 else
827 {
828 lcl_calculateRawEffectExtent(nLeftExt, nTopExt, nRightExt, nBottomExt, *pObj, false,
829 bIsWord2007Image);
830 // nDistT,... contain the needed distances from import or set by user. But Word
831 // ignores Dist attributes of inline shapes. So we move all needed distances to
832 // effectExtent and force effectExtent to non-negative.
833 lcl_makeDistZeroAndExtentNonNegative(nDistT, nDistB, nDistL, nDistR, nLeftExt, nTopExt,
834 nRightExt, nBottomExt);
835 }
836 }
837
838 if (isAnchor)
839 {
842
843 bool bOpaque = pFrameFormat->GetOpaque().GetValue();
844 if (pObj)
845 {
846 // SdrObjects know their layer, consider that instead of the frame format.
847 bOpaque = pObj->GetLayer()
848 != pFrameFormat->GetDoc()->getIDocumentDrawModelAccess().GetHellId()
849 && pObj->GetLayer()
850 != pFrameFormat->GetDoc()
853 }
854 attrList->add(XML_behindDoc, bOpaque ? "0" : "1");
855
856 attrList->add(XML_distT, OString::number(TwipsToEMU(nDistT)));
857 attrList->add(XML_distB, OString::number(TwipsToEMU(nDistB)));
858 attrList->add(XML_distL, OString::number(TwipsToEMU(nDistL)));
859 attrList->add(XML_distR, OString::number(TwipsToEMU(nDistR)));
860
861 attrList->add(XML_simplePos, "0");
862 attrList->add(XML_locked, "0");
863
864 bool bLclInTabCell = true;
865 if (pObj)
866 {
867 uno::Reference<drawing::XShape> xShape((const_cast<SdrObject*>(pObj)->getUnoShape()),
868 uno::UNO_QUERY);
869 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
870 if (xShapeProps.is())
871 xShapeProps->getPropertyValue("IsFollowingTextFlow") >>= bLclInTabCell;
872 }
873
874 if (pFrameFormat->GetSurround().GetValue() == text::WrapTextMode_THROUGH
875 && pFrameFormat->GetHoriOrient().GetRelationOrient() == text::RelOrientation::FRAME)
876 {
877 // "In front of text" and horizontal positioning relative to Column is ignored on
878 // import, add it back here.
879 bLclInTabCell = true;
880 }
881
882 if (bLclInTabCell)
883 attrList->add(XML_layoutInCell, "1");
884 else
885 attrList->add(XML_layoutInCell, "0");
886
887 bool bAllowOverlap = pFrameFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap();
888 attrList->add(XML_allowOverlap, bAllowOverlap ? "1" : "0");
889
890 if (pObj)
891 // It seems 0 and 1 have special meaning: just start counting from 2 to avoid issues with that.
892 attrList->add(XML_relativeHeight, OString::number(pObj->GetOrdNum() + 2));
893 else
894 // relativeHeight is mandatory attribute, if value is not present, we must write default value
895 attrList->add(XML_relativeHeight, "0");
896
897 if (pObj)
898 {
899 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
900 if (!sAnchorId.isEmpty())
901 attrList->addNS(XML_wp14, XML_anchorId, sAnchorId);
902 }
903
904 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_anchor, attrList);
905
906 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_simplePos, XML_x, "0", XML_y,
907 "0"); // required, unused
908
909 // Position is either determined by coordinates aPos or alignment keywords like 'center'.
910 // First prepare them.
911 awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(),
912 pFrameFormat->GetVertOrient().GetPos());
913
914 aPos.X += nPosXDiff; // Make the postponed position move of frames.
915 aPos.Y += nPosYDiff;
916 if (pObj && lcl_IsRotateAngleValid(*pObj)
917 && pObj->GetObjIdentifier() != SdrObjKind::SwFlyDrawObjIdentifier)
918 lclMovePositionWithRotation(aPos, rSize, pObj->GetRotateAngle());
919
920 const char* relativeFromH;
921 const char* relativeFromV;
922 const char* alignH = nullptr;
923 const char* alignV = nullptr;
924 switch (pFrameFormat->GetVertOrient().GetRelationOrient())
925 {
926 case text::RelOrientation::PAGE_PRINT_AREA:
927 relativeFromV = "margin";
928 break;
929 case text::RelOrientation::PAGE_PRINT_AREA_TOP:
930 relativeFromV = "topMargin";
931 break;
932 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
933 relativeFromV = "bottomMargin";
934 break;
935 case text::RelOrientation::PAGE_FRAME:
936 relativeFromV = "page";
937 break;
938 case text::RelOrientation::FRAME:
939 relativeFromV = "paragraph";
940 break;
941 case text::RelOrientation::TEXT_LINE:
942 default:
943 relativeFromV = "line";
944 break;
945 }
946 switch (pFrameFormat->GetVertOrient().GetVertOrient())
947 {
948 case text::VertOrientation::TOP:
949 case text::VertOrientation::CHAR_TOP:
950 case text::VertOrientation::LINE_TOP:
951 if (pFrameFormat->GetVertOrient().GetRelationOrient()
952 == text::RelOrientation::TEXT_LINE)
953 alignV = "bottom";
954 else
955 alignV = "top";
956 break;
957 case text::VertOrientation::BOTTOM:
958 case text::VertOrientation::CHAR_BOTTOM:
959 case text::VertOrientation::LINE_BOTTOM:
960 if (pFrameFormat->GetVertOrient().GetRelationOrient()
961 == text::RelOrientation::TEXT_LINE)
962 alignV = "top";
963 else
964 alignV = "bottom";
965 break;
966 case text::VertOrientation::CENTER:
967 case text::VertOrientation::CHAR_CENTER:
968 case text::VertOrientation::LINE_CENTER:
969 alignV = "center";
970 break;
971 default:
972 break;
973 }
974 switch (pFrameFormat->GetHoriOrient().GetRelationOrient())
975 {
976 case text::RelOrientation::PAGE_PRINT_AREA:
977 relativeFromH = "margin";
978 break;
979 case text::RelOrientation::PAGE_FRAME:
980 relativeFromH = "page";
981 break;
982 case text::RelOrientation::CHAR:
983 relativeFromH = "character";
984 break;
985 case text::RelOrientation::PAGE_RIGHT:
986 relativeFromH = "rightMargin";
987 break;
988 case text::RelOrientation::PAGE_LEFT:
989 relativeFromH = "leftMargin";
990 break;
991 case text::RelOrientation::FRAME:
992 default:
993 relativeFromH = "column";
994 break;
995 }
996 switch (pFrameFormat->GetHoriOrient().GetHoriOrient())
997 {
998 case text::HoriOrientation::LEFT:
999 alignH = "left";
1000 break;
1001 case text::HoriOrientation::RIGHT:
1002 alignH = "right";
1003 break;
1004 case text::HoriOrientation::CENTER:
1005 alignH = "center";
1006 break;
1007 case text::HoriOrientation::INSIDE:
1008 alignH = "inside";
1009 break;
1010 case text::HoriOrientation::OUTSIDE:
1011 alignH = "outside";
1012 break;
1013 default:
1014 break;
1015 }
1016
1017 // write out horizontal position
1018 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionH, XML_relativeFrom,
1019 relativeFromH);
1020
1026 const sal_Int64 MAX_INTEGER_VALUE = SAL_MAX_INT32;
1027 const sal_Int64 MIN_INTEGER_VALUE = SAL_MIN_INT32;
1028
1029 if (alignH != nullptr)
1030 {
1031 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
1032 m_pImpl->getSerializer()->write(alignH);
1033 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
1034 }
1035 else
1036 {
1037 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
1038 sal_Int64 nPosXEMU = TwipsToEMU(aPos.X);
1039
1040 /* Absolute Position Offset Value is of type Int. Hence it should not be greater than
1041 * Maximum value for Int OR Less than the Minimum value for Int.
1042 * - Maximum value for Int = 2147483647
1043 * - Minimum value for Int = -2147483648
1044 *
1045 * As per ECMA Specification : ECMA-376, Second Edition,
1046 * Part 1 - Fundamentals And Markup Language Reference[20.4.3.3 ST_PositionOffset (Absolute Position Offset Value)]
1047 *
1048 * Please refer : http://www.schemacentral.com/sc/xsd/t-xsd_int.html
1049 */
1050
1051 if (nPosXEMU > MAX_INTEGER_VALUE)
1052 {
1053 nPosXEMU = MAX_INTEGER_VALUE;
1054 }
1055 else if (nPosXEMU < MIN_INTEGER_VALUE)
1056 {
1057 nPosXEMU = MIN_INTEGER_VALUE;
1058 }
1059 m_pImpl->getSerializer()->write(nPosXEMU);
1060 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
1061 }
1062 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionH);
1063
1064 // write out vertical position
1065 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionV, XML_relativeFrom,
1066 relativeFromV);
1067 sal_Int64 nPosYEMU = TwipsToEMU(aPos.Y);
1068
1069 // tdf#93675, 0 below line/paragraph and/or top line/paragraph with
1070 // wrap top+bottom or other wraps is affecting the line directly
1071 // above the anchor line, which seems odd, but a tiny adjustment
1072 // here to bring the top down convinces msoffice to wrap like us
1073 if (nPosYEMU == 0
1074 && (strcmp(relativeFromV, "line") == 0 || strcmp(relativeFromV, "paragraph") == 0)
1075 && (!alignV || strcmp(alignV, "top") == 0))
1076 {
1077 alignV = nullptr;
1078 nPosYEMU = TwipsToEMU(1);
1079 }
1080
1081 if (alignV != nullptr)
1082 {
1083 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
1084 m_pImpl->getSerializer()->write(alignV);
1085 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
1086 }
1087 else
1088 {
1089 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
1090 if (nPosYEMU > MAX_INTEGER_VALUE)
1091 {
1092 nPosYEMU = MAX_INTEGER_VALUE;
1093 }
1094 else if (nPosYEMU < MIN_INTEGER_VALUE)
1095 {
1096 nPosYEMU = MIN_INTEGER_VALUE;
1097 }
1098 m_pImpl->getSerializer()->write(nPosYEMU);
1099 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
1100 }
1101 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionV);
1102 }
1103 else // inline
1104 {
1105 // nDist is forced to zero above and ignored by Word anyway, so write 0 directly.
1108 aAttrList->add(XML_distT, OString::number(0));
1109 aAttrList->add(XML_distB, OString::number(0));
1110 aAttrList->add(XML_distL, OString::number(0));
1111 aAttrList->add(XML_distR, OString::number(0));
1112 if (pObj)
1113 {
1114 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
1115 if (!sAnchorId.isEmpty())
1116 aAttrList->addNS(XML_wp14, XML_anchorId, sAnchorId);
1117 }
1118 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_inline, aAttrList);
1119 }
1120
1121 // now the common parts 'extent' and 'effectExtent'
1139 sal_uInt64 cx = TwipsToEMU(
1140 std::clamp(rSize.Width() + nWidthDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
1141 OString aWidth(OString::number(std::min(cx, sal_uInt64(SAL_MAX_INT32))));
1142 sal_uInt64 cy = TwipsToEMU(
1143 std::clamp(rSize.Height() + nHeightDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
1144 OString aHeight(OString::number(std::min(cy, sal_uInt64(SAL_MAX_INT32))));
1145
1146 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_extent, XML_cx, aWidth, XML_cy, aHeight);
1147
1148 // XML_effectExtent, includes effects, fat stroke and rotation
1149 // FixMe: tdf141880. Because LibreOffice currently cannot handle negative vertical margins, they
1150 // were forced to zero on import. Especially bottom margin of inline anchored rotated objects are
1151 // affected. If the object was not changed, it would be better to export the original values
1152 // from grab-Bag. Unfortunately there exists no marker for "not changed", so a heuristic is used
1153 // here: If current left, top and right margins do not differ more than 1Hmm = 635EMU from the
1154 // values in grab-Bag, it is likely, that the object was not changed and we restore the values
1155 // from grab-Bag.
1156 sal_Int64 nLeftExtEMU = TwipsToEMU(nLeftExt);
1157 sal_Int64 nTopExtEMU = TwipsToEMU(nTopExt);
1158 sal_Int64 nRightExtEMU = TwipsToEMU(nRightExt);
1159 sal_Int64 nBottomExtEMU = TwipsToEMU(nBottomExt);
1160 if (pObj)
1161 {
1162 uno::Any aAny;
1163 pObj->GetGrabBagItem(aAny);
1164 comphelper::SequenceAsHashMap aGrabBag(aAny);
1165 auto it = aGrabBag.find("CT_EffectExtent");
1166 if (it != aGrabBag.end())
1167 {
1168 comphelper::SequenceAsHashMap aEffectExtent(it->second);
1169 sal_Int64 nLeftExtGrabBag(0);
1170 sal_Int64 nTopExtGrabBag(0);
1171 sal_Int64 nRightExtGrabBag(0);
1172 sal_Int64 nBottomExtGrabBag(0);
1173 for (const std::pair<const comphelper::OUStringAndHashCode, uno::Any>& rDirection :
1174 aEffectExtent)
1175 {
1176 const OUString& rName = rDirection.first.maString;
1177 if (rName == "l" && rDirection.second.has<sal_Int32>())
1178 nLeftExtGrabBag = rDirection.second.get<sal_Int32>();
1179 else if (rName == "t" && rDirection.second.has<sal_Int32>())
1180 nTopExtGrabBag = rDirection.second.get<sal_Int32>();
1181 else if (rName == "r" && rDirection.second.has<sal_Int32>())
1182 nRightExtGrabBag = rDirection.second.get<sal_Int32>();
1183 else if (rName == "b" && rDirection.second.has<sal_Int32>())
1184 nBottomExtGrabBag = rDirection.second.get<sal_Int32>();
1185 }
1186 if (abs(nLeftExtEMU - nLeftExtGrabBag) <= 635 && abs(nTopExtEMU - nTopExtGrabBag) <= 635
1187 && abs(nRightExtEMU - nRightExtGrabBag) <= 635)
1188 {
1189 nLeftExtEMU = nLeftExtGrabBag;
1190 nTopExtEMU = nTopExtGrabBag;
1191 nRightExtEMU = nRightExtGrabBag;
1192 nBottomExtEMU = nBottomExtGrabBag;
1193 }
1194 }
1195 }
1196 m_pImpl->getSerializer()->singleElementNS(
1197 XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExtEMU), XML_t,
1198 OString::number(nTopExtEMU), XML_r, OString::number(nRightExtEMU), XML_b,
1199 OString::number(nBottomExtEMU));
1200
1201 if (!isAnchor)
1202 return; // OOXML 'inline' has not wrap type at all
1203
1204 // XML_anchor has exact one of types wrapNone, wrapSquare, wrapTight, wrapThrough and
1205 // WrapTopAndBottom. Map our own types to them as far as possible.
1206
1207 if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGH
1208 || pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGHT)
1209 {
1210 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone);
1211 return;
1212 }
1213
1214 if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_NONE)
1215 {
1216 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom);
1217 return;
1218 }
1219
1220 // All remaining cases need attribute XML_wrapText
1221 OUString sWrapType;
1222 switch (pFrameFormat->GetSurround().GetSurround())
1223 {
1224 case text::WrapTextMode_DYNAMIC:
1225 sWrapType = OUString("largest");
1226 break;
1227 case text::WrapTextMode_LEFT:
1228 sWrapType = OUString("left");
1229 break;
1230 case text::WrapTextMode_RIGHT:
1231 sWrapType = OUString("right");
1232 break;
1233 case text::WrapTextMode_PARALLEL:
1234 default:
1235 sWrapType = OUString("bothSides");
1236 break;
1237 }
1238
1239 // ToDo: Exclude cases where LibreOffice wrap without contour is different
1240 // from Word XML_wrapSquare or where direct use of distances not possible and workaround
1241 // will be done using wrapPolygon.
1242 // ToDo: handle case Writer frame, where contour can be set in LibreOffice but is not rendered.
1243
1244 // This case needs no wrapPolygon
1245 if (!pFrameFormat->GetSurround().IsContour())
1246 {
1247 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, sWrapType);
1248 return;
1249 }
1250
1251 // Contour wrap.
1252 sal_Int32 nWrapToken
1253 = pFrameFormat->GetSurround().IsOutside() ? XML_wrapTight : XML_wrapThrough;
1254
1255 // ToDo: cases where wrapPolygon is used as workaround.
1256
1257 // Own wrap polygon exists only for TextGraphicObject and TextEmbeddedObject. It might be edited
1258 // by user. If such exists, we use it and we are done.
1259 if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat))
1260 {
1261 const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
1262 if (pPolyPoly && pPolyPoly->Count())
1263 {
1264 tools::Polygon aPoly
1265 = sw::util::CorrectWordWrapPolygonForExport(*pPolyPoly, pNd, /*bCorrectCrop=*/true);
1266 if (aPoly.GetSize() >= 3)
1267 {
1268 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText,
1269 sWrapType);
1270 // ToDo: Test whether XML_edited true or false gives better results.
1271 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1272 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
1273 OString::number(aPoly[0].X()), XML_y,
1274 OString::number(aPoly[0].Y()));
1275 for (sal_uInt16 i = 1; i < aPoly.GetSize(); ++i)
1276 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_lineTo, XML_x,
1277 OString::number(aPoly[i].X()), XML_y,
1278 OString::number(aPoly[i].Y()));
1279 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1280
1281 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1282 return;
1283 }
1284 }
1285 }
1286
1287 // If this shape comes from ooxml import, there might be a wrap polygon in InteropGrabBag.
1288 // Wrap polygons can be edited by users in Word. They are independent from changing shape size or
1289 // rotation. So it is likely, that it is still usable.
1290 if (pObj)
1291 {
1292 uno::Any aAny;
1293 pObj->GetGrabBagItem(aAny);
1294 comphelper::SequenceAsHashMap aGrabBag(aAny);
1295 auto it = aGrabBag.find("CT_WrapPath");
1296 if (it != aGrabBag.end())
1297 {
1298 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
1299
1300 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1301 auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>();
1302 const auto& rPoints = aSeqSeq[0];
1303 for (auto i = rPoints.begin(); i != rPoints.end(); ++i)
1304 {
1305 const awt::Point& rPoint = *i;
1306 m_pImpl->getSerializer()->singleElementNS(
1307 XML_wp, (i == rPoints.begin() ? XML_start : XML_lineTo), XML_x,
1308 OString::number(rPoint.X), XML_y, OString::number(rPoint.Y));
1309 }
1310 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1311
1312 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1313 return;
1314 }
1315 }
1316
1317 // In this case we likely had an odt document to be exported to docx. ODF does not know the
1318 // concept of a wrap polygon and LibreOffice has no one internally. So as a workaround, we
1319 // generate a wrap polygon from the shape geometry.
1320 tools::Polygon aContour = lcl_CreateContourPolygon(const_cast<SdrObject*>(pObj));
1321
1322 // lcl_CreateContourPolygon() ensures at least three points
1323 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
1324
1325 // ToDo: Test whether XML_edited true or false gives better results.
1326 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1327 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
1328 OString::number(aContour.GetPoint(0).getX()), XML_y,
1329 OString::number(aContour.GetPoint(0).getY()));
1330 for (sal_uInt32 i = 1; i < aContour.GetSize(); i++)
1331 m_pImpl->getSerializer()->singleElementNS(
1332 XML_wp, XML_lineTo, XML_x, OString::number(aContour.GetPoint(i).getX()), XML_y,
1333 OString::number(aContour.GetPoint(i).getY()));
1334 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1335
1336 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1337}
1338
1340{
1341 bool isAnchor;
1342 if (m_pImpl->getFlyFrameGraphic())
1343 {
1344 isAnchor = false; // end Inline Graphic object inside DMLTextFrame
1345 }
1346 else
1347 {
1348 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
1349 }
1350 m_pImpl->getSerializer()->endElementNS(XML_wp, isAnchor ? XML_anchor : XML_inline);
1351
1352 m_pImpl->getSerializer()->endElementNS(XML_w, XML_drawing);
1353 m_pImpl->setDrawingOpen(false);
1354}
1355
1356void DocxSdrExport::writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat)
1357{
1358 m_pImpl->getSerializer()->startElementNS(XML_w, XML_pict);
1359 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
1360 // See WinwordAnchoring::SetAnchoring(), these are not part of the SdrObject, have to be passed around manually.
1361
1362 SwFormatFollowTextFlow const& rFlow(rFrameFormat.GetFollowTextFlow());
1363 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
1364 const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
1365 SwFormatSurround const& rSurround(rFrameFormat.GetSurround());
1366
1368 m_pImpl->getExport().VMLExporter().AddSdrObject(
1369 *sdrObj, rFlow.GetValue(), rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(),
1370 rHoriOri.GetRelationOrient(), rVertOri.GetRelationOrient(), pAttrList.get(), true);
1371 m_pImpl->getSerializer()->endElementNS(XML_w, XML_pict);
1372}
1373
1374static bool lcl_isLockedCanvas(const uno::Reference<drawing::XShape>& xShape)
1375{
1376 const uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, "InteropGrabBag");
1377 /*
1378 * Export as Locked Canvas only if the property
1379 * is in the PropertySet
1380 */
1381 return std::any_of(propList.begin(), propList.end(), [](const beans::PropertyValue& rProp) {
1382 return rProp.Name == "LockedCanvas";
1383 });
1384}
1385
1386void AddExtLst(sax_fastparser::FSHelperPtr const& pFS, DocxExport const& rExport,
1387 uno::Reference<beans::XPropertySet> const& xShape)
1388{
1389 if (xShape->getPropertyValue("Decorative").get<bool>())
1390 {
1391 pFS->startElementNS(XML_a, XML_extLst,
1392 // apparently for DOCX the namespace isn't declared on the root
1393 FSNS(XML_xmlns, XML_a),
1394 rExport.GetFilter().getNamespaceURL(OOX_NS(dml)));
1395 pFS->startElementNS(XML_a, XML_ext,
1396 // Word uses this "URI" which is obviously not a URI
1397 XML_uri, "{C183D7F6-B498-43B3-948B-1728B52AA6E4}");
1398 pFS->singleElementNS(XML_adec, XML_decorative, FSNS(XML_xmlns, XML_adec),
1399 "http://schemas.microsoft.com/office/drawing/2017/decorative", XML_val,
1400 "1");
1401 pFS->endElementNS(XML_a, XML_ext);
1402 pFS->endElementNS(XML_a, XML_extLst);
1403 }
1404}
1405
1406void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat,
1407 int nAnchorId)
1408{
1409 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape());
1410 if (!Impl::isSupportedDMLShape(xShape, pSdrObject))
1411 return;
1412
1413 m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject);
1414
1415 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1416 Size aSize(pSdrObject->GetLogicRect().getOpenWidth(),
1417 pSdrObject->GetLogicRect().getOpenHeight());
1418 startDMLAnchorInline(pFrameFormat, aSize);
1419
1422 pDocPrAttrList->add(XML_id, OString::number(nAnchorId));
1423 pDocPrAttrList->add(XML_name, pSdrObject->GetName());
1424 if (!pSdrObject->GetTitle().isEmpty())
1425 pDocPrAttrList->add(XML_title, pSdrObject->GetTitle());
1426 if (!pSdrObject->GetDescription().isEmpty())
1427 pDocPrAttrList->add(XML_descr, pSdrObject->GetDescription());
1428 if (!pSdrObject->IsVisible()
1429 && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
1430
1431 pDocPrAttrList->add(XML_hidden, OString::number(1));
1432
1433 pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
1434 OUString sHyperlink = pSdrObject->getHyperlink();
1435 if (!sHyperlink.isEmpty())
1436 {
1437 OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
1438 pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1441 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1442 FSNS(XML_xmlns, XML_a),
1443 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1444 }
1445 uno::Reference<beans::XPropertySet> const xShapeProps(xShape, uno::UNO_QUERY_THROW);
1446 AddExtLst(pFS, m_pImpl->getExport(), xShapeProps);
1447
1448 pFS->endElementNS(XML_wp, XML_docPr);
1449
1450 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
1451 const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
1452 if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"))
1453 pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
1454 else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1455 pNamespace = "http://schemas.openxmlformats.org/drawingml/2006/picture";
1456 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1457 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1458 pFS->startElementNS(XML_a, XML_graphicData, XML_uri, pNamespace);
1459
1460 bool bLockedCanvas = lcl_isLockedCanvas(xShape);
1461 if (bLockedCanvas)
1462 pFS->startElementNS(
1463 XML_lc, XML_lockedCanvas, FSNS(XML_xmlns, XML_lc),
1464 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlLockedCanvas)));
1465
1466 m_pImpl->getExport().OutputDML(xShape);
1467
1468 if (bLockedCanvas)
1469 pFS->endElementNS(XML_lc, XML_lockedCanvas);
1470 pFS->endElementNS(XML_a, XML_graphicData);
1471 pFS->endElementNS(XML_a, XML_graphic);
1472
1473 // Relative size of the drawing.
1474 if (pSdrObject->GetRelativeWidth())
1475 {
1476 // At the moment drawinglayer objects are always relative from page.
1477 OUString sValue;
1478 switch (pSdrObject->GetRelativeWidthRelation())
1479 {
1480 case text::RelOrientation::FRAME:
1481 sValue = "margin";
1482 break;
1483 case text::RelOrientation::PAGE_LEFT:
1484 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
1485 sValue = "outsideMargin";
1486 else
1487 sValue = "leftMargin";
1488 break;
1489 case text::RelOrientation::PAGE_RIGHT:
1490 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
1491 sValue = "insideMargin";
1492 else
1493 sValue = "rightMargin";
1494 break;
1495 case text::RelOrientation::PAGE_FRAME:
1496 default:
1497 sValue = "page";
1498 break;
1499 }
1500 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, sValue);
1501 pFS->startElementNS(XML_wp14, XML_pctWidth);
1502 pFS->writeEscaped(
1503 OUString::number(*pSdrObject->GetRelativeWidth() * 100 * oox::drawingml::PER_PERCENT));
1504 pFS->endElementNS(XML_wp14, XML_pctWidth);
1505 pFS->endElementNS(XML_wp14, XML_sizeRelH);
1506 }
1507 if (pSdrObject->GetRelativeHeight())
1508 {
1509 OUString sValue;
1510 switch (pSdrObject->GetRelativeHeightRelation())
1511 {
1512 case text::RelOrientation::FRAME:
1513 sValue = "margin";
1514 break;
1515 case text::RelOrientation::PAGE_PRINT_AREA:
1516 sValue = "topMargin";
1517 break;
1518 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
1519 sValue = "bottomMargin";
1520 break;
1521 case text::RelOrientation::PAGE_FRAME:
1522 default:
1523 sValue = "page";
1524 break;
1525 }
1526 pFS->startElementNS(XML_wp14, XML_sizeRelV, XML_relativeFrom, sValue);
1527 pFS->startElementNS(XML_wp14, XML_pctHeight);
1528 pFS->writeEscaped(
1529 OUString::number(*pSdrObject->GetRelativeHeight() * 100 * oox::drawingml::PER_PERCENT));
1530 pFS->endElementNS(XML_wp14, XML_pctHeight);
1531 pFS->endElementNS(XML_wp14, XML_sizeRelV);
1532 }
1533
1534 endDMLAnchorInline(pFrameFormat);
1535}
1536
1538{
1539 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1540 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1541 return;
1542
1543 OString aShadowWidth(OString::number(double(aShadowItem.GetWidth()) / 20) + "pt");
1544 OString aOffset;
1545 switch (aShadowItem.GetLocation())
1546 {
1547 case SvxShadowLocation::TopLeft:
1548 aOffset = "-" + aShadowWidth + ",-" + aShadowWidth;
1549 break;
1550 case SvxShadowLocation::TopRight:
1551 aOffset = aShadowWidth + ",-" + aShadowWidth;
1552 break;
1553 case SvxShadowLocation::BottomLeft:
1554 aOffset = "-" + aShadowWidth + "," + aShadowWidth;
1555 break;
1556 case SvxShadowLocation::BottomRight:
1557 aOffset = aShadowWidth + "," + aShadowWidth;
1558 break;
1559 case SvxShadowLocation::NONE:
1560 case SvxShadowLocation::End:
1561 break;
1562 }
1563 if (aOffset.isEmpty())
1564 return;
1565
1566 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1567 m_pSerializer->singleElementNS(XML_v, XML_shadow, XML_on, "t", XML_color, "#" + aShadowColor,
1568 XML_offset, aOffset);
1569}
1570
1571bool DocxSdrExport::Impl::isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape,
1572 const SdrObject* pSdrObject)
1573{
1574 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
1575 if (xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonShape")
1576 || xServiceInfo->supportsService("com.sun.star.drawing.PolyLineShape"))
1577 return false;
1578
1579 uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
1580 // For signature line shapes, we don't want DML, just the VML shape.
1581 if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1582 {
1583 bool bIsSignatureLineShape = false;
1584 xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLineShape;
1585 if (bIsSignatureLineShape)
1586 return false;
1587 }
1588
1589 // A FontWork shape with bitmap fill cannot be expressed as a modern 'abc transform'
1590 // in Word. Only the legacy VML WordArt allows bitmap fill.
1591 if (pSdrObject->IsTextPath())
1592 {
1593 css::drawing::FillStyle eFillStyle = css::drawing::FillStyle_SOLID;
1594 xShapeProperties->getPropertyValue("FillStyle") >>= eFillStyle;
1595 if (eFillStyle == css::drawing::FillStyle_BITMAP)
1596 return false;
1597 }
1598 return true;
1599}
1600
1602 const SwFrameFormat& rFrameFormat, int nAnchorId)
1603{
1604 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1605 m_pImpl->setDMLAndVMLDrawingOpen(true);
1606
1607 // Depending on the shape type, we actually don't write the shape as DML.
1608 OUString sShapeType;
1609 ShapeFlag nMirrorFlags = ShapeFlag::NONE;
1610 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObj)->getUnoShape());
1611
1612 MSO_SPT eShapeType
1613 = EscherPropertyContainer::GetCustomShapeType(xShape, nMirrorFlags, sShapeType);
1614
1615 // In case we are already inside a DML block, then write the shape only as VML, turn out that's allowed to do.
1616 // A common service created in util to check for VML shapes which are allowed to have textbox in content
1617 if ((msfilter::util::HasTextBoxContent(eShapeType)) && Impl::isSupportedDMLShape(xShape, sdrObj)
1618 && (!bDMLAndVMLDrawingOpen || lcl_isLockedCanvas(xShape))) // Locked canvas is OK inside DML
1619 {
1620 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_AlternateContent);
1621
1622 auto pObjGroup = dynamic_cast<const SdrObjGroup*>(sdrObj);
1623 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Choice, XML_Requires,
1624 (pObjGroup ? "wpg" : "wps"));
1625 writeDMLDrawing(sdrObj, &rFrameFormat, nAnchorId);
1626 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Choice);
1627
1628 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Fallback);
1629 writeVMLDrawing(sdrObj, rFrameFormat);
1630 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Fallback);
1631
1632 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_AlternateContent);
1633 }
1634 else
1635 writeVMLDrawing(sdrObj, rFrameFormat);
1636
1637 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
1638}
1639
1640// Converts ARGB transparency (0..255) to drawingml alpha (opposite, and 0..100000)
1641static OString lcl_TransparencyToDrawingMlAlpha(const Color& rColor)
1642{
1643 if (rColor.IsTransparent())
1644 {
1645 sal_Int32 nAlphaPercent = float(rColor.GetAlpha()) / 2.55;
1646 return OString::number(nAlphaPercent * oox::drawingml::PER_PERCENT);
1647 }
1648
1649 return OString();
1650}
1651
1653{
1654 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1655
1656 // Output effects
1657 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1658 return;
1659
1660 // Distance is measured diagonally from corner
1661 double nShadowDist
1662 = sqrt(static_cast<double>(aShadowItem.GetWidth()) * aShadowItem.GetWidth() * 2.0);
1663 OString aShadowDist(OString::number(TwipsToEMU(nShadowDist)));
1664 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1665 OString aShadowAlpha = lcl_TransparencyToDrawingMlAlpha(aShadowItem.GetColor());
1666 sal_uInt32 nShadowDir = 0;
1667 switch (aShadowItem.GetLocation())
1668 {
1669 case SvxShadowLocation::TopLeft:
1670 nShadowDir = 13500000;
1671 break;
1672 case SvxShadowLocation::TopRight:
1673 nShadowDir = 18900000;
1674 break;
1675 case SvxShadowLocation::BottomLeft:
1676 nShadowDir = 8100000;
1677 break;
1678 case SvxShadowLocation::BottomRight:
1679 nShadowDir = 2700000;
1680 break;
1681 case SvxShadowLocation::NONE:
1682 case SvxShadowLocation::End:
1683 break;
1684 }
1685 OString aShadowDir(OString::number(nShadowDir));
1686
1687 m_pImpl->getSerializer()->startElementNS(XML_a, XML_effectLst);
1688 m_pImpl->getSerializer()->startElementNS(XML_a, XML_outerShdw, XML_dist, aShadowDist, XML_dir,
1689 aShadowDir);
1690 if (aShadowAlpha.isEmpty())
1691 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1692 else
1693 {
1694 m_pImpl->getSerializer()->startElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1695 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_alpha, XML_val, aShadowAlpha);
1696 m_pImpl->getSerializer()->endElementNS(XML_a, XML_srgbClr);
1697 }
1698 m_pImpl->getSerializer()->endElementNS(XML_a, XML_outerShdw);
1699 m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst);
1700}
1701
1702void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat,
1703 int nDiagramId)
1704{
1705 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(),
1706 uno::UNO_QUERY);
1707
1708 // write necessary tags to document.xml
1709 Size aSize(sdrObject->GetSnapRect().getOpenWidth(), sdrObject->GetSnapRect().getOpenHeight());
1710 startDMLAnchorInline(&rFrameFormat, aSize);
1711
1712 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
1713 m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId);
1714
1715 endDMLAnchorInline(&rFrameFormat);
1716}
1717
1719{
1720 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1721 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1722
1723 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1724 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1725
1726 //Save data here and restore when out of scope
1727 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1728
1730 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1731 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
1732 m_pImpl->getExport().WriteText();
1733}
1734
1736{
1737 const editeng::SvxBorderLine* pBorderLine = nullptr;
1738
1739 if (rBox.GetTop())
1740 {
1741 pBorderLine = rBox.GetTop();
1742 }
1743 else if (rBox.GetLeft())
1744 {
1745 pBorderLine = rBox.GetLeft();
1746 }
1747 else if (rBox.GetBottom())
1748 {
1749 pBorderLine = rBox.GetBottom();
1750 }
1751 else if (rBox.GetRight())
1752 {
1753 pBorderLine = rBox.GetRight();
1754 }
1755
1756 if (!pBorderLine)
1757 {
1758 return;
1759 }
1760
1761 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1762 if (pBorderLine->GetWidth() == SvxBorderLineWidth::Hairline)
1763 pFS->startElementNS(XML_a, XML_ln);
1764 else
1765 {
1766 double fConverted(editeng::ConvertBorderWidthToWord(pBorderLine->GetBorderLineStyle(),
1767 pBorderLine->GetWidth()));
1768 OString sWidth(OString::number(TwipsToEMU(fConverted)));
1769 pFS->startElementNS(XML_a, XML_ln, XML_w, sWidth);
1770 }
1771
1772 pFS->startElementNS(XML_a, XML_solidFill);
1773 OString sColor(msfilter::util::ConvertColor(pBorderLine->GetColor()));
1774 pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
1775 pFS->endElementNS(XML_a, XML_solidFill);
1776
1777 if (SvxBorderLineStyle::DASHED == pBorderLine->GetBorderLineStyle()) // Line Style is Dash type
1778 pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
1779
1780 pFS->endElementNS(XML_a, XML_ln);
1781}
1782
1783void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId,
1784 bool bTextBoxOnly)
1785{
1786 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1787 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
1788
1789 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1790 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1791 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1792
1793 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1794 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1795
1796 //Save data here and restore when out of scope
1797 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1798
1799 // When a frame has some low height, but automatically expanded due
1800 // to lots of contents, this size contains the real size.
1801 const Size aSize = pParentFrame->GetSize();
1802
1803 uno::Reference<drawing::XShape> xShape;
1804 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
1805 if (pSdrObj)
1806 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
1807 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1808 uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
1809 if (xPropertySet.is())
1810 xPropSetInfo = xPropertySet->getPropertySetInfo();
1811
1813 {
1814 drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP;
1815 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("TextVerticalAdjust"))
1816 xPropertySet->getPropertyValue("TextVerticalAdjust") >>= eAdjust;
1817 m_pImpl->getBodyPrAttrList()->add(XML_anchor,
1819 }
1820
1821 if (!bTextBoxOnly)
1822 {
1823 startDMLAnchorInline(&rFrameFormat, aSize);
1824
1827 pDocPrAttrList->add(XML_id, OString::number(nAnchorId));
1828 pDocPrAttrList->add(XML_name, rFrameFormat.GetName());
1829
1830 pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
1831
1832 OUString sHyperlink;
1833 if (xPropertySet.is())
1834 xPropertySet->getPropertyValue("HyperLinkURL") >>= sHyperlink;
1835 if (!sHyperlink.isEmpty())
1836 {
1837 OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
1838 pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1841 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1842 FSNS(XML_xmlns, XML_a),
1843 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1844 }
1845
1846 pFS->endElementNS(XML_wp, XML_docPr);
1847
1848 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1849 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1850 pFS->startElementNS(XML_a, XML_graphicData, XML_uri,
1851 "http://schemas.microsoft.com/office/word/2010/wordprocessingShape");
1852 pFS->startElementNS(XML_wps, XML_wsp);
1853 pFS->singleElementNS(XML_wps, XML_cNvSpPr, XML_txBox, "1");
1854
1855 uno::Any aRotation;
1856 m_pImpl->setDMLandVMLTextFrameRotation(0_deg100);
1857 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1858 {
1859 uno::Sequence<beans::PropertyValue> propList;
1860 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1861 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
1862 [](const beans::PropertyValue& rProp) {
1863 return rProp.Name == "mso-rotation-angle";
1864 });
1865 if (pProp != std::cend(propList))
1866 aRotation = pProp->Value;
1867 }
1868 sal_Int32 nTmp;
1869 if (aRotation >>= nTmp)
1870 m_pImpl->getDMLandVMLTextFrameRotation() = Degree100(nTmp);
1871 OString sRotation(OString::number(
1872 oox::drawingml::ExportRotateClockwisify(m_pImpl->getDMLandVMLTextFrameRotation())));
1873 // Shape properties
1874 pFS->startElementNS(XML_wps, XML_spPr);
1875 if (m_pImpl->getDMLandVMLTextFrameRotation())
1876 {
1877 pFS->startElementNS(XML_a, XML_xfrm, XML_rot, sRotation);
1878 }
1879 else
1880 {
1881 pFS->startElementNS(XML_a, XML_xfrm);
1882 }
1883 pFS->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
1884 OString aWidth(OString::number(TwipsToEMU(aSize.Width())));
1885 OString aHeight(OString::number(TwipsToEMU(aSize.Height())));
1886 pFS->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
1887 pFS->endElementNS(XML_a, XML_xfrm);
1888 OUString shapeType = "rect";
1889 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1890 {
1891 uno::Sequence<beans::PropertyValue> propList;
1892 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1893 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
1894 [](const beans::PropertyValue& rProp) {
1895 return rProp.Name == "mso-orig-shape-type";
1896 });
1897 if (pProp != std::cend(propList))
1898 pProp->Value >>= shapeType;
1899 }
1900 //Empty shapeType will lead to corruption so to avoid that shapeType is set to default i.e. "rect"
1901 if (shapeType.isEmpty())
1902 shapeType = "rect";
1903
1904 pFS->singleElementNS(XML_a, XML_prstGeom, XML_prst, shapeType);
1905 m_pImpl->setDMLTextFrameSyntax(true);
1906 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
1907 m_pImpl->setDMLTextFrameSyntax(false);
1908 writeDMLEffectLst(rFrameFormat);
1909 pFS->endElementNS(XML_wps, XML_spPr);
1910 }
1911
1912 //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.
1913 if (!m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized)
1914 {
1915 sal_Int32 nSeq = 0;
1916 for (auto& rEntry : m_pImpl->getExport().m_aLinkedTextboxesHelper)
1917 {
1918 //find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link
1919 if (rEntry.second.sPrevChain.isEmpty() && !rEntry.second.sNextChain.isEmpty())
1920 {
1921 //assign this chain a unique ID and start a new sequence
1922 nSeq = 0;
1923 rEntry.second.nId = ++m_pImpl->getExport().m_nLinkedTextboxesChainId;
1924 rEntry.second.nSeq = nSeq;
1925
1926 OUString sCheckForBrokenChains = rEntry.first;
1927
1928 //follow the chain and assign the same id, and incremental sequence numbers.
1929 auto followChainIter
1930 = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(rEntry.second.sNextChain);
1931 while (followChainIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1932 {
1933 //verify that the NEXT textbox also points to me as the PREVIOUS.
1934 // A broken link indicates a leftover remnant that can be ignored.
1935 if (followChainIter->second.sPrevChain != sCheckForBrokenChains)
1936 break;
1937
1938 followChainIter->second.nId = m_pImpl->getExport().m_nLinkedTextboxesChainId;
1939 followChainIter->second.nSeq = ++nSeq;
1940
1941 //empty next chain indicates the end of the linked chain.
1942 if (followChainIter->second.sNextChain.isEmpty())
1943 break;
1944
1945 sCheckForBrokenChains = followChainIter->first;
1946 followChainIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(
1947 followChainIter->second.sNextChain);
1948 }
1949 }
1950 }
1951 m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized = true;
1952 }
1953
1954 m_pImpl->getExport().m_pParentFrame = nullptr;
1955 bool skipTxBxContent = false;
1956 bool isTxbxLinked = false;
1957
1958 OUString sLinkChainName;
1959 if (xPropSetInfo.is())
1960 {
1961 if (xPropSetInfo->hasPropertyByName("LinkDisplayName"))
1962 xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
1963 else if (xPropSetInfo->hasPropertyByName("ChainName"))
1964 xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
1965 }
1966
1967 // second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx
1968 auto linkedTextboxesIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(sLinkChainName);
1969 if (linkedTextboxesIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1970 {
1971 if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq != 0))
1972 {
1973 //not the first in the chain, so write the tag as linkedTxbx
1974 pFS->singleElementNS(XML_wps, XML_linkedTxbx, XML_id,
1975 OString::number(linkedTextboxesIter->second.nId), XML_seq,
1976 OString::number(linkedTextboxesIter->second.nSeq));
1977 /* no text content should be added to this tag,
1978 since the textbox is linked, the entire content
1979 is written in txbx block
1980 */
1981 skipTxBxContent = true;
1982 }
1983 else if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0))
1984 {
1985 /* this is the first textbox in the chaining, we add the text content
1986 to this block*/
1987 //since the text box is linked, it needs an id.
1988 pFS->startElementNS(XML_wps, XML_txbx, XML_id,
1989 OString::number(linkedTextboxesIter->second.nId));
1990 isTxbxLinked = true;
1991 }
1992 }
1993
1994 if (!skipTxBxContent)
1995 {
1996 if (!isTxbxLinked)
1997 pFS->startElementNS(XML_wps, XML_txbx); //text box is not linked, therefore no id.
1998
1999 pFS->startElementNS(XML_w, XML_txbxContent);
2000
2001 const SvxFrameDirectionItem& rDirection = rFrameFormat.GetFrameDir();
2002 if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB)
2003 m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert");
2004 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
2005 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270");
2006 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB)
2007 m_pImpl->getBodyPrAttrList()->add(XML_vert, "mongolianVert");
2008 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB90)
2009 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert");
2010 {
2011 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
2012 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
2013 m_pImpl->getExport().WriteText();
2014 if (m_pImpl->getParagraphSdtOpen())
2015 {
2016 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
2017 m_pImpl->setParagraphSdtOpen(false);
2018 }
2019 }
2020
2021 pFS->endElementNS(XML_w, XML_txbxContent);
2022 pFS->endElementNS(XML_wps, XML_txbx);
2023 }
2024
2025 // We need to init padding to 0, if it's not set.
2026 // In LO the default is 0 and so ins attributes are not set when padding is 0
2027 // but in MSO the default is 254 / 127, so we need to set 0 padding explicitly
2028 if (m_pImpl->getBodyPrAttrList())
2029 {
2030 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_lIns))
2031 m_pImpl->getBodyPrAttrList()->add(XML_lIns, OString::number(0));
2032 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_tIns))
2033 m_pImpl->getBodyPrAttrList()->add(XML_tIns, OString::number(0));
2034 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_rIns))
2035 m_pImpl->getBodyPrAttrList()->add(XML_rIns, OString::number(0));
2036 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_bIns))
2037 m_pImpl->getBodyPrAttrList()->add(XML_bIns, OString::number(0));
2038 }
2039
2040 rtl::Reference<FastAttributeList> xBodyPrAttrList(m_pImpl->getBodyPrAttrList());
2041 m_pImpl->setBodyPrAttrList(nullptr);
2042 if (!bTextBoxOnly)
2043 {
2044 pFS->startElementNS(XML_wps, XML_bodyPr, xBodyPrAttrList);
2045 // AutoSize of the Text Frame.
2046 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
2047 pFS->singleElementNS(
2048 XML_a,
2049 (rSize.GetHeightSizeType() == SwFrameSize::Variable ? XML_spAutoFit : XML_noAutofit));
2050 pFS->endElementNS(XML_wps, XML_bodyPr);
2051
2052 pFS->endElementNS(XML_wps, XML_wsp);
2053 pFS->endElementNS(XML_a, XML_graphicData);
2054 pFS->endElementNS(XML_a, XML_graphic);
2055
2056 // Relative size of the Text Frame.
2057 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
2058 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
2059 {
2060 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom,
2061 (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME
2062 ? "page"
2063 : "margin"));
2064 pFS->startElementNS(XML_wp14, XML_pctWidth);
2065 pFS->writeEscaped(OUString::number(nWidthPercent * oox::drawingml::PER_PERCENT));
2066 pFS->endElementNS(XML_wp14, XML_pctWidth);
2067 pFS->endElementNS(XML_wp14, XML_sizeRelH);
2068 }
2069 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
2070 if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED)
2071 {
2072 pFS->startElementNS(
2073 XML_wp14, XML_sizeRelV, XML_relativeFrom,
2074 (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME ? "page"
2075 : "margin"));
2076 pFS->startElementNS(XML_wp14, XML_pctHeight);
2077 pFS->writeEscaped(OUString::number(nHeightPercent * oox::drawingml::PER_PERCENT));
2078 pFS->endElementNS(XML_wp14, XML_pctHeight);
2079 pFS->endElementNS(XML_wp14, XML_sizeRelV);
2080 }
2081
2082 endDMLAnchorInline(&rFrameFormat);
2083 }
2084 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
2085}
2086
2087void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly)
2088{
2089 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
2090 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
2091
2092 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
2093 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
2094 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
2095
2096 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
2097 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
2098
2099 //Save data here and restore when out of scope
2100 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
2101
2102 // When a frame has some low height, but automatically expanded due
2103 // to lots of contents, this size contains the real size.
2104 const Size aSize = pParentFrame->GetSize();
2105 m_pImpl->setFlyFrameSize(&aSize);
2106
2107 m_pImpl->setTextFrameSyntax(true);
2110 m_pImpl->getTextFrameStyle() = "position:absolute";
2111 if (!bTextBoxOnly)
2112 {
2113 OString sRotation(OString::number(-toDegrees(m_pImpl->getDMLandVMLTextFrameRotation())));
2114 m_pImpl->getExport().SdrExporter().getTextFrameStyle().append(";rotation:" + sRotation);
2115 }
2116 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
2117 m_pImpl->getFlyAttrList()->add(XML_style, m_pImpl->getTextFrameStyle().makeStringAndClear());
2118
2119 const SdrObject* pObject = pParentFrame->GetFrameFormat().FindRealSdrObject();
2120 if (pObject != nullptr)
2121 {
2122 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObject);
2123 if (!sAnchorId.isEmpty())
2124 m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId, sAnchorId);
2125
2126 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(),
2127 uno::UNO_QUERY);
2128 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
2129 OUString sHyperlink;
2130 if (xShapeProps.is())
2131 xShapeProps->getPropertyValue("HyperLinkURL") >>= sHyperlink;
2132 if (!sHyperlink.isEmpty())
2133 m_pImpl->getFlyAttrList()->add(XML_href, sHyperlink);
2134 }
2135 rtl::Reference<FastAttributeList> xFlyAttrList(m_pImpl->getFlyAttrList());
2136 m_pImpl->getFlyAttrList().clear();
2137 rtl::Reference<FastAttributeList> xTextboxAttrList(m_pImpl->getTextboxAttrList());
2138 m_pImpl->getTextboxAttrList().clear();
2139 m_pImpl->setTextFrameSyntax(false);
2140 m_pImpl->setFlyFrameSize(nullptr);
2141 m_pImpl->getExport().m_pParentFrame = nullptr;
2142
2143 if (!bTextBoxOnly)
2144 {
2145 pFS->startElementNS(XML_w, XML_pict);
2146 pFS->startElementNS(XML_v, XML_rect, xFlyAttrList);
2147 m_pImpl->textFrameShadow(rFrameFormat);
2148 if (m_pImpl->getFlyFillAttrList().is())
2149 {
2150 rtl::Reference<FastAttributeList> xFlyFillAttrList(m_pImpl->getFlyFillAttrList());
2151 pFS->singleElementNS(XML_v, XML_fill, xFlyFillAttrList);
2152 }
2153 if (m_pImpl->getDashLineStyleAttr().is())
2154 {
2155 rtl::Reference<FastAttributeList> xDashLineStyleAttr(m_pImpl->getDashLineStyleAttr());
2156 pFS->singleElementNS(XML_v, XML_stroke, xDashLineStyleAttr);
2157 }
2158 pFS->startElementNS(XML_v, XML_textbox, xTextboxAttrList);
2159 }
2160 m_pImpl->getFlyFillAttrList().clear();
2161 m_pImpl->getDashLineStyleAttr().clear();
2162
2163 pFS->startElementNS(XML_w, XML_txbxContent);
2164 {
2165 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
2166 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
2167 m_pImpl->getExport().WriteText();
2168 if (m_pImpl->getParagraphSdtOpen())
2169 {
2170 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
2171 m_pImpl->setParagraphSdtOpen(false);
2172 }
2173 }
2174 pFS->endElementNS(XML_w, XML_txbxContent);
2175 if (!bTextBoxOnly)
2176 {
2177 pFS->endElementNS(XML_v, XML_textbox);
2178
2179 if (m_pImpl->getFlyWrapAttrList())
2180 {
2181 rtl::Reference<FastAttributeList> xFlyWrapAttrList(m_pImpl->getFlyWrapAttrList());
2182 m_pImpl->setFlyWrapAttrList(nullptr);
2183 pFS->singleElementNS(XML_w10, XML_wrap, xFlyWrapAttrList);
2184 }
2185
2186 pFS->endElementNS(XML_v, XML_rect);
2187 pFS->endElementNS(XML_w, XML_pict);
2188 }
2189
2190 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
2191}
2192
2194{
2195 return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT);
2196}
2197
2198/* 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
DocxExportFilter & GetFilter()
Definition: docxexport.hxx:134
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:1897
virtual void RestoreData()
Restore what was saved in SaveData().
Definition: wrtww8.cxx:1929
const ww8::Frame * m_pParentFrame
Definition: wrtww8.hxx:520
constexpr tools::Long getX() const
constexpr tools::Long getY() const
virtual Degree100 GetRotateAngle() const
const OUString & getHyperlink() const
const double * GetRelativeHeight() const
virtual OUString GetTitle() const
sal_uInt32 GetOrdNum() const
rtl::Reference< SdrObject > ConvertToPolyObj(bool bBezier, bool bLineToArea) const
virtual const tools::Rectangle & GetCurrentBoundRect() const
virtual OUString GetDescription() const
const double * GetRelativeWidth() const
virtual const tools::Rectangle & GetSnapRect() const
virtual const OUString & GetName() const
virtual bool IsTextPath() 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:85
IDocumentDrawModelAccess const & getIDocumentDrawModelAccess() const
Definition: doc.cxx:169
const SwPageDesc & GetPageDesc(const size_t i) const
Definition: doc.hxx:896
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
Defines the horizontal position of a fly frame.
Definition: fmtornt.hxx:73
sal_Int16 GetHoriOrient() const
Definition: fmtornt.hxx:94
SwTwips GetPos() const
Definition: fmtornt.hxx:99
sal_Int16 GetRelationOrient() const
Definition: fmtornt.hxx:95
bool IsOutside() const
Definition: fmtsrnd.hxx:54
bool IsContour() const
Definition: fmtsrnd.hxx:53
css::text::WrapTextMode GetSurround() const
Definition: fmtsrnd.hxx:51
Defines the vertical position of a fly frame.
Definition: fmtornt.hxx:37
sal_Int16 GetRelationOrient() const
Definition: fmtornt.hxx:58
SwTwips GetPos() const
Definition: fmtornt.hxx:62
sal_Int16 GetVertOrient() const
Definition: fmtornt.hxx:57
const SvxFrameDirectionItem & GetFrameDir(bool=true) const
Definition: frmatr.hxx:118
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:104
const SwFormatFrameSize & GetFrameSize(bool=true) const
Definition: fmtfsize.hxx:104
const SvxLRSpaceItem & GetLRSpace(bool=true) const
Definition: frmatr.hxx:98
const OUString & GetName() const
Definition: format.hxx:131
const SwFormatVertOrient & GetVertOrient(bool=true) const
Definition: fmtornt.hxx:113
const SvxShadowItem & GetShadow(bool=true) const
Definition: frmatr.hxx:112
const SwFormatFollowTextFlow & GetFollowTextFlow(bool=true) const
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx:88
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:115
const SvxULSpaceItem & GetULSpace(bool=true) const
Definition: frmatr.hxx:100
const SwFormatContent & GetContent(bool=true) const
Definition: fmtcntnt.hxx:55
Style of a layout element.
Definition: frmfmt.hxx:72
SwRect FindLayoutRect(const bool bPrtArea=false, const Point *pPoint=nullptr) const
Definition: atrfrm.cxx:2749
SdrObject * FindRealSdrObject()
Definition: atrfrm.cxx:2802
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:123
SwNodeOffset GetIndex() const
Definition: ndindex.hxx:111
SwNodeOffset EndOfSectionIndex() const
Definition: node.hxx:691
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
OUString getNamespaceURL(sal_Int32 nNSID) 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.
constexpr OUStringLiteral sColor
double toRadians(D x)
double toDegrees(D x)
void AddExtLst(sax_fastparser::FSHelperPtr const &pFS, DocxExport const &rExport, uno::Reference< beans::XPropertySet > const &xShape)
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(162)
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)
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.
static bool isSupportedDMLShape(const uno::Reference< drawing::XShape > &xShape, const SdrObject *pSdrObject)
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()
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