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>
50 #include <o3tl/string_view.hxx>
52 #include <dmapper/GraphicZOrderHelper.hxx>
53 #include "rtfdocumentimpl.hxx"
55 using namespace com::sun::star
;
57 namespace writerfilter::rtftok
59 RTFSdrImport::RTFSdrImport(RTFDocumentImpl
& rDocument
,
60 uno::Reference
<lang::XComponent
> const& xDstDoc
)
61 : m_rImport(rDocument
)
63 , m_bTextGraphicObject(false)
66 uno::Reference
<drawing::XDrawPageSupplier
> xDrawings(xDstDoc
, uno::UNO_QUERY
);
68 m_aParents
.push(xDrawings
->getDrawPage());
69 m_aGraphicZOrderHelpers
.push(writerfilter::dmapper::GraphicZOrderHelper());
72 RTFSdrImport::~RTFSdrImport()
74 if (!m_aGraphicZOrderHelpers
.empty())
75 m_aGraphicZOrderHelpers
.pop();
76 if (!m_aParents
.empty())
80 void RTFSdrImport::createShape(const OUString
& rService
, uno::Reference
<drawing::XShape
>& xShape
,
81 uno::Reference
<beans::XPropertySet
>& xPropertySet
)
83 if (m_rImport
.getModelFactory().is())
84 xShape
.set(m_rImport
.getModelFactory()->createInstance(rService
), uno::UNO_QUERY
);
85 xPropertySet
.set(xShape
, uno::UNO_QUERY
);
88 std::vector
<beans::PropertyValue
> RTFSdrImport::getTextFrameDefaults(bool bNew
)
90 std::vector
<beans::PropertyValue
> aRet
;
91 beans::PropertyValue aPropertyValue
;
93 aPropertyValue
.Name
= "HoriOrient";
94 aPropertyValue
.Value
<<= text::HoriOrientation::NONE
;
95 aRet
.push_back(aPropertyValue
);
96 aPropertyValue
.Name
= "HoriOrientRelation";
97 aPropertyValue
.Value
<<= text::RelOrientation::FRAME
;
98 aRet
.push_back(aPropertyValue
);
99 aPropertyValue
.Name
= "VertOrient";
100 aPropertyValue
.Value
<<= text::VertOrientation::NONE
;
101 aRet
.push_back(aPropertyValue
);
102 aPropertyValue
.Name
= "VertOrientRelation";
103 aPropertyValue
.Value
<<= text::RelOrientation::FRAME
;
104 aRet
.push_back(aPropertyValue
);
107 aPropertyValue
.Name
= "BackColorTransparency";
108 aPropertyValue
.Value
<<= sal_Int32(100);
109 aRet
.push_back(aPropertyValue
);
111 // See the spec, new-style frame default margins are specified in EMUs.
112 aPropertyValue
.Name
= "LeftBorderDistance";
113 aPropertyValue
.Value
<<= sal_Int32(bNew
? (91440 / 360) : 0);
114 aRet
.push_back(aPropertyValue
);
115 aPropertyValue
.Name
= "RightBorderDistance";
116 aPropertyValue
.Value
<<= sal_Int32(bNew
? (91440 / 360) : 0);
117 aRet
.push_back(aPropertyValue
);
118 aPropertyValue
.Name
= "TopBorderDistance";
119 aPropertyValue
.Value
<<= sal_Int32(bNew
? (45720 / 360) : 0);
120 aRet
.push_back(aPropertyValue
);
121 aPropertyValue
.Name
= "BottomBorderDistance";
122 aPropertyValue
.Value
<<= sal_Int32(bNew
? (45720 / 360) : 0);
123 aRet
.push_back(aPropertyValue
);
124 aPropertyValue
.Name
= "SizeType";
125 aPropertyValue
.Value
<<= text::SizeType::FIX
;
126 aRet
.push_back(aPropertyValue
);
130 void RTFSdrImport::pushParent(uno::Reference
<drawing::XShapes
> const& xParent
)
132 m_aParents
.push(xParent
);
133 m_aGraphicZOrderHelpers
.push(writerfilter::dmapper::GraphicZOrderHelper());
136 void RTFSdrImport::popParent()
138 if (!m_aGraphicZOrderHelpers
.empty())
139 m_aGraphicZOrderHelpers
.pop();
140 if (!m_aParents
.empty())
144 void RTFSdrImport::resolveDhgt(uno::Reference
<beans::XPropertySet
> const& xPropertySet
,
145 sal_Int32
const nZOrder
, bool const bOldStyle
)
147 if (!m_aGraphicZOrderHelpers
.empty())
149 writerfilter::dmapper::GraphicZOrderHelper
& rHelper
= m_aGraphicZOrderHelpers
.top();
150 xPropertySet
->setPropertyValue("ZOrder", uno::Any(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::Any(aBorderLine
));
182 void RTFSdrImport::resolveFLine(uno::Reference
<beans::XPropertySet
> const& xPropertySet
,
183 sal_Int32
const nFLine
)
186 xPropertySet
->setPropertyValue("LineStyle", uno::Any(drawing::LineStyle_NONE
));
188 xPropertySet
->setPropertyValue("LineStyle", uno::Any(drawing::LineStyle_SOLID
));
191 void RTFSdrImport::applyProperty(uno::Reference
<drawing::XShape
> const& xShape
,
192 std::u16string_view aKey
, std::u16string_view 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 (o3tl::toInt32(aValue
))
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 (o3tl::toInt32(aValue
))
228 nVertOrient
= text::VertOrientation::TOP
;
231 nVertOrient
= text::VertOrientation::CENTER
;
234 nVertOrient
= text::VertOrientation::BOTTOM
;
240 else if (aKey
== u
"fFitShapeToText")
241 obFitShapeToText
= o3tl::toInt32(aValue
) == 1;
242 else if (aKey
== u
"fFilled")
243 bFilled
= o3tl::toInt32(aValue
) == 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
= o3tl::toInt32(aValue
) * 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::Any(NormAngle36000(Degree100(nRotation
* -1)).get()));
255 if (nHoriOrient
!= 0 && xPropertySet
.is())
256 xPropertySet
->setPropertyValue("HoriOrient", uno::Any(nHoriOrient
));
257 if (nVertOrient
!= 0 && xPropertySet
.is())
258 xPropertySet
->setPropertyValue("VertOrient", uno::Any(nVertOrient
));
259 if (obFitShapeToText
.has_value() && xPropertySet
.is())
261 xPropertySet
->setPropertyValue(
262 "SizeType", uno::Any(*obFitShapeToText
? text::SizeType::MIN
: text::SizeType::FIX
));
263 xPropertySet
->setPropertyValue("FrameIsAutomaticHeight", uno::Any(*obFitShapeToText
));
265 if (!bFilled
&& xPropertySet
.is())
268 xPropertySet
->setPropertyValue("BackColorTransparency", uno::Any(sal_Int32(100)));
270 xPropertySet
->setPropertyValue("FillStyle", uno::Any(drawing::FillStyle_NONE
));
274 int RTFSdrImport::initShape(uno::Reference
<drawing::XShape
>& o_xShape
,
275 uno::Reference
<beans::XPropertySet
>& o_xPropSet
, bool& o_rIsCustomShape
,
276 RTFShape
const& rShape
, bool const bClose
,
277 ShapeOrPict
const shapeOrPict
)
279 assert(!o_xShape
.is());
280 assert(!o_xPropSet
.is());
281 o_rIsCustomShape
= false;
284 // first, find the shape type
286 auto iter
= std::find_if(rShape
.getProperties().begin(), rShape
.getProperties().end(),
287 [](const std::pair
<OUString
, OUString
>& rProperty
) {
288 return rProperty
.first
== "shapeType";
291 if (iter
== rShape
.getProperties().end())
293 if (SHAPE
== shapeOrPict
)
295 // The spec doesn't state what is the default for shapeType,
296 // Word seems to implement it as a rectangle.
297 nType
= ESCHER_ShpInst_Rectangle
;
301 // pict is picture by default but can be a rectangle too fdo#79319
302 nType
= ESCHER_ShpInst_PictureFrame
;
307 nType
= iter
->second
.toInt32();
308 if (PICT
== shapeOrPict
&& ESCHER_ShpInst_PictureFrame
!= nType
)
316 case ESCHER_ShpInst_PictureFrame
:
317 createShape("com.sun.star.drawing.GraphicObjectShape", o_xShape
, o_xPropSet
);
318 m_bTextGraphicObject
= true;
320 case ESCHER_ShpInst_Line
:
321 createShape("com.sun.star.drawing.LineShape", o_xShape
, o_xPropSet
);
323 case ESCHER_ShpInst_Rectangle
:
324 case ESCHER_ShpInst_TextBox
:
325 // If we're inside a groupshape, can't use text frames.
326 if (!bClose
&& m_aParents
.size() == 1)
328 createShape("com.sun.star.text.TextFrame", o_xShape
, o_xPropSet
);
330 std::vector
<beans::PropertyValue
> aDefaults
= getTextFrameDefaults(true);
331 for (const beans::PropertyValue
& i
: aDefaults
)
332 o_xPropSet
->setPropertyValue(i
.Name
, i
.Value
);
337 createShape("com.sun.star.drawing.CustomShape", o_xShape
, o_xPropSet
);
338 o_rIsCustomShape
= true;
343 if (o_xPropSet
.is() && !m_bTextFrame
)
345 o_xPropSet
->setPropertyValue(
347 uno::Any(sal_uInt32(0xffffff))); // White in Word, kind of blue in Writer.
348 o_xPropSet
->setPropertyValue("VertOrient", uno::Any(text::VertOrientation::NONE
));
354 void RTFSdrImport::resolve(RTFShape
& rShape
, bool bClose
, ShapeOrPict
const shapeOrPict
)
357 m_bTextFrame
= false;
358 m_bTextGraphicObject
= false;
360 uno::Reference
<drawing::XShape
> xShape
;
361 uno::Reference
<beans::XPropertySet
> xPropertySet
;
363 beans::PropertyValue aPropertyValue
;
364 awt::Rectangle aViewBox
;
365 std::vector
<beans::PropertyValue
> aPath
;
366 // Default line color is black in Word, blue in Writer.
367 uno::Any
aLineColor(COL_BLACK
);
368 // Default line width is 0.75 pt (26 mm100) in Word, 0 in Writer.
369 uno::Any
aLineWidth(sal_Int32(26));
370 sal_Int16 eWritingMode
= text::WritingMode2::LR_TB
;
371 // Groupshape support
372 std::optional
<sal_Int32
> oGroupLeft
;
373 std::optional
<sal_Int32
> oGroupTop
;
374 std::optional
<sal_Int32
> oGroupRight
;
375 std::optional
<sal_Int32
> oGroupBottom
;
376 std::optional
<sal_Int32
> oRelLeft
;
377 std::optional
<sal_Int32
> oRelTop
;
378 std::optional
<sal_Int32
> oRelRight
;
379 std::optional
<sal_Int32
> oRelBottom
;
381 // Importing these are not trivial, let the VML import do the hard work.
382 oox::vml::FillModel aFillModel
; // Gradient.
383 oox::vml::ShadowModel aShadowModel
; // Shadow.
387 std::optional
<sal_Int16
> oRelativeWidth
;
388 std::optional
<sal_Int16
> oRelativeHeight
;
389 sal_Int16 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
390 sal_Int16 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
391 std::optional
<bool> obRelFlipV
;
395 OUString aShapeText
= "";
396 OUString aFontFamily
= "";
397 float nFontSize
= 1.0;
399 sal_Int32 nContrast
= 0x10000;
400 sal_Int16 nBrightness
= 0;
403 int const nType
= initShape(xShape
, xPropertySet
, bCustom
, rShape
, bClose
, shapeOrPict
);
405 for (auto& rProperty
: rShape
.getProperties())
407 if (rProperty
.first
== "shapeType")
409 continue; // ignore: already handled by initShape
411 if (rProperty
.first
== "wzName")
415 uno::Reference
<container::XNamed
> xNamed(xShape
, uno::UNO_QUERY
);
416 xNamed
->setName(rProperty
.second
);
419 xPropertySet
->setPropertyValue("Name", uno::Any(rProperty
.second
));
421 else if (rProperty
.first
== "wzDescription")
422 xPropertySet
->setPropertyValue("Description", uno::Any(rProperty
.second
));
423 else if (rProperty
.first
== "gtextUNICODE")
424 aShapeText
= rProperty
.second
;
425 else if (rProperty
.first
== "gtextFont")
426 aFontFamily
= rProperty
.second
;
427 else if (rProperty
.first
== "gtextSize")
429 // RTF size is multiplied by 2^16
430 nFontSize
= static_cast<float>(rProperty
.second
.toUInt32()) / RTF_MULTIPLIER
;
432 else if (rProperty
.first
== "pib")
434 m_rImport
.setDestinationText(rProperty
.second
);
437 else if (rProperty
.first
== "fillColor" && xPropertySet
.is())
439 aAny
<<= msfilter::util::BGRToRGB(rProperty
.second
.toUInt32());
441 xPropertySet
->setPropertyValue("BackColor", aAny
);
443 xPropertySet
->setPropertyValue("FillColor", aAny
);
445 // fillType will decide, possible it'll be the start color of a gradient.
448 + msfilter::util::ConvertColorOU(Color(ColorTransparency
, aAny
.get
<sal_Int32
>()));
450 else if (rProperty
.first
== "fillBackColor")
451 // fillType will decide, possible it'll be the end color of a gradient.
452 aFillModel
.moColor2
= "#"
453 + msfilter::util::ConvertColorOU(
454 msfilter::util::BGRToRGB(rProperty
.second
.toInt32()));
455 else if (rProperty
.first
== "lineColor")
456 aLineColor
<<= msfilter::util::BGRToRGB(rProperty
.second
.toInt32());
457 else if (rProperty
.first
== "lineBackColor")
458 ; // Ignore: complementer of lineColor
459 else if (rProperty
.first
== "txflTextFlow" && xPropertySet
.is())
461 switch (rProperty
.second
.toInt32())
463 case 1: // Top to bottom ASCII font
464 case 3: // Top to bottom non-ASCII font
465 eWritingMode
= text::WritingMode2::TB_RL
;
467 case 2: // Bottom to top non-ASCII font
468 eWritingMode
= text::WritingMode2::BT_LR
;
472 else if (rProperty
.first
== "fLine" && xPropertySet
.is())
473 resolveFLine(xPropertySet
, rProperty
.second
.toInt32());
474 else if (rProperty
.first
== "fillOpacity" && xPropertySet
.is())
476 int opacity
= 100 - (rProperty
.second
.toInt32()) * 100 / RTF_MULTIPLIER
;
477 xPropertySet
->setPropertyValue("FillTransparence", uno::Any(sal_uInt32(opacity
)));
479 else if (rProperty
.first
== "lineWidth")
480 aLineWidth
<<= rProperty
.second
.toInt32() / 360;
481 else if (rProperty
.first
== "pVerticies")
483 std::vector
<drawing::EnhancedCustomShapeParameterPair
> aCoordinates
;
484 sal_Int32 nSize
= 0; // Size of a token (its value is hardwired in the exporter)
485 sal_Int32 nCount
= 0; // Number of tokens
486 sal_Int32 nCharIndex
= 0; // Character index
489 std::u16string_view aToken
= o3tl::getToken(rProperty
.second
, 0, ';', nCharIndex
);
491 nSize
= o3tl::toInt32(aToken
);
493 nCount
= o3tl::toInt32(aToken
);
494 else if (!aToken
.empty())
496 // The coordinates are in an (x,y) form.
497 aToken
= aToken
.substr(1, aToken
.size() - 2);
499 sal_Int32 nX
= o3tl::toInt32(o3tl::getToken(aToken
, 0, ',', nI
));
501 = (nI
>= 0) ? o3tl::toInt32(o3tl::getToken(aToken
, 0, ',', nI
)) : 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;
521 = o3tl::toInt32(o3tl::getToken(rProperty
.second
, 0, ';', nCharIndex
));
528 sal_Int32 nPoints
= 1;
529 if (nSeg
>= 0x2000 && nSeg
< 0x20FF)
531 nPoints
= nSeg
& 0x0FFF;
535 drawing::EnhancedCustomShapeSegment aSegment
;
538 case 0x0001: // lineto
539 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
540 aSegment
.Count
= sal_Int32(1);
541 aSegments
.push_back(aSegment
);
543 case 0x4000: // moveto
544 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::MOVETO
;
545 aSegment
.Count
= sal_Int32(1);
546 aSegments
.push_back(aSegment
);
548 case 0x2000: // curveto
549 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::CURVETO
;
550 aSegment
.Count
= nPoints
;
551 aSegments
.push_back(aSegment
);
553 case 0xb300: // arcto
554 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::ARCTO
;
555 aSegment
.Count
= sal_Int32(0);
556 aSegments
.push_back(aSegment
);
559 case 0xaa00: // nofill
560 case 0xab00: // nostroke
561 case 0x6001: // close
565 = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH
;
566 aSegment
.Count
= sal_Int32(0);
567 aSegments
.push_back(aSegment
);
569 default: // given number of lineto elements
570 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
571 aSegment
.Count
= nSeg
;
572 aSegments
.push_back(aSegment
);
576 } while (nCharIndex
>= 0);
577 aPropertyValue
.Name
= "Segments";
578 aPropertyValue
.Value
<<= comphelper::containerToSequence(aSegments
);
579 aPath
.push_back(aPropertyValue
);
581 else if (rProperty
.first
== "geoLeft")
582 aViewBox
.X
= rProperty
.second
.toInt32();
583 else if (rProperty
.first
== "geoTop")
584 aViewBox
.Y
= rProperty
.second
.toInt32();
585 else if (rProperty
.first
== "geoRight")
586 aViewBox
.Width
= rProperty
.second
.toInt32();
587 else if (rProperty
.first
== "geoBottom")
588 aViewBox
.Height
= rProperty
.second
.toInt32();
589 else if (rProperty
.first
== "dhgt")
591 // dhgt is Word 2007, \shpz is Word 97-2003, the later has priority.
593 resolveDhgt(xPropertySet
, rProperty
.second
.toInt32(), /*bOldStyle=*/false);
595 // These are in EMU, convert to mm100.
596 else if (rProperty
.first
== "dxTextLeft")
598 if (xPropertySet
.is())
599 xPropertySet
->setPropertyValue("LeftBorderDistance",
600 uno::Any(rProperty
.second
.toInt32() / 360));
602 else if (rProperty
.first
== "dyTextTop")
604 if (xPropertySet
.is())
605 xPropertySet
->setPropertyValue("TopBorderDistance",
606 uno::Any(rProperty
.second
.toInt32() / 360));
608 else if (rProperty
.first
== "dxTextRight")
610 if (xPropertySet
.is())
611 xPropertySet
->setPropertyValue("RightBorderDistance",
612 uno::Any(rProperty
.second
.toInt32() / 360));
614 else if (rProperty
.first
== "dyTextBottom")
616 if (xPropertySet
.is())
617 xPropertySet
->setPropertyValue("BottomBorderDistance",
618 uno::Any(rProperty
.second
.toInt32() / 360));
620 else if (rProperty
.first
== "dxWrapDistLeft")
622 if (m_bTextGraphicObject
)
623 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distL
,
624 new RTFValue(rProperty
.second
.toInt32()));
625 else if (xPropertySet
.is())
626 xPropertySet
->setPropertyValue("LeftMargin",
627 uno::Any(rProperty
.second
.toInt32() / 360));
629 else if (rProperty
.first
== "dyWrapDistTop")
631 if (m_bTextGraphicObject
)
632 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distT
,
633 new RTFValue(rProperty
.second
.toInt32()));
634 else if (xPropertySet
.is())
635 xPropertySet
->setPropertyValue("TopMargin",
636 uno::Any(rProperty
.second
.toInt32() / 360));
638 else if (rProperty
.first
== "dxWrapDistRight")
640 if (m_bTextGraphicObject
)
641 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distR
,
642 new RTFValue(rProperty
.second
.toInt32()));
643 else if (xPropertySet
.is())
644 xPropertySet
->setPropertyValue("RightMargin",
645 uno::Any(rProperty
.second
.toInt32() / 360));
647 else if (rProperty
.first
== "dyWrapDistBottom")
649 if (m_bTextGraphicObject
)
650 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distB
,
651 new RTFValue(rProperty
.second
.toInt32()));
652 else if (xPropertySet
.is())
653 xPropertySet
->setPropertyValue("BottomMargin",
654 uno::Any(rProperty
.second
.toInt32() / 360));
656 else if (rProperty
.first
== "fillType")
658 switch (rProperty
.second
.toInt32())
660 case 7: // Shade using the fillAngle
661 aFillModel
.moType
= oox::XML_gradient
;
664 SAL_INFO("writerfilter",
665 "TODO handle fillType value '" << rProperty
.second
<< "'");
669 else if (rProperty
.first
== "fillFocus")
670 aFillModel
.moFocus
= rProperty
.second
.toDouble() / 100; // percent
671 else if (rProperty
.first
== "fShadow" && xPropertySet
.is())
673 if (rProperty
.second
.toInt32() == 1)
674 aShadowModel
.mbHasShadow
= true;
676 else if (rProperty
.first
== "shadowColor")
677 aShadowModel
.moColor
= "#"
678 + msfilter::util::ConvertColorOU(
679 msfilter::util::BGRToRGB(rProperty
.second
.toInt32()));
680 else if (rProperty
.first
== "shadowOffsetX")
682 aShadowModel
.moOffset
= OUString::number(rProperty
.second
.toDouble() / 12700) + "pt";
683 else if (rProperty
.first
== "posh" || rProperty
.first
== "posv"
684 || rProperty
.first
== "fFitShapeToText" || rProperty
.first
== "fFilled"
685 || rProperty
.first
== "rotation")
686 applyProperty(xShape
, rProperty
.first
, rProperty
.second
);
687 else if (rProperty
.first
== "posrelh")
689 switch (rProperty
.second
.toInt32())
692 rShape
.setHoriOrientRelation(text::RelOrientation::PAGE_FRAME
);
698 else if (rProperty
.first
== "posrelv")
700 switch (rProperty
.second
.toInt32())
703 rShape
.setVertOrientRelation(text::RelOrientation::PAGE_FRAME
);
709 else if (rProperty
.first
== "groupLeft")
710 oGroupLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
711 else if (rProperty
.first
== "groupTop")
712 oGroupTop
= convertTwipToMm100(rProperty
.second
.toInt32());
713 else if (rProperty
.first
== "groupRight")
714 oGroupRight
= convertTwipToMm100(rProperty
.second
.toInt32());
715 else if (rProperty
.first
== "groupBottom")
716 oGroupBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
717 else if (rProperty
.first
== "relLeft")
718 oRelLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
719 else if (rProperty
.first
== "relTop")
720 oRelTop
= convertTwipToMm100(rProperty
.second
.toInt32());
721 else if (rProperty
.first
== "relRight")
722 oRelRight
= convertTwipToMm100(rProperty
.second
.toInt32());
723 else if (rProperty
.first
== "relBottom")
724 oRelBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
725 else if (rProperty
.first
== "fBehindDocument")
726 bOpaque
= !rProperty
.second
.toInt32();
727 else if (rProperty
.first
== "pctHoriz" || rProperty
.first
== "pctVert")
729 sal_Int16 nPercentage
= rtl::math::round(rProperty
.second
.toDouble() / 10);
732 std::optional
<sal_Int16
>& rPercentage
733 = rProperty
.first
== "pctHoriz" ? oRelativeWidth
: oRelativeHeight
;
734 rPercentage
= nPercentage
;
737 else if (rProperty
.first
== "sizerelh")
739 if (xPropertySet
.is())
741 switch (rProperty
.second
.toInt32())
744 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
747 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
750 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: "
751 << rProperty
.second
);
756 else if (rProperty
.first
== "sizerelv")
758 if (xPropertySet
.is())
760 switch (rProperty
.second
.toInt32())
763 nRelativeHeightRelation
= text::RelOrientation::FRAME
;
766 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
769 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: "
770 << rProperty
.second
);
775 else if (rProperty
.first
== "fHorizRule") // TODO: what does "fStandardHR" do?
777 // horizontal rule: relative width defaults to 100% of paragraph
778 // TODO: does it have a default height?
781 oRelativeWidth
= 100;
783 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
784 if (xPropertySet
.is())
786 sal_Int16
const nVertOrient
= text::VertOrientation::CENTER
;
787 xPropertySet
->setPropertyValue("VertOrient", uno::Any(nVertOrient
));
790 else if (rProperty
.first
== "pctHR")
792 // horizontal rule relative width in permille
793 oRelativeWidth
= rProperty
.second
.toInt32() / 10;
795 else if (rProperty
.first
== "dxHeightHR")
797 // horizontal rule height
798 sal_uInt32
const nHeight(convertTwipToMm100(rProperty
.second
.toInt32()));
799 rShape
.setBottom(rShape
.getTop() + nHeight
);
801 else if (rProperty
.first
== "dxWidthHR")
803 // horizontal rule width
804 sal_uInt32
const nWidth(convertTwipToMm100(rProperty
.second
.toInt32()));
805 rShape
.setRight(rShape
.getLeft() + nWidth
);
807 else if (rProperty
.first
== "alignHR")
809 // horizontal orientation *for horizontal rule*
810 sal_Int16 nHoriOrient
= text::HoriOrientation::NONE
;
811 switch (rProperty
.second
.toInt32())
814 nHoriOrient
= text::HoriOrientation::LEFT
;
817 nHoriOrient
= text::HoriOrientation::CENTER
;
820 nHoriOrient
= text::HoriOrientation::RIGHT
;
823 if (xPropertySet
.is() && text::HoriOrientation::NONE
!= nHoriOrient
)
825 xPropertySet
->setPropertyValue("HoriOrient", uno::Any(nHoriOrient
));
828 else if (rProperty
.first
== "pWrapPolygonVertices")
830 RTFSprms aPolygonSprms
;
831 sal_Int32 nSize
= 0; // Size of a token
832 sal_Int32 nCount
= 0; // Number of tokens
833 sal_Int32 nCharIndex
= 0; // Character index
836 std::u16string_view aToken
= o3tl::getToken(rProperty
.second
, 0, ';', nCharIndex
);
838 nSize
= o3tl::toInt32(aToken
);
840 nCount
= o3tl::toInt32(aToken
);
841 else if (!aToken
.empty())
843 // The coordinates are in an (x,y) form.
844 aToken
= aToken
.substr(1, aToken
.size() - 2);
846 sal_Int32 nX
= o3tl::toInt32(o3tl::getToken(aToken
, 0, ',', nI
));
848 = (nI
>= 0) ? o3tl::toInt32(o3tl::getToken(aToken
, 0, ',', nI
)) : 0;
849 RTFSprms aPathAttributes
;
850 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_x
, new RTFValue(nX
));
851 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_y
, new RTFValue(nY
));
852 aPolygonSprms
.set(NS_ooxml::LN_CT_WrapPath_lineTo
,
853 new RTFValue(aPathAttributes
), RTFOverwrite::NO_APPEND
);
855 } while (nCharIndex
>= 0);
856 rShape
.getWrapPolygonSprms() = aPolygonSprms
;
858 else if (rProperty
.first
== "fRelFlipV")
859 obRelFlipV
= rProperty
.second
.toInt32() == 1;
860 else if (rProperty
.first
== "fFlipH")
861 obFlipH
= rProperty
.second
.toInt32() == 1;
862 else if (rProperty
.first
== "fFlipV")
863 obFlipV
= rProperty
.second
.toInt32() == 1;
864 else if (rProperty
.first
== "pictureContrast")
867 nContrast
= rProperty
.second
.toInt32();
868 if (nContrast
< 0x10000)
870 nContrast
*= 101; // 100 + 1 to round
871 nContrast
/= 0x10000;
875 else if (rProperty
.first
== "pictureBrightness")
877 // Blacklevel / brightness.
878 nBrightness
= rProperty
.second
.toInt32();
879 if (nBrightness
!= 0)
885 SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty
.first
<< "':'"
886 << rProperty
.second
<< "'");
889 if (xPropertySet
.is())
891 resolveLineColorAndWidth(m_bTextFrame
, xPropertySet
, aLineColor
, aLineWidth
);
894 bool bOldStyle
= m_aParents
.size() > 1;
895 resolveDhgt(xPropertySet
, rShape
.getZ(), bOldStyle
);
898 xPropertySet
->setPropertyValue("WritingMode", uno::Any(eWritingMode
));
900 // Only Writer textframes implement text::WritingMode2.
901 xPropertySet
->setPropertyValue("TextWritingMode",
902 uno::Any(text::WritingMode(eWritingMode
)));
905 if (!m_aParents
.empty() && m_aParents
.top().is() && !m_bTextFrame
)
906 m_aParents
.top()->add(xShape
);
908 if (nContrast
== -70 && nBrightness
== 70 && xPropertySet
.is())
910 // Map MSO 'washout' to our watermark colormode.
911 xPropertySet
->setPropertyValue("GraphicColorMode", uno::Any(drawing::ColorMode_WATERMARK
));
914 if (bCustom
&& xShape
.is() && !bPib
)
916 uno::Reference
<drawing::XEnhancedCustomShapeDefaulter
> xDefaulter(xShape
, uno::UNO_QUERY
);
917 xDefaulter
->createCustomShapeDefaults(OUString::number(nType
));
921 if (bCustom
&& !aShapeText
.isEmpty())
923 uno::Reference
<text::XTextRange
> xTextRange(xShape
, uno::UNO_QUERY
);
925 xTextRange
->setString(aShapeText
);
927 xPropertySet
->setPropertyValue("CharFontName", uno::Any(aFontFamily
));
928 xPropertySet
->setPropertyValue("CharHeight", uno::Any(nFontSize
));
931 // Creating CustomShapeGeometry property
932 if (bCustom
&& xPropertySet
.is())
934 bool bChanged
= false;
935 comphelper::SequenceAsHashMap
aCustomShapeGeometry(
936 xPropertySet
->getPropertyValue("CustomShapeGeometry"));
938 if (aViewBox
.X
|| aViewBox
.Y
|| aViewBox
.Width
|| aViewBox
.Height
)
940 aViewBox
.Width
-= aViewBox
.X
;
941 aViewBox
.Height
-= aViewBox
.Y
;
942 aCustomShapeGeometry
["ViewBox"] <<= aViewBox
;
948 aCustomShapeGeometry
["Path"] <<= comphelper::containerToSequence(aPath
);
952 if (!aShapeText
.isEmpty())
954 uno::Sequence
<beans::PropertyValue
> aSequence(comphelper::InitPropertySequence({
955 { "TextPath", uno::Any(true) },
957 aCustomShapeGeometry
["TextPath"] <<= aSequence
;
958 xPropertySet
->setPropertyValue("TextAutoGrowHeight", uno::Any(false));
959 xPropertySet
->setPropertyValue("TextAutoGrowWidth", uno::Any(false));
965 xPropertySet
->setPropertyValue(
966 "CustomShapeGeometry",
967 uno::Any(aCustomShapeGeometry
.getAsConstPropertyValueList()));
971 if (obRelFlipV
.has_value() && xPropertySet
.is())
973 if (nType
== ESCHER_ShpInst_Line
)
975 // Line shape inside group shape: get the polygon sequence and transform it.
976 uno::Sequence
<uno::Sequence
<awt::Point
>> aPolyPolySequence
;
977 if ((xPropertySet
->getPropertyValue("PolyPolygon") >>= aPolyPolySequence
)
978 && aPolyPolySequence
.hasElements())
980 uno::Sequence
<awt::Point
>& rPolygon
= aPolyPolySequence
.getArray()[0];
981 basegfx::B2DPolygon aPoly
;
982 for (const awt::Point
& rPoint
: std::as_const(rPolygon
))
984 aPoly
.append(basegfx::B2DPoint(rPoint
.X
, rPoint
.Y
));
986 basegfx::B2DHomMatrix aTransformation
;
987 aTransformation
.scale(1.0, *obRelFlipV
? -1.0 : 1.0);
988 aPoly
.transform(aTransformation
);
989 auto pPolygon
= rPolygon
.getArray();
990 for (sal_Int32 i
= 0; i
< rPolygon
.getLength(); ++i
)
992 basegfx::B2DPoint
aPoint(aPoly
.getB2DPoint(i
));
993 pPolygon
[i
] = awt::Point(static_cast<sal_Int32
>(aPoint
.getX()),
994 static_cast<sal_Int32
>(aPoint
.getY()));
996 xPropertySet
->setPropertyValue("PolyPolygon", uno::Any(aPolyPolySequence
));
1001 // Set position and size
1004 sal_Int32 nLeft
= rShape
.getLeft();
1005 sal_Int32 nTop
= rShape
.getTop();
1007 bool bInShapeGroup
= oGroupLeft
&& oGroupTop
&& oGroupRight
&& oGroupBottom
&& oRelLeft
1008 && oRelTop
&& oRelRight
&& oRelBottom
;
1012 // See lclGetAbsPoint() in the VML import: rShape is the group shape, oGroup is its coordinate system, oRel is the relative child shape.
1013 sal_Int32 nShapeWidth
= rShape
.getRight() - rShape
.getLeft();
1014 sal_Int32 nShapeHeight
= rShape
.getBottom() - rShape
.getTop();
1015 sal_Int32 nCoordSysWidth
= *oGroupRight
- *oGroupLeft
;
1016 sal_Int32 nCoordSysHeight
= *oGroupBottom
- *oGroupTop
;
1017 double fWidthRatio
= static_cast<double>(nShapeWidth
) / nCoordSysWidth
;
1018 double fHeightRatio
= static_cast<double>(nShapeHeight
) / nCoordSysHeight
;
1019 nLeft
= static_cast<sal_Int32
>(rShape
.getLeft()
1020 + fWidthRatio
* (*oRelLeft
- *oGroupLeft
));
1021 nTop
= static_cast<sal_Int32
>(rShape
.getTop() + fHeightRatio
* (*oRelTop
- *oGroupTop
));
1023 // See lclGetAbsRect() in the VML import.
1024 aSize
.Width
= std::lround(fWidthRatio
* (*oRelRight
- *oRelLeft
));
1025 aSize
.Height
= std::lround(fHeightRatio
* (*oRelBottom
- *oRelTop
));
1030 xPropertySet
->setPropertyValue("HoriOrientPosition", uno::Any(nLeft
));
1031 xPropertySet
->setPropertyValue("VertOrientPosition", uno::Any(nTop
));
1034 xShape
->setPosition(awt::Point(nLeft
, nTop
));
1037 xShape
->setSize(aSize
);
1039 xShape
->setSize(awt::Size(rShape
.getRight() - rShape
.getLeft(),
1040 rShape
.getBottom() - rShape
.getTop()));
1042 if (obFlipH
|| obFlipV
)
1046 // This has to be set after position and size is set, otherwise flip will affect the position.
1047 comphelper::SequenceAsHashMap
aCustomShapeGeometry(
1048 xPropertySet
->getPropertyValue("CustomShapeGeometry"));
1050 aCustomShapeGeometry
["MirroredX"] <<= true;
1052 aCustomShapeGeometry
["MirroredY"] <<= true;
1053 xPropertySet
->setPropertyValue(
1054 "CustomShapeGeometry",
1055 uno::Any(aCustomShapeGeometry
.getAsConstPropertyValueList()));
1057 else if (SdrObject
* pObject
= SdrObject::getSdrObjectFromXShape(xShape
))
1059 Point aRef1
= pObject
->GetSnapRect().Center();
1063 // Horizontal mirror means a vertical reference line.
1068 // Vertical mirror means a horizontal reference line.
1071 pObject
->Mirror(aRef1
, aRef2
);
1075 if (rShape
.getHoriOrientRelation() != 0)
1076 xPropertySet
->setPropertyValue("HoriOrientRelation",
1077 uno::Any(rShape
.getHoriOrientRelation()));
1078 if (rShape
.getVertOrientRelation() != 0)
1079 xPropertySet
->setPropertyValue("VertOrientRelation",
1080 uno::Any(rShape
.getVertOrientRelation()));
1081 if (rShape
.getWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
)
1082 xPropertySet
->setPropertyValue("Surround", uno::Any(rShape
.getWrap()));
1083 oox::ModelObjectHelper
aModelObjectHelper(m_rImport
.getModelFactory());
1084 if (aFillModel
.moType
.has_value())
1086 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1087 aFillModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1088 // Sets the FillStyle and FillGradient UNO properties.
1089 oox::PropertySet(xShape
).setProperties(aPropMap
);
1092 if (aShadowModel
.mbHasShadow
)
1094 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1095 aShadowModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1096 // Sets the ShadowFormat UNO property.
1097 oox::PropertySet(xShape
).setProperties(aPropMap
);
1099 xPropertySet
->setPropertyValue("AnchorType",
1100 uno::Any(text::TextContentAnchorType_AT_CHARACTER
));
1101 xPropertySet
->setPropertyValue("Opaque", uno::Any(bOpaque
));
1104 xPropertySet
->setPropertyValue("RelativeWidth", uno::Any(*oRelativeWidth
));
1105 xPropertySet
->setPropertyValue("RelativeWidthRelation",
1106 uno::Any(nRelativeWidthRelation
));
1108 if (oRelativeHeight
)
1110 xPropertySet
->setPropertyValue("RelativeHeight", uno::Any(*oRelativeHeight
));
1111 xPropertySet
->setPropertyValue("RelativeHeightRelation",
1112 uno::Any(nRelativeHeightRelation
));
1118 m_rImport
.resolvePict(false, xShape
);
1121 if (nType
== ESCHER_ShpInst_PictureFrame
) // picture frame
1123 assert(!m_bTextFrame
);
1124 if (!bPib
) // ??? not sure if the early return should be removed on else?
1126 m_xShape
= xShape
; // store it for later resolvePict call
1129 // Handle horizontal flip.
1130 if (obFlipH
&& xPropertySet
.is())
1131 xPropertySet
->setPropertyValue("IsMirrored", uno::Any(true));
1135 if (m_rImport
.isInBackground())
1137 RTFSprms aAttributes
;
1138 aAttributes
.set(NS_ooxml::LN_CT_Background_color
,
1139 new RTFValue(xPropertySet
->getPropertyValue("FillColor").get
<sal_Int32
>()));
1140 m_rImport
.Mapper().props(new RTFReferenceProperties(std::move(aAttributes
)));
1142 uno::Reference
<lang::XComponent
> xComponent(xShape
, uno::UNO_QUERY
);
1143 xComponent
->dispose();
1147 // Send it to dmapper
1150 m_rImport
.Mapper().startShape(xShape
);
1153 m_rImport
.Mapper().endShape();
1157 // If the shape has an inner shape, the inner object's properties should not be influenced by
1159 rShape
.getProperties().clear();
1164 void RTFSdrImport::close() { m_rImport
.Mapper().endShape(); }
1166 void RTFSdrImport::append(std::u16string_view aKey
, std::u16string_view aValue
)
1168 applyProperty(m_xShape
, aKey
, aValue
);
1171 void RTFSdrImport::appendGroupProperty(std::u16string_view aKey
, std::u16string_view aValue
)
1173 if (m_aParents
.empty())
1175 uno::Reference
<drawing::XShape
> xShape(m_aParents
.top(), uno::UNO_QUERY
);
1177 applyProperty(xShape
, aKey
, aValue
);
1180 } // namespace writerfilter
1182 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */