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)).getStr());
857 attrList->add(XML_distB, OString::number(TwipsToEMU(nDistB)).getStr());
858 attrList->add(XML_distL, OString::number(TwipsToEMU(nDistL)).getStr());
859 attrList->add(XML_distR, OString::number(TwipsToEMU(nDistR)).getStr());
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 if (bLclInTabCell)
874 attrList->add(XML_layoutInCell, "1");
875 else
876 attrList->add(XML_layoutInCell, "0");
877
878 bool bAllowOverlap = pFrameFormat->GetWrapInfluenceOnObjPos().GetAllowOverlap();
879 attrList->add(XML_allowOverlap, bAllowOverlap ? "1" : "0");
880
881 if (pObj)
882 // It seems 0 and 1 have special meaning: just start counting from 2 to avoid issues with that.
883 attrList->add(XML_relativeHeight, OString::number(pObj->GetOrdNum() + 2));
884 else
885 // relativeHeight is mandatory attribute, if value is not present, we must write default value
886 attrList->add(XML_relativeHeight, "0");
887
888 if (pObj)
889 {
890 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
891 if (!sAnchorId.isEmpty())
892 attrList->addNS(XML_wp14, XML_anchorId,
893 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
894 }
895
896 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_anchor, attrList);
897
898 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_simplePos, XML_x, "0", XML_y,
899 "0"); // required, unused
900
901 // Position is either determined by coordinates aPos or alignment keywords like 'center'.
902 // First prepare them.
903 awt::Point aPos(pFrameFormat->GetHoriOrient().GetPos(),
904 pFrameFormat->GetVertOrient().GetPos());
905
906 aPos.X += nPosXDiff; // Make the postponed position move of frames.
907 aPos.Y += nPosYDiff;
908 if (pObj && lcl_IsRotateAngleValid(*pObj)
909 && pObj->GetObjIdentifier() != SdrObjKind::SwFlyDrawObjIdentifier)
910 lclMovePositionWithRotation(aPos, rSize, pObj->GetRotateAngle());
911
912 const char* relativeFromH;
913 const char* relativeFromV;
914 const char* alignH = nullptr;
915 const char* alignV = nullptr;
916 switch (pFrameFormat->GetVertOrient().GetRelationOrient())
917 {
918 case text::RelOrientation::PAGE_PRINT_AREA:
919 relativeFromV = "margin";
920 break;
921 case text::RelOrientation::PAGE_PRINT_AREA_TOP:
922 relativeFromV = "topMargin";
923 break;
924 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
925 relativeFromV = "bottomMargin";
926 break;
927 case text::RelOrientation::PAGE_FRAME:
928 relativeFromV = "page";
929 break;
930 case text::RelOrientation::FRAME:
931 relativeFromV = "paragraph";
932 break;
933 case text::RelOrientation::TEXT_LINE:
934 default:
935 relativeFromV = "line";
936 break;
937 }
938 switch (pFrameFormat->GetVertOrient().GetVertOrient())
939 {
940 case text::VertOrientation::TOP:
941 case text::VertOrientation::CHAR_TOP:
942 case text::VertOrientation::LINE_TOP:
943 if (pFrameFormat->GetVertOrient().GetRelationOrient()
944 == text::RelOrientation::TEXT_LINE)
945 alignV = "bottom";
946 else
947 alignV = "top";
948 break;
949 case text::VertOrientation::BOTTOM:
950 case text::VertOrientation::CHAR_BOTTOM:
951 case text::VertOrientation::LINE_BOTTOM:
952 if (pFrameFormat->GetVertOrient().GetRelationOrient()
953 == text::RelOrientation::TEXT_LINE)
954 alignV = "top";
955 else
956 alignV = "bottom";
957 break;
958 case text::VertOrientation::CENTER:
959 case text::VertOrientation::CHAR_CENTER:
960 case text::VertOrientation::LINE_CENTER:
961 alignV = "center";
962 break;
963 default:
964 break;
965 }
966 switch (pFrameFormat->GetHoriOrient().GetRelationOrient())
967 {
968 case text::RelOrientation::PAGE_PRINT_AREA:
969 relativeFromH = "margin";
970 break;
971 case text::RelOrientation::PAGE_FRAME:
972 relativeFromH = "page";
973 break;
974 case text::RelOrientation::CHAR:
975 relativeFromH = "character";
976 break;
977 case text::RelOrientation::PAGE_RIGHT:
978 relativeFromH = "rightMargin";
979 break;
980 case text::RelOrientation::PAGE_LEFT:
981 relativeFromH = "leftMargin";
982 break;
983 case text::RelOrientation::FRAME:
984 default:
985 relativeFromH = "column";
986 break;
987 }
988 switch (pFrameFormat->GetHoriOrient().GetHoriOrient())
989 {
990 case text::HoriOrientation::LEFT:
991 alignH = "left";
992 break;
993 case text::HoriOrientation::RIGHT:
994 alignH = "right";
995 break;
996 case text::HoriOrientation::CENTER:
997 alignH = "center";
998 break;
999 case text::HoriOrientation::INSIDE:
1000 alignH = "inside";
1001 break;
1002 case text::HoriOrientation::OUTSIDE:
1003 alignH = "outside";
1004 break;
1005 default:
1006 break;
1007 }
1008
1009 // write out horizontal position
1010 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionH, XML_relativeFrom,
1011 relativeFromH);
1012
1018 const sal_Int64 MAX_INTEGER_VALUE = SAL_MAX_INT32;
1019 const sal_Int64 MIN_INTEGER_VALUE = SAL_MIN_INT32;
1020
1021 if (alignH != nullptr)
1022 {
1023 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
1024 m_pImpl->getSerializer()->write(alignH);
1025 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
1026 }
1027 else
1028 {
1029 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
1030 sal_Int64 nPosXEMU = TwipsToEMU(aPos.X);
1031
1032 /* Absolute Position Offset Value is of type Int. Hence it should not be greater than
1033 * Maximum value for Int OR Less than the Minimum value for Int.
1034 * - Maximum value for Int = 2147483647
1035 * - Minimum value for Int = -2147483648
1036 *
1037 * As per ECMA Specification : ECMA-376, Second Edition,
1038 * Part 1 - Fundamentals And Markup Language Reference[20.4.3.3 ST_PositionOffset (Absolute Position Offset Value)]
1039 *
1040 * Please refer : http://www.schemacentral.com/sc/xsd/t-xsd_int.html
1041 */
1042
1043 if (nPosXEMU > MAX_INTEGER_VALUE)
1044 {
1045 nPosXEMU = MAX_INTEGER_VALUE;
1046 }
1047 else if (nPosXEMU < MIN_INTEGER_VALUE)
1048 {
1049 nPosXEMU = MIN_INTEGER_VALUE;
1050 }
1051 m_pImpl->getSerializer()->write(nPosXEMU);
1052 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
1053 }
1054 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionH);
1055
1056 // write out vertical position
1057 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_positionV, XML_relativeFrom,
1058 relativeFromV);
1059 sal_Int64 nPosYEMU = TwipsToEMU(aPos.Y);
1060
1061 // tdf#93675, 0 below line/paragraph and/or top line/paragraph with
1062 // wrap top+bottom or other wraps is affecting the line directly
1063 // above the anchor line, which seems odd, but a tiny adjustment
1064 // here to bring the top down convinces msoffice to wrap like us
1065 if (nPosYEMU == 0
1066 && (strcmp(relativeFromV, "line") == 0 || strcmp(relativeFromV, "paragraph") == 0)
1067 && (!alignV || strcmp(alignV, "top") == 0))
1068 {
1069 alignV = nullptr;
1070 nPosYEMU = TwipsToEMU(1);
1071 }
1072
1073 if (alignV != nullptr)
1074 {
1075 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_align);
1076 m_pImpl->getSerializer()->write(alignV);
1077 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_align);
1078 }
1079 else
1080 {
1081 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_posOffset);
1082 if (nPosYEMU > MAX_INTEGER_VALUE)
1083 {
1084 nPosYEMU = MAX_INTEGER_VALUE;
1085 }
1086 else if (nPosYEMU < MIN_INTEGER_VALUE)
1087 {
1088 nPosYEMU = MIN_INTEGER_VALUE;
1089 }
1090 m_pImpl->getSerializer()->write(nPosYEMU);
1091 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_posOffset);
1092 }
1093 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_positionV);
1094 }
1095 else // inline
1096 {
1097 // nDist is forced to zero above and ignored by Word anyway, so write 0 directly.
1100 aAttrList->add(XML_distT, OString::number(0).getStr());
1101 aAttrList->add(XML_distB, OString::number(0).getStr());
1102 aAttrList->add(XML_distL, OString::number(0).getStr());
1103 aAttrList->add(XML_distR, OString::number(0).getStr());
1104 if (pObj)
1105 {
1106 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObj);
1107 if (!sAnchorId.isEmpty())
1108 aAttrList->addNS(XML_wp14, XML_anchorId,
1109 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
1110 }
1111 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_inline, aAttrList);
1112 }
1113
1114 // now the common parts 'extent' and 'effectExtent'
1132 sal_uInt64 cx = TwipsToEMU(
1133 std::clamp(rSize.Width() + nWidthDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
1134 OString aWidth(OString::number(std::min(cx, sal_uInt64(SAL_MAX_INT32))));
1135 sal_uInt64 cy = TwipsToEMU(
1136 std::clamp(rSize.Height() + nHeightDiff, tools::Long(0), tools::Long(SAL_MAX_INT32)));
1137 OString aHeight(OString::number(std::min(cy, sal_uInt64(SAL_MAX_INT32))));
1138
1139 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_extent, XML_cx, aWidth, XML_cy, aHeight);
1140
1141 // XML_effectExtent, includes effects, fat stroke and rotation
1142 // FixMe: tdf141880. Because LibreOffice currently cannot handle negative vertical margins, they
1143 // were forced to zero on import. Especially bottom margin of inline anchored rotated objects are
1144 // affected. If the object was not changed, it would be better to export the original values
1145 // from grab-Bag. Unfortunately there exists no marker for "not changed", so a heuristic is used
1146 // here: If current left, top and right margins do not differ more than 1Hmm = 635EMU from the
1147 // values in grab-Bag, it is likely, that the object was not changed and we restore the values
1148 // from grab-Bag.
1149 sal_Int64 nLeftExtEMU = TwipsToEMU(nLeftExt);
1150 sal_Int64 nTopExtEMU = TwipsToEMU(nTopExt);
1151 sal_Int64 nRightExtEMU = TwipsToEMU(nRightExt);
1152 sal_Int64 nBottomExtEMU = TwipsToEMU(nBottomExt);
1153 if (pObj)
1154 {
1155 uno::Any aAny;
1156 pObj->GetGrabBagItem(aAny);
1157 comphelper::SequenceAsHashMap aGrabBag(aAny);
1158 auto it = aGrabBag.find("CT_EffectExtent");
1159 if (it != aGrabBag.end())
1160 {
1161 comphelper::SequenceAsHashMap aEffectExtent(it->second);
1162 sal_Int64 nLeftExtGrabBag(0);
1163 sal_Int64 nTopExtGrabBag(0);
1164 sal_Int64 nRightExtGrabBag(0);
1165 sal_Int64 nBottomExtGrabBag(0);
1166 for (const std::pair<const comphelper::OUStringAndHashCode, uno::Any>& rDirection :
1167 aEffectExtent)
1168 {
1169 const OUString& rName = rDirection.first.maString;
1170 if (rName == "l" && rDirection.second.has<sal_Int32>())
1171 nLeftExtGrabBag = rDirection.second.get<sal_Int32>();
1172 else if (rName == "t" && rDirection.second.has<sal_Int32>())
1173 nTopExtGrabBag = rDirection.second.get<sal_Int32>();
1174 else if (rName == "r" && rDirection.second.has<sal_Int32>())
1175 nRightExtGrabBag = rDirection.second.get<sal_Int32>();
1176 else if (rName == "b" && rDirection.second.has<sal_Int32>())
1177 nBottomExtGrabBag = rDirection.second.get<sal_Int32>();
1178 }
1179 if (abs(nLeftExtEMU - nLeftExtGrabBag) <= 635 && abs(nTopExtEMU - nTopExtGrabBag) <= 635
1180 && abs(nRightExtEMU - nRightExtGrabBag) <= 635)
1181 {
1182 nLeftExtEMU = nLeftExtGrabBag;
1183 nTopExtEMU = nTopExtGrabBag;
1184 nRightExtEMU = nRightExtGrabBag;
1185 nBottomExtEMU = nBottomExtGrabBag;
1186 }
1187 }
1188 }
1189 m_pImpl->getSerializer()->singleElementNS(
1190 XML_wp, XML_effectExtent, XML_l, OString::number(nLeftExtEMU), XML_t,
1191 OString::number(nTopExtEMU), XML_r, OString::number(nRightExtEMU), XML_b,
1192 OString::number(nBottomExtEMU));
1193
1194 if (!isAnchor)
1195 return; // OOXML 'inline' has not wrap type at all
1196
1197 // XML_anchor has exact one of types wrapNone, wrapSquare, wrapTight, wrapThrough and
1198 // WrapTopAndBottom. Map our own types to them as far as possible.
1199
1200 if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGH
1201 || pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_THROUGHT)
1202 {
1203 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapNone);
1204 return;
1205 }
1206
1207 if (pFrameFormat->GetSurround().GetValue() == css::text::WrapTextMode_NONE)
1208 {
1209 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapTopAndBottom);
1210 return;
1211 }
1212
1213 // All remaining cases need attribute XML_wrapText
1214 OUString sWrapType;
1215 switch (pFrameFormat->GetSurround().GetSurround())
1216 {
1217 case text::WrapTextMode_DYNAMIC:
1218 sWrapType = OUString("largest");
1219 break;
1220 case text::WrapTextMode_LEFT:
1221 sWrapType = OUString("left");
1222 break;
1223 case text::WrapTextMode_RIGHT:
1224 sWrapType = OUString("right");
1225 break;
1226 case text::WrapTextMode_PARALLEL:
1227 default:
1228 sWrapType = OUString("bothSides");
1229 break;
1230 }
1231
1232 // ToDo: Exclude cases where LibreOffice wrap without contour is different
1233 // from Word XML_wrapSquare or where direct use of distances not possible and workaround
1234 // will be done using wrapPolygon.
1235 // ToDo: handle case Writer frame, where contour can be set in LibreOffice but is not rendered.
1236
1237 // This case needs no wrapPolygon
1238 if (!pFrameFormat->GetSurround().IsContour())
1239 {
1240 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_wrapSquare, XML_wrapText, sWrapType);
1241 return;
1242 }
1243
1244 // Contour wrap.
1245 sal_Int32 nWrapToken
1246 = pFrameFormat->GetSurround().IsOutside() ? XML_wrapTight : XML_wrapThrough;
1247
1248 // ToDo: cases where wrapPolygon is used as workaround.
1249
1250 // Own wrap polygon exists only for TextGraphicObject and TextEmbeddedObject. It might be edited
1251 // by user. If such exists, we use it and we are done.
1252 if (const SwNoTextNode* pNd = sw::util::GetNoTextNodeFromSwFrameFormat(*pFrameFormat))
1253 {
1254 const tools::PolyPolygon* pPolyPoly = pNd->HasContour();
1255 if (pPolyPoly && pPolyPoly->Count())
1256 {
1257 tools::Polygon aPoly
1258 = sw::util::CorrectWordWrapPolygonForExport(*pPolyPoly, pNd, /*bCorrectCrop=*/true);
1259 if (aPoly.GetSize() >= 3)
1260 {
1261 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText,
1262 sWrapType);
1263 // ToDo: Test whether XML_edited true or false gives better results.
1264 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1265 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
1266 OString::number(aPoly[0].X()), XML_y,
1267 OString::number(aPoly[0].Y()));
1268 for (sal_uInt16 i = 1; i < aPoly.GetSize(); ++i)
1269 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_lineTo, XML_x,
1270 OString::number(aPoly[i].X()), XML_y,
1271 OString::number(aPoly[i].Y()));
1272 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1273
1274 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1275 return;
1276 }
1277 }
1278 }
1279
1280 // If this shape comes from ooxml import, there might be a wrap polygon in InteropGrabBag.
1281 // Wrap polygons can be edited by users in Word. They are independent from changing shape size or
1282 // rotation. So it is likely, that it is still usable.
1283 if (pObj)
1284 {
1285 uno::Any aAny;
1286 pObj->GetGrabBagItem(aAny);
1287 comphelper::SequenceAsHashMap aGrabBag(aAny);
1288 auto it = aGrabBag.find("CT_WrapPath");
1289 if (it != aGrabBag.end())
1290 {
1291 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
1292
1293 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1294 auto aSeqSeq = it->second.get<drawing::PointSequenceSequence>();
1295 const auto& rPoints = aSeqSeq[0];
1296 for (auto i = rPoints.begin(); i != rPoints.end(); ++i)
1297 {
1298 const awt::Point& rPoint = *i;
1299 m_pImpl->getSerializer()->singleElementNS(
1300 XML_wp, (i == rPoints.begin() ? XML_start : XML_lineTo), XML_x,
1301 OString::number(rPoint.X), XML_y, OString::number(rPoint.Y));
1302 }
1303 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1304
1305 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1306 return;
1307 }
1308 }
1309
1310 // In this case we likely had an odt document to be exported to docx. ODF does not know the
1311 // concept of a wrap polygon and LibreOffice has no one internally. So as a workaround, we
1312 // generate a wrap polygon from the shape geometry.
1313 tools::Polygon aContour = lcl_CreateContourPolygon(const_cast<SdrObject*>(pObj));
1314
1315 // lcl_CreateContourPolygon() ensures at least three points
1316 m_pImpl->getSerializer()->startElementNS(XML_wp, nWrapToken, XML_wrapText, sWrapType);
1317
1318 // ToDo: Test whether XML_edited true or false gives better results.
1319 m_pImpl->getSerializer()->startElementNS(XML_wp, XML_wrapPolygon, XML_edited, "0");
1320 m_pImpl->getSerializer()->singleElementNS(XML_wp, XML_start, XML_x,
1321 OString::number(aContour.GetPoint(0).getX()), XML_y,
1322 OString::number(aContour.GetPoint(0).getY()));
1323 for (sal_uInt32 i = 1; i < aContour.GetSize(); i++)
1324 m_pImpl->getSerializer()->singleElementNS(
1325 XML_wp, XML_lineTo, XML_x, OString::number(aContour.GetPoint(i).getX()), XML_y,
1326 OString::number(aContour.GetPoint(i).getY()));
1327 m_pImpl->getSerializer()->endElementNS(XML_wp, XML_wrapPolygon);
1328
1329 m_pImpl->getSerializer()->endElementNS(XML_wp, nWrapToken);
1330}
1331
1333{
1334 bool isAnchor;
1335 if (m_pImpl->getFlyFrameGraphic())
1336 {
1337 isAnchor = false; // end Inline Graphic object inside DMLTextFrame
1338 }
1339 else
1340 {
1341 isAnchor = pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR;
1342 }
1343 m_pImpl->getSerializer()->endElementNS(XML_wp, isAnchor ? XML_anchor : XML_inline);
1344
1345 m_pImpl->getSerializer()->endElementNS(XML_w, XML_drawing);
1346 m_pImpl->setDrawingOpen(false);
1347}
1348
1349void DocxSdrExport::writeVMLDrawing(const SdrObject* sdrObj, const SwFrameFormat& rFrameFormat)
1350{
1351 m_pImpl->getSerializer()->startElementNS(XML_w, XML_pict);
1352 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
1353 // See WinwordAnchoring::SetAnchoring(), these are not part of the SdrObject, have to be passed around manually.
1354
1355 SwFormatFollowTextFlow const& rFlow(rFrameFormat.GetFollowTextFlow());
1356 const SwFormatHoriOrient& rHoriOri = rFrameFormat.GetHoriOrient();
1357 const SwFormatVertOrient& rVertOri = rFrameFormat.GetVertOrient();
1358 SwFormatSurround const& rSurround(rFrameFormat.GetSurround());
1359
1361 m_pImpl->getExport().VMLExporter().AddSdrObject(
1362 *sdrObj, rFlow.GetValue(), rHoriOri.GetHoriOrient(), rVertOri.GetVertOrient(),
1363 rHoriOri.GetRelationOrient(), rVertOri.GetRelationOrient(), pAttrList.get(), true);
1364 m_pImpl->getSerializer()->endElementNS(XML_w, XML_pict);
1365}
1366
1367static bool lcl_isLockedCanvas(const uno::Reference<drawing::XShape>& xShape)
1368{
1369 const uno::Sequence<beans::PropertyValue> propList = lclGetProperty(xShape, "InteropGrabBag");
1370 /*
1371 * Export as Locked Canvas only if the property
1372 * is in the PropertySet
1373 */
1374 return std::any_of(propList.begin(), propList.end(), [](const beans::PropertyValue& rProp) {
1375 return rProp.Name == "LockedCanvas";
1376 });
1377}
1378
1379void DocxSdrExport::writeDMLDrawing(const SdrObject* pSdrObject, const SwFrameFormat* pFrameFormat,
1380 int nAnchorId)
1381{
1382 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pSdrObject)->getUnoShape());
1383 if (!Impl::isSupportedDMLShape(xShape, pSdrObject))
1384 return;
1385
1386 m_pImpl->getExport().DocxAttrOutput().GetSdtEndBefore(pSdrObject);
1387
1388 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1389 Size aSize(pSdrObject->GetLogicRect().getOpenWidth(),
1390 pSdrObject->GetLogicRect().getOpenHeight());
1391 startDMLAnchorInline(pFrameFormat, aSize);
1392
1395 pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr());
1396 pDocPrAttrList->add(XML_name, OUStringToOString(pSdrObject->GetName(), RTL_TEXTENCODING_UTF8));
1397 if (!pSdrObject->GetTitle().isEmpty())
1398 pDocPrAttrList->add(XML_title,
1399 OUStringToOString(pSdrObject->GetTitle(), RTL_TEXTENCODING_UTF8));
1400 if (!pSdrObject->GetDescription().isEmpty())
1401 pDocPrAttrList->add(XML_descr,
1402 OUStringToOString(pSdrObject->GetDescription(), RTL_TEXTENCODING_UTF8));
1403 if (!pSdrObject->IsVisible()
1404 && pFrameFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
1405
1406 pDocPrAttrList->add(XML_hidden, OString::number(1).getStr());
1407
1408 pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
1409 OUString sHyperlink = pSdrObject->getHyperlink();
1410 if (!sHyperlink.isEmpty())
1411 {
1412 OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
1413 pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1416 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1417 FSNS(XML_xmlns, XML_a),
1418 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1419 }
1420 pFS->endElementNS(XML_wp, XML_docPr);
1421
1422 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
1423 const char* pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
1424 if (xServiceInfo->supportsService("com.sun.star.drawing.GroupShape"))
1425 pNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup";
1426 else if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1427 pNamespace = "http://schemas.openxmlformats.org/drawingml/2006/picture";
1428 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1429 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1430 pFS->startElementNS(XML_a, XML_graphicData, XML_uri, pNamespace);
1431
1432 bool bLockedCanvas = lcl_isLockedCanvas(xShape);
1433 if (bLockedCanvas)
1434 pFS->startElementNS(
1435 XML_lc, XML_lockedCanvas, FSNS(XML_xmlns, XML_lc),
1436 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dmlLockedCanvas)));
1437
1438 m_pImpl->getExport().OutputDML(xShape);
1439
1440 if (bLockedCanvas)
1441 pFS->endElementNS(XML_lc, XML_lockedCanvas);
1442 pFS->endElementNS(XML_a, XML_graphicData);
1443 pFS->endElementNS(XML_a, XML_graphic);
1444
1445 // Relative size of the drawing.
1446 if (pSdrObject->GetRelativeWidth())
1447 {
1448 // At the moment drawinglayer objects are always relative from page.
1449 OUString sValue;
1450 switch (pSdrObject->GetRelativeWidthRelation())
1451 {
1452 case text::RelOrientation::FRAME:
1453 sValue = "margin";
1454 break;
1455 case text::RelOrientation::PAGE_LEFT:
1456 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
1457 sValue = "outsideMargin";
1458 else
1459 sValue = "leftMargin";
1460 break;
1461 case text::RelOrientation::PAGE_RIGHT:
1462 if (pFrameFormat->GetDoc()->GetPageDesc(0).GetUseOn() == UseOnPage::Mirror)
1463 sValue = "insideMargin";
1464 else
1465 sValue = "rightMargin";
1466 break;
1467 case text::RelOrientation::PAGE_FRAME:
1468 default:
1469 sValue = "page";
1470 break;
1471 }
1472 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom, sValue);
1473 pFS->startElementNS(XML_wp14, XML_pctWidth);
1474 pFS->writeEscaped(
1475 OUString::number(*pSdrObject->GetRelativeWidth() * 100 * oox::drawingml::PER_PERCENT));
1476 pFS->endElementNS(XML_wp14, XML_pctWidth);
1477 pFS->endElementNS(XML_wp14, XML_sizeRelH);
1478 }
1479 if (pSdrObject->GetRelativeHeight())
1480 {
1481 OUString sValue;
1482 switch (pSdrObject->GetRelativeHeightRelation())
1483 {
1484 case text::RelOrientation::FRAME:
1485 sValue = "margin";
1486 break;
1487 case text::RelOrientation::PAGE_PRINT_AREA:
1488 sValue = "topMargin";
1489 break;
1490 case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
1491 sValue = "bottomMargin";
1492 break;
1493 case text::RelOrientation::PAGE_FRAME:
1494 default:
1495 sValue = "page";
1496 break;
1497 }
1498 pFS->startElementNS(XML_wp14, XML_sizeRelV, XML_relativeFrom, sValue);
1499 pFS->startElementNS(XML_wp14, XML_pctHeight);
1500 pFS->writeEscaped(
1501 OUString::number(*pSdrObject->GetRelativeHeight() * 100 * oox::drawingml::PER_PERCENT));
1502 pFS->endElementNS(XML_wp14, XML_pctHeight);
1503 pFS->endElementNS(XML_wp14, XML_sizeRelV);
1504 }
1505
1506 endDMLAnchorInline(pFrameFormat);
1507}
1508
1510{
1511 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1512 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1513 return;
1514
1515 OString aShadowWidth(OString::number(double(aShadowItem.GetWidth()) / 20) + "pt");
1516 OString aOffset;
1517 switch (aShadowItem.GetLocation())
1518 {
1519 case SvxShadowLocation::TopLeft:
1520 aOffset = "-" + aShadowWidth + ",-" + aShadowWidth;
1521 break;
1522 case SvxShadowLocation::TopRight:
1523 aOffset = aShadowWidth + ",-" + aShadowWidth;
1524 break;
1525 case SvxShadowLocation::BottomLeft:
1526 aOffset = "-" + aShadowWidth + "," + aShadowWidth;
1527 break;
1528 case SvxShadowLocation::BottomRight:
1529 aOffset = aShadowWidth + "," + aShadowWidth;
1530 break;
1531 case SvxShadowLocation::NONE:
1532 case SvxShadowLocation::End:
1533 break;
1534 }
1535 if (aOffset.isEmpty())
1536 return;
1537
1538 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1539 m_pSerializer->singleElementNS(XML_v, XML_shadow, XML_on, "t", XML_color, "#" + aShadowColor,
1540 XML_offset, aOffset);
1541}
1542
1543bool DocxSdrExport::Impl::isSupportedDMLShape(const uno::Reference<drawing::XShape>& xShape,
1544 const SdrObject* pSdrObject)
1545{
1546 uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
1547 if (xServiceInfo->supportsService("com.sun.star.drawing.PolyPolygonShape")
1548 || xServiceInfo->supportsService("com.sun.star.drawing.PolyLineShape"))
1549 return false;
1550
1551 uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY);
1552 // For signature line shapes, we don't want DML, just the VML shape.
1553 if (xServiceInfo->supportsService("com.sun.star.drawing.GraphicObjectShape"))
1554 {
1555 bool bIsSignatureLineShape = false;
1556 xShapeProperties->getPropertyValue("IsSignatureLine") >>= bIsSignatureLineShape;
1557 if (bIsSignatureLineShape)
1558 return false;
1559 }
1560
1561 // A FontWork shape with bitmap fill cannot be expressed as a modern 'abc transform'
1562 // in Word. Only the legacy VML WordArt allows bitmap fill.
1563 if (pSdrObject->IsTextPath())
1564 {
1565 css::drawing::FillStyle eFillStyle = css::drawing::FillStyle_SOLID;
1566 xShapeProperties->getPropertyValue("FillStyle") >>= eFillStyle;
1567 if (eFillStyle == css::drawing::FillStyle_BITMAP)
1568 return false;
1569 }
1570 return true;
1571}
1572
1574 const SwFrameFormat& rFrameFormat, int nAnchorId)
1575{
1576 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1577 m_pImpl->setDMLAndVMLDrawingOpen(true);
1578
1579 // Depending on the shape type, we actually don't write the shape as DML.
1580 OUString sShapeType;
1581 ShapeFlag nMirrorFlags = ShapeFlag::NONE;
1582 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObj)->getUnoShape());
1583
1584 MSO_SPT eShapeType
1585 = EscherPropertyContainer::GetCustomShapeType(xShape, nMirrorFlags, sShapeType);
1586
1587 // In case we are already inside a DML block, then write the shape only as VML, turn out that's allowed to do.
1588 // A common service created in util to check for VML shapes which are allowed to have textbox in content
1589 if ((msfilter::util::HasTextBoxContent(eShapeType)) && Impl::isSupportedDMLShape(xShape, sdrObj)
1590 && (!bDMLAndVMLDrawingOpen || lcl_isLockedCanvas(xShape))) // Locked canvas is OK inside DML
1591 {
1592 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_AlternateContent);
1593
1594 auto pObjGroup = dynamic_cast<const SdrObjGroup*>(sdrObj);
1595 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Choice, XML_Requires,
1596 (pObjGroup ? "wpg" : "wps"));
1597 writeDMLDrawing(sdrObj, &rFrameFormat, nAnchorId);
1598 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Choice);
1599
1600 m_pImpl->getSerializer()->startElementNS(XML_mc, XML_Fallback);
1601 writeVMLDrawing(sdrObj, rFrameFormat);
1602 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_Fallback);
1603
1604 m_pImpl->getSerializer()->endElementNS(XML_mc, XML_AlternateContent);
1605 }
1606 else
1607 writeVMLDrawing(sdrObj, rFrameFormat);
1608
1609 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
1610}
1611
1612// Converts ARGB transparency (0..255) to drawingml alpha (opposite, and 0..100000)
1613static OString lcl_TransparencyToDrawingMlAlpha(const Color& rColor)
1614{
1615 if (rColor.IsTransparent())
1616 {
1617 sal_Int32 nAlphaPercent = float(rColor.GetAlpha()) / 2.55;
1618 return OString::number(nAlphaPercent * oox::drawingml::PER_PERCENT);
1619 }
1620
1621 return OString();
1622}
1623
1625{
1626 const SvxShadowItem& aShadowItem = rFrameFormat.GetShadow();
1627
1628 // Output effects
1629 if (aShadowItem.GetLocation() == SvxShadowLocation::NONE)
1630 return;
1631
1632 // Distance is measured diagonally from corner
1633 double nShadowDist
1634 = sqrt(static_cast<double>(aShadowItem.GetWidth()) * aShadowItem.GetWidth() * 2.0);
1635 OString aShadowDist(OString::number(TwipsToEMU(nShadowDist)));
1636 OString aShadowColor = msfilter::util::ConvertColor(aShadowItem.GetColor());
1637 OString aShadowAlpha = lcl_TransparencyToDrawingMlAlpha(aShadowItem.GetColor());
1638 sal_uInt32 nShadowDir = 0;
1639 switch (aShadowItem.GetLocation())
1640 {
1641 case SvxShadowLocation::TopLeft:
1642 nShadowDir = 13500000;
1643 break;
1644 case SvxShadowLocation::TopRight:
1645 nShadowDir = 18900000;
1646 break;
1647 case SvxShadowLocation::BottomLeft:
1648 nShadowDir = 8100000;
1649 break;
1650 case SvxShadowLocation::BottomRight:
1651 nShadowDir = 2700000;
1652 break;
1653 case SvxShadowLocation::NONE:
1654 case SvxShadowLocation::End:
1655 break;
1656 }
1657 OString aShadowDir(OString::number(nShadowDir));
1658
1659 m_pImpl->getSerializer()->startElementNS(XML_a, XML_effectLst);
1660 m_pImpl->getSerializer()->startElementNS(XML_a, XML_outerShdw, XML_dist, aShadowDist, XML_dir,
1661 aShadowDir);
1662 if (aShadowAlpha.isEmpty())
1663 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1664 else
1665 {
1666 m_pImpl->getSerializer()->startElementNS(XML_a, XML_srgbClr, XML_val, aShadowColor);
1667 m_pImpl->getSerializer()->singleElementNS(XML_a, XML_alpha, XML_val, aShadowAlpha);
1668 m_pImpl->getSerializer()->endElementNS(XML_a, XML_srgbClr);
1669 }
1670 m_pImpl->getSerializer()->endElementNS(XML_a, XML_outerShdw);
1671 m_pImpl->getSerializer()->endElementNS(XML_a, XML_effectLst);
1672}
1673
1674void DocxSdrExport::writeDiagram(const SdrObject* sdrObject, const SwFrameFormat& rFrameFormat,
1675 int nDiagramId)
1676{
1677 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(sdrObject)->getUnoShape(),
1678 uno::UNO_QUERY);
1679
1680 // write necessary tags to document.xml
1681 Size aSize(sdrObject->GetSnapRect().getOpenWidth(), sdrObject->GetSnapRect().getOpenHeight());
1682 startDMLAnchorInline(&rFrameFormat, aSize);
1683
1684 m_pImpl->getDrawingML()->SetFS(m_pImpl->getSerializer());
1685 m_pImpl->getDrawingML()->WriteDiagram(xShape, nDiagramId);
1686
1687 endDMLAnchorInline(&rFrameFormat);
1688}
1689
1691{
1692 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1693 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1694
1695 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1696 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1697
1698 //Save data here and restore when out of scope
1699 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1700
1702 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1703 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
1704 m_pImpl->getExport().WriteText();
1705}
1706
1708{
1709 const editeng::SvxBorderLine* pBorderLine = nullptr;
1710
1711 if (rBox.GetTop())
1712 {
1713 pBorderLine = rBox.GetTop();
1714 }
1715 else if (rBox.GetLeft())
1716 {
1717 pBorderLine = rBox.GetLeft();
1718 }
1719 else if (rBox.GetBottom())
1720 {
1721 pBorderLine = rBox.GetBottom();
1722 }
1723 else if (rBox.GetRight())
1724 {
1725 pBorderLine = rBox.GetRight();
1726 }
1727
1728 if (!pBorderLine)
1729 {
1730 return;
1731 }
1732
1733 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1734 if (pBorderLine->GetWidth() == SvxBorderLineWidth::Hairline)
1735 pFS->startElementNS(XML_a, XML_ln);
1736 else
1737 {
1738 double fConverted(editeng::ConvertBorderWidthToWord(pBorderLine->GetBorderLineStyle(),
1739 pBorderLine->GetWidth()));
1740 OString sWidth(OString::number(TwipsToEMU(fConverted)));
1741 pFS->startElementNS(XML_a, XML_ln, XML_w, sWidth);
1742 }
1743
1744 pFS->startElementNS(XML_a, XML_solidFill);
1745 OString sColor(msfilter::util::ConvertColor(pBorderLine->GetColor()));
1746 pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
1747 pFS->endElementNS(XML_a, XML_solidFill);
1748
1749 if (SvxBorderLineStyle::DASHED == pBorderLine->GetBorderLineStyle()) // Line Style is Dash type
1750 pFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
1751
1752 pFS->endElementNS(XML_a, XML_ln);
1753}
1754
1755void DocxSdrExport::writeDMLTextFrame(ww8::Frame const* pParentFrame, int nAnchorId,
1756 bool bTextBoxOnly)
1757{
1758 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
1759 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
1760
1761 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
1762 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
1763 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
1764
1765 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
1766 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
1767
1768 //Save data here and restore when out of scope
1769 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
1770
1771 // When a frame has some low height, but automatically expanded due
1772 // to lots of contents, this size contains the real size.
1773 const Size aSize = pParentFrame->GetSize();
1774
1775 uno::Reference<drawing::XShape> xShape;
1776 const SdrObject* pSdrObj = rFrameFormat.FindRealSdrObject();
1777 if (pSdrObj)
1778 xShape.set(const_cast<SdrObject*>(pSdrObj)->getUnoShape(), uno::UNO_QUERY);
1779 uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
1780 uno::Reference<beans::XPropertySetInfo> xPropSetInfo;
1781 if (xPropertySet.is())
1782 xPropSetInfo = xPropertySet->getPropertySetInfo();
1783
1785 {
1786 drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP;
1787 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("TextVerticalAdjust"))
1788 xPropertySet->getPropertyValue("TextVerticalAdjust") >>= eAdjust;
1789 m_pImpl->getBodyPrAttrList()->add(XML_anchor,
1791 }
1792
1793 if (!bTextBoxOnly)
1794 {
1795 startDMLAnchorInline(&rFrameFormat, aSize);
1796
1799 pDocPrAttrList->add(XML_id, OString::number(nAnchorId).getStr());
1800 pDocPrAttrList->add(XML_name,
1801 OUStringToOString(rFrameFormat.GetName(), RTL_TEXTENCODING_UTF8));
1802
1803 pFS->startElementNS(XML_wp, XML_docPr, pDocPrAttrList);
1804
1805 OUString sHyperlink;
1806 if (xPropertySet.is())
1807 xPropertySet->getPropertyValue("HyperLinkURL") >>= sHyperlink;
1808 if (!sHyperlink.isEmpty())
1809 {
1810 OUString sRelId = m_pImpl->getExport().GetFilter().addRelation(
1811 pFS->getOutputStream(), oox::getRelationship(Relationship::HYPERLINK),
1814 pFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
1815 FSNS(XML_xmlns, XML_a),
1816 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1817 }
1818
1819 pFS->endElementNS(XML_wp, XML_docPr);
1820
1821 pFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
1822 m_pImpl->getExport().GetFilter().getNamespaceURL(OOX_NS(dml)));
1823 pFS->startElementNS(XML_a, XML_graphicData, XML_uri,
1824 "http://schemas.microsoft.com/office/word/2010/wordprocessingShape");
1825 pFS->startElementNS(XML_wps, XML_wsp);
1826 pFS->singleElementNS(XML_wps, XML_cNvSpPr, XML_txBox, "1");
1827
1828 uno::Any aRotation;
1829 m_pImpl->setDMLandVMLTextFrameRotation(0_deg100);
1830 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1831 {
1832 uno::Sequence<beans::PropertyValue> propList;
1833 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1834 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
1835 [](const beans::PropertyValue& rProp) {
1836 return rProp.Name == "mso-rotation-angle";
1837 });
1838 if (pProp != std::cend(propList))
1839 aRotation = pProp->Value;
1840 }
1841 sal_Int32 nTmp;
1842 if (aRotation >>= nTmp)
1843 m_pImpl->getDMLandVMLTextFrameRotation() = Degree100(nTmp);
1844 OString sRotation(OString::number(
1845 oox::drawingml::ExportRotateClockwisify(m_pImpl->getDMLandVMLTextFrameRotation())));
1846 // Shape properties
1847 pFS->startElementNS(XML_wps, XML_spPr);
1848 if (m_pImpl->getDMLandVMLTextFrameRotation())
1849 {
1850 pFS->startElementNS(XML_a, XML_xfrm, XML_rot, sRotation);
1851 }
1852 else
1853 {
1854 pFS->startElementNS(XML_a, XML_xfrm);
1855 }
1856 pFS->singleElementNS(XML_a, XML_off, XML_x, "0", XML_y, "0");
1857 OString aWidth(OString::number(TwipsToEMU(aSize.Width())));
1858 OString aHeight(OString::number(TwipsToEMU(aSize.Height())));
1859 pFS->singleElementNS(XML_a, XML_ext, XML_cx, aWidth, XML_cy, aHeight);
1860 pFS->endElementNS(XML_a, XML_xfrm);
1861 OUString shapeType = "rect";
1862 if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("FrameInteropGrabBag"))
1863 {
1864 uno::Sequence<beans::PropertyValue> propList;
1865 xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= propList;
1866 auto pProp = std::find_if(std::cbegin(propList), std::cend(propList),
1867 [](const beans::PropertyValue& rProp) {
1868 return rProp.Name == "mso-orig-shape-type";
1869 });
1870 if (pProp != std::cend(propList))
1871 pProp->Value >>= shapeType;
1872 }
1873 //Empty shapeType will lead to corruption so to avoid that shapeType is set to default i.e. "rect"
1874 if (shapeType.isEmpty())
1875 shapeType = "rect";
1876
1877 pFS->singleElementNS(XML_a, XML_prstGeom, XML_prst, shapeType);
1878 m_pImpl->setDMLTextFrameSyntax(true);
1879 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
1880 m_pImpl->setDMLTextFrameSyntax(false);
1881 writeDMLEffectLst(rFrameFormat);
1882 pFS->endElementNS(XML_wps, XML_spPr);
1883 }
1884
1885 //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.
1886 if (!m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized)
1887 {
1888 sal_Int32 nSeq = 0;
1889 for (auto& rEntry : m_pImpl->getExport().m_aLinkedTextboxesHelper)
1890 {
1891 //find the start of a textbox chain: has no PREVIOUS link, but does have NEXT link
1892 if (rEntry.second.sPrevChain.isEmpty() && !rEntry.second.sNextChain.isEmpty())
1893 {
1894 //assign this chain a unique ID and start a new sequence
1895 nSeq = 0;
1896 rEntry.second.nId = ++m_pImpl->getExport().m_nLinkedTextboxesChainId;
1897 rEntry.second.nSeq = nSeq;
1898
1899 OUString sCheckForBrokenChains = rEntry.first;
1900
1901 //follow the chain and assign the same id, and incremental sequence numbers.
1902 auto followChainIter
1903 = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(rEntry.second.sNextChain);
1904 while (followChainIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1905 {
1906 //verify that the NEXT textbox also points to me as the PREVIOUS.
1907 // A broken link indicates a leftover remnant that can be ignored.
1908 if (followChainIter->second.sPrevChain != sCheckForBrokenChains)
1909 break;
1910
1911 followChainIter->second.nId = m_pImpl->getExport().m_nLinkedTextboxesChainId;
1912 followChainIter->second.nSeq = ++nSeq;
1913
1914 //empty next chain indicates the end of the linked chain.
1915 if (followChainIter->second.sNextChain.isEmpty())
1916 break;
1917
1918 sCheckForBrokenChains = followChainIter->first;
1919 followChainIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(
1920 followChainIter->second.sNextChain);
1921 }
1922 }
1923 }
1924 m_pImpl->getExport().m_bLinkedTextboxesHelperInitialized = true;
1925 }
1926
1927 m_pImpl->getExport().m_pParentFrame = nullptr;
1928 bool skipTxBxContent = false;
1929 bool isTxbxLinked = false;
1930
1931 OUString sLinkChainName;
1932 if (xPropSetInfo.is())
1933 {
1934 if (xPropSetInfo->hasPropertyByName("LinkDisplayName"))
1935 xPropertySet->getPropertyValue("LinkDisplayName") >>= sLinkChainName;
1936 else if (xPropSetInfo->hasPropertyByName("ChainName"))
1937 xPropertySet->getPropertyValue("ChainName") >>= sLinkChainName;
1938 }
1939
1940 // second, check if THIS textbox is linked and then decide whether to write the tag txbx or linkedTxbx
1941 auto linkedTextboxesIter = m_pImpl->getExport().m_aLinkedTextboxesHelper.find(sLinkChainName);
1942 if (linkedTextboxesIter != m_pImpl->getExport().m_aLinkedTextboxesHelper.end())
1943 {
1944 if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq != 0))
1945 {
1946 //not the first in the chain, so write the tag as linkedTxbx
1947 pFS->singleElementNS(XML_wps, XML_linkedTxbx, XML_id,
1948 OString::number(linkedTextboxesIter->second.nId), XML_seq,
1949 OString::number(linkedTextboxesIter->second.nSeq));
1950 /* no text content should be added to this tag,
1951 since the textbox is linked, the entire content
1952 is written in txbx block
1953 */
1954 skipTxBxContent = true;
1955 }
1956 else if ((linkedTextboxesIter->second.nId != 0) && (linkedTextboxesIter->second.nSeq == 0))
1957 {
1958 /* this is the first textbox in the chaining, we add the text content
1959 to this block*/
1960 //since the text box is linked, it needs an id.
1961 pFS->startElementNS(XML_wps, XML_txbx, XML_id,
1962 OString::number(linkedTextboxesIter->second.nId));
1963 isTxbxLinked = true;
1964 }
1965 }
1966
1967 if (!skipTxBxContent)
1968 {
1969 if (!isTxbxLinked)
1970 pFS->startElementNS(XML_wps, XML_txbx); //text box is not linked, therefore no id.
1971
1972 pFS->startElementNS(XML_w, XML_txbxContent);
1973
1974 const SvxFrameDirectionItem& rDirection = rFrameFormat.GetFrameDir();
1975 if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB)
1976 m_pImpl->getBodyPrAttrList()->add(XML_vert, "eaVert");
1977 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_BT)
1978 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert270");
1979 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_LR_TB)
1980 m_pImpl->getBodyPrAttrList()->add(XML_vert, "mongolianVert");
1981 else if (rDirection.GetValue() == SvxFrameDirection::Vertical_RL_TB90)
1982 m_pImpl->getBodyPrAttrList()->add(XML_vert, "vert");
1983 {
1984 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
1985 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
1986 m_pImpl->getExport().WriteText();
1987 if (m_pImpl->getParagraphSdtOpen())
1988 {
1989 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
1990 m_pImpl->setParagraphSdtOpen(false);
1991 }
1992 }
1993
1994 pFS->endElementNS(XML_w, XML_txbxContent);
1995 pFS->endElementNS(XML_wps, XML_txbx);
1996 }
1997
1998 // We need to init padding to 0, if it's not set.
1999 // In LO the default is 0 and so ins attributes are not set when padding is 0
2000 // but in MSO the default is 254 / 127, so we need to set 0 padding explicitly
2001 if (m_pImpl->getBodyPrAttrList())
2002 {
2003 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_lIns))
2004 m_pImpl->getBodyPrAttrList()->add(XML_lIns, OString::number(0));
2005 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_tIns))
2006 m_pImpl->getBodyPrAttrList()->add(XML_tIns, OString::number(0));
2007 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_rIns))
2008 m_pImpl->getBodyPrAttrList()->add(XML_rIns, OString::number(0));
2009 if (!m_pImpl->getBodyPrAttrList()->hasAttribute(XML_bIns))
2010 m_pImpl->getBodyPrAttrList()->add(XML_bIns, OString::number(0));
2011 }
2012
2013 rtl::Reference<FastAttributeList> xBodyPrAttrList(m_pImpl->getBodyPrAttrList());
2014 m_pImpl->setBodyPrAttrList(nullptr);
2015 if (!bTextBoxOnly)
2016 {
2017 pFS->startElementNS(XML_wps, XML_bodyPr, xBodyPrAttrList);
2018 // AutoSize of the Text Frame.
2019 const SwFormatFrameSize& rSize = rFrameFormat.GetFrameSize();
2020 pFS->singleElementNS(
2021 XML_a,
2022 (rSize.GetHeightSizeType() == SwFrameSize::Variable ? XML_spAutoFit : XML_noAutofit));
2023 pFS->endElementNS(XML_wps, XML_bodyPr);
2024
2025 pFS->endElementNS(XML_wps, XML_wsp);
2026 pFS->endElementNS(XML_a, XML_graphicData);
2027 pFS->endElementNS(XML_a, XML_graphic);
2028
2029 // Relative size of the Text Frame.
2030 const sal_uInt8 nWidthPercent = rSize.GetWidthPercent();
2031 if (nWidthPercent && nWidthPercent != SwFormatFrameSize::SYNCED)
2032 {
2033 pFS->startElementNS(XML_wp14, XML_sizeRelH, XML_relativeFrom,
2034 (rSize.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME
2035 ? "page"
2036 : "margin"));
2037 pFS->startElementNS(XML_wp14, XML_pctWidth);
2038 pFS->writeEscaped(OUString::number(nWidthPercent * oox::drawingml::PER_PERCENT));
2039 pFS->endElementNS(XML_wp14, XML_pctWidth);
2040 pFS->endElementNS(XML_wp14, XML_sizeRelH);
2041 }
2042 const sal_uInt8 nHeightPercent = rSize.GetHeightPercent();
2043 if (nHeightPercent && nHeightPercent != SwFormatFrameSize::SYNCED)
2044 {
2045 pFS->startElementNS(
2046 XML_wp14, XML_sizeRelV, XML_relativeFrom,
2047 (rSize.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME ? "page"
2048 : "margin"));
2049 pFS->startElementNS(XML_wp14, XML_pctHeight);
2050 pFS->writeEscaped(OUString::number(nHeightPercent * oox::drawingml::PER_PERCENT));
2051 pFS->endElementNS(XML_wp14, XML_pctHeight);
2052 pFS->endElementNS(XML_wp14, XML_sizeRelV);
2053 }
2054
2055 endDMLAnchorInline(&rFrameFormat);
2056 }
2057 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
2058}
2059
2060void DocxSdrExport::writeVMLTextFrame(ww8::Frame const* pParentFrame, bool bTextBoxOnly)
2061{
2062 bool bDMLAndVMLDrawingOpen = m_pImpl->getDMLAndVMLDrawingOpen();
2063 m_pImpl->setDMLAndVMLDrawingOpen(IsAnchorTypeInsideParagraph(pParentFrame));
2064
2065 sax_fastparser::FSHelperPtr pFS = m_pImpl->getSerializer();
2066 const SwFrameFormat& rFrameFormat = pParentFrame->GetFrameFormat();
2067 const SwNodeIndex* pNodeIndex = rFrameFormat.GetContent().GetContentIdx();
2068
2069 SwNodeOffset nStt = pNodeIndex ? pNodeIndex->GetIndex() + 1 : SwNodeOffset(0);
2070 SwNodeOffset nEnd = pNodeIndex ? pNodeIndex->GetNode().EndOfSectionIndex() : SwNodeOffset(0);
2071
2072 //Save data here and restore when out of scope
2073 ExportDataSaveRestore aDataGuard(m_pImpl->getExport(), nStt, nEnd, pParentFrame);
2074
2075 // When a frame has some low height, but automatically expanded due
2076 // to lots of contents, this size contains the real size.
2077 const Size aSize = pParentFrame->GetSize();
2078 m_pImpl->setFlyFrameSize(&aSize);
2079
2080 m_pImpl->setTextFrameSyntax(true);
2083 m_pImpl->getTextFrameStyle() = "position:absolute";
2084 if (!bTextBoxOnly)
2085 {
2086 OString sRotation(OString::number(-toDegrees(m_pImpl->getDMLandVMLTextFrameRotation())));
2087 m_pImpl->getExport().SdrExporter().getTextFrameStyle().append(";rotation:" + sRotation);
2088 }
2089 m_pImpl->getExport().OutputFormat(pParentFrame->GetFrameFormat(), false, false, true);
2090 m_pImpl->getFlyAttrList()->add(XML_style, m_pImpl->getTextFrameStyle().makeStringAndClear());
2091
2092 const SdrObject* pObject = pParentFrame->GetFrameFormat().FindRealSdrObject();
2093 if (pObject != nullptr)
2094 {
2095 OUString sAnchorId = lclGetAnchorIdFromGrabBag(pObject);
2096 if (!sAnchorId.isEmpty())
2097 m_pImpl->getFlyAttrList()->addNS(XML_w14, XML_anchorId,
2098 OUStringToOString(sAnchorId, RTL_TEXTENCODING_UTF8));
2099
2100 uno::Reference<drawing::XShape> xShape(const_cast<SdrObject*>(pObject)->getUnoShape(),
2101 uno::UNO_QUERY);
2102 uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
2103 OUString sHyperlink;
2104 if (xShapeProps.is())
2105 xShapeProps->getPropertyValue("HyperLinkURL") >>= sHyperlink;
2106 if (!sHyperlink.isEmpty())
2107 m_pImpl->getFlyAttrList()->add(XML_href,
2108 OUStringToOString(sHyperlink, RTL_TEXTENCODING_UTF8));
2109 }
2110 rtl::Reference<FastAttributeList> xFlyAttrList(m_pImpl->getFlyAttrList());
2111 m_pImpl->getFlyAttrList().clear();
2112 rtl::Reference<FastAttributeList> xTextboxAttrList(m_pImpl->getTextboxAttrList());
2113 m_pImpl->getTextboxAttrList().clear();
2114 m_pImpl->setTextFrameSyntax(false);
2115 m_pImpl->setFlyFrameSize(nullptr);
2116 m_pImpl->getExport().m_pParentFrame = nullptr;
2117
2118 if (!bTextBoxOnly)
2119 {
2120 pFS->startElementNS(XML_w, XML_pict);
2121 pFS->startElementNS(XML_v, XML_rect, xFlyAttrList);
2122 m_pImpl->textFrameShadow(rFrameFormat);
2123 if (m_pImpl->getFlyFillAttrList().is())
2124 {
2125 rtl::Reference<FastAttributeList> xFlyFillAttrList(m_pImpl->getFlyFillAttrList());
2126 pFS->singleElementNS(XML_v, XML_fill, xFlyFillAttrList);
2127 }
2128 if (m_pImpl->getDashLineStyleAttr().is())
2129 {
2130 rtl::Reference<FastAttributeList> xDashLineStyleAttr(m_pImpl->getDashLineStyleAttr());
2131 pFS->singleElementNS(XML_v, XML_stroke, xDashLineStyleAttr);
2132 }
2133 pFS->startElementNS(XML_v, XML_textbox, xTextboxAttrList);
2134 }
2135 m_pImpl->getFlyFillAttrList().clear();
2136 m_pImpl->getDashLineStyleAttr().clear();
2137
2138 pFS->startElementNS(XML_w, XML_txbxContent);
2139 {
2140 ::comphelper::FlagRestorationGuard const g(m_pImpl->m_bFlyFrameGraphic, true);
2141 comphelper::ValueRestorationGuard vg(m_pImpl->getExport().m_nTextTyp, TXT_TXTBOX);
2142 m_pImpl->getExport().WriteText();
2143 if (m_pImpl->getParagraphSdtOpen())
2144 {
2145 m_pImpl->getExport().DocxAttrOutput().EndParaSdtBlock();
2146 m_pImpl->setParagraphSdtOpen(false);
2147 }
2148 }
2149 pFS->endElementNS(XML_w, XML_txbxContent);
2150 if (!bTextBoxOnly)
2151 {
2152 pFS->endElementNS(XML_v, XML_textbox);
2153
2154 if (m_pImpl->getFlyWrapAttrList())
2155 {
2156 rtl::Reference<FastAttributeList> xFlyWrapAttrList(m_pImpl->getFlyWrapAttrList());
2157 m_pImpl->setFlyWrapAttrList(nullptr);
2158 pFS->singleElementNS(XML_w10, XML_wrap, xFlyWrapAttrList);
2159 }
2160
2161 pFS->endElementNS(XML_v, XML_rect);
2162 pFS->endElementNS(XML_w, XML_pict);
2163 }
2164
2165 m_pImpl->setDMLAndVMLDrawingOpen(bDMLAndVMLDrawingOpen);
2166}
2167
2169{
2170 return SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT);
2171}
2172
2173/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
::std::unique_ptr< XmlIdRegistry_Impl > m_pImpl
sal_uInt8 GetAlpha() const
bool IsTransparent() const
The class that does all the actual DOCX export-related work.
Definition: docxexport.hxx:75
void writeVMLDrawing(const SdrObject *sdrObj, const SwFrameFormat &rFrameFormat)
Writes a drawing as VML data.
void endDMLAnchorInline(const SwFrameFormat *pFrameFormat)
void setParagraphSdtOpen(bool bParagraphSdtOpen)
Set if paragraph sdt open in the current drawing.
void writeDMLEffectLst(const SwFrameFormat &rFrameFormat)
Write <a:effectLst>, the effect list.
static bool isTextBox(const SwFrameFormat &rFrameFormat)
Is this a standalone TextFrame, or used as a TextBox of a shape?
rtl::Reference< sax_fastparser::FastAttributeList > & getFlyAttrList()
bool getTextFrameSyntax() const
void writeDMLDrawing(const SdrObject *pSdrObject, const SwFrameFormat *pFrameFormat, int nAnchorId)
Writes a drawing as DML.
bool IsDrawingOpen() const
void writeBoxItemLine(const SvxBoxItem &rBox)
Writes the drawingML <a:ln> markup of a box item.
rtl::Reference< sax_fastparser::FastAttributeList > & getFlyFillAttrList()
bool getDMLTextFrameSyntax() const
void writeVMLTextFrame(ww8::Frame const *pParentFrame, bool bTextBoxOnly=false)
Writes text frame in VML format.
rtl::Reference< sax_fastparser::FastAttributeList > & getTextboxAttrList()
Attributes of the next v:textbox element.
void setFlyWrapAttrList(rtl::Reference< sax_fastparser::FastAttributeList > const &pAttrList)
DocxSdrExport(DocxExport &rExport, const sax_fastparser::FSHelperPtr &pSerializer, oox::drawingml::DrawingML *pDrawingML)
bool IsDMLAndVMLDrawingOpen() const
void writeDiagram(const SdrObject *sdrObject, const SwFrameFormat &rFrameFormat, int nDiagramId)
Writes a diagram (smartart).
void writeOnlyTextOfFrame(ww8::Frame const *pParentFrame)
Writes text from Textbox for <w:framePr>
void writeDMLTextFrame(ww8::Frame const *pParentFrame, int nAnchorId, bool bTextBoxOnly=false)
Writes text frame in DML format.
const Size * getFlyFrameSize() const
When exporting fly frames, this holds the real size of the frame.
bool IsParagraphHasDrawing() const
void setParagraphHasDrawing(bool bParagraphHasDrawing)
sax_fastparser::FastAttributeList * getBodyPrAttrList()
Attributes of <wps:bodyPr>, used during DML export of text frames.
void startDMLAnchorInline(const SwFrameFormat *pFrameFormat, const Size &rSize)
void writeDMLAndVMLDrawing(const SdrObject *sdrObj, const SwFrameFormat &rFrameFormat, int nAnchorId)
Writes shape in both DML and VML format.
rtl::Reference< sax_fastparser::FastAttributeList > & getDashLineStyle()
OStringBuffer & getTextFrameStyle()
std::unique_ptr< Impl > m_pImpl
void setSerializer(const sax_fastparser::FSHelperPtr &pSerializer)
static MSO_SPT GetCustomShapeType(const css::uno::Reference< css::drawing::XShape > &rXShape, ShapeFlag &nMirrorFlags, OUString &rShapeType, bool bOOXML=false)
Helper class, so that the DocxExport::RestoreData() call will always happen.
ExportDataSaveRestore(DocxExport &rExport, SwNodeOffset nStt, SwNodeOffset nEnd, ww8::Frame const *pParentFrame)
virtual SdrLayerID GetHellId() const =0
virtual SdrLayerID GetInvisibleHellId() const =0
virtual void SaveData(SwNodeOffset nStt, SwNodeOffset nEnd)
Remember some of the members so that we can recurse in WriteText().
Definition: wrtww8.cxx:1893
virtual void RestoreData()
Restore what was saved in SaveData().
Definition: wrtww8.cxx:1925
const ww8::Frame * m_pParentFrame
Definition: wrtww8.hxx:520
virtual Degree100 GetRotateAngle() const
const OUString & getHyperlink() const
const double * GetRelativeHeight() const
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:163
const SwPageDesc & GetPageDesc(const size_t i) const
Definition: doc.hxx:890
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:94
const SwDoc * GetDoc() const
The document is set in SwAttrPool now, therefore you always can access it.
Definition: format.hxx:139
const SwFormatWrapInfluenceOnObjPos & GetWrapInfluenceOnObjPos(bool=true) const
const SvxOpaqueItem & GetOpaque(bool=true) const
Definition: frmatr.hxx:80
const SwFormatFrameSize & GetFrameSize(bool=true) const
Definition: fmtfsize.hxx:104
const SvxLRSpaceItem & GetLRSpace(bool=true) const
Definition: frmatr.hxx:74
const OUString & GetName() const
Definition: format.hxx:131
const SwFormatVertOrient & GetVertOrient(bool=true) const
Definition: fmtornt.hxx:113
const SvxShadowItem & GetShadow(bool=true) const
Definition: frmatr.hxx:88
const SwFormatFollowTextFlow & GetFollowTextFlow(bool=true) const
const SwFormatAnchor & GetAnchor(bool=true) const
Definition: fmtanchr.hxx: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:76
const SwFormatContent & GetContent(bool=true) const
Definition: fmtcntnt.hxx:55
Style of a layout element.
Definition: frmfmt.hxx:62
SwRect FindLayoutRect(const bool bPrtArea=false, const Point *pPoint=nullptr) const
Definition: atrfrm.cxx:2742
SdrObject * FindRealSdrObject()
Definition: atrfrm.cxx:2795
Layout frame for SwNoTextNode, i.e. graphics and OLE nodes (including charts).
Definition: ndnotxt.hxx:30
Marks a node in the document model.
Definition: ndindex.hxx:31
SwNode & GetNode() const
Definition: ndindex.hxx:136
SwNodeOffset GetIndex() const
Definition: ndindex.hxx:171
SwNodeOffset EndOfSectionIndex() const
Definition: node.hxx: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(156)
MSO_SPT
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
rtl::Reference< FastAttributeList > SurroundToVMLWrap(SwFormatSurround const &rSurround)
double ConvertBorderWidthToWord(SvxBorderLineStyle, double)
int i
OString ConvertColor(const Color &rColor)
bool HasTextBoxContent(sal_uInt32 nShapeType)
TextVerticalAdjust GetTextVerticalAdjust(sal_Int32 nToken)
sal_Int32 ExportRotateClockwisify(Degree100 input)
const sal_Int32 PER_PERCENT
OUString getRelationship(Relationship eRelationship)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
std::shared_ptr< FastSerializerHelper > FSHelperPtr
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
tools::Polygon PolygonFromPolyPolygon(const tools::PolyPolygon &rPolyPoly)
Make a best fit Polygon from a PolyPolygon.
SwNoTextNode * GetNoTextNodeFromSwFrameFormat(const SwFrameFormat &rFormat)
Get the SwNoTextNode associated with a SwFrameFormat if here is one.
tools::Polygon CorrectWordWrapPolygonForExport(const tools::PolyPolygon &rPolyPoly, const SwNoTextNode *pNd, bool bCorrectCrop)
Undo all scaling / move tricks of the wrap polygon done during import.
long Long
o3tl::strong_int< sal_Int32, struct Tag_SwNodeOffset > SwNodeOffset
Definition: nodeoffset.hxx:16
SwNodeOffset min(const SwNodeOffset &a, const SwNodeOffset &b)
Definition: nodeoffset.hxx:35
SwNodeOffset abs(const SwNodeOffset &a)
Definition: nodeoffset.hxx:34
#define Y
Holds data used by DocxSdrExport only.
rtl::Reference< sax_fastparser::FastAttributeList > m_pFlyFillAttrList
Flag for checking drawing in a paragraph.
void setDMLTextFrameSyntax(bool bDMLTextFrameSyntax)
Degree100 & getDMLandVMLTextFrameRotation()
Degree100 m_nDMLandVMLTextFrameRotation
List of TextBoxes in this document: they are exported as part of their shape, never alone.
void setFlyAttrList(const rtl::Reference< sax_fastparser::FastAttributeList > &pFlyAttrList)
bool getDrawingOpen() const
void setTextFrameSyntax(bool bTextFrameSyntax)
bool getDMLAndVMLDrawingOpen() const
rtl::Reference< sax_fastparser::FastAttributeList > m_pFlyAttrList
sax_fastparser::FastAttributeList * getFlyWrapAttrList() const
const Size * m_pFlyFrameSize
OStringBuffer m_aTextFrameStyle
bool getDMLTextFrameSyntax() const
rtl::Reference< sax_fastparser::FastAttributeList > m_pTextboxAttrList
bool getParagraphSdtOpen() const
rtl::Reference< sax_fastparser::FastAttributeList > m_pDashLineStyleAttr
oox::drawingml::DrawingML * m_pDrawingML
DocxExport & getExport() const
const sax_fastparser::FSHelperPtr & getSerializer() const
DocxExport & m_rExport
void setDrawingOpen(bool bDrawingOpen)
void setDMLAndVMLDrawingOpen(bool bDMLAndVMLDrawingOpen)
rtl::Reference< sax_fastparser::FastAttributeList > & getDashLineStyleAttr()
const Size * getFlyFrameSize() const
Impl(DocxExport &rExport, sax_fastparser::FSHelperPtr pSerializer, oox::drawingml::DrawingML *pDrawingML)
bool getTextFrameSyntax() const
void setSerializer(const sax_fastparser::FSHelperPtr &pSerializer)
OStringBuffer & getTextFrameStyle()
void setDMLandVMLTextFrameRotation(Degree100 nDMLandVMLTextFrameRotation)
rtl::Reference< sax_fastparser::FastAttributeList > m_pFlyWrapAttrList
void textFrameShadow(const SwFrameFormat &rFrameFormat)
Writes wp wrapper code around an SdrObject, which itself is written using drawingML syntax.
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