1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include "rtfsdrimport.hxx"
13 #include <com/sun/star/container/XNamed.hpp>
14 #include <com/sun/star/drawing/FillStyle.hpp>
15 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
16 #include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
17 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
18 #include <com/sun/star/drawing/LineStyle.hpp>
19 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
20 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
21 #include <com/sun/star/drawing/ColorMode.hpp>
22 #include <com/sun/star/lang/XServiceInfo.hpp>
23 #include <com/sun/star/table/BorderLine2.hpp>
24 #include <com/sun/star/text/HoriOrientation.hpp>
25 #include <com/sun/star/text/RelOrientation.hpp>
26 #include <com/sun/star/text/SizeType.hpp>
27 #include <com/sun/star/text/VertOrientation.hpp>
28 #include <com/sun/star/text/WrapTextMode.hpp>
29 #include <com/sun/star/text/WritingMode.hpp>
30 #include <com/sun/star/text/WritingMode2.hpp>
31 #include <com/sun/star/text/TextContentAnchorType.hpp>
32 #include <com/sun/star/text/XTextRange.hpp>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <ooxml/resourceids.hxx>
35 #include <filter/msfilter/escherex.hxx>
36 #include <filter/msfilter/util.hxx>
37 #include <filter/msfilter/rtfutil.hxx>
38 #include <sal/log.hxx>
39 #include <svx/svdtrans.hxx>
40 #include <comphelper/sequence.hxx>
41 #include <comphelper/propertysequence.hxx>
42 #include "rtfreferenceproperties.hxx"
43 #include <oox/vml/vmlformatting.hxx>
44 #include <oox/helper/modelobjecthelper.hxx>
45 #include <oox/drawingml/shapepropertymap.hxx>
46 #include <oox/helper/propertyset.hxx>
47 #include <basegfx/matrix/b2dhommatrix.hxx>
48 #include <svx/svdobj.hxx>
49 #include <tools/UnitConversion.hxx>
51 #include <dmapper/GraphicZOrderHelper.hxx>
52 #include "rtfdocumentimpl.hxx"
54 using namespace com::sun::star
;
56 namespace writerfilter::rtftok
58 RTFSdrImport::RTFSdrImport(RTFDocumentImpl
& rDocument
,
59 uno::Reference
<lang::XComponent
> const& xDstDoc
)
60 : m_rImport(rDocument
)
62 , m_bTextGraphicObject(false)
65 uno::Reference
<drawing::XDrawPageSupplier
> xDrawings(xDstDoc
, uno::UNO_QUERY
);
67 m_aParents
.push(xDrawings
->getDrawPage());
68 m_aGraphicZOrderHelpers
.push(writerfilter::dmapper::GraphicZOrderHelper());
71 RTFSdrImport::~RTFSdrImport()
73 if (!m_aGraphicZOrderHelpers
.empty())
74 m_aGraphicZOrderHelpers
.pop();
75 if (!m_aParents
.empty())
79 void RTFSdrImport::createShape(const OUString
& rService
, uno::Reference
<drawing::XShape
>& xShape
,
80 uno::Reference
<beans::XPropertySet
>& xPropertySet
)
82 if (m_rImport
.getModelFactory().is())
83 xShape
.set(m_rImport
.getModelFactory()->createInstance(rService
), uno::UNO_QUERY
);
84 xPropertySet
.set(xShape
, uno::UNO_QUERY
);
87 std::vector
<beans::PropertyValue
> RTFSdrImport::getTextFrameDefaults(bool bNew
)
89 std::vector
<beans::PropertyValue
> aRet
;
90 beans::PropertyValue aPropertyValue
;
92 aPropertyValue
.Name
= "HoriOrient";
93 aPropertyValue
.Value
<<= text::HoriOrientation::NONE
;
94 aRet
.push_back(aPropertyValue
);
95 aPropertyValue
.Name
= "HoriOrientRelation";
96 aPropertyValue
.Value
<<= text::RelOrientation::FRAME
;
97 aRet
.push_back(aPropertyValue
);
98 aPropertyValue
.Name
= "VertOrient";
99 aPropertyValue
.Value
<<= text::VertOrientation::NONE
;
100 aRet
.push_back(aPropertyValue
);
101 aPropertyValue
.Name
= "VertOrientRelation";
102 aPropertyValue
.Value
<<= text::RelOrientation::FRAME
;
103 aRet
.push_back(aPropertyValue
);
106 aPropertyValue
.Name
= "BackColorTransparency";
107 aPropertyValue
.Value
<<= sal_Int32(100);
108 aRet
.push_back(aPropertyValue
);
110 // See the spec, new-style frame default margins are specified in EMUs.
111 aPropertyValue
.Name
= "LeftBorderDistance";
112 aPropertyValue
.Value
<<= sal_Int32(bNew
? (91440 / 360) : 0);
113 aRet
.push_back(aPropertyValue
);
114 aPropertyValue
.Name
= "RightBorderDistance";
115 aPropertyValue
.Value
<<= sal_Int32(bNew
? (91440 / 360) : 0);
116 aRet
.push_back(aPropertyValue
);
117 aPropertyValue
.Name
= "TopBorderDistance";
118 aPropertyValue
.Value
<<= sal_Int32(bNew
? (45720 / 360) : 0);
119 aRet
.push_back(aPropertyValue
);
120 aPropertyValue
.Name
= "BottomBorderDistance";
121 aPropertyValue
.Value
<<= sal_Int32(bNew
? (45720 / 360) : 0);
122 aRet
.push_back(aPropertyValue
);
123 aPropertyValue
.Name
= "SizeType";
124 aPropertyValue
.Value
<<= text::SizeType::FIX
;
125 aRet
.push_back(aPropertyValue
);
129 void RTFSdrImport::pushParent(uno::Reference
<drawing::XShapes
> const& xParent
)
131 m_aParents
.push(xParent
);
132 m_aGraphicZOrderHelpers
.push(writerfilter::dmapper::GraphicZOrderHelper());
135 void RTFSdrImport::popParent()
137 if (!m_aGraphicZOrderHelpers
.empty())
138 m_aGraphicZOrderHelpers
.pop();
139 if (!m_aParents
.empty())
143 void RTFSdrImport::resolveDhgt(uno::Reference
<beans::XPropertySet
> const& xPropertySet
,
144 sal_Int32
const nZOrder
, bool const bOldStyle
)
146 if (!m_aGraphicZOrderHelpers
.empty())
148 writerfilter::dmapper::GraphicZOrderHelper
& rHelper
= m_aGraphicZOrderHelpers
.top();
149 xPropertySet
->setPropertyValue("ZOrder",
150 uno::makeAny(rHelper
.findZOrder(nZOrder
, bOldStyle
)));
151 rHelper
.addItem(xPropertySet
, nZOrder
);
155 void RTFSdrImport::resolveLineColorAndWidth(bool bTextFrame
,
156 const uno::Reference
<beans::XPropertySet
>& xPropertySet
,
157 uno::Any
const& rLineColor
, uno::Any
const& rLineWidth
)
161 xPropertySet
->setPropertyValue("LineColor", rLineColor
);
162 xPropertySet
->setPropertyValue("LineWidth", rLineWidth
);
166 static const char* aBorders
[]
167 = { "TopBorder", "LeftBorder", "BottomBorder", "RightBorder" };
168 for (const char* pBorder
: aBorders
)
170 auto aBorderLine
= xPropertySet
->getPropertyValue(OUString::createFromAscii(pBorder
))
171 .get
<table::BorderLine2
>();
172 if (rLineColor
.hasValue())
173 aBorderLine
.Color
= rLineColor
.get
<sal_Int32
>();
174 if (rLineWidth
.hasValue())
175 aBorderLine
.LineWidth
= rLineWidth
.get
<sal_Int32
>();
176 xPropertySet
->setPropertyValue(OUString::createFromAscii(pBorder
),
177 uno::makeAny(aBorderLine
));
182 void RTFSdrImport::resolveFLine(uno::Reference
<beans::XPropertySet
> const& xPropertySet
,
183 sal_Int32
const nFLine
)
186 xPropertySet
->setPropertyValue("LineStyle", uno::makeAny(drawing::LineStyle_NONE
));
188 xPropertySet
->setPropertyValue("LineStyle", uno::makeAny(drawing::LineStyle_SOLID
));
191 void RTFSdrImport::applyProperty(uno::Reference
<drawing::XShape
> const& xShape
,
192 std::u16string_view aKey
, const OUString
& aValue
) const
194 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, uno::UNO_QUERY
);
195 sal_Int16 nHoriOrient
= 0;
196 sal_Int16 nVertOrient
= 0;
197 std::optional
<bool> obFitShapeToText
;
202 switch (aValue
.toInt32())
205 nHoriOrient
= text::HoriOrientation::LEFT
;
208 nHoriOrient
= text::HoriOrientation::CENTER
;
211 nHoriOrient
= text::HoriOrientation::RIGHT
;
214 nHoriOrient
= text::HoriOrientation::INSIDE
;
217 nHoriOrient
= text::HoriOrientation::OUTSIDE
;
223 else if (aKey
== u
"posv")
225 switch (aValue
.toInt32())
228 nVertOrient
= text::VertOrientation::TOP
;
231 nVertOrient
= text::VertOrientation::CENTER
;
234 nVertOrient
= text::VertOrientation::BOTTOM
;
240 else if (aKey
== u
"fFitShapeToText")
241 obFitShapeToText
= aValue
.toInt32() == 1;
242 else if (aKey
== u
"fFilled")
243 bFilled
= aValue
.toInt32() == 1;
244 else if (aKey
== u
"rotation")
246 // See DffPropertyReader::Fix16ToAngle(): in RTF, positive rotation angles are clockwise, we have them as counter-clockwise.
247 // Additionally, RTF type is 0..360*2^16, our is 0..360*100.
248 sal_Int32 nRotation
= aValue
.toInt32() * 100 / RTF_MULTIPLIER
;
249 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xShape
, uno::UNO_QUERY
);
250 if (!xServiceInfo
->supportsService("com.sun.star.text.TextFrame"))
251 xPropertySet
->setPropertyValue(
252 "RotateAngle", uno::makeAny(NormAngle36000(Degree100(nRotation
* -1)).get()));
255 if (nHoriOrient
!= 0 && xPropertySet
.is())
256 xPropertySet
->setPropertyValue("HoriOrient", uno::makeAny(nHoriOrient
));
257 if (nVertOrient
!= 0 && xPropertySet
.is())
258 xPropertySet
->setPropertyValue("VertOrient", uno::makeAny(nVertOrient
));
259 if (obFitShapeToText
.has_value() && xPropertySet
.is())
261 xPropertySet
->setPropertyValue(
263 uno::makeAny(*obFitShapeToText
? text::SizeType::MIN
: text::SizeType::FIX
));
264 xPropertySet
->setPropertyValue("FrameIsAutomaticHeight", uno::makeAny(*obFitShapeToText
));
266 if (!bFilled
&& xPropertySet
.is())
269 xPropertySet
->setPropertyValue("BackColorTransparency", uno::makeAny(sal_Int32(100)));
271 xPropertySet
->setPropertyValue("FillStyle", uno::makeAny(drawing::FillStyle_NONE
));
275 int RTFSdrImport::initShape(uno::Reference
<drawing::XShape
>& o_xShape
,
276 uno::Reference
<beans::XPropertySet
>& o_xPropSet
, bool& o_rIsCustomShape
,
277 RTFShape
const& rShape
, bool const bClose
,
278 ShapeOrPict
const shapeOrPict
)
280 assert(!o_xShape
.is());
281 assert(!o_xPropSet
.is());
282 o_rIsCustomShape
= false;
285 // first, find the shape type
287 auto iter
= std::find_if(rShape
.getProperties().begin(), rShape
.getProperties().end(),
288 [](const std::pair
<OUString
, OUString
>& rProperty
) {
289 return rProperty
.first
== "shapeType";
292 if (iter
== rShape
.getProperties().end())
294 if (SHAPE
== shapeOrPict
)
296 // The spec doesn't state what is the default for shapeType,
297 // Word seems to implement it as a rectangle.
298 nType
= ESCHER_ShpInst_Rectangle
;
302 // pict is picture by default but can be a rectangle too fdo#79319
303 nType
= ESCHER_ShpInst_PictureFrame
;
308 nType
= iter
->second
.toInt32();
309 if (PICT
== shapeOrPict
&& ESCHER_ShpInst_PictureFrame
!= nType
)
317 case ESCHER_ShpInst_PictureFrame
:
318 createShape("com.sun.star.drawing.GraphicObjectShape", o_xShape
, o_xPropSet
);
319 m_bTextGraphicObject
= true;
321 case ESCHER_ShpInst_Line
:
322 createShape("com.sun.star.drawing.LineShape", o_xShape
, o_xPropSet
);
324 case ESCHER_ShpInst_Rectangle
:
325 case ESCHER_ShpInst_TextBox
:
326 // If we're inside a groupshape, can't use text frames.
327 if (!bClose
&& m_aParents
.size() == 1)
329 createShape("com.sun.star.text.TextFrame", o_xShape
, o_xPropSet
);
331 std::vector
<beans::PropertyValue
> aDefaults
= getTextFrameDefaults(true);
332 for (const beans::PropertyValue
& i
: aDefaults
)
333 o_xPropSet
->setPropertyValue(i
.Name
, i
.Value
);
338 createShape("com.sun.star.drawing.CustomShape", o_xShape
, o_xPropSet
);
339 o_rIsCustomShape
= true;
344 if (o_xPropSet
.is() && !m_bTextFrame
)
346 o_xPropSet
->setPropertyValue(
348 uno::makeAny(sal_uInt32(0xffffff))); // White in Word, kind of blue in Writer.
349 o_xPropSet
->setPropertyValue("VertOrient", uno::makeAny(text::VertOrientation::NONE
));
355 void RTFSdrImport::resolve(RTFShape
& rShape
, bool bClose
, ShapeOrPict
const shapeOrPict
)
358 m_bTextFrame
= false;
359 m_bTextGraphicObject
= false;
361 uno::Reference
<drawing::XShape
> xShape
;
362 uno::Reference
<beans::XPropertySet
> xPropertySet
;
364 beans::PropertyValue aPropertyValue
;
365 awt::Rectangle aViewBox
;
366 std::vector
<beans::PropertyValue
> aPath
;
367 // Default line color is black in Word, blue in Writer.
368 uno::Any aLineColor
= uno::makeAny(COL_BLACK
);
369 // Default line width is 0.75 pt (26 mm100) in Word, 0 in Writer.
370 uno::Any aLineWidth
= uno::makeAny(sal_Int32(26));
371 sal_Int16 eWritingMode
= text::WritingMode2::LR_TB
;
372 // Groupshape support
373 std::optional
<sal_Int32
> oGroupLeft
;
374 std::optional
<sal_Int32
> oGroupTop
;
375 std::optional
<sal_Int32
> oGroupRight
;
376 std::optional
<sal_Int32
> oGroupBottom
;
377 std::optional
<sal_Int32
> oRelLeft
;
378 std::optional
<sal_Int32
> oRelTop
;
379 std::optional
<sal_Int32
> oRelRight
;
380 std::optional
<sal_Int32
> oRelBottom
;
382 // Importing these are not trivial, let the VML import do the hard work.
383 oox::vml::FillModel aFillModel
; // Gradient.
384 oox::vml::ShadowModel aShadowModel
; // Shadow.
388 std::optional
<sal_Int16
> oRelativeWidth
;
389 std::optional
<sal_Int16
> oRelativeHeight
;
390 sal_Int16 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
391 sal_Int16 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
392 std::optional
<bool> obRelFlipV
;
396 OUString aShapeText
= "";
397 OUString aFontFamily
= "";
398 float nFontSize
= 1.0;
400 sal_Int32 nContrast
= 0x10000;
401 sal_Int16 nBrightness
= 0;
404 int const nType
= initShape(xShape
, xPropertySet
, bCustom
, rShape
, bClose
, shapeOrPict
);
406 for (auto& rProperty
: rShape
.getProperties())
408 if (rProperty
.first
== "shapeType")
410 continue; // ignore: already handled by initShape
412 if (rProperty
.first
== "wzName")
416 uno::Reference
<container::XNamed
> xNamed(xShape
, uno::UNO_QUERY
);
417 xNamed
->setName(rProperty
.second
);
420 xPropertySet
->setPropertyValue("Name", uno::makeAny(rProperty
.second
));
422 else if (rProperty
.first
== "wzDescription")
423 xPropertySet
->setPropertyValue("Description", uno::makeAny(rProperty
.second
));
424 else if (rProperty
.first
== "gtextUNICODE")
425 aShapeText
= rProperty
.second
;
426 else if (rProperty
.first
== "gtextFont")
427 aFontFamily
= rProperty
.second
;
428 else if (rProperty
.first
== "gtextSize")
430 // RTF size is multiplied by 2^16
431 nFontSize
= static_cast<float>(rProperty
.second
.toUInt32()) / RTF_MULTIPLIER
;
433 else if (rProperty
.first
== "pib")
435 m_rImport
.setDestinationText(rProperty
.second
);
438 else if (rProperty
.first
== "fillColor" && xPropertySet
.is())
440 aAny
<<= msfilter::util::BGRToRGB(rProperty
.second
.toUInt32());
442 xPropertySet
->setPropertyValue("BackColor", aAny
);
444 xPropertySet
->setPropertyValue("FillColor", aAny
);
446 // fillType will decide, possible it'll be the start color of a gradient.
447 aFillModel
.moColor
.set(
449 + msfilter::util::ConvertColorOU(Color(ColorTransparency
, aAny
.get
<sal_Int32
>())));
451 else if (rProperty
.first
== "fillBackColor")
452 // fillType will decide, possible it'll be the end color of a gradient.
453 aFillModel
.moColor2
.set("#"
454 + msfilter::util::ConvertColorOU(
455 msfilter::util::BGRToRGB(rProperty
.second
.toInt32())));
456 else if (rProperty
.first
== "lineColor")
457 aLineColor
<<= msfilter::util::BGRToRGB(rProperty
.second
.toInt32());
458 else if (rProperty
.first
== "lineBackColor")
459 ; // Ignore: complementer of lineColor
460 else if (rProperty
.first
== "txflTextFlow" && xPropertySet
.is())
462 switch (rProperty
.second
.toInt32())
464 case 1: // Top to bottom ASCII font
465 case 3: // Top to bottom non-ASCII font
466 eWritingMode
= text::WritingMode2::TB_RL
;
468 case 2: // Bottom to top non-ASCII font
469 eWritingMode
= text::WritingMode2::BT_LR
;
473 else if (rProperty
.first
== "fLine" && xPropertySet
.is())
474 resolveFLine(xPropertySet
, rProperty
.second
.toInt32());
475 else if (rProperty
.first
== "fillOpacity" && xPropertySet
.is())
477 int opacity
= 100 - (rProperty
.second
.toInt32()) * 100 / RTF_MULTIPLIER
;
478 xPropertySet
->setPropertyValue("FillTransparence", uno::Any(sal_uInt32(opacity
)));
480 else if (rProperty
.first
== "lineWidth")
481 aLineWidth
<<= rProperty
.second
.toInt32() / 360;
482 else if (rProperty
.first
== "pVerticies")
484 std::vector
<drawing::EnhancedCustomShapeParameterPair
> aCoordinates
;
485 sal_Int32 nSize
= 0; // Size of a token (its value is hardwired in the exporter)
486 sal_Int32 nCount
= 0; // Number of tokens
487 sal_Int32 nCharIndex
= 0; // Character index
490 OUString aToken
= rProperty
.second
.getToken(0, ';', nCharIndex
);
492 nSize
= aToken
.toInt32();
494 nCount
= aToken
.toInt32();
495 else if (aToken
.getLength())
497 // The coordinates are in an (x,y) form.
498 aToken
= aToken
.copy(1, aToken
.getLength() - 2);
500 sal_Int32 nX
= aToken
.getToken(0, ',', nI
).toInt32();
501 sal_Int32 nY
= (nI
>= 0) ? aToken
.getToken(0, ',', nI
).toInt32() : 0;
502 drawing::EnhancedCustomShapeParameterPair aPair
;
503 aPair
.First
.Value
<<= nX
;
504 aPair
.Second
.Value
<<= nY
;
505 aCoordinates
.push_back(aPair
);
507 } while (nCharIndex
>= 0);
508 aPropertyValue
.Name
= "Coordinates";
509 aPropertyValue
.Value
<<= comphelper::containerToSequence(aCoordinates
);
510 aPath
.push_back(aPropertyValue
);
512 else if (rProperty
.first
== "pSegmentInfo")
514 std::vector
<drawing::EnhancedCustomShapeSegment
> aSegments
;
516 sal_Int32 nCount
= 0;
517 sal_Int32 nCharIndex
= 0;
520 sal_Int32 nSeg
= rProperty
.second
.getToken(0, ';', nCharIndex
).toInt32();
527 sal_Int32 nPoints
= 1;
528 if (nSeg
>= 0x2000 && nSeg
< 0x20FF)
530 nPoints
= nSeg
& 0x0FFF;
534 drawing::EnhancedCustomShapeSegment aSegment
;
537 case 0x0001: // lineto
538 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
539 aSegment
.Count
= sal_Int32(1);
540 aSegments
.push_back(aSegment
);
542 case 0x4000: // moveto
543 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::MOVETO
;
544 aSegment
.Count
= sal_Int32(1);
545 aSegments
.push_back(aSegment
);
547 case 0x2000: // curveto
548 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::CURVETO
;
549 aSegment
.Count
= nPoints
;
550 aSegments
.push_back(aSegment
);
552 case 0xb300: // arcto
553 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::ARCTO
;
554 aSegment
.Count
= sal_Int32(0);
555 aSegments
.push_back(aSegment
);
558 case 0xaa00: // nofill
559 case 0xab00: // nostroke
560 case 0x6001: // close
564 = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH
;
565 aSegment
.Count
= sal_Int32(0);
566 aSegments
.push_back(aSegment
);
568 default: // given number of lineto elements
569 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
570 aSegment
.Count
= nSeg
;
571 aSegments
.push_back(aSegment
);
575 } while (nCharIndex
>= 0);
576 aPropertyValue
.Name
= "Segments";
577 aPropertyValue
.Value
<<= comphelper::containerToSequence(aSegments
);
578 aPath
.push_back(aPropertyValue
);
580 else if (rProperty
.first
== "geoLeft")
581 aViewBox
.X
= rProperty
.second
.toInt32();
582 else if (rProperty
.first
== "geoTop")
583 aViewBox
.Y
= rProperty
.second
.toInt32();
584 else if (rProperty
.first
== "geoRight")
585 aViewBox
.Width
= rProperty
.second
.toInt32();
586 else if (rProperty
.first
== "geoBottom")
587 aViewBox
.Height
= rProperty
.second
.toInt32();
588 else if (rProperty
.first
== "dhgt")
590 // dhgt is Word 2007, \shpz is Word 97-2003, the later has priority.
592 resolveDhgt(xPropertySet
, rProperty
.second
.toInt32(), /*bOldStyle=*/false);
594 // These are in EMU, convert to mm100.
595 else if (rProperty
.first
== "dxTextLeft")
597 if (xPropertySet
.is())
598 xPropertySet
->setPropertyValue("LeftBorderDistance",
599 uno::makeAny(rProperty
.second
.toInt32() / 360));
601 else if (rProperty
.first
== "dyTextTop")
603 if (xPropertySet
.is())
604 xPropertySet
->setPropertyValue("TopBorderDistance",
605 uno::makeAny(rProperty
.second
.toInt32() / 360));
607 else if (rProperty
.first
== "dxTextRight")
609 if (xPropertySet
.is())
610 xPropertySet
->setPropertyValue("RightBorderDistance",
611 uno::makeAny(rProperty
.second
.toInt32() / 360));
613 else if (rProperty
.first
== "dyTextBottom")
615 if (xPropertySet
.is())
616 xPropertySet
->setPropertyValue("BottomBorderDistance",
617 uno::makeAny(rProperty
.second
.toInt32() / 360));
619 else if (rProperty
.first
== "dxWrapDistLeft")
621 if (m_bTextGraphicObject
)
622 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distL
,
623 new RTFValue(rProperty
.second
.toInt32()));
624 else if (xPropertySet
.is())
625 xPropertySet
->setPropertyValue("LeftMargin",
626 uno::makeAny(rProperty
.second
.toInt32() / 360));
628 else if (rProperty
.first
== "dyWrapDistTop")
630 if (m_bTextGraphicObject
)
631 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distT
,
632 new RTFValue(rProperty
.second
.toInt32()));
633 else if (xPropertySet
.is())
634 xPropertySet
->setPropertyValue("TopMargin",
635 uno::makeAny(rProperty
.second
.toInt32() / 360));
637 else if (rProperty
.first
== "dxWrapDistRight")
639 if (m_bTextGraphicObject
)
640 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distR
,
641 new RTFValue(rProperty
.second
.toInt32()));
642 else if (xPropertySet
.is())
643 xPropertySet
->setPropertyValue("RightMargin",
644 uno::makeAny(rProperty
.second
.toInt32() / 360));
646 else if (rProperty
.first
== "dyWrapDistBottom")
648 if (m_bTextGraphicObject
)
649 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distB
,
650 new RTFValue(rProperty
.second
.toInt32()));
651 else if (xPropertySet
.is())
652 xPropertySet
->setPropertyValue("BottomMargin",
653 uno::makeAny(rProperty
.second
.toInt32() / 360));
655 else if (rProperty
.first
== "fillType")
657 switch (rProperty
.second
.toInt32())
659 case 7: // Shade using the fillAngle
660 aFillModel
.moType
.set(oox::XML_gradient
);
663 SAL_INFO("writerfilter",
664 "TODO handle fillType value '" << rProperty
.second
<< "'");
668 else if (rProperty
.first
== "fillFocus")
669 aFillModel
.moFocus
.set(rProperty
.second
.toDouble() / 100); // percent
670 else if (rProperty
.first
== "fShadow" && xPropertySet
.is())
672 if (rProperty
.second
.toInt32() == 1)
673 aShadowModel
.mbHasShadow
= true;
675 else if (rProperty
.first
== "shadowColor")
676 aShadowModel
.moColor
.set("#"
677 + msfilter::util::ConvertColorOU(
678 msfilter::util::BGRToRGB(rProperty
.second
.toInt32())));
679 else if (rProperty
.first
== "shadowOffsetX")
681 aShadowModel
.moOffset
.set(OUString::number(rProperty
.second
.toDouble() / 12700) + "pt");
682 else if (rProperty
.first
== "posh" || rProperty
.first
== "posv"
683 || rProperty
.first
== "fFitShapeToText" || rProperty
.first
== "fFilled"
684 || rProperty
.first
== "rotation")
685 applyProperty(xShape
, rProperty
.first
, rProperty
.second
);
686 else if (rProperty
.first
== "posrelh")
688 switch (rProperty
.second
.toInt32())
691 rShape
.setHoriOrientRelation(text::RelOrientation::PAGE_FRAME
);
697 else if (rProperty
.first
== "posrelv")
699 switch (rProperty
.second
.toInt32())
702 rShape
.setVertOrientRelation(text::RelOrientation::PAGE_FRAME
);
708 else if (rProperty
.first
== "groupLeft")
709 oGroupLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
710 else if (rProperty
.first
== "groupTop")
711 oGroupTop
= convertTwipToMm100(rProperty
.second
.toInt32());
712 else if (rProperty
.first
== "groupRight")
713 oGroupRight
= convertTwipToMm100(rProperty
.second
.toInt32());
714 else if (rProperty
.first
== "groupBottom")
715 oGroupBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
716 else if (rProperty
.first
== "relLeft")
717 oRelLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
718 else if (rProperty
.first
== "relTop")
719 oRelTop
= convertTwipToMm100(rProperty
.second
.toInt32());
720 else if (rProperty
.first
== "relRight")
721 oRelRight
= convertTwipToMm100(rProperty
.second
.toInt32());
722 else if (rProperty
.first
== "relBottom")
723 oRelBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
724 else if (rProperty
.first
== "fBehindDocument")
725 bOpaque
= !rProperty
.second
.toInt32();
726 else if (rProperty
.first
== "pctHoriz" || rProperty
.first
== "pctVert")
728 sal_Int16 nPercentage
= rtl::math::round(rProperty
.second
.toDouble() / 10);
731 std::optional
<sal_Int16
>& rPercentage
732 = rProperty
.first
== "pctHoriz" ? oRelativeWidth
: oRelativeHeight
;
733 rPercentage
= nPercentage
;
736 else if (rProperty
.first
== "sizerelh")
738 if (xPropertySet
.is())
740 switch (rProperty
.second
.toInt32())
743 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
746 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
749 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: "
750 << rProperty
.second
);
755 else if (rProperty
.first
== "sizerelv")
757 if (xPropertySet
.is())
759 switch (rProperty
.second
.toInt32())
762 nRelativeHeightRelation
= text::RelOrientation::FRAME
;
765 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
768 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: "
769 << rProperty
.second
);
774 else if (rProperty
.first
== "fHorizRule") // TODO: what does "fStandardHR" do?
776 // horizontal rule: relative width defaults to 100% of paragraph
777 // TODO: does it have a default height?
780 oRelativeWidth
= 100;
782 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
783 if (xPropertySet
.is())
785 sal_Int16
const nVertOrient
= text::VertOrientation::CENTER
;
786 xPropertySet
->setPropertyValue("VertOrient", uno::makeAny(nVertOrient
));
789 else if (rProperty
.first
== "pctHR")
791 // horizontal rule relative width in permille
792 oRelativeWidth
= rProperty
.second
.toInt32() / 10;
794 else if (rProperty
.first
== "dxHeightHR")
796 // horizontal rule height
797 sal_uInt32
const nHeight(convertTwipToMm100(rProperty
.second
.toInt32()));
798 rShape
.setBottom(rShape
.getTop() + nHeight
);
800 else if (rProperty
.first
== "dxWidthHR")
802 // horizontal rule width
803 sal_uInt32
const nWidth(convertTwipToMm100(rProperty
.second
.toInt32()));
804 rShape
.setRight(rShape
.getLeft() + nWidth
);
806 else if (rProperty
.first
== "alignHR")
808 // horizontal orientation *for horizontal rule*
809 sal_Int16 nHoriOrient
= text::HoriOrientation::NONE
;
810 switch (rProperty
.second
.toInt32())
813 nHoriOrient
= text::HoriOrientation::LEFT
;
816 nHoriOrient
= text::HoriOrientation::CENTER
;
819 nHoriOrient
= text::HoriOrientation::RIGHT
;
822 if (xPropertySet
.is() && text::HoriOrientation::NONE
!= nHoriOrient
)
824 xPropertySet
->setPropertyValue("HoriOrient", uno::makeAny(nHoriOrient
));
827 else if (rProperty
.first
== "pWrapPolygonVertices")
829 RTFSprms aPolygonSprms
;
830 sal_Int32 nSize
= 0; // Size of a token
831 sal_Int32 nCount
= 0; // Number of tokens
832 sal_Int32 nCharIndex
= 0; // Character index
835 OUString aToken
= rProperty
.second
.getToken(0, ';', nCharIndex
);
837 nSize
= aToken
.toInt32();
839 nCount
= aToken
.toInt32();
840 else if (aToken
.getLength())
842 // The coordinates are in an (x,y) form.
843 aToken
= aToken
.copy(1, aToken
.getLength() - 2);
845 sal_Int32 nX
= aToken
.getToken(0, ',', nI
).toInt32();
846 sal_Int32 nY
= (nI
>= 0) ? aToken
.getToken(0, ',', nI
).toInt32() : 0;
847 RTFSprms aPathAttributes
;
848 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_x
, new RTFValue(nX
));
849 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_y
, new RTFValue(nY
));
850 aPolygonSprms
.set(NS_ooxml::LN_CT_WrapPath_lineTo
,
851 new RTFValue(aPathAttributes
), RTFOverwrite::NO_APPEND
);
853 } while (nCharIndex
>= 0);
854 rShape
.getWrapPolygonSprms() = aPolygonSprms
;
856 else if (rProperty
.first
== "fRelFlipV")
857 obRelFlipV
= rProperty
.second
.toInt32() == 1;
858 else if (rProperty
.first
== "fFlipH")
859 obFlipH
= rProperty
.second
.toInt32() == 1;
860 else if (rProperty
.first
== "fFlipV")
861 obFlipV
= rProperty
.second
.toInt32() == 1;
862 else if (rProperty
.first
== "pictureContrast")
865 nContrast
= rProperty
.second
.toInt32();
866 if (nContrast
< 0x10000)
868 nContrast
*= 101; // 100 + 1 to round
869 nContrast
/= 0x10000;
873 else if (rProperty
.first
== "pictureBrightness")
875 // Blacklevel / brightness.
876 nBrightness
= rProperty
.second
.toInt32();
877 if (nBrightness
!= 0)
883 SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty
.first
<< "':'"
884 << rProperty
.second
<< "'");
887 if (xPropertySet
.is())
889 resolveLineColorAndWidth(m_bTextFrame
, xPropertySet
, aLineColor
, aLineWidth
);
892 bool bOldStyle
= m_aParents
.size() > 1;
893 resolveDhgt(xPropertySet
, rShape
.getZ(), bOldStyle
);
896 xPropertySet
->setPropertyValue("WritingMode", uno::makeAny(eWritingMode
));
898 // Only Writer textframes implement text::WritingMode2.
899 xPropertySet
->setPropertyValue("TextWritingMode",
900 uno::makeAny(text::WritingMode(eWritingMode
)));
903 if (!m_aParents
.empty() && m_aParents
.top().is() && !m_bTextFrame
)
904 m_aParents
.top()->add(xShape
);
906 if (nContrast
== -70 && nBrightness
== 70 && xPropertySet
.is())
908 // Map MSO 'washout' to our watermark colormode.
909 xPropertySet
->setPropertyValue("GraphicColorMode",
910 uno::makeAny(drawing::ColorMode_WATERMARK
));
915 m_rImport
.resolvePict(false, xShape
);
918 if (nType
== ESCHER_ShpInst_PictureFrame
) // picture frame
920 assert(!m_bTextFrame
);
921 if (!bPib
) // ??? not sure if the early return should be removed on else?
923 m_xShape
= xShape
; // store it for later resolvePict call
926 // Handle horizontal flip.
927 if (obFlipH
&& xPropertySet
.is())
928 xPropertySet
->setPropertyValue("IsMirrored", uno::makeAny(true));
932 if (bCustom
&& xShape
.is() && !bPib
)
934 uno::Reference
<drawing::XEnhancedCustomShapeDefaulter
> xDefaulter(xShape
, uno::UNO_QUERY
);
935 xDefaulter
->createCustomShapeDefaults(OUString::number(nType
));
939 if (bCustom
&& !aShapeText
.isEmpty())
941 uno::Reference
<text::XTextRange
> xTextRange(xShape
, uno::UNO_QUERY
);
943 xTextRange
->setString(aShapeText
);
945 xPropertySet
->setPropertyValue("CharFontName", uno::makeAny(aFontFamily
));
946 xPropertySet
->setPropertyValue("CharHeight", uno::makeAny(nFontSize
));
949 // Creating CustomShapeGeometry property
950 if (bCustom
&& xPropertySet
.is())
952 bool bChanged
= false;
953 comphelper::SequenceAsHashMap
aCustomShapeGeometry(
954 xPropertySet
->getPropertyValue("CustomShapeGeometry"));
956 if (aViewBox
.X
|| aViewBox
.Y
|| aViewBox
.Width
|| aViewBox
.Height
)
958 aViewBox
.Width
-= aViewBox
.X
;
959 aViewBox
.Height
-= aViewBox
.Y
;
960 aCustomShapeGeometry
["ViewBox"] <<= aViewBox
;
966 aCustomShapeGeometry
["Path"] <<= comphelper::containerToSequence(aPath
);
970 if (!aShapeText
.isEmpty())
972 uno::Sequence
<beans::PropertyValue
> aSequence(comphelper::InitPropertySequence({
973 { "TextPath", uno::makeAny(true) },
975 aCustomShapeGeometry
["TextPath"] <<= aSequence
;
976 xPropertySet
->setPropertyValue("TextAutoGrowHeight", uno::makeAny(false));
977 xPropertySet
->setPropertyValue("TextAutoGrowWidth", uno::makeAny(false));
983 xPropertySet
->setPropertyValue(
984 "CustomShapeGeometry",
985 uno::makeAny(aCustomShapeGeometry
.getAsConstPropertyValueList()));
989 if (obRelFlipV
.has_value() && xPropertySet
.is())
991 if (nType
== ESCHER_ShpInst_Line
)
993 // Line shape inside group shape: get the polygon sequence and transform it.
994 uno::Sequence
<uno::Sequence
<awt::Point
>> aPolyPolySequence
;
995 if ((xPropertySet
->getPropertyValue("PolyPolygon") >>= aPolyPolySequence
)
996 && aPolyPolySequence
.hasElements())
998 uno::Sequence
<awt::Point
>& rPolygon
= aPolyPolySequence
.getArray()[0];
999 basegfx::B2DPolygon aPoly
;
1000 for (const awt::Point
& rPoint
: std::as_const(rPolygon
))
1002 aPoly
.append(basegfx::B2DPoint(rPoint
.X
, rPoint
.Y
));
1004 basegfx::B2DHomMatrix aTransformation
;
1005 aTransformation
.scale(1.0, *obRelFlipV
? -1.0 : 1.0);
1006 aPoly
.transform(aTransformation
);
1007 auto pPolygon
= rPolygon
.getArray();
1008 for (sal_Int32 i
= 0; i
< rPolygon
.getLength(); ++i
)
1010 basegfx::B2DPoint
aPoint(aPoly
.getB2DPoint(i
));
1011 pPolygon
[i
] = awt::Point(static_cast<sal_Int32
>(aPoint
.getX()),
1012 static_cast<sal_Int32
>(aPoint
.getY()));
1014 xPropertySet
->setPropertyValue("PolyPolygon", uno::makeAny(aPolyPolySequence
));
1019 // Set position and size
1022 sal_Int32 nLeft
= rShape
.getLeft();
1023 sal_Int32 nTop
= rShape
.getTop();
1025 bool bInShapeGroup
= oGroupLeft
&& oGroupTop
&& oGroupRight
&& oGroupBottom
&& oRelLeft
1026 && oRelTop
&& oRelRight
&& oRelBottom
;
1030 // See lclGetAbsPoint() in the VML import: rShape is the group shape, oGroup is its coordinate system, oRel is the relative child shape.
1031 sal_Int32 nShapeWidth
= rShape
.getRight() - rShape
.getLeft();
1032 sal_Int32 nShapeHeight
= rShape
.getBottom() - rShape
.getTop();
1033 sal_Int32 nCoordSysWidth
= *oGroupRight
- *oGroupLeft
;
1034 sal_Int32 nCoordSysHeight
= *oGroupBottom
- *oGroupTop
;
1035 double fWidthRatio
= static_cast<double>(nShapeWidth
) / nCoordSysWidth
;
1036 double fHeightRatio
= static_cast<double>(nShapeHeight
) / nCoordSysHeight
;
1037 nLeft
= static_cast<sal_Int32
>(rShape
.getLeft()
1038 + fWidthRatio
* (*oRelLeft
- *oGroupLeft
));
1039 nTop
= static_cast<sal_Int32
>(rShape
.getTop() + fHeightRatio
* (*oRelTop
- *oGroupTop
));
1041 // See lclGetAbsRect() in the VML import.
1042 aSize
.Width
= std::lround(fWidthRatio
* (*oRelRight
- *oRelLeft
));
1043 aSize
.Height
= std::lround(fHeightRatio
* (*oRelBottom
- *oRelTop
));
1048 xPropertySet
->setPropertyValue("HoriOrientPosition", uno::makeAny(nLeft
));
1049 xPropertySet
->setPropertyValue("VertOrientPosition", uno::makeAny(nTop
));
1052 xShape
->setPosition(awt::Point(nLeft
, nTop
));
1055 xShape
->setSize(aSize
);
1057 xShape
->setSize(awt::Size(rShape
.getRight() - rShape
.getLeft(),
1058 rShape
.getBottom() - rShape
.getTop()));
1060 if (obFlipH
|| obFlipV
)
1064 // This has to be set after position and size is set, otherwise flip will affect the position.
1065 comphelper::SequenceAsHashMap
aCustomShapeGeometry(
1066 xPropertySet
->getPropertyValue("CustomShapeGeometry"));
1068 aCustomShapeGeometry
["MirroredX"] <<= true;
1070 aCustomShapeGeometry
["MirroredY"] <<= true;
1071 xPropertySet
->setPropertyValue(
1072 "CustomShapeGeometry",
1073 uno::makeAny(aCustomShapeGeometry
.getAsConstPropertyValueList()));
1075 else if (SdrObject
* pObject
= SdrObject::getSdrObjectFromXShape(xShape
))
1077 Point aRef1
= pObject
->GetSnapRect().Center();
1081 // Horizontal mirror means a vertical reference line.
1086 // Vertical mirror means a horizontal reference line.
1089 pObject
->Mirror(aRef1
, aRef2
);
1093 if (rShape
.getHoriOrientRelation() != 0)
1094 xPropertySet
->setPropertyValue("HoriOrientRelation",
1095 uno::makeAny(rShape
.getHoriOrientRelation()));
1096 if (rShape
.getVertOrientRelation() != 0)
1097 xPropertySet
->setPropertyValue("VertOrientRelation",
1098 uno::makeAny(rShape
.getVertOrientRelation()));
1099 if (rShape
.getWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
)
1100 xPropertySet
->setPropertyValue("Surround", uno::makeAny(rShape
.getWrap()));
1101 oox::ModelObjectHelper
aModelObjectHelper(m_rImport
.getModelFactory());
1102 if (aFillModel
.moType
.has())
1104 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1105 aFillModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1106 // Sets the FillStyle and FillGradient UNO properties.
1107 oox::PropertySet(xShape
).setProperties(aPropMap
);
1110 if (aShadowModel
.mbHasShadow
)
1112 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1113 aShadowModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1114 // Sets the ShadowFormat UNO property.
1115 oox::PropertySet(xShape
).setProperties(aPropMap
);
1117 xPropertySet
->setPropertyValue("AnchorType",
1118 uno::makeAny(text::TextContentAnchorType_AT_CHARACTER
));
1119 xPropertySet
->setPropertyValue("Opaque", uno::makeAny(bOpaque
));
1122 xPropertySet
->setPropertyValue("RelativeWidth", uno::makeAny(*oRelativeWidth
));
1123 xPropertySet
->setPropertyValue("RelativeWidthRelation",
1124 uno::makeAny(nRelativeWidthRelation
));
1126 if (oRelativeHeight
)
1128 xPropertySet
->setPropertyValue("RelativeHeight", uno::makeAny(*oRelativeHeight
));
1129 xPropertySet
->setPropertyValue("RelativeHeightRelation",
1130 uno::makeAny(nRelativeHeightRelation
));
1134 if (m_rImport
.isInBackground())
1136 RTFSprms aAttributes
;
1137 aAttributes
.set(NS_ooxml::LN_CT_Background_color
,
1138 new RTFValue(xPropertySet
->getPropertyValue("FillColor").get
<sal_Int32
>()));
1139 m_rImport
.Mapper().props(new RTFReferenceProperties(aAttributes
));
1141 uno::Reference
<lang::XComponent
> xComponent(xShape
, uno::UNO_QUERY
);
1142 xComponent
->dispose();
1146 // Send it to dmapper
1149 m_rImport
.Mapper().startShape(xShape
);
1152 m_rImport
.Mapper().endShape();
1156 // If the shape has an inner shape, the inner object's properties should not be influenced by
1158 rShape
.getProperties().clear();
1163 void RTFSdrImport::close() { m_rImport
.Mapper().endShape(); }
1165 void RTFSdrImport::append(std::u16string_view aKey
, const OUString
& aValue
)
1167 applyProperty(m_xShape
, aKey
, aValue
);
1170 void RTFSdrImport::appendGroupProperty(std::u16string_view aKey
, const OUString
& aValue
)
1172 if (m_aParents
.empty())
1174 uno::Reference
<drawing::XShape
> xShape(m_aParents
.top(), uno::UNO_QUERY
);
1176 applyProperty(xShape
, aKey
, aValue
);
1179 } // namespace writerfilter
1181 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */