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"
12 #include <com/sun/star/container/XNamed.hpp>
13 #include <com/sun/star/drawing/FillStyle.hpp>
14 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
15 #include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
16 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
17 #include <com/sun/star/drawing/LineStyle.hpp>
18 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
19 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
20 #include <com/sun/star/lang/XServiceInfo.hpp>
21 #include <com/sun/star/table/BorderLine2.hpp>
22 #include <com/sun/star/text/HoriOrientation.hpp>
23 #include <com/sun/star/text/RelOrientation.hpp>
24 #include <com/sun/star/text/SizeType.hpp>
25 #include <com/sun/star/text/VertOrientation.hpp>
26 #include <com/sun/star/text/WrapTextMode.hpp>
27 #include <com/sun/star/text/WritingMode.hpp>
28 #include <com/sun/star/text/WritingMode2.hpp>
29 #include <com/sun/star/text/TextContentAnchorType.hpp>
30 #include <com/sun/star/text/XTextRange.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <ooxml/resourceids.hxx>
33 #include <filter/msfilter/escherex.hxx>
34 #include <filter/msfilter/util.hxx>
35 #include <filter/msfilter/rtfutil.hxx>
36 #include <sal/log.hxx>
37 #include <svx/svdtrans.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <comphelper/propertyvalue.hxx>
40 #include <comphelper/propertysequence.hxx>
41 #include "rtfreferenceproperties.hxx"
42 #include <oox/vml/vmlformatting.hxx>
43 #include <oox/helper/modelobjecthelper.hxx>
44 #include <oox/drawingml/shapepropertymap.hxx>
45 #include <oox/helper/propertyset.hxx>
46 #include <boost/logic/tribool.hpp>
47 #include <basegfx/matrix/b2dhommatrix.hxx>
48 #include <dmapper/GraphicZOrderHelper.hxx>
49 #include "rtfdocumentimpl.hxx"
51 using namespace com::sun::star
;
53 namespace writerfilter
57 RTFSdrImport::RTFSdrImport(RTFDocumentImpl
& rDocument
,
58 uno::Reference
<lang::XComponent
> const& xDstDoc
)
59 : m_rImport(rDocument
)
61 , m_bTextGraphicObject(false)
64 uno::Reference
<drawing::XDrawPageSupplier
> xDrawings(xDstDoc
, uno::UNO_QUERY
);
66 m_aParents
.push(xDrawings
->getDrawPage());
67 m_aGraphicZOrderHelpers
.push(writerfilter::dmapper::GraphicZOrderHelper());
70 RTFSdrImport::~RTFSdrImport()
72 if (!m_aGraphicZOrderHelpers
.empty())
73 m_aGraphicZOrderHelpers
.pop();
74 if (!m_aParents
.empty())
78 void RTFSdrImport::createShape(const OUString
& rService
, uno::Reference
<drawing::XShape
>& xShape
,
79 uno::Reference
<beans::XPropertySet
>& xPropertySet
)
81 if (m_rImport
.getModelFactory().is())
82 xShape
.set(m_rImport
.getModelFactory()->createInstance(rService
), uno::UNO_QUERY
);
83 xPropertySet
.set(xShape
, uno::UNO_QUERY
);
86 std::vector
<beans::PropertyValue
> RTFSdrImport::getTextFrameDefaults(bool bNew
)
88 std::vector
<beans::PropertyValue
> aRet
;
89 beans::PropertyValue aPropertyValue
;
91 aPropertyValue
.Name
= "HoriOrient";
92 aPropertyValue
.Value
<<= text::HoriOrientation::NONE
;
93 aRet
.push_back(aPropertyValue
);
94 aPropertyValue
.Name
= "HoriOrientRelation";
95 aPropertyValue
.Value
<<= text::RelOrientation::FRAME
;
96 aRet
.push_back(aPropertyValue
);
97 aPropertyValue
.Name
= "VertOrient";
98 aPropertyValue
.Value
<<= text::VertOrientation::NONE
;
99 aRet
.push_back(aPropertyValue
);
100 aPropertyValue
.Name
= "VertOrientRelation";
101 aPropertyValue
.Value
<<= text::RelOrientation::FRAME
;
102 aRet
.push_back(aPropertyValue
);
105 aPropertyValue
.Name
= "BackColorTransparency";
106 aPropertyValue
.Value
<<= sal_Int32(100);
107 aRet
.push_back(aPropertyValue
);
109 // See the spec, new-style frame default margins are specified in EMUs.
110 aPropertyValue
.Name
= "LeftBorderDistance";
111 aPropertyValue
.Value
<<= sal_Int32(bNew
? (91440 / 360) : 0);
112 aRet
.push_back(aPropertyValue
);
113 aPropertyValue
.Name
= "RightBorderDistance";
114 aPropertyValue
.Value
<<= sal_Int32(bNew
? (91440 / 360) : 0);
115 aRet
.push_back(aPropertyValue
);
116 aPropertyValue
.Name
= "TopBorderDistance";
117 aPropertyValue
.Value
<<= sal_Int32(bNew
? (45720 / 360) : 0);
118 aRet
.push_back(aPropertyValue
);
119 aPropertyValue
.Name
= "BottomBorderDistance";
120 aPropertyValue
.Value
<<= sal_Int32(bNew
? (45720 / 360) : 0);
121 aRet
.push_back(aPropertyValue
);
122 aPropertyValue
.Name
= "SizeType";
123 aPropertyValue
.Value
<<= text::SizeType::FIX
;
124 aRet
.push_back(aPropertyValue
);
128 void RTFSdrImport::pushParent(uno::Reference
<drawing::XShapes
> const& xParent
)
130 m_aParents
.push(xParent
);
131 m_aGraphicZOrderHelpers
.push(writerfilter::dmapper::GraphicZOrderHelper());
134 void RTFSdrImport::popParent()
136 if (!m_aGraphicZOrderHelpers
.empty())
137 m_aGraphicZOrderHelpers
.pop();
138 if (!m_aParents
.empty())
142 void RTFSdrImport::resolveDhgt(uno::Reference
<beans::XPropertySet
> const& xPropertySet
,
143 sal_Int32
const nZOrder
, bool const bOldStyle
)
145 if (!m_aGraphicZOrderHelpers
.empty())
147 writerfilter::dmapper::GraphicZOrderHelper
& rHelper
= m_aGraphicZOrderHelpers
.top();
148 xPropertySet
->setPropertyValue("ZOrder",
149 uno::makeAny(rHelper
.findZOrder(nZOrder
, bOldStyle
)));
150 rHelper
.addItem(xPropertySet
, nZOrder
);
154 void RTFSdrImport::resolveLineColorAndWidth(bool bTextFrame
,
155 const uno::Reference
<beans::XPropertySet
>& xPropertySet
,
156 uno::Any
const& rLineColor
, uno::Any
const& rLineWidth
)
160 xPropertySet
->setPropertyValue("LineColor", rLineColor
);
161 xPropertySet
->setPropertyValue("LineWidth", rLineWidth
);
165 static const char* aBorders
[]
166 = { "TopBorder", "LeftBorder", "BottomBorder", "RightBorder" };
167 for (const char* pBorder
: aBorders
)
169 auto aBorderLine
= xPropertySet
->getPropertyValue(OUString::createFromAscii(pBorder
))
170 .get
<table::BorderLine2
>();
171 if (rLineColor
.hasValue())
172 aBorderLine
.Color
= rLineColor
.get
<sal_Int32
>();
173 if (rLineWidth
.hasValue())
174 aBorderLine
.LineWidth
= rLineWidth
.get
<sal_Int32
>();
175 xPropertySet
->setPropertyValue(OUString::createFromAscii(pBorder
),
176 uno::makeAny(aBorderLine
));
181 void RTFSdrImport::resolveFLine(uno::Reference
<beans::XPropertySet
> const& xPropertySet
,
182 sal_Int32
const nFLine
)
185 xPropertySet
->setPropertyValue("LineStyle", uno::makeAny(drawing::LineStyle_NONE
));
187 xPropertySet
->setPropertyValue("LineStyle", uno::makeAny(drawing::LineStyle_SOLID
));
190 void RTFSdrImport::applyProperty(uno::Reference
<drawing::XShape
> const& xShape
,
191 const OUString
& aKey
, const OUString
& aValue
)
193 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, uno::UNO_QUERY
);
194 sal_Int16 nHoriOrient
= 0;
195 sal_Int16 nVertOrient
= 0;
196 boost::logic::tribool
obFitShapeToText(boost::logic::indeterminate
);
201 switch (aValue
.toInt32())
204 nHoriOrient
= text::HoriOrientation::LEFT
;
207 nHoriOrient
= text::HoriOrientation::CENTER
;
210 nHoriOrient
= text::HoriOrientation::RIGHT
;
213 nHoriOrient
= text::HoriOrientation::INSIDE
;
216 nHoriOrient
= text::HoriOrientation::OUTSIDE
;
222 else if (aKey
== "posv")
224 switch (aValue
.toInt32())
227 nVertOrient
= text::VertOrientation::TOP
;
230 nVertOrient
= text::VertOrientation::CENTER
;
233 nVertOrient
= text::VertOrientation::BOTTOM
;
239 else if (aKey
== "fFitShapeToText")
240 obFitShapeToText
= aValue
.toInt32() == 1;
241 else if (aKey
== "fFilled")
242 bFilled
= aValue
.toInt32() == 1;
243 else if (aKey
== "rotation")
245 // See DffPropertyReader::Fix16ToAngle(): in RTF, positive rotation angles are clockwise, we have them as counter-clockwise.
246 // Additionally, RTF type is 0..360*2^16, our is 0..360*100.
247 sal_Int32 nRotation
= aValue
.toInt32() * 100 / RTF_MULTIPLIER
;
248 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xShape
, uno::UNO_QUERY
);
249 if (!xServiceInfo
->supportsService("com.sun.star.text.TextFrame"))
250 xPropertySet
->setPropertyValue(
252 uno::makeAny(sal_Int32(NormAngle36000(static_cast<long>(nRotation
) * -1))));
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 (!boost::logic::indeterminate(obFitShapeToText
) && xPropertySet
.is())
261 xPropertySet
->setPropertyValue(
262 "SizeType", uno::makeAny(obFitShapeToText
? text::SizeType::MIN
: text::SizeType::FIX
));
263 xPropertySet
->setPropertyValue("FrameIsAutomaticHeight",
264 uno::makeAny(static_cast<bool>(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(
288 rShape
.aProperties
.begin(), rShape
.aProperties
.end(),
289 [](std::pair
<OUString
, OUString
> aProperty
) { return aProperty
.first
== "shapeType"; });
291 if (iter
== rShape
.aProperties
.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 (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::makeAny(sal_uInt32(0xffffff))); // White in Word, kind of blue in Writer.
348 o_xPropSet
->setPropertyValue("VertOrient", uno::makeAny(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
= uno::makeAny(COL_BLACK
);
368 // Default line width is 0.75 pt (26 mm100) in Word, 0 in Writer.
369 uno::Any aLineWidth
= uno::makeAny(sal_Int32(26));
370 sal_Int16 eWritingMode
= text::WritingMode2::LR_TB
;
371 // Groupshape support
372 boost::optional
<sal_Int32
> oGroupLeft
, oGroupTop
, oGroupRight
, oGroupBottom
;
373 boost::optional
<sal_Int32
> oRelLeft
, oRelTop
, oRelRight
, oRelBottom
;
375 // Importing these are not trivial, let the VML import do the hard work.
376 oox::vml::FillModel aFillModel
; // Gradient.
377 oox::vml::ShadowModel aShadowModel
; // Shadow.
381 boost::optional
<sal_Int16
> oRelativeWidth
, oRelativeHeight
;
382 sal_Int16 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
383 sal_Int16 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
384 boost::logic::tribool
obRelFlipV(boost::logic::indeterminate
);
385 boost::logic::tribool
obFlipH(boost::logic::indeterminate
);
386 boost::logic::tribool
obFlipV(boost::logic::indeterminate
);
388 OUString aShapeText
= "";
389 OUString aFontFamily
= "";
390 float nFontSize
= 1.0;
393 int const nType
= initShape(xShape
, xPropertySet
, bCustom
, rShape
, bClose
, shapeOrPict
);
395 for (auto& rProperty
: rShape
.aProperties
)
397 if (rProperty
.first
== "shapeType")
399 continue; // ignore: already handled by initShape
401 if (rProperty
.first
== "wzName")
405 uno::Reference
<container::XNamed
> xNamed(xShape
, uno::UNO_QUERY
);
406 xNamed
->setName(rProperty
.second
);
409 xPropertySet
->setPropertyValue("Name", uno::makeAny(rProperty
.second
));
411 else if (rProperty
.first
== "wzDescription")
412 xPropertySet
->setPropertyValue("Description", uno::makeAny(rProperty
.second
));
413 else if (rProperty
.first
== "gtextUNICODE")
414 aShapeText
= rProperty
.second
;
415 else if (rProperty
.first
== "gtextFont")
416 aFontFamily
= rProperty
.second
;
417 else if (rProperty
.first
== "gtextSize")
419 // RTF size is multiplied by 2^16
420 nFontSize
= static_cast<float>(rProperty
.second
.toUInt32()) / RTF_MULTIPLIER
;
422 else if (rProperty
.first
== "pib")
424 m_rImport
.setDestinationText(rProperty
.second
);
427 else if (rProperty
.first
== "fillColor" && xPropertySet
.is())
429 aAny
<<= msfilter::util::BGRToRGB(rProperty
.second
.toUInt32());
431 xPropertySet
->setPropertyValue("BackColor", aAny
);
433 xPropertySet
->setPropertyValue("FillColor", aAny
);
435 // fillType will decide, possible it'll be the start color of a gradient.
436 aFillModel
.moColor
.set(
437 "#" + OUString::fromUtf8(msfilter::util::ConvertColor(aAny
.get
<sal_Int32
>())));
439 else if (rProperty
.first
== "fillBackColor")
440 // fillType will decide, possible it'll be the end color of a gradient.
441 aFillModel
.moColor2
.set("#"
442 + OUString::fromUtf8(msfilter::util::ConvertColor(
443 msfilter::util::BGRToRGB(rProperty
.second
.toInt32()))));
444 else if (rProperty
.first
== "lineColor")
445 aLineColor
<<= msfilter::util::BGRToRGB(rProperty
.second
.toInt32());
446 else if (rProperty
.first
== "lineBackColor")
447 ; // Ignore: complementer of lineColor
448 else if (rProperty
.first
== "txflTextFlow" && xPropertySet
.is())
450 switch (rProperty
.second
.toInt32())
452 case 1: // Top to bottom ASCII font
453 case 3: // Top to bottom non-ASCII font
454 eWritingMode
= text::WritingMode2::TB_RL
;
456 case 2: // Bottom to top non-ASCII font
457 eWritingMode
= text::WritingMode2::BT_LR
;
461 else if (rProperty
.first
== "fLine" && xPropertySet
.is())
462 resolveFLine(xPropertySet
, rProperty
.second
.toInt32());
463 else if (rProperty
.first
== "fillOpacity" && xPropertySet
.is())
465 int opacity
= 100 - (rProperty
.second
.toInt32()) * 100 / RTF_MULTIPLIER
;
466 xPropertySet
->setPropertyValue("FillTransparence", uno::Any(sal_uInt32(opacity
)));
468 else if (rProperty
.first
== "lineWidth")
469 aLineWidth
<<= rProperty
.second
.toInt32() / 360;
470 else if (rProperty
.first
== "pVerticies")
472 std::vector
<drawing::EnhancedCustomShapeParameterPair
> aCoordinates
;
473 sal_Int32 nSize
= 0; // Size of a token (its value is hardwired in the exporter)
474 sal_Int32 nCount
= 0; // Number of tokens
475 sal_Int32 nCharIndex
= 0; // Character index
478 OUString aToken
= rProperty
.second
.getToken(0, ';', nCharIndex
);
480 nSize
= aToken
.toInt32();
482 nCount
= aToken
.toInt32();
483 else if (aToken
.getLength())
485 // The coordinates are in an (x,y) form.
486 aToken
= aToken
.copy(1, aToken
.getLength() - 2);
488 sal_Int32 nX
= aToken
.getToken(0, ',', nI
).toInt32();
489 sal_Int32 nY
= (nI
>= 0) ? aToken
.getToken(0, ',', nI
).toInt32() : 0;
490 drawing::EnhancedCustomShapeParameterPair aPair
;
491 aPair
.First
.Value
<<= nX
;
492 aPair
.Second
.Value
<<= nY
;
493 aCoordinates
.push_back(aPair
);
495 } while (nCharIndex
>= 0);
496 aPropertyValue
.Name
= "Coordinates";
497 aPropertyValue
.Value
<<= comphelper::containerToSequence(aCoordinates
);
498 aPath
.push_back(aPropertyValue
);
500 else if (rProperty
.first
== "pSegmentInfo")
502 std::vector
<drawing::EnhancedCustomShapeSegment
> aSegments
;
504 sal_Int32 nCount
= 0;
505 sal_Int32 nCharIndex
= 0;
508 sal_Int32 nSeg
= rProperty
.second
.getToken(0, ';', nCharIndex
).toInt32();
515 sal_Int32 nPoints
= 1;
516 if (nSeg
>= 0x2000 && nSeg
< 0x20FF)
518 nPoints
= nSeg
& 0x0FFF;
522 drawing::EnhancedCustomShapeSegment aSegment
;
525 case 0x0001: // lineto
526 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
527 aSegment
.Count
= sal_Int32(1);
528 aSegments
.push_back(aSegment
);
530 case 0x4000: // moveto
531 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::MOVETO
;
532 aSegment
.Count
= sal_Int32(1);
533 aSegments
.push_back(aSegment
);
535 case 0x2000: // curveto
536 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::CURVETO
;
537 aSegment
.Count
= nPoints
;
538 aSegments
.push_back(aSegment
);
540 case 0xb300: // arcto
541 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::ARCTO
;
542 aSegment
.Count
= sal_Int32(0);
543 aSegments
.push_back(aSegment
);
546 case 0xaa00: // nofill
547 case 0xab00: // nostroke
548 case 0x6001: // close
552 = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH
;
553 aSegment
.Count
= sal_Int32(0);
554 aSegments
.push_back(aSegment
);
556 default: // given number of lineto elements
557 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
558 aSegment
.Count
= nSeg
;
559 aSegments
.push_back(aSegment
);
563 } while (nCharIndex
>= 0);
564 aPropertyValue
.Name
= "Segments";
565 aPropertyValue
.Value
<<= comphelper::containerToSequence(aSegments
);
566 aPath
.push_back(aPropertyValue
);
568 else if (rProperty
.first
== "geoLeft")
569 aViewBox
.X
= rProperty
.second
.toInt32();
570 else if (rProperty
.first
== "geoTop")
571 aViewBox
.Y
= rProperty
.second
.toInt32();
572 else if (rProperty
.first
== "geoRight")
573 aViewBox
.Width
= rProperty
.second
.toInt32();
574 else if (rProperty
.first
== "geoBottom")
575 aViewBox
.Height
= rProperty
.second
.toInt32();
576 else if (rProperty
.first
== "dhgt")
578 // dhgt is Word 2007, \shpz is Word 97-2003, the later has priority.
580 resolveDhgt(xPropertySet
, rProperty
.second
.toInt32(), /*bOldStyle=*/false);
582 // These are in EMU, convert to mm100.
583 else if (rProperty
.first
== "dxTextLeft")
585 if (xPropertySet
.is())
586 xPropertySet
->setPropertyValue("LeftBorderDistance",
587 uno::makeAny(rProperty
.second
.toInt32() / 360));
589 else if (rProperty
.first
== "dyTextTop")
591 if (xPropertySet
.is())
592 xPropertySet
->setPropertyValue("TopBorderDistance",
593 uno::makeAny(rProperty
.second
.toInt32() / 360));
595 else if (rProperty
.first
== "dxTextRight")
597 if (xPropertySet
.is())
598 xPropertySet
->setPropertyValue("RightBorderDistance",
599 uno::makeAny(rProperty
.second
.toInt32() / 360));
601 else if (rProperty
.first
== "dyTextBottom")
603 if (xPropertySet
.is())
604 xPropertySet
->setPropertyValue("BottomBorderDistance",
605 uno::makeAny(rProperty
.second
.toInt32() / 360));
607 else if (rProperty
.first
== "dxWrapDistLeft")
609 if (m_bTextGraphicObject
)
610 rShape
.aAnchorAttributes
.set(NS_ooxml::LN_CT_Anchor_distL
,
611 new RTFValue(rProperty
.second
.toInt32()));
612 else if (xPropertySet
.is())
613 xPropertySet
->setPropertyValue("LeftMargin",
614 uno::makeAny(rProperty
.second
.toInt32() / 360));
616 else if (rProperty
.first
== "dyWrapDistTop")
618 if (m_bTextGraphicObject
)
619 rShape
.aAnchorAttributes
.set(NS_ooxml::LN_CT_Anchor_distT
,
620 new RTFValue(rProperty
.second
.toInt32()));
621 else if (xPropertySet
.is())
622 xPropertySet
->setPropertyValue("TopMargin",
623 uno::makeAny(rProperty
.second
.toInt32() / 360));
625 else if (rProperty
.first
== "dxWrapDistRight")
627 if (m_bTextGraphicObject
)
628 rShape
.aAnchorAttributes
.set(NS_ooxml::LN_CT_Anchor_distR
,
629 new RTFValue(rProperty
.second
.toInt32()));
630 else if (xPropertySet
.is())
631 xPropertySet
->setPropertyValue("RightMargin",
632 uno::makeAny(rProperty
.second
.toInt32() / 360));
634 else if (rProperty
.first
== "dyWrapDistBottom")
636 if (m_bTextGraphicObject
)
637 rShape
.aAnchorAttributes
.set(NS_ooxml::LN_CT_Anchor_distB
,
638 new RTFValue(rProperty
.second
.toInt32()));
639 else if (xPropertySet
.is())
640 xPropertySet
->setPropertyValue("BottomMargin",
641 uno::makeAny(rProperty
.second
.toInt32() / 360));
643 else if (rProperty
.first
== "fillType")
645 switch (rProperty
.second
.toInt32())
647 case 7: // Shade using the fillAngle
648 aFillModel
.moType
.set(oox::XML_gradient
);
651 SAL_INFO("writerfilter",
652 "TODO handle fillType value '" << rProperty
.second
<< "'");
656 else if (rProperty
.first
== "fillFocus")
657 aFillModel
.moFocus
.set(rProperty
.second
.toDouble() / 100); // percent
658 else if (rProperty
.first
== "fShadow" && xPropertySet
.is())
660 if (rProperty
.second
.toInt32() == 1)
661 aShadowModel
.mbHasShadow
= true;
663 else if (rProperty
.first
== "shadowColor")
664 aShadowModel
.moColor
.set("#"
665 + OUString::fromUtf8(msfilter::util::ConvertColor(
666 msfilter::util::BGRToRGB(rProperty
.second
.toInt32()))));
667 else if (rProperty
.first
== "shadowOffsetX")
669 aShadowModel
.moOffset
.set(OUString::number(rProperty
.second
.toDouble() / 12700) + "pt");
670 else if (rProperty
.first
== "posh" || rProperty
.first
== "posv"
671 || rProperty
.first
== "fFitShapeToText" || rProperty
.first
== "fFilled"
672 || rProperty
.first
== "rotation")
673 applyProperty(xShape
, rProperty
.first
, rProperty
.second
);
674 else if (rProperty
.first
== "posrelh")
676 switch (rProperty
.second
.toInt32())
679 rShape
.nHoriOrientRelation
= text::RelOrientation::PAGE_FRAME
;
685 else if (rProperty
.first
== "posrelv")
687 switch (rProperty
.second
.toInt32())
690 rShape
.nVertOrientRelation
= text::RelOrientation::PAGE_FRAME
;
696 else if (rProperty
.first
== "groupLeft")
697 oGroupLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
698 else if (rProperty
.first
== "groupTop")
699 oGroupTop
= convertTwipToMm100(rProperty
.second
.toInt32());
700 else if (rProperty
.first
== "groupRight")
701 oGroupRight
= convertTwipToMm100(rProperty
.second
.toInt32());
702 else if (rProperty
.first
== "groupBottom")
703 oGroupBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
704 else if (rProperty
.first
== "relLeft")
705 oRelLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
706 else if (rProperty
.first
== "relTop")
707 oRelTop
= convertTwipToMm100(rProperty
.second
.toInt32());
708 else if (rProperty
.first
== "relRight")
709 oRelRight
= convertTwipToMm100(rProperty
.second
.toInt32());
710 else if (rProperty
.first
== "relBottom")
711 oRelBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
712 else if (rProperty
.first
== "fBehindDocument")
713 bOpaque
= !rProperty
.second
.toInt32();
714 else if (rProperty
.first
== "pctHoriz" || rProperty
.first
== "pctVert")
716 sal_Int16 nPercentage
= rtl::math::round(rProperty
.second
.toDouble() / 10);
719 boost::optional
<sal_Int16
>& rPercentage
720 = rProperty
.first
== "pctHoriz" ? oRelativeWidth
: oRelativeHeight
;
721 rPercentage
= nPercentage
;
724 else if (rProperty
.first
== "sizerelh")
726 if (xPropertySet
.is())
728 switch (rProperty
.second
.toInt32())
731 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
734 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
737 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: "
738 << rProperty
.second
);
743 else if (rProperty
.first
== "sizerelv")
745 if (xPropertySet
.is())
747 switch (rProperty
.second
.toInt32())
750 nRelativeHeightRelation
= text::RelOrientation::FRAME
;
753 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
756 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: "
757 << rProperty
.second
);
762 else if (rProperty
.first
== "fHorizRule") // TODO: what does "fStandardHR" do?
764 // horizontal rule: relative width defaults to 100% of paragraph
765 // TODO: does it have a default height?
768 oRelativeWidth
= 100;
770 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
771 sal_Int16
const nVertOrient
= text::VertOrientation::CENTER
;
772 if (xPropertySet
.is())
774 xPropertySet
->setPropertyValue("VertOrient", uno::makeAny(nVertOrient
));
777 else if (rProperty
.first
== "pctHR")
779 // horizontal rule relative width in permille
780 oRelativeWidth
= rProperty
.second
.toInt32() / 10;
782 else if (rProperty
.first
== "dxHeightHR")
784 // horizontal rule height
785 sal_uInt32
const nHeight(convertTwipToMm100(rProperty
.second
.toInt32()));
786 rShape
.nBottom
= rShape
.nTop
+ nHeight
;
788 else if (rProperty
.first
== "dxWidthHR")
790 // horizontal rule width
791 sal_uInt32
const nWidth(convertTwipToMm100(rProperty
.second
.toInt32()));
792 rShape
.nRight
= rShape
.nLeft
+ nWidth
;
794 else if (rProperty
.first
== "alignHR")
796 // horizontal orientation *for horizontal rule*
797 sal_Int16 nHoriOrient
= text::HoriOrientation::NONE
;
798 switch (rProperty
.second
.toInt32())
801 nHoriOrient
= text::HoriOrientation::LEFT
;
804 nHoriOrient
= text::HoriOrientation::CENTER
;
807 nHoriOrient
= text::HoriOrientation::RIGHT
;
810 if (xPropertySet
.is() && text::HoriOrientation::NONE
!= nHoriOrient
)
812 xPropertySet
->setPropertyValue("HoriOrient", uno::makeAny(nHoriOrient
));
815 else if (rProperty
.first
== "pWrapPolygonVertices")
817 RTFSprms aPolygonSprms
;
818 sal_Int32 nSize
= 0; // Size of a token
819 sal_Int32 nCount
= 0; // Number of tokens
820 sal_Int32 nCharIndex
= 0; // Character index
823 OUString aToken
= rProperty
.second
.getToken(0, ';', nCharIndex
);
825 nSize
= aToken
.toInt32();
827 nCount
= aToken
.toInt32();
828 else if (aToken
.getLength())
830 // The coordinates are in an (x,y) form.
831 aToken
= aToken
.copy(1, aToken
.getLength() - 2);
833 sal_Int32 nX
= aToken
.getToken(0, ',', nI
).toInt32();
834 sal_Int32 nY
= (nI
>= 0) ? aToken
.getToken(0, ',', nI
).toInt32() : 0;
835 RTFSprms aPathAttributes
;
836 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_x
, new RTFValue(nX
));
837 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_y
, new RTFValue(nY
));
838 aPolygonSprms
.set(NS_ooxml::LN_CT_WrapPath_lineTo
,
839 new RTFValue(aPathAttributes
), RTFOverwrite::NO_APPEND
);
841 } while (nCharIndex
>= 0);
842 rShape
.aWrapPolygonSprms
= aPolygonSprms
;
844 else if (rProperty
.first
== "fRelFlipV")
845 obRelFlipV
= rProperty
.second
.toInt32() == 1;
846 else if (rProperty
.first
== "fFlipH")
847 obFlipH
= rProperty
.second
.toInt32() == 1;
848 else if (rProperty
.first
== "fFlipV")
849 obFlipV
= rProperty
.second
.toInt32() == 1;
851 SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty
.first
<< "':'"
852 << rProperty
.second
<< "'");
855 if (xPropertySet
.is())
857 resolveLineColorAndWidth(m_bTextFrame
, xPropertySet
, aLineColor
, aLineWidth
);
860 bool bOldStyle
= m_aParents
.size() > 1;
861 resolveDhgt(xPropertySet
, *rShape
.oZ
, bOldStyle
);
864 xPropertySet
->setPropertyValue("WritingMode", uno::makeAny(eWritingMode
));
866 // Only Writer textframes implement text::WritingMode2.
867 xPropertySet
->setPropertyValue("TextWritingMode",
868 uno::makeAny(text::WritingMode(eWritingMode
)));
871 if (!m_aParents
.empty() && m_aParents
.top().is() && !m_bTextFrame
)
872 m_aParents
.top()->add(xShape
);
876 m_rImport
.resolvePict(false, xShape
);
879 if (nType
== ESCHER_ShpInst_PictureFrame
) // picture frame
881 assert(!m_bTextFrame
);
882 if (!bPib
) // ??? not sure if the early return should be removed on else?
884 m_xShape
= xShape
; // store it for later resolvePict call
887 // Handle horizontal flip.
888 if (obFlipH
== true && xPropertySet
.is())
889 xPropertySet
->setPropertyValue("IsMirrored", uno::makeAny(true));
893 if (bCustom
&& xShape
.is() && !bPib
)
895 uno::Reference
<drawing::XEnhancedCustomShapeDefaulter
> xDefaulter(xShape
, uno::UNO_QUERY
);
896 xDefaulter
->createCustomShapeDefaults(OUString::number(nType
));
900 if (bCustom
&& !aShapeText
.isEmpty())
902 uno::Reference
<text::XTextRange
> xTextRange(xShape
, uno::UNO_QUERY
);
904 xTextRange
->setString(aShapeText
);
906 xPropertySet
->setPropertyValue("CharFontName", uno::makeAny(aFontFamily
));
907 xPropertySet
->setPropertyValue("CharHeight", uno::makeAny(nFontSize
));
910 // Creating CustomShapeGeometry property
911 std::vector
<beans::PropertyValue
> aGeometry
;
912 if (aViewBox
.X
|| aViewBox
.Y
|| aViewBox
.Width
|| aViewBox
.Height
)
914 aViewBox
.Width
-= aViewBox
.X
;
915 aViewBox
.Height
-= aViewBox
.Y
;
916 aPropertyValue
.Name
= "ViewBox";
917 aPropertyValue
.Value
<<= aViewBox
;
918 aGeometry
.push_back(aPropertyValue
);
922 aPropertyValue
.Name
= "Path";
923 aPropertyValue
.Value
<<= comphelper::containerToSequence(aPath
);
924 aGeometry
.push_back(aPropertyValue
);
926 if (!aGeometry
.empty() && xPropertySet
.is() && !m_bTextFrame
)
927 xPropertySet
->setPropertyValue("CustomShapeGeometry",
928 uno::Any(comphelper::containerToSequence(aGeometry
)));
930 if (!aShapeText
.isEmpty())
932 auto aGeomPropSeq
= xPropertySet
->getPropertyValue("CustomShapeGeometry")
933 .get
<uno::Sequence
<beans::PropertyValue
>>();
935 = comphelper::sequenceToContainer
<std::vector
<beans::PropertyValue
>>(aGeomPropSeq
);
936 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence({
937 { "TextPath", uno::makeAny(true) },
939 auto it
= std::find_if(
940 aGeomPropVec
.begin(), aGeomPropVec
.end(),
941 [](const beans::PropertyValue
& rValue
) { return rValue
.Name
== "TextPath"; });
942 if (it
== aGeomPropVec
.end())
943 aGeomPropVec
.push_back(comphelper::makePropertyValue("TextPath", aPropertyValues
));
945 it
->Value
<<= aPropertyValues
;
947 xPropertySet
->setPropertyValue("CustomShapeGeometry",
948 uno::makeAny(comphelper::containerToSequence(aGeomPropVec
)));
949 xPropertySet
->setPropertyValue("TextAutoGrowHeight", uno::makeAny(false));
950 xPropertySet
->setPropertyValue("TextAutoGrowWidth", uno::makeAny(false));
953 if (!boost::logic::indeterminate(obRelFlipV
) && xPropertySet
.is())
955 if (nType
== ESCHER_ShpInst_Line
)
957 // Line shape inside group shape: get the polygon sequence and transform it.
958 uno::Sequence
<uno::Sequence
<awt::Point
>> aPolyPolySequence
;
959 if ((xPropertySet
->getPropertyValue("PolyPolygon") >>= aPolyPolySequence
)
960 && aPolyPolySequence
.hasElements())
962 uno::Sequence
<awt::Point
>& rPolygon
= aPolyPolySequence
[0];
963 basegfx::B2DPolygon aPoly
;
964 for (sal_Int32 i
= 0; i
< rPolygon
.getLength(); ++i
)
966 const awt::Point
& rPoint
= rPolygon
[i
];
967 aPoly
.insert(i
, basegfx::B2DPoint(rPoint
.X
, rPoint
.Y
));
969 basegfx::B2DHomMatrix aTransformation
;
970 aTransformation
.scale(1.0, obRelFlipV
? -1.0 : 1.0);
971 aPoly
.transform(aTransformation
);
972 for (sal_Int32 i
= 0; i
< rPolygon
.getLength(); ++i
)
974 basegfx::B2DPoint
aPoint(aPoly
.getB2DPoint(i
));
976 = awt::Point(static_cast<sal_Int32
>(convertMm100ToTwip(aPoint
.getX())),
977 static_cast<sal_Int32
>(convertMm100ToTwip(aPoint
.getY())));
979 xPropertySet
->setPropertyValue("PolyPolygon", uno::makeAny(aPolyPolySequence
));
984 // Set position and size
987 sal_Int32 nLeft
= rShape
.nLeft
;
988 sal_Int32 nTop
= rShape
.nTop
;
990 bool bInShapeGroup
= oGroupLeft
&& oGroupTop
&& oGroupRight
&& oGroupBottom
&& oRelLeft
991 && oRelTop
&& oRelRight
&& oRelBottom
;
995 // See lclGetAbsPoint() in the VML import: rShape is the group shape, oGroup is its coordinate system, oRel is the relative child shape.
996 sal_Int32 nShapeWidth
= rShape
.nRight
- rShape
.nLeft
;
997 sal_Int32 nShapeHeight
= rShape
.nBottom
- rShape
.nTop
;
998 sal_Int32 nCoordSysWidth
= *oGroupRight
- *oGroupLeft
;
999 sal_Int32 nCoordSysHeight
= *oGroupBottom
- *oGroupTop
;
1000 double fWidthRatio
= static_cast<double>(nShapeWidth
) / nCoordSysWidth
;
1001 double fHeightRatio
= static_cast<double>(nShapeHeight
) / nCoordSysHeight
;
1002 nLeft
= static_cast<sal_Int32
>(rShape
.nLeft
+ fWidthRatio
* (*oRelLeft
- *oGroupLeft
));
1003 nTop
= static_cast<sal_Int32
>(rShape
.nTop
+ fHeightRatio
* (*oRelTop
- *oGroupTop
));
1005 // See lclGetAbsRect() in the VML import.
1006 aSize
.Width
= std::lround(fWidthRatio
* (*oRelRight
- *oRelLeft
));
1007 aSize
.Height
= std::lround(fHeightRatio
* (*oRelBottom
- *oRelTop
));
1012 xPropertySet
->setPropertyValue("HoriOrientPosition", uno::makeAny(nLeft
));
1013 xPropertySet
->setPropertyValue("VertOrientPosition", uno::makeAny(nTop
));
1016 xShape
->setPosition(awt::Point(nLeft
, nTop
));
1019 xShape
->setSize(aSize
);
1021 xShape
->setSize(awt::Size(rShape
.nRight
- rShape
.nLeft
, rShape
.nBottom
- rShape
.nTop
));
1023 if (obFlipH
== true || obFlipV
== true)
1025 // Line shapes have no CustomShapeGeometry.
1026 if (nType
!= ESCHER_ShpInst_Line
)
1028 // This has to be set after position and size is set, otherwise flip will affect the position.
1029 comphelper::SequenceAsHashMap
aCustomShapeGeometry(
1030 xPropertySet
->getPropertyValue("CustomShapeGeometry"));
1031 if (obFlipH
== true)
1032 aCustomShapeGeometry
["MirroredX"] <<= true;
1033 if (obFlipV
== true)
1034 aCustomShapeGeometry
["MirroredY"] <<= true;
1035 xPropertySet
->setPropertyValue(
1036 "CustomShapeGeometry",
1037 uno::makeAny(aCustomShapeGeometry
.getAsConstPropertyValueList()));
1041 if (rShape
.nHoriOrientRelation
!= 0)
1042 xPropertySet
->setPropertyValue("HoriOrientRelation",
1043 uno::makeAny(rShape
.nHoriOrientRelation
));
1044 if (rShape
.nVertOrientRelation
!= 0)
1045 xPropertySet
->setPropertyValue("VertOrientRelation",
1046 uno::makeAny(rShape
.nVertOrientRelation
));
1047 if (rShape
.nWrap
!= text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
)
1048 xPropertySet
->setPropertyValue("Surround",
1049 uno::makeAny(text::WrapTextMode(rShape
.nWrap
)));
1050 oox::ModelObjectHelper
aModelObjectHelper(m_rImport
.getModelFactory());
1051 if (aFillModel
.moType
.has())
1053 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1054 aFillModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1055 // Sets the FillStyle and FillGradient UNO properties.
1056 oox::PropertySet(xShape
).setProperties(aPropMap
);
1059 if (aShadowModel
.mbHasShadow
)
1061 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1062 aShadowModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1063 // Sets the ShadowFormat UNO property.
1064 oox::PropertySet(xShape
).setProperties(aPropMap
);
1066 xPropertySet
->setPropertyValue("AnchorType",
1067 uno::makeAny(text::TextContentAnchorType_AT_CHARACTER
));
1068 xPropertySet
->setPropertyValue("Opaque", uno::makeAny(bOpaque
));
1071 xPropertySet
->setPropertyValue("RelativeWidth", uno::makeAny(*oRelativeWidth
));
1072 xPropertySet
->setPropertyValue("RelativeWidthRelation",
1073 uno::makeAny(nRelativeWidthRelation
));
1075 if (oRelativeHeight
)
1077 xPropertySet
->setPropertyValue("RelativeHeight", uno::makeAny(*oRelativeHeight
));
1078 xPropertySet
->setPropertyValue("RelativeHeightRelation",
1079 uno::makeAny(nRelativeHeightRelation
));
1083 if (m_rImport
.isInBackground())
1085 RTFSprms aAttributes
;
1086 aAttributes
.set(NS_ooxml::LN_CT_Background_color
,
1087 new RTFValue(xPropertySet
->getPropertyValue("FillColor").get
<sal_Int32
>()));
1088 m_rImport
.Mapper().props(new RTFReferenceProperties(aAttributes
));
1090 uno::Reference
<lang::XComponent
> xComponent(xShape
, uno::UNO_QUERY
);
1091 xComponent
->dispose();
1095 // Send it to dmapper
1096 m_rImport
.Mapper().startShape(xShape
);
1099 m_rImport
.Mapper().endShape();
1104 void RTFSdrImport::close() { m_rImport
.Mapper().endShape(); }
1106 void RTFSdrImport::append(const OUString
& aKey
, const OUString
& aValue
)
1108 applyProperty(m_xShape
, aKey
, aValue
);
1111 void RTFSdrImport::appendGroupProperty(const OUString
& aKey
, const OUString
& aValue
)
1113 if (m_aParents
.empty())
1115 uno::Reference
<drawing::XShape
> xShape(m_aParents
.top(), uno::UNO_QUERY
);
1117 applyProperty(xShape
, aKey
, aValue
);
1120 } // namespace rtftok
1121 } // namespace writerfilter
1123 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */