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(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 boost::optional
<sal_Int32
> oGroupLeft
;
374 boost::optional
<sal_Int32
> oGroupTop
;
375 boost::optional
<sal_Int32
> oGroupRight
;
376 boost::optional
<sal_Int32
> oGroupBottom
;
377 boost::optional
<sal_Int32
> oRelLeft
;
378 boost::optional
<sal_Int32
> oRelTop
;
379 boost::optional
<sal_Int32
> oRelRight
;
380 boost::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 boost::optional
<sal_Int16
> oRelativeWidth
;
389 boost::optional
<sal_Int16
> oRelativeHeight
;
390 sal_Int16 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
391 sal_Int16 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
392 boost::logic::tribool
obRelFlipV(boost::logic::indeterminate
);
393 boost::logic::tribool
obFlipH(boost::logic::indeterminate
);
394 boost::logic::tribool
obFlipV(boost::logic::indeterminate
);
396 OUString aShapeText
= "";
397 OUString aFontFamily
= "";
398 float nFontSize
= 1.0;
401 int const nType
= initShape(xShape
, xPropertySet
, bCustom
, rShape
, bClose
, shapeOrPict
);
403 for (auto& rProperty
: rShape
.getProperties())
405 if (rProperty
.first
== "shapeType")
407 continue; // ignore: already handled by initShape
409 if (rProperty
.first
== "wzName")
413 uno::Reference
<container::XNamed
> xNamed(xShape
, uno::UNO_QUERY
);
414 xNamed
->setName(rProperty
.second
);
417 xPropertySet
->setPropertyValue("Name", uno::makeAny(rProperty
.second
));
419 else if (rProperty
.first
== "wzDescription")
420 xPropertySet
->setPropertyValue("Description", uno::makeAny(rProperty
.second
));
421 else if (rProperty
.first
== "gtextUNICODE")
422 aShapeText
= rProperty
.second
;
423 else if (rProperty
.first
== "gtextFont")
424 aFontFamily
= rProperty
.second
;
425 else if (rProperty
.first
== "gtextSize")
427 // RTF size is multiplied by 2^16
428 nFontSize
= static_cast<float>(rProperty
.second
.toUInt32()) / RTF_MULTIPLIER
;
430 else if (rProperty
.first
== "pib")
432 m_rImport
.setDestinationText(rProperty
.second
);
435 else if (rProperty
.first
== "fillColor" && xPropertySet
.is())
437 aAny
<<= msfilter::util::BGRToRGB(rProperty
.second
.toUInt32());
439 xPropertySet
->setPropertyValue("BackColor", aAny
);
441 xPropertySet
->setPropertyValue("FillColor", aAny
);
443 // fillType will decide, possible it'll be the start color of a gradient.
444 aFillModel
.moColor
.set(
445 "#" + OUString::fromUtf8(msfilter::util::ConvertColor(aAny
.get
<sal_Int32
>())));
447 else if (rProperty
.first
== "fillBackColor")
448 // fillType will decide, possible it'll be the end color of a gradient.
449 aFillModel
.moColor2
.set("#"
450 + OUString::fromUtf8(msfilter::util::ConvertColor(
451 msfilter::util::BGRToRGB(rProperty
.second
.toInt32()))));
452 else if (rProperty
.first
== "lineColor")
453 aLineColor
<<= msfilter::util::BGRToRGB(rProperty
.second
.toInt32());
454 else if (rProperty
.first
== "lineBackColor")
455 ; // Ignore: complementer of lineColor
456 else if (rProperty
.first
== "txflTextFlow" && xPropertySet
.is())
458 switch (rProperty
.second
.toInt32())
460 case 1: // Top to bottom ASCII font
461 case 3: // Top to bottom non-ASCII font
462 eWritingMode
= text::WritingMode2::TB_RL
;
464 case 2: // Bottom to top non-ASCII font
465 eWritingMode
= text::WritingMode2::BT_LR
;
469 else if (rProperty
.first
== "fLine" && xPropertySet
.is())
470 resolveFLine(xPropertySet
, rProperty
.second
.toInt32());
471 else if (rProperty
.first
== "fillOpacity" && xPropertySet
.is())
473 int opacity
= 100 - (rProperty
.second
.toInt32()) * 100 / RTF_MULTIPLIER
;
474 xPropertySet
->setPropertyValue("FillTransparence", uno::Any(sal_uInt32(opacity
)));
476 else if (rProperty
.first
== "lineWidth")
477 aLineWidth
<<= rProperty
.second
.toInt32() / 360;
478 else if (rProperty
.first
== "pVerticies")
480 std::vector
<drawing::EnhancedCustomShapeParameterPair
> aCoordinates
;
481 sal_Int32 nSize
= 0; // Size of a token (its value is hardwired in the exporter)
482 sal_Int32 nCount
= 0; // Number of tokens
483 sal_Int32 nCharIndex
= 0; // Character index
486 OUString aToken
= rProperty
.second
.getToken(0, ';', nCharIndex
);
488 nSize
= aToken
.toInt32();
490 nCount
= aToken
.toInt32();
491 else if (aToken
.getLength())
493 // The coordinates are in an (x,y) form.
494 aToken
= aToken
.copy(1, aToken
.getLength() - 2);
496 sal_Int32 nX
= aToken
.getToken(0, ',', nI
).toInt32();
497 sal_Int32 nY
= (nI
>= 0) ? aToken
.getToken(0, ',', nI
).toInt32() : 0;
498 drawing::EnhancedCustomShapeParameterPair aPair
;
499 aPair
.First
.Value
<<= nX
;
500 aPair
.Second
.Value
<<= nY
;
501 aCoordinates
.push_back(aPair
);
503 } while (nCharIndex
>= 0);
504 aPropertyValue
.Name
= "Coordinates";
505 aPropertyValue
.Value
<<= comphelper::containerToSequence(aCoordinates
);
506 aPath
.push_back(aPropertyValue
);
508 else if (rProperty
.first
== "pSegmentInfo")
510 std::vector
<drawing::EnhancedCustomShapeSegment
> aSegments
;
512 sal_Int32 nCount
= 0;
513 sal_Int32 nCharIndex
= 0;
516 sal_Int32 nSeg
= rProperty
.second
.getToken(0, ';', nCharIndex
).toInt32();
523 sal_Int32 nPoints
= 1;
524 if (nSeg
>= 0x2000 && nSeg
< 0x20FF)
526 nPoints
= nSeg
& 0x0FFF;
530 drawing::EnhancedCustomShapeSegment aSegment
;
533 case 0x0001: // lineto
534 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
535 aSegment
.Count
= sal_Int32(1);
536 aSegments
.push_back(aSegment
);
538 case 0x4000: // moveto
539 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::MOVETO
;
540 aSegment
.Count
= sal_Int32(1);
541 aSegments
.push_back(aSegment
);
543 case 0x2000: // curveto
544 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::CURVETO
;
545 aSegment
.Count
= nPoints
;
546 aSegments
.push_back(aSegment
);
548 case 0xb300: // arcto
549 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::ARCTO
;
550 aSegment
.Count
= sal_Int32(0);
551 aSegments
.push_back(aSegment
);
554 case 0xaa00: // nofill
555 case 0xab00: // nostroke
556 case 0x6001: // close
560 = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH
;
561 aSegment
.Count
= sal_Int32(0);
562 aSegments
.push_back(aSegment
);
564 default: // given number of lineto elements
565 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
566 aSegment
.Count
= nSeg
;
567 aSegments
.push_back(aSegment
);
571 } while (nCharIndex
>= 0);
572 aPropertyValue
.Name
= "Segments";
573 aPropertyValue
.Value
<<= comphelper::containerToSequence(aSegments
);
574 aPath
.push_back(aPropertyValue
);
576 else if (rProperty
.first
== "geoLeft")
577 aViewBox
.X
= rProperty
.second
.toInt32();
578 else if (rProperty
.first
== "geoTop")
579 aViewBox
.Y
= rProperty
.second
.toInt32();
580 else if (rProperty
.first
== "geoRight")
581 aViewBox
.Width
= rProperty
.second
.toInt32();
582 else if (rProperty
.first
== "geoBottom")
583 aViewBox
.Height
= rProperty
.second
.toInt32();
584 else if (rProperty
.first
== "dhgt")
586 // dhgt is Word 2007, \shpz is Word 97-2003, the later has priority.
588 resolveDhgt(xPropertySet
, rProperty
.second
.toInt32(), /*bOldStyle=*/false);
590 // These are in EMU, convert to mm100.
591 else if (rProperty
.first
== "dxTextLeft")
593 if (xPropertySet
.is())
594 xPropertySet
->setPropertyValue("LeftBorderDistance",
595 uno::makeAny(rProperty
.second
.toInt32() / 360));
597 else if (rProperty
.first
== "dyTextTop")
599 if (xPropertySet
.is())
600 xPropertySet
->setPropertyValue("TopBorderDistance",
601 uno::makeAny(rProperty
.second
.toInt32() / 360));
603 else if (rProperty
.first
== "dxTextRight")
605 if (xPropertySet
.is())
606 xPropertySet
->setPropertyValue("RightBorderDistance",
607 uno::makeAny(rProperty
.second
.toInt32() / 360));
609 else if (rProperty
.first
== "dyTextBottom")
611 if (xPropertySet
.is())
612 xPropertySet
->setPropertyValue("BottomBorderDistance",
613 uno::makeAny(rProperty
.second
.toInt32() / 360));
615 else if (rProperty
.first
== "dxWrapDistLeft")
617 if (m_bTextGraphicObject
)
618 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distL
,
619 new RTFValue(rProperty
.second
.toInt32()));
620 else if (xPropertySet
.is())
621 xPropertySet
->setPropertyValue("LeftMargin",
622 uno::makeAny(rProperty
.second
.toInt32() / 360));
624 else if (rProperty
.first
== "dyWrapDistTop")
626 if (m_bTextGraphicObject
)
627 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distT
,
628 new RTFValue(rProperty
.second
.toInt32()));
629 else if (xPropertySet
.is())
630 xPropertySet
->setPropertyValue("TopMargin",
631 uno::makeAny(rProperty
.second
.toInt32() / 360));
633 else if (rProperty
.first
== "dxWrapDistRight")
635 if (m_bTextGraphicObject
)
636 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distR
,
637 new RTFValue(rProperty
.second
.toInt32()));
638 else if (xPropertySet
.is())
639 xPropertySet
->setPropertyValue("RightMargin",
640 uno::makeAny(rProperty
.second
.toInt32() / 360));
642 else if (rProperty
.first
== "dyWrapDistBottom")
644 if (m_bTextGraphicObject
)
645 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distB
,
646 new RTFValue(rProperty
.second
.toInt32()));
647 else if (xPropertySet
.is())
648 xPropertySet
->setPropertyValue("BottomMargin",
649 uno::makeAny(rProperty
.second
.toInt32() / 360));
651 else if (rProperty
.first
== "fillType")
653 switch (rProperty
.second
.toInt32())
655 case 7: // Shade using the fillAngle
656 aFillModel
.moType
.set(oox::XML_gradient
);
659 SAL_INFO("writerfilter",
660 "TODO handle fillType value '" << rProperty
.second
<< "'");
664 else if (rProperty
.first
== "fillFocus")
665 aFillModel
.moFocus
.set(rProperty
.second
.toDouble() / 100); // percent
666 else if (rProperty
.first
== "fShadow" && xPropertySet
.is())
668 if (rProperty
.second
.toInt32() == 1)
669 aShadowModel
.mbHasShadow
= true;
671 else if (rProperty
.first
== "shadowColor")
672 aShadowModel
.moColor
.set("#"
673 + OUString::fromUtf8(msfilter::util::ConvertColor(
674 msfilter::util::BGRToRGB(rProperty
.second
.toInt32()))));
675 else if (rProperty
.first
== "shadowOffsetX")
677 aShadowModel
.moOffset
.set(OUString::number(rProperty
.second
.toDouble() / 12700) + "pt");
678 else if (rProperty
.first
== "posh" || rProperty
.first
== "posv"
679 || rProperty
.first
== "fFitShapeToText" || rProperty
.first
== "fFilled"
680 || rProperty
.first
== "rotation")
681 applyProperty(xShape
, rProperty
.first
, rProperty
.second
);
682 else if (rProperty
.first
== "posrelh")
684 switch (rProperty
.second
.toInt32())
687 rShape
.setHoriOrientRelation(text::RelOrientation::PAGE_FRAME
);
693 else if (rProperty
.first
== "posrelv")
695 switch (rProperty
.second
.toInt32())
698 rShape
.setVertOrientRelation(text::RelOrientation::PAGE_FRAME
);
704 else if (rProperty
.first
== "groupLeft")
705 oGroupLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
706 else if (rProperty
.first
== "groupTop")
707 oGroupTop
= convertTwipToMm100(rProperty
.second
.toInt32());
708 else if (rProperty
.first
== "groupRight")
709 oGroupRight
= convertTwipToMm100(rProperty
.second
.toInt32());
710 else if (rProperty
.first
== "groupBottom")
711 oGroupBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
712 else if (rProperty
.first
== "relLeft")
713 oRelLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
714 else if (rProperty
.first
== "relTop")
715 oRelTop
= convertTwipToMm100(rProperty
.second
.toInt32());
716 else if (rProperty
.first
== "relRight")
717 oRelRight
= convertTwipToMm100(rProperty
.second
.toInt32());
718 else if (rProperty
.first
== "relBottom")
719 oRelBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
720 else if (rProperty
.first
== "fBehindDocument")
721 bOpaque
= !rProperty
.second
.toInt32();
722 else if (rProperty
.first
== "pctHoriz" || rProperty
.first
== "pctVert")
724 sal_Int16 nPercentage
= rtl::math::round(rProperty
.second
.toDouble() / 10);
727 boost::optional
<sal_Int16
>& rPercentage
728 = rProperty
.first
== "pctHoriz" ? oRelativeWidth
: oRelativeHeight
;
729 rPercentage
= nPercentage
;
732 else if (rProperty
.first
== "sizerelh")
734 if (xPropertySet
.is())
736 switch (rProperty
.second
.toInt32())
739 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
742 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
745 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: "
746 << rProperty
.second
);
751 else if (rProperty
.first
== "sizerelv")
753 if (xPropertySet
.is())
755 switch (rProperty
.second
.toInt32())
758 nRelativeHeightRelation
= text::RelOrientation::FRAME
;
761 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
764 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: "
765 << rProperty
.second
);
770 else if (rProperty
.first
== "fHorizRule") // TODO: what does "fStandardHR" do?
772 // horizontal rule: relative width defaults to 100% of paragraph
773 // TODO: does it have a default height?
776 oRelativeWidth
= 100;
778 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
779 sal_Int16
const nVertOrient
= text::VertOrientation::CENTER
;
780 if (xPropertySet
.is())
782 xPropertySet
->setPropertyValue("VertOrient", uno::makeAny(nVertOrient
));
785 else if (rProperty
.first
== "pctHR")
787 // horizontal rule relative width in permille
788 oRelativeWidth
= rProperty
.second
.toInt32() / 10;
790 else if (rProperty
.first
== "dxHeightHR")
792 // horizontal rule height
793 sal_uInt32
const nHeight(convertTwipToMm100(rProperty
.second
.toInt32()));
794 rShape
.setBottom(rShape
.getTop() + nHeight
);
796 else if (rProperty
.first
== "dxWidthHR")
798 // horizontal rule width
799 sal_uInt32
const nWidth(convertTwipToMm100(rProperty
.second
.toInt32()));
800 rShape
.setRight(rShape
.getLeft() + nWidth
);
802 else if (rProperty
.first
== "alignHR")
804 // horizontal orientation *for horizontal rule*
805 sal_Int16 nHoriOrient
= text::HoriOrientation::NONE
;
806 switch (rProperty
.second
.toInt32())
809 nHoriOrient
= text::HoriOrientation::LEFT
;
812 nHoriOrient
= text::HoriOrientation::CENTER
;
815 nHoriOrient
= text::HoriOrientation::RIGHT
;
818 if (xPropertySet
.is() && text::HoriOrientation::NONE
!= nHoriOrient
)
820 xPropertySet
->setPropertyValue("HoriOrient", uno::makeAny(nHoriOrient
));
823 else if (rProperty
.first
== "pWrapPolygonVertices")
825 RTFSprms aPolygonSprms
;
826 sal_Int32 nSize
= 0; // Size of a token
827 sal_Int32 nCount
= 0; // Number of tokens
828 sal_Int32 nCharIndex
= 0; // Character index
831 OUString aToken
= rProperty
.second
.getToken(0, ';', nCharIndex
);
833 nSize
= aToken
.toInt32();
835 nCount
= aToken
.toInt32();
836 else if (aToken
.getLength())
838 // The coordinates are in an (x,y) form.
839 aToken
= aToken
.copy(1, aToken
.getLength() - 2);
841 sal_Int32 nX
= aToken
.getToken(0, ',', nI
).toInt32();
842 sal_Int32 nY
= (nI
>= 0) ? aToken
.getToken(0, ',', nI
).toInt32() : 0;
843 RTFSprms aPathAttributes
;
844 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_x
, new RTFValue(nX
));
845 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_y
, new RTFValue(nY
));
846 aPolygonSprms
.set(NS_ooxml::LN_CT_WrapPath_lineTo
,
847 new RTFValue(aPathAttributes
), RTFOverwrite::NO_APPEND
);
849 } while (nCharIndex
>= 0);
850 rShape
.getWrapPolygonSprms() = aPolygonSprms
;
852 else if (rProperty
.first
== "fRelFlipV")
853 obRelFlipV
= rProperty
.second
.toInt32() == 1;
854 else if (rProperty
.first
== "fFlipH")
855 obFlipH
= rProperty
.second
.toInt32() == 1;
856 else if (rProperty
.first
== "fFlipV")
857 obFlipV
= rProperty
.second
.toInt32() == 1;
859 SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty
.first
<< "':'"
860 << rProperty
.second
<< "'");
863 if (xPropertySet
.is())
865 resolveLineColorAndWidth(m_bTextFrame
, xPropertySet
, aLineColor
, aLineWidth
);
868 bool bOldStyle
= m_aParents
.size() > 1;
869 resolveDhgt(xPropertySet
, rShape
.getZ(), bOldStyle
);
872 xPropertySet
->setPropertyValue("WritingMode", uno::makeAny(eWritingMode
));
874 // Only Writer textframes implement text::WritingMode2.
875 xPropertySet
->setPropertyValue("TextWritingMode",
876 uno::makeAny(text::WritingMode(eWritingMode
)));
879 if (!m_aParents
.empty() && m_aParents
.top().is() && !m_bTextFrame
)
880 m_aParents
.top()->add(xShape
);
884 m_rImport
.resolvePict(false, xShape
);
887 if (nType
== ESCHER_ShpInst_PictureFrame
) // picture frame
889 assert(!m_bTextFrame
);
890 if (!bPib
) // ??? not sure if the early return should be removed on else?
892 m_xShape
= xShape
; // store it for later resolvePict call
895 // Handle horizontal flip.
896 if (obFlipH
== true && xPropertySet
.is())
897 xPropertySet
->setPropertyValue("IsMirrored", uno::makeAny(true));
901 if (bCustom
&& xShape
.is() && !bPib
)
903 uno::Reference
<drawing::XEnhancedCustomShapeDefaulter
> xDefaulter(xShape
, uno::UNO_QUERY
);
904 xDefaulter
->createCustomShapeDefaults(OUString::number(nType
));
908 if (bCustom
&& !aShapeText
.isEmpty())
910 uno::Reference
<text::XTextRange
> xTextRange(xShape
, uno::UNO_QUERY
);
912 xTextRange
->setString(aShapeText
);
914 xPropertySet
->setPropertyValue("CharFontName", uno::makeAny(aFontFamily
));
915 xPropertySet
->setPropertyValue("CharHeight", uno::makeAny(nFontSize
));
918 // Creating CustomShapeGeometry property
919 std::vector
<beans::PropertyValue
> aGeometry
;
920 if (aViewBox
.X
|| aViewBox
.Y
|| aViewBox
.Width
|| aViewBox
.Height
)
922 aViewBox
.Width
-= aViewBox
.X
;
923 aViewBox
.Height
-= aViewBox
.Y
;
924 aPropertyValue
.Name
= "ViewBox";
925 aPropertyValue
.Value
<<= aViewBox
;
926 aGeometry
.push_back(aPropertyValue
);
930 aPropertyValue
.Name
= "Path";
931 aPropertyValue
.Value
<<= comphelper::containerToSequence(aPath
);
932 aGeometry
.push_back(aPropertyValue
);
934 if (!aGeometry
.empty() && xPropertySet
.is() && !m_bTextFrame
)
935 xPropertySet
->setPropertyValue("CustomShapeGeometry",
936 uno::Any(comphelper::containerToSequence(aGeometry
)));
938 if (!aShapeText
.isEmpty())
940 auto aGeomPropSeq
= xPropertySet
->getPropertyValue("CustomShapeGeometry")
941 .get
<uno::Sequence
<beans::PropertyValue
>>();
943 = comphelper::sequenceToContainer
<std::vector
<beans::PropertyValue
>>(aGeomPropSeq
);
944 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence({
945 { "TextPath", uno::makeAny(true) },
947 auto it
= std::find_if(
948 aGeomPropVec
.begin(), aGeomPropVec
.end(),
949 [](const beans::PropertyValue
& rValue
) { return rValue
.Name
== "TextPath"; });
950 if (it
== aGeomPropVec
.end())
951 aGeomPropVec
.push_back(comphelper::makePropertyValue("TextPath", aPropertyValues
));
953 it
->Value
<<= aPropertyValues
;
955 xPropertySet
->setPropertyValue("CustomShapeGeometry",
956 uno::makeAny(comphelper::containerToSequence(aGeomPropVec
)));
957 xPropertySet
->setPropertyValue("TextAutoGrowHeight", uno::makeAny(false));
958 xPropertySet
->setPropertyValue("TextAutoGrowWidth", uno::makeAny(false));
961 if (!boost::logic::indeterminate(obRelFlipV
) && xPropertySet
.is())
963 if (nType
== ESCHER_ShpInst_Line
)
965 // Line shape inside group shape: get the polygon sequence and transform it.
966 uno::Sequence
<uno::Sequence
<awt::Point
>> aPolyPolySequence
;
967 if ((xPropertySet
->getPropertyValue("PolyPolygon") >>= aPolyPolySequence
)
968 && aPolyPolySequence
.hasElements())
970 uno::Sequence
<awt::Point
>& rPolygon
= aPolyPolySequence
[0];
971 basegfx::B2DPolygon aPoly
;
972 for (sal_Int32 i
= 0; i
< rPolygon
.getLength(); ++i
)
974 const awt::Point
& rPoint
= rPolygon
[i
];
975 aPoly
.insert(i
, basegfx::B2DPoint(rPoint
.X
, rPoint
.Y
));
977 basegfx::B2DHomMatrix aTransformation
;
978 aTransformation
.scale(1.0, obRelFlipV
? -1.0 : 1.0);
979 aPoly
.transform(aTransformation
);
980 for (sal_Int32 i
= 0; i
< rPolygon
.getLength(); ++i
)
982 basegfx::B2DPoint
aPoint(aPoly
.getB2DPoint(i
));
984 = awt::Point(static_cast<sal_Int32
>(convertMm100ToTwip(aPoint
.getX())),
985 static_cast<sal_Int32
>(convertMm100ToTwip(aPoint
.getY())));
987 xPropertySet
->setPropertyValue("PolyPolygon", uno::makeAny(aPolyPolySequence
));
992 // Set position and size
995 sal_Int32 nLeft
= rShape
.getLeft();
996 sal_Int32 nTop
= rShape
.getTop();
998 bool bInShapeGroup
= oGroupLeft
&& oGroupTop
&& oGroupRight
&& oGroupBottom
&& oRelLeft
999 && oRelTop
&& oRelRight
&& oRelBottom
;
1003 // See lclGetAbsPoint() in the VML import: rShape is the group shape, oGroup is its coordinate system, oRel is the relative child shape.
1004 sal_Int32 nShapeWidth
= rShape
.getRight() - rShape
.getLeft();
1005 sal_Int32 nShapeHeight
= rShape
.getBottom() - rShape
.getTop();
1006 sal_Int32 nCoordSysWidth
= *oGroupRight
- *oGroupLeft
;
1007 sal_Int32 nCoordSysHeight
= *oGroupBottom
- *oGroupTop
;
1008 double fWidthRatio
= static_cast<double>(nShapeWidth
) / nCoordSysWidth
;
1009 double fHeightRatio
= static_cast<double>(nShapeHeight
) / nCoordSysHeight
;
1010 nLeft
= static_cast<sal_Int32
>(rShape
.getLeft()
1011 + fWidthRatio
* (*oRelLeft
- *oGroupLeft
));
1012 nTop
= static_cast<sal_Int32
>(rShape
.getTop() + fHeightRatio
* (*oRelTop
- *oGroupTop
));
1014 // See lclGetAbsRect() in the VML import.
1015 aSize
.Width
= std::lround(fWidthRatio
* (*oRelRight
- *oRelLeft
));
1016 aSize
.Height
= std::lround(fHeightRatio
* (*oRelBottom
- *oRelTop
));
1021 xPropertySet
->setPropertyValue("HoriOrientPosition", uno::makeAny(nLeft
));
1022 xPropertySet
->setPropertyValue("VertOrientPosition", uno::makeAny(nTop
));
1025 xShape
->setPosition(awt::Point(nLeft
, nTop
));
1028 xShape
->setSize(aSize
);
1030 xShape
->setSize(awt::Size(rShape
.getRight() - rShape
.getLeft(),
1031 rShape
.getBottom() - rShape
.getTop()));
1033 if (obFlipH
== true || obFlipV
== true)
1035 // Line shapes have no CustomShapeGeometry.
1036 if (nType
!= ESCHER_ShpInst_Line
)
1038 // This has to be set after position and size is set, otherwise flip will affect the position.
1039 comphelper::SequenceAsHashMap
aCustomShapeGeometry(
1040 xPropertySet
->getPropertyValue("CustomShapeGeometry"));
1041 if (obFlipH
== true)
1042 aCustomShapeGeometry
["MirroredX"] <<= true;
1043 if (obFlipV
== true)
1044 aCustomShapeGeometry
["MirroredY"] <<= true;
1045 xPropertySet
->setPropertyValue(
1046 "CustomShapeGeometry",
1047 uno::makeAny(aCustomShapeGeometry
.getAsConstPropertyValueList()));
1051 if (rShape
.getHoriOrientRelation() != 0)
1052 xPropertySet
->setPropertyValue("HoriOrientRelation",
1053 uno::makeAny(rShape
.getHoriOrientRelation()));
1054 if (rShape
.getVertOrientRelation() != 0)
1055 xPropertySet
->setPropertyValue("VertOrientRelation",
1056 uno::makeAny(rShape
.getVertOrientRelation()));
1057 if (rShape
.getWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
)
1058 xPropertySet
->setPropertyValue("Surround", uno::makeAny(rShape
.getWrap()));
1059 oox::ModelObjectHelper
aModelObjectHelper(m_rImport
.getModelFactory());
1060 if (aFillModel
.moType
.has())
1062 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1063 aFillModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1064 // Sets the FillStyle and FillGradient UNO properties.
1065 oox::PropertySet(xShape
).setProperties(aPropMap
);
1068 if (aShadowModel
.mbHasShadow
)
1070 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1071 aShadowModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1072 // Sets the ShadowFormat UNO property.
1073 oox::PropertySet(xShape
).setProperties(aPropMap
);
1075 xPropertySet
->setPropertyValue("AnchorType",
1076 uno::makeAny(text::TextContentAnchorType_AT_CHARACTER
));
1077 xPropertySet
->setPropertyValue("Opaque", uno::makeAny(bOpaque
));
1080 xPropertySet
->setPropertyValue("RelativeWidth", uno::makeAny(*oRelativeWidth
));
1081 xPropertySet
->setPropertyValue("RelativeWidthRelation",
1082 uno::makeAny(nRelativeWidthRelation
));
1084 if (oRelativeHeight
)
1086 xPropertySet
->setPropertyValue("RelativeHeight", uno::makeAny(*oRelativeHeight
));
1087 xPropertySet
->setPropertyValue("RelativeHeightRelation",
1088 uno::makeAny(nRelativeHeightRelation
));
1092 if (m_rImport
.isInBackground())
1094 RTFSprms aAttributes
;
1095 aAttributes
.set(NS_ooxml::LN_CT_Background_color
,
1096 new RTFValue(xPropertySet
->getPropertyValue("FillColor").get
<sal_Int32
>()));
1097 m_rImport
.Mapper().props(new RTFReferenceProperties(aAttributes
));
1099 uno::Reference
<lang::XComponent
> xComponent(xShape
, uno::UNO_QUERY
);
1100 xComponent
->dispose();
1104 // Send it to dmapper
1107 m_rImport
.Mapper().startShape(xShape
);
1110 m_rImport
.Mapper().endShape();
1116 void RTFSdrImport::close() { m_rImport
.Mapper().endShape(); }
1118 void RTFSdrImport::append(const OUString
& aKey
, const OUString
& aValue
)
1120 applyProperty(m_xShape
, aKey
, aValue
);
1123 void RTFSdrImport::appendGroupProperty(const OUString
& aKey
, const OUString
& aValue
)
1125 if (m_aParents
.empty())
1127 uno::Reference
<drawing::XShape
> xShape(m_aParents
.top(), uno::UNO_QUERY
);
1129 applyProperty(xShape
, aKey
, aValue
);
1132 } // namespace rtftok
1133 } // namespace writerfilter
1135 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */