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 DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat,
1387 int nAnchorId)
1388{
1389 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape());
1390 if (!Impl::isSupportedDMLShape(xShape, pSdrObject))
1391 return;
1392
1393 m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject);
1394
1395 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1396 Size aSize(pSdrObject->GetLogicRect().getOpenWidth(),
1397 pSdrObject->GetLogicRect().getOpenHeight());
1398 startDMLAnchorInline(pFrameFormat, aSize);
1399
1402 pDocPrAttrList->add(XML_id, OString::number(nAnchorId));
1403 pDocPrAttrList->add(XML_name, pSdrObject->GetName());
1404 if (!pSdrObject->GetTitle().isEmpty())
1405 pDocPrAttrList->add(XML_title, pSdrObject->GetTitle());
1406 if (!pSdrObject->GetDescription().isEmpty())
1407 pDocPrAttrList->add(XML_descr, pSdrObject->GetDescription());
1408 if (!pSdrObject->IsVisible()
1409 && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
1410
1411 pDocPrAttrList->add(XML_hidden, OString::number(1));
1412
1413 pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
1414 OUString sHyperlink = pSdrObject->getHyperlink();
1415 if (!sHyperlink.isEmpty())
1416 {
1417 OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
1418 pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1421 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1422 FSNS(XML_xmlns, XML_a),
1423 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1424 }
1425 pFS->endElementNS(XML_wp, XML_docPr);
1426
1427 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
1428 const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
1429 if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"))
1430 pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
1431 else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1432 pNamespace = "http://schemas.openxmlformats.org/drawingml/2006/picture";
1433 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1434 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1435 pFS->startElementNS(XML_a, XML_graphicData, XML_uri, pNamespace);
1436
1437 bool bLockedCanvas = lcl_isLockedCanvas(xShape);
1438 if (bLockedCanvas)
1439 pFS->startElementNS(
1440 XML_lc, XML_lockedCanvas, FSNS(XML_xmlns, XML_lc),
1441 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlLockedCanvas)));
1442
1443 m_pImpl->getExport().OutputDML(xShape);
1444
1445 if (bLockedCanvas)
1446 pFS->endElementNS(XML_lc, XML_lockedCanvas);
1447 pFS->endElementNS(XML_a, XML_graphicData);
1448 pFS->endElementNS(XML_a, XML_graphic);
1449
1450 // Relative size of the drawing.
1451 if (pSdrObject->GetRelativeWidth())
1452 {
1453 // At the moment drawinglayer objects are always relative from page.
1454 OUString sValue;
1455 switch (pSdrObject->GetRelativeWidthRelation())
1456 {
1457 case text::RelOrientation::FRAME:
1458 sValue = "margin";
1459 break;
1460 case text::RelOrientation::PAGE_LEFT:
1461 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
1462 sValue = "outsideMargin";
1463 else
1464 sValue = "leftMargin";
1465 break;
1466 case text::RelOrientation::PAGE_RIGHT:
1467 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
1468 sValue = "insideMargin";
1469 else
1470 sValue = "rightMargin";
1471 break;
1472 case text::RelOrientation::PAGE_FRAME:
1473 default:
1474 sValue = "page";
1475 break;
1476 }
1477 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, sValue);
1478 pFS->startElementNS(XML_wp14, XML_pctWidth);
1479 pFS->writeEscaped(
1480 OUString::number(*pSdrObject->GetRelativeWidth() * 100 * oox::drawingml::PER_PERCENT));
1481 pFS->endElementNS(XML_wp14, XML_pctWidth);
1482 pFS->endElementNS(XML_wp14, XML_sizeRelH);
1483 }
1484 if (pSdrObject->GetRelativeHeight())
1485 {
1486 OUString sValue;
1487 switch (pSdrObject->GetRelativeHeightRelation())
1488 {
1489 case text::RelOrientation::FRAME:
1490 sValue = "margin";
1491 break;
1492 case text::RelOrientation::PAGE_PRINT_AREA:
1493 sValue = "topMargin";
1494 break;
1495 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
1496 sValue = "bottomMargin";
1497 break;
1498 case text::RelOrientation::PAGE_FRAME:
1499 default:
1500 sValue = "page";
1501 break;
1502 }
1503 pFS->startElementNS(XML_wp14, XML_sizeRelV, XML_relativeFrom, sValue);
1504 pFS->startElementNS(XML_wp14, XML_pctHeight);
1505 pFS->writeEscaped(
1506 OUString::number(*pSdrObject->GetRelativeHeight() * 100 * oox::drawingml::PER_PERCENT));
1507 pFS->endElementNS(XML_wp14, XML_pctHeight);
1508 pFS->endElementNS(XML_wp14, XML_sizeRelV);
1509 }
1510
1511 endDMLAnchorInline(pFrameFormat);
1512}
1513
1515{
1516 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1517 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1518 return;
1519
1520 OString aShadowWidth(OString::number(double(aShadowItem.GetWidth()) / 20) + "pt");
1521 OString aOffset;
1522 switch (aShadowItem.GetLocation())
1523 {
1524 case SvxShadowLocation::TopLeft:
1525 aOffset = "-" + aShadowWidth + ",-" + aShadowWidth;
1526 break;
1527 case SvxShadowLocation::TopRight:
1528 aOffset = aShadowWidth + ",-" + aShadowWidth;
1529 break;
1530 case SvxShadowLocation::BottomLeft:
1531 aOffset = "-" + aShadowWidth + "," + aShadowWidth;
1532 break;
1533 case SvxShadowLocation::BottomRight:
1534 aOffset = aShadowWidth + "," + aShadowWidth;
1535 break;
1536 case SvxShadowLocation::NONE:
1537 case SvxShadowLocation::End:
1538 break;
1539 }
1540 if (aOffset.isEmpty())
1541 return;
1542
1543 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1544 m_pSerializer->singleElementNS(XML_v, XML_shadow, XML_on, "t", XML_color, "#" + aShadowColor,
1545 XML_offset, aOffset);
1546}
1547
1548bool DocxSdrExport::Impl::isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape,
1549 const SdrObject* pSdrObject)
1550{
1551 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
1552 if (xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonShape")
1553 || xServiceInfo->supportsService("com.sun.star.drawing.PolyLineShape"))
1554 return false;
1555
1556 uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
1557 // For signature line shapes, we don't want DML, just the VML shape.
1558 if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1559 {
1560 bool bIsSignatureLineShape = false;
1561 xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLineShape;
1562 if (bIsSignatureLineShape)
1563 return false;
1564 }
1565
1566 // A FontWork shape with bitmap fill cannot be expressed as a modern 'abc transform'
1567 // in Word. Only the legacy VML WordArt allows bitmap fill.
1568 if (pSdrObject->IsTextPath())
1569 {
1570 css::drawing::FillStyle eFillStyle = css::drawing::FillStyle_SOLID;
1571 xShapeProperties->getPropertyValue("FillStyle") >>= eFillStyle;
1572 if (eFillStyle == css::drawing::FillStyle_BITMAP)
1573 return false;
1574 }
1575 return true;
1576}
1577
1579 const SwFrameFormat& rFrameFormat, int nAnchorId)
1580{
1581 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1582 m_pImpl->setDMLAndVMLDrawingOpen(true);
1583
1584 // Depending on the shape type, we actually don't write the shape as DML.
1585 OUString sShapeType;
1586 ShapeFlag nMirrorFlags = ShapeFlag::NONE;
1587 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObj)->getUnoShape());
1588
1589 MSO_SPT eShapeType
1590 = EscherPropertyContainer::GetCustomShapeType(xShape, nMirrorFlags, sShapeType);
1591
1592 // In case we are already inside a DML block, then write the shape only as VML, turn out that's allowed to do.
1593 // A common service created in util to check for VML shapes which are allowed to have textbox in content
1594 if ((msfilter::util::HasTextBoxContent(eShapeType)) && Impl::isSupportedDMLShape(xShape, sdrObj)
1595 && (!bDMLAndVMLDrawingOpen || lcl_isLockedCanvas(xShape))) // Locked canvas is OK inside DML
1596 {
1597 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_AlternateContent);
1598
1599 auto pObjGroup = dynamic_cast<const SdrObjGroup*>(sdrObj);
1600 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Choice, XML_Requires,
1601 (pObjGroup ? "wpg" : "wps"));
1602 writeDMLDrawing(sdrObj, &rFrameFormat, nAnchorId);
1603 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Choice);
1604
1605 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Fallback);
1606 writeVMLDrawing(sdrObj, rFrameFormat);
1607 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Fallback);
1608
1609 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_AlternateContent);
1610 }
1611 else
1612 writeVMLDrawing(sdrObj, rFrameFormat);
1613
1614 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
1615}
1616
1617// Converts ARGB transparency (0..255) to drawingml alpha (opposite, and 0..100000)
1618static OString lcl_TransparencyToDrawingMlAlpha(const Color& rColor)
1619{
1620 if (rColor.IsTransparent())
1621 {
1622 sal_Int32 nAlphaPercent = float(rColor.GetAlpha()) / 2.55;
1623 return OString::number(nAlphaPercent * oox::drawingml::PER_PERCENT);
1624 }
1625
1626 return OString();
1627}
1628
1630{
1631 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1632
1633 // Output effects
1634 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1635 return;
1636
1637 // Distance is measured diagonally from corner
1638 double nShadowDist
1639 = sqrt(static_cast<double>(aShadowItem.GetWidth()) * aShadowItem.GetWidth() * 2.0);
1640 OString aShadowDist(OString::number(TwipsToEMU(nShadowDist)));
1641 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1642 OString aShadowAlpha = lcl_TransparencyToDrawingMlAlpha(aShadowItem.GetColor());
1643 sal_uInt32 nShadowDir = 0;
1644 switch (aShadowItem.GetLocation())
1645 {
1646 case SvxShadowLocation::TopLeft:
1647 nShadowDir = 13500000;
1648 break;
1649 case SvxShadowLocation::TopRight:
1650 nShadowDir = 18900000;
1651 break;
1652 case SvxShadowLocation::BottomLeft:
1653 nShadowDir = 8100000;
1654 break;
1655 case SvxShadowLocation::BottomRight:
1656 nShadowDir = 2700000;
1657 break;
1658 case SvxShadowLocation::NONE:
1659 case SvxShadowLocation::End:
1660 break;
1661 }
1662 OString aShadowDir(OString::number(nShadowDir));
1663
1664 m_pImpl->getSerializer()->startElementNS(XML_a, XML_effectLst);
1665 m_pImpl->getSerializer()->startElementNS(XML_a, XML_outerShdw, XML_dist, aShadowDist, XML_dir,
1666 aShadowDir);
1667 if (aShadowAlpha.isEmpty())
1668 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1669 else
1670 {
1671 m_pImpl->getSerializer()->startElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1672 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_alpha, XML_val, aShadowAlpha);
1673 m_pImpl->getSerializer()->endElementNS(XML_a, XML_srgbClr);
1674 }
1675 m_pImpl->getSerializer()->endElementNS(XML_a, XML_outerShdw);
1676 m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst);
1677}
1678
1679void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat,
1680 int nDiagramId)
1681{
1682 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(),
1683 uno::UNO_QUERY);
1684
1685 // write necessary tags to document.xml
1686 Size aSize(sdrObject->GetSnapRect().getOpenWidth(), sdrObject->GetSnapRect().getOpenHeight());
1687 startDMLAnchorInline(&rFrameFormat, aSize);
1688
1689 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
1690 m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId);
1691
1692 endDMLAnchorInline(&rFrameFormat);
1693}
1694
1696{
1697 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1698 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1699
1700 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1701 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1702
1703 //Save data here and restore when out of scope
1704 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1705
1707 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1708 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
1709 m_pImpl->getExport().WriteText();
1710}
1711
1713{
1714 const editeng::SvxBorderLine* pBorderLine = nullptr;
1715
1716 if (rBox.GetTop())
1717 {
1718 pBorderLine = rBox.GetTop();
1719 }
1720 else if (rBox.GetLeft())
1721 {
1722 pBorderLine = rBox.GetLeft();
1723 }
1724 else if (rBox.GetBottom())
1725 {
1726 pBorderLine = rBox.GetBottom();
1727 }
1728 else if (rBox.GetRight())
1729 {
1730 pBorderLine = rBox.GetRight();
1731 }
1732
1733 if (!pBorderLine)
1734 {
1735 return;
1736 }
1737
1738 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1739 if (pBorderLine->GetWidth() == SvxBorderLineWidth::Hairline)
1740 pFS->startElementNS(XML_a, XML_ln);
1741 else
1742 {
1743 double fConverted(editeng::ConvertBorderWidthToWord(pBorderLine->GetBorderLineStyle(),
1744 pBorderLine->GetWidth()));
1745 OString sWidth(OString::number(TwipsToEMU(fConverted)));
1746 pFS->startElementNS(XML_a, XML_ln, XML_w, sWidth);
1747 }
1748
1749 pFS->startElementNS(XML_a, XML_solidFill);
1750 OString sColor(msfilter::util::ConvertColor(pBorderLine->GetColor()));
1751 pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
1752 pFS->endElementNS(XML_a, XML_solidFill);
1753
1754 if (SvxBorderLineStyle::DASHED == pBorderLine->GetBorderLineStyle()) // Line Style is Dash type
1755 pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
1756
1757 pFS->endElementNS(XML_a, XML_ln);
1758}
1759
1760void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId,
1761 bool bTextBoxOnly)
1762{
1763 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1764 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
1765
1766 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1767 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1768 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1769
1770 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1771 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1772
1773 //Save data here and restore when out of scope
1774 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1775
1776 // When a frame has some low height, but automatically expanded due
1777 // to lots of contents, this size contains the real size.
1778 const Size aSize = pParentFrame->GetSize();
1779
1780 uno::Reference<drawing::XShape> xShape;
1781 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
1782 if (pSdrObj)
1783 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
1784 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1785 uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
1786 if (xPropertySet.is())
1787 xPropSetInfo = xPropertySet->getPropertySetInfo();
1788
1790 {
1791 drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP;
1792 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("TextVerticalAdjust"))
1793 xPropertySet->getPropertyValue("TextVerticalAdjust") >>= eAdjust;
1794 m_pImpl->getBodyPrAttrList()->add(XML_anchor,
1796 }
1797
1798 if (!bTextBoxOnly)
1799 {
1800 startDMLAnchorInline(&rFrameFormat, aSize);
1801
1804 pDocPrAttrList->add(XML_id, OString::number(nAnchorId));
1805 pDocPrAttrList->add(XML_name, rFrameFormat.GetName());
1806
1807 pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
1808
1809 OUString sHyperlink;
1810 if (xPropertySet.is())
1811 xPropertySet->getPropertyValue("HyperLinkURL") >>= sHyperlink;
1812 if (!sHyperlink.isEmpty())
1813 {
1814 OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
1815 pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1818 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1819 FSNS(XML_xmlns, XML_a),
1820 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1821 }
1822
1823 pFS->endElementNS(XML_wp, XML_docPr);
1824
1825 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1826 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1827 pFS->startElementNS(XML_a, XML_graphicData, XML_uri,
1828 "http://schemas.microsoft.com/office/word/2010/wordprocessingShape");
1829 pFS->startElementNS(XML_wps, XML_wsp);
1830 pFS->singleElementNS(XML_wps, XML_cNvSpPr, XML_txBox, "1");
1831
1832 uno::Any aRotation;
1833 m_pImpl->setDMLandVMLTextFrameRotation(0_deg100);
1834 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1835 {
1836 uno::Sequence<beans::PropertyValue> propList;
1837 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1838 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
1839 [](const beans::PropertyValue& rProp) {
1840 return rProp.Name == "mso-rotation-angle";
1841 });
1842 if (pProp != std::cend(propList))
1843 aRotation = pProp->Value;
1844 }
1845 sal_Int32 nTmp;
1846 if (aRotation >>= nTmp)
1847 m_pImpl->getDMLandVMLTextFrameRotation() = Degree100(nTmp);
1848 OString sRotation(OString::number(
1849 oox::drawingml::ExportRotateClockwisify(m_pImpl->getDMLandVMLTextFrameRotation())));
1850 // Shape properties
1851 pFS->startElementNS(XML_wps, XML_spPr);
1852 if (m_pImpl->getDMLandVMLTextFrameRotation())
1853 {
1854 pFS->startElementNS(XML_a, XML_xfrm, XML_rot, sRotation);
1855 }
1856 else
1857 {
1858 pFS->startElementNS(XML_a, XML_xfrm);
1859 }
1860 pFS->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
1861 OString aWidth(OString::number(TwipsToEMU(aSize.Width())));
1862 OString aHeight(OString::number(TwipsToEMU(aSize.Height())));
1863 pFS->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
1864 pFS->endElementNS(XML_a, XML_xfrm);
1865 OUString shapeType = "rect";
1866 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1867 {
1868 uno::Sequence<beans::PropertyValue> propList;
1869 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1870 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
1871 [](const beans::PropertyValue& rProp) {
1872 return rProp.Name == "mso-orig-shape-type";
1873 });
1874 if (pProp != std::cend(propList))
1875 pProp->Value >>= shapeType;
1876 }
1877 //Empty shapeType will lead to corruption so to avoid that shapeType is set to default i.e. "rect"
1878 if (shapeType.isEmpty())
1879 shapeType = "rect";
1880
1881 pFS->singleElementNS(XML_a, XML_prstGeom, XML_prst, shapeType);
1882 m_pImpl->setDMLTextFrameSyntax(true);
1883 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
1884 m_pImpl->setDMLTextFrameSyntax(false);
1885 writeDMLEffectLst(rFrameFormat);
1886 pFS->endElementNS(XML_wps, XML_spPr);
1887 }
1888
1889 //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.
1890 if (!m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized)
1891 {
1892 sal_Int32 nSeq = 0;
1893 for (auto& rEntry : m_pImpl->getExport().m_aLinkedTextboxesHelper)
1894 {
1895 //find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link
1896 if (rEntry.second.sPrevChain.isEmpty() && !rEntry.second.sNextChain.isEmpty())
1897 {
1898 //assign this chain a unique ID and start a new sequence
1899 nSeq = 0;
1900 rEntry.second.nId = ++m_pImpl->getExport().m_nLinkedTextboxesChainId;
1901 rEntry.second.nSeq = nSeq;
1902
1903 OUString sCheckForBrokenChains = rEntry.first;
1904
1905 //follow the chain and assign the same id, and incremental sequence numbers.
1906 auto followChainIter
1907 = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(rEntry.second.sNextChain);
1908 while (followChainIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1909 {
1910 //verify that the NEXT textbox also points to me as the PREVIOUS.
1911 // A broken link indicates a leftover remnant that can be ignored.
1912 if (followChainIter->second.sPrevChain != sCheckForBrokenChains)
1913 break;
1914
1915 followChainIter->second.nId = m_pImpl->getExport().m_nLinkedTextboxesChainId;
1916 followChainIter->second.nSeq = ++nSeq;
1917
1918 //empty next chain indicates the end of the linked chain.
1919 if (followChainIter->second.sNextChain.isEmpty())
1920 break;
1921
1922 sCheckForBrokenChains = followChainIter->first;
1923 followChainIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(
1924 followChainIter->second.sNextChain);
1925 }
1926 }
1927 }
1928 m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized = true;
1929 }
1930
1931 m_pImpl->getExport().m_pParentFrame = nullptr;
1932 bool skipTxBxContent = false;
1933 bool isTxbxLinked = false;
1934
1935 OUString sLinkChainName;
1936 if (xPropSetInfo.is())
1937 {
1938 if (xPropSetInfo->hasPropertyByName("LinkDisplayName"))
1939 xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
1940 else if (xPropSetInfo->hasPropertyByName("ChainName"))
1941 xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
1942 }
1943
1944 // second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx
1945 auto linkedTextboxesIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(sLinkChainName);
1946 if (linkedTextboxesIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1947 {
1948 if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq != 0))
1949 {
1950 //not the first in the chain, so write the tag as linkedTxbx
1951 pFS->singleElementNS(XML_wps, XML_linkedTxbx, XML_id,
1952 OString::number(linkedTextboxesIter->second.nId), XML_seq,
1953 OString::number(linkedTextboxesIter->second.nSeq));
1954 /* no text content should be added to this tag,
1955 since the textbox is linked, the entire content
1956 is written in txbx block
1957 */
1958 skipTxBxContent = true;
1959 }
1960 else if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0))
1961 {
1962 /* this is the first textbox in the chaining, we add the text content
1963 to this block*/
1964 //since the text box is linked, it needs an id.
1965 pFS->startElementNS(XML_wps, XML_txbx, XML_id,
1966 OString::number(linkedTextboxesIter->second.nId));
1967 isTxbxLinked = true;
1968 }
1969 }
1970
1971 if (!skipTxBxContent)
1972 {
1973 if (!isTxbxLinked)
1974 pFS->startElementNS(XML_wps, XML_txbx); //text box is not linked, therefore no id.
1975
1976 pFS->startElementNS(XML_w, XML_txbxContent);
1977
1978 const SvxFrameDirectionItem& rDirection = rFrameFormat.GetFrameDir();
1979 if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB)
1980 m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert");
1981 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
1982 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270");
1983 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB)
1984 m_pImpl->getBodyPrAttrList()->add(XML_vert, "mongolianVert");
1985 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB90)
1986 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert");
1987 {
1988 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1989 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
1990 m_pImpl->getExport().WriteText();
1991 if (m_pImpl->getParagraphSdtOpen())
1992 {
1993 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
1994 m_pImpl->setParagraphSdtOpen(false);
1995 }
1996 }
1997
1998 pFS->endElementNS(XML_w, XML_txbxContent);
1999 pFS->endElementNS(XML_wps, XML_txbx);
2000 }
2001
2002 // We need to init padding to 0, if it's not set.
2003 // In LO the default is 0 and so ins attributes are not set when padding is 0
2004 // but in MSO the default is 254 / 127, so we need to set 0 padding explicitly
2005 if (m_pImpl->getBodyPrAttrList())
2006 {
2007 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_lIns))
2008 m_pImpl->getBodyPrAttrList()->add(XML_lIns, OString::number(0));
2009 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_tIns))
2010 m_pImpl->getBodyPrAttrList()->add(XML_tIns, OString::number(0));
2011 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_rIns))
2012 m_pImpl->getBodyPrAttrList()->add(XML_rIns, OString::number(0));
2013 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_bIns))
2014 m_pImpl->getBodyPrAttrList()->add(XML_bIns, OString::number(0));
2015 }
2016
2017 rtl::Reference<FastAttributeList> xBodyPrAttrList(m_pImpl->getBodyPrAttrList());
2018 m_pImpl->setBodyPrAttrList(nullptr);
2019 if (!bTextBoxOnly)
2020 {
2021 pFS->startElementNS(XML_wps, XML_bodyPr, xBodyPrAttrList);
2022 // AutoSize of the Text Frame.
2023 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
2024 pFS->singleElementNS(
2025 XML_a,
2026 (rSize.GetHeightSizeType() == SwFrameSize::Variable ? XML_spAutoFit : XML_noAutofit));
2027 pFS->endElementNS(XML_wps, XML_bodyPr);
2028
2029 pFS->endElementNS(XML_wps, XML_wsp);
2030 pFS->endElementNS(XML_a, XML_graphicData);
2031 pFS->endElementNS(XML_a, XML_graphic);
2032
2033 // Relative size of the Text Frame.
2034 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
2035 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
2036 {
2037 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom,
2038 (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME
2039 ? "page"
2040 : "margin"));
2041 pFS->startElementNS(XML_wp14, XML_pctWidth);
2042 pFS->writeEscaped(OUString::number(nWidthPercent * oox::drawingml::PER_PERCENT));
2043 pFS->endElementNS(XML_wp14, XML_pctWidth);
2044 pFS->endElementNS(XML_wp14, XML_sizeRelH);
2045 }
2046 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
2047 if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED)
2048 {
2049 pFS->startElementNS(
2050 XML_wp14, XML_sizeRelV, XML_relativeFrom,
2051 (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME ? "page"
2052 : "margin"));
2053 pFS->startElementNS(XML_wp14, XML_pctHeight);
2054 pFS->writeEscaped(OUString::number(nHeightPercent * oox::drawingml::PER_PERCENT));
2055 pFS->endElementNS(XML_wp14, XML_pctHeight);
2056 pFS->endElementNS(XML_wp14, XML_sizeRelV);
2057 }
2058
2059 endDMLAnchorInline(&rFrameFormat);
2060 }
2061 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
2062}
2063
2064void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly)
2065{
2066 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
2067 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
2068
2069 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
2070 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
2071 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
2072
2073 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
2074 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
2075
2076 //Save data here and restore when out of scope
2077 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
2078
2079 // When a frame has some low height, but automatically expanded due
2080 // to lots of contents, this size contains the real size.
2081 const Size aSize = pParentFrame->GetSize();
2082 m_pImpl->setFlyFrameSize(&aSize);
2083
2084 m_pImpl->setTextFrameSyntax(true);
2087 m_pImpl->getTextFrameStyle() = "position:absolute";
2088 if (!bTextBoxOnly)
2089 {
2090 OString sRotation(OString::number(-toDegrees(m_pImpl->getDMLandVMLTextFrameRotation())));
2091 m_pImpl->getExport().SdrExporter().getTextFrameStyle().append(";rotation:" + sRotation);
2092 }
2093 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
2094 m_pImpl->getFlyAttrList()->add(XML_style, m_pImpl->getTextFrameStyle().makeStringAndClear());
2095
2096 const SdrObject* pObject = pParentFrame->GetFrameFormat().FindRealSdrObject();
2097 if (pObject != nullptr)
2098 {
2099 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObject);
2100 if (!sAnchorId.isEmpty())
2101 m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId, sAnchorId);
2102
2103 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(),
2104 uno::UNO_QUERY);
2105 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
2106 OUString sHyperlink;
2107 if (xShapeProps.is())
2108 xShapeProps->getPropertyValue("HyperLinkURL") >>= sHyperlink;
2109 if (!sHyperlink.isEmpty())
2110 m_pImpl->getFlyAttrList()->add(XML_href, sHyperlink);
2111 }
2112 rtl::Reference<FastAttributeList> xFlyAttrList(m_pImpl->getFlyAttrList());
2113 m_pImpl->getFlyAttrList().clear();
2114 rtl::Reference<FastAttributeList> xTextboxAttrList(m_pImpl->getTextboxAttrList());
2115 m_pImpl->getTextboxAttrList().clear();
2116 m_pImpl->setTextFrameSyntax(false);
2117 m_pImpl->setFlyFrameSize(nullptr);
2118 m_pImpl->getExport().m_pParentFrame = nullptr;
2119
2120 if (!bTextBoxOnly)
2121 {
2122 pFS->startElementNS(XML_w, XML_pict);
2123 pFS->startElementNS(XML_v, XML_rect, xFlyAttrList);
2124 m_pImpl->textFrameShadow(rFrameFormat);
2125 if (m_pImpl->getFlyFillAttrList().is())
2126 {
2127 rtl::Reference<FastAttributeList> xFlyFillAttrList(m_pImpl->getFlyFillAttrList());
2128 pFS->singleElementNS(XML_v, XML_fill, xFlyFillAttrList);
2129 }
2130 if (m_pImpl->getDashLineStyleAttr().is())
2131 {
2132 rtl::Reference<FastAttributeList> xDashLineStyleAttr(m_pImpl->getDashLineStyleAttr());
2133 pFS->singleElementNS(XML_v, XML_stroke, xDashLineStyleAttr);
2134 }
2135 pFS->startElementNS(XML_v, XML_textbox, xTextboxAttrList);
2136 }
2137 m_pImpl->getFlyFillAttrList().clear();
2138 m_pImpl->getDashLineStyleAttr().clear();
2139
2140 pFS->startElementNS(XML_w, XML_txbxContent);
2141 {
2142 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
2143 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
2144 m_pImpl->getExport().WriteText();
2145 if (m_pImpl->getParagraphSdtOpen())
2146 {
2147 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
2148 m_pImpl->setParagraphSdtOpen(false);
2149 }
2150 }
2151 pFS->endElementNS(XML_w, XML_txbxContent);
2152 if (!bTextBoxOnly)
2153 {
2154 pFS->endElementNS(XML_v, XML_textbox);
2155
2156 if (m_pImpl->getFlyWrapAttrList())
2157 {
2158 rtl::Reference<FastAttributeList> xFlyWrapAttrList(m_pImpl->getFlyWrapAttrList());
2159 m_pImpl->setFlyWrapAttrList(nullptr);
2160 pFS->singleElementNS(XML_w10, XML_wrap, xFlyWrapAttrList);
2161 }
2162
2163 pFS->endElementNS(XML_v, XML_rect);
2164 pFS->endElementNS(XML_w, XML_pict);
2165 }
2166
2167 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
2168}
2169
2171{
2172 return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT);
2173}
2174
2175/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
sal_uInt8 GetAlpha() const
bool IsTransparent() const
The class that does all the actual DOCX export-related work.
Definition: docxexport.hxx:75
void writeVMLDrawing(const SdrObject *sdrObj, const SwFrameFormat &rFrameFormat)
Writes a drawing as VML data.
void endDMLAnchorInline(const SwFrameFormat *pFrameFormat)
void setParagraphSdtOpen(bool bParagraphSdtOpen)
Set if paragraph sdt open in the current drawing.
void writeDMLEffectLst(const SwFrameFormat &rFrameFormat)
Write <a:effectLst>, the effect list.
static bool isTextBox(const SwFrameFormat &rFrameFormat)
Is this a standalone TextFrame, or used as a TextBox of a shape?
rtl::Reference< sax_fastparser::FastAttributeList > & getFlyAttrList()
bool getTextFrameSyntax() const
void writeDMLDrawing(const SdrObject *pSdrObject, const SwFrameFormat *pFrameFormat, int nAnchorId)
Writes a drawing as DML.
bool IsDrawingOpen() const
void writeBoxItemLine(const SvxBoxItem &rBox)
Writes the drawingML <a:ln> markup of a box item.
rtl::Reference< sax_fastparser::FastAttributeList > & getFlyFillAttrList()
bool getDMLTextFrameSyntax() const
void writeVMLTextFrame(ww8::Frame const *pParentFrame, bool bTextBoxOnly=false)
Writes text frame in VML format.
rtl::Reference< sax_fastparser::FastAttributeList > & getTextboxAttrList()
Attributes of the next v:textbox element.
void setFlyWrapAttrList(rtl::Reference< sax_fastparser::FastAttributeList > const &pAttrList)
DocxSdrExport(DocxExport &rExport, const sax_fastparser::FSHelperPtr &pSerializer, oox::drawingml::DrawingML *pDrawingML)
bool IsDMLAndVMLDrawingOpen() const
void writeDiagram(const SdrObject *sdrObject, const SwFrameFormat &rFrameFormat, int nDiagramId)
Writes a diagram (smartart).
void writeOnlyTextOfFrame(ww8::Frame const *pParentFrame)
Writes text from Textbox for <w:framePr>
void writeDMLTextFrame(ww8::Frame const *pParentFrame, int nAnchorId, bool bTextBoxOnly=false)
Writes text frame in DML format.
const Size * getFlyFrameSize() const
When exporting fly frames, this holds the real size of the frame.
bool IsParagraphHasDrawing() const
void setParagraphHasDrawing(bool bParagraphHasDrawing)
sax_fastparser::FastAttributeList * getBodyPrAttrList()
Attributes of <wps:bodyPr>, used during DML export of text frames.
void startDMLAnchorInline(const SwFrameFormat *pFrameFormat, const Size &rSize)
void writeDMLAndVMLDrawing(const SdrObject *sdrObj, const SwFrameFormat &rFrameFormat, int nAnchorId)
Writes shape in both DML and VML format.
rtl::Reference< sax_fastparser::FastAttributeList > & getDashLineStyle()
OStringBuffer & getTextFrameStyle()
std::unique_ptr< Impl > m_pImpl
void setSerializer(const sax_fastparser::FSHelperPtr &pSerializer)
static MSO_SPT GetCustomShapeType(const css::uno::Reference< css::drawing::XShape > &rXShape, ShapeFlag &nMirrorFlags, OUString &rShapeType, bool bOOXML=false)
Helper class, so that the DocxExport::RestoreData() call will always happen.
ExportDataSaveRestore(DocxExport &rExport, SwNodeOffset nStt, SwNodeOffset nEnd, ww8::Frame const *pParentFrame)
virtual SdrLayerID GetHellId() const =0
virtual SdrLayerID GetInvisibleHellId() const =0
virtual void SaveData(SwNodeOffset nStt, SwNodeOffset nEnd)
Remember some of the members so that we can recurse in WriteText().
Definition: wrtww8.cxx:1897
virtual void RestoreData()
Restore what was saved in SaveData().
Definition: wrtww8.cxx:1929
const ww8::Frame * m_pParentFrame
Definition: wrtww8.hxx:520
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:894
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:728
UseOnPage GetUseOn() const
Definition: pagedesc.hxx:353
Of course Writer needs its own rectangles.
Definition: swrect.hxx:35
void Height(tools::Long nNew)
Definition: swrect.hxx:193
void Width(tools::Long nNew)
Definition: swrect.hxx:189
static bool isTextBox(const SwFrameFormat *pFormat, sal_uInt16 nType, const SdrObject *pObject=nullptr)
Is the frame format a text box?
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
void transform(const basegfx::B2DHomMatrix &rMatrix)
sal_uInt32 count() const
iterator find(const OUString &rKey)
const Color & GetColor() const
tools::Long GetWidth() const
SvxBorderLineStyle GetBorderLineStyle() const
virtual bool isExternalURL(const OUString &rURL) const
virtual OUString getTransformedString(const OUString &rURL) const
static rtl::Reference< FastAttributeList > createAttrList()
sal_uInt16 Count() const
void Insert(sal_uInt16 nPos, const Point &rPt)
sal_uInt16 GetSize() const
const Point & GetPoint(sal_uInt16 nPos) const
constexpr Point Center() const
constexpr tools::Long Top() const
tools::Long getOpenHeight() const
constexpr tools::Long Right() const
tools::Long getOpenWidth() const
constexpr tools::Long Left() const
constexpr tools::Long Bottom() const
Make exporting a Writer Frame easy.
const Size & GetSize() const
The Size of the contained element.
const SwFrameFormat & GetFrameFormat() const
Get the writer SwFrameFormat that this object describes.
const OUStringLiteral sColor
double toRadians(D x)
double toDegrees(D x)
static bool lcl_isLockedCanvas(const uno::Reference< drawing::XShape > &xShape)
static OString lcl_TransparencyToDrawingMlAlpha(const Color &rColor)
EmbeddedObjectRef * pObject
ShapeFlag
@ Variable
Frame is variable in Var-direction.
constexpr sal_Int32 FSNS(sal_Int32 namespc, sal_Int32 element)
constexpr TypedWhichId< SwFlyFrameFormat > RES_FLYFRMFMT(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