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 <svx/unoapi.hxx>
49 #include <svx/svdobj.hxx>
51 #include <dmapper/GraphicZOrderHelper.hxx>
52 #include "rtfdocumentimpl.hxx"
54 using namespace com::sun::star
;
56 namespace writerfilter
60 RTFSdrImport::RTFSdrImport(RTFDocumentImpl
& rDocument
,
61 uno::Reference
<lang::XComponent
> const& xDstDoc
)
62 : m_rImport(rDocument
)
64 , m_bTextGraphicObject(false)
67 uno::Reference
<drawing::XDrawPageSupplier
> xDrawings(xDstDoc
, uno::UNO_QUERY
);
69 m_aParents
.push(xDrawings
->getDrawPage());
70 m_aGraphicZOrderHelpers
.push(writerfilter::dmapper::GraphicZOrderHelper());
73 RTFSdrImport::~RTFSdrImport()
75 if (!m_aGraphicZOrderHelpers
.empty())
76 m_aGraphicZOrderHelpers
.pop();
77 if (!m_aParents
.empty())
81 void RTFSdrImport::createShape(const OUString
& rService
, uno::Reference
<drawing::XShape
>& xShape
,
82 uno::Reference
<beans::XPropertySet
>& xPropertySet
)
84 if (m_rImport
.getModelFactory().is())
85 xShape
.set(m_rImport
.getModelFactory()->createInstance(rService
), uno::UNO_QUERY
);
86 xPropertySet
.set(xShape
, uno::UNO_QUERY
);
89 std::vector
<beans::PropertyValue
> RTFSdrImport::getTextFrameDefaults(bool bNew
)
91 std::vector
<beans::PropertyValue
> aRet
;
92 beans::PropertyValue aPropertyValue
;
94 aPropertyValue
.Name
= "HoriOrient";
95 aPropertyValue
.Value
<<= text::HoriOrientation::NONE
;
96 aRet
.push_back(aPropertyValue
);
97 aPropertyValue
.Name
= "HoriOrientRelation";
98 aPropertyValue
.Value
<<= text::RelOrientation::FRAME
;
99 aRet
.push_back(aPropertyValue
);
100 aPropertyValue
.Name
= "VertOrient";
101 aPropertyValue
.Value
<<= text::VertOrientation::NONE
;
102 aRet
.push_back(aPropertyValue
);
103 aPropertyValue
.Name
= "VertOrientRelation";
104 aPropertyValue
.Value
<<= text::RelOrientation::FRAME
;
105 aRet
.push_back(aPropertyValue
);
108 aPropertyValue
.Name
= "BackColorTransparency";
109 aPropertyValue
.Value
<<= sal_Int32(100);
110 aRet
.push_back(aPropertyValue
);
112 // See the spec, new-style frame default margins are specified in EMUs.
113 aPropertyValue
.Name
= "LeftBorderDistance";
114 aPropertyValue
.Value
<<= sal_Int32(bNew
? (91440 / 360) : 0);
115 aRet
.push_back(aPropertyValue
);
116 aPropertyValue
.Name
= "RightBorderDistance";
117 aPropertyValue
.Value
<<= sal_Int32(bNew
? (91440 / 360) : 0);
118 aRet
.push_back(aPropertyValue
);
119 aPropertyValue
.Name
= "TopBorderDistance";
120 aPropertyValue
.Value
<<= sal_Int32(bNew
? (45720 / 360) : 0);
121 aRet
.push_back(aPropertyValue
);
122 aPropertyValue
.Name
= "BottomBorderDistance";
123 aPropertyValue
.Value
<<= sal_Int32(bNew
? (45720 / 360) : 0);
124 aRet
.push_back(aPropertyValue
);
125 aPropertyValue
.Name
= "SizeType";
126 aPropertyValue
.Value
<<= text::SizeType::FIX
;
127 aRet
.push_back(aPropertyValue
);
131 void RTFSdrImport::pushParent(uno::Reference
<drawing::XShapes
> const& xParent
)
133 m_aParents
.push(xParent
);
134 m_aGraphicZOrderHelpers
.push(writerfilter::dmapper::GraphicZOrderHelper());
137 void RTFSdrImport::popParent()
139 if (!m_aGraphicZOrderHelpers
.empty())
140 m_aGraphicZOrderHelpers
.pop();
141 if (!m_aParents
.empty())
145 void RTFSdrImport::resolveDhgt(uno::Reference
<beans::XPropertySet
> const& xPropertySet
,
146 sal_Int32
const nZOrder
, bool const bOldStyle
)
148 if (!m_aGraphicZOrderHelpers
.empty())
150 writerfilter::dmapper::GraphicZOrderHelper
& rHelper
= m_aGraphicZOrderHelpers
.top();
151 xPropertySet
->setPropertyValue("ZOrder",
152 uno::makeAny(rHelper
.findZOrder(nZOrder
, bOldStyle
)));
153 rHelper
.addItem(xPropertySet
, nZOrder
);
157 void RTFSdrImport::resolveLineColorAndWidth(bool bTextFrame
,
158 const uno::Reference
<beans::XPropertySet
>& xPropertySet
,
159 uno::Any
const& rLineColor
, uno::Any
const& rLineWidth
)
163 xPropertySet
->setPropertyValue("LineColor", rLineColor
);
164 xPropertySet
->setPropertyValue("LineWidth", rLineWidth
);
168 static const char* aBorders
[]
169 = { "TopBorder", "LeftBorder", "BottomBorder", "RightBorder" };
170 for (const char* pBorder
: aBorders
)
172 auto aBorderLine
= xPropertySet
->getPropertyValue(OUString::createFromAscii(pBorder
))
173 .get
<table::BorderLine2
>();
174 if (rLineColor
.hasValue())
175 aBorderLine
.Color
= rLineColor
.get
<sal_Int32
>();
176 if (rLineWidth
.hasValue())
177 aBorderLine
.LineWidth
= rLineWidth
.get
<sal_Int32
>();
178 xPropertySet
->setPropertyValue(OUString::createFromAscii(pBorder
),
179 uno::makeAny(aBorderLine
));
184 void RTFSdrImport::resolveFLine(uno::Reference
<beans::XPropertySet
> const& xPropertySet
,
185 sal_Int32
const nFLine
)
188 xPropertySet
->setPropertyValue("LineStyle", uno::makeAny(drawing::LineStyle_NONE
));
190 xPropertySet
->setPropertyValue("LineStyle", uno::makeAny(drawing::LineStyle_SOLID
));
193 void RTFSdrImport::applyProperty(uno::Reference
<drawing::XShape
> const& xShape
,
194 const OUString
& aKey
, const OUString
& aValue
)
196 uno::Reference
<beans::XPropertySet
> xPropertySet(xShape
, uno::UNO_QUERY
);
197 sal_Int16 nHoriOrient
= 0;
198 sal_Int16 nVertOrient
= 0;
199 boost::logic::tribool
obFitShapeToText(boost::logic::indeterminate
);
204 switch (aValue
.toInt32())
207 nHoriOrient
= text::HoriOrientation::LEFT
;
210 nHoriOrient
= text::HoriOrientation::CENTER
;
213 nHoriOrient
= text::HoriOrientation::RIGHT
;
216 nHoriOrient
= text::HoriOrientation::INSIDE
;
219 nHoriOrient
= text::HoriOrientation::OUTSIDE
;
225 else if (aKey
== "posv")
227 switch (aValue
.toInt32())
230 nVertOrient
= text::VertOrientation::TOP
;
233 nVertOrient
= text::VertOrientation::CENTER
;
236 nVertOrient
= text::VertOrientation::BOTTOM
;
242 else if (aKey
== "fFitShapeToText")
243 obFitShapeToText
= aValue
.toInt32() == 1;
244 else if (aKey
== "fFilled")
245 bFilled
= aValue
.toInt32() == 1;
246 else if (aKey
== "rotation")
248 // See DffPropertyReader::Fix16ToAngle(): in RTF, positive rotation angles are clockwise, we have them as counter-clockwise.
249 // Additionally, RTF type is 0..360*2^16, our is 0..360*100.
250 sal_Int32 nRotation
= aValue
.toInt32() * 100 / RTF_MULTIPLIER
;
251 uno::Reference
<lang::XServiceInfo
> xServiceInfo(xShape
, uno::UNO_QUERY
);
252 if (!xServiceInfo
->supportsService("com.sun.star.text.TextFrame"))
253 xPropertySet
->setPropertyValue(
255 uno::makeAny(sal_Int32(NormAngle36000(static_cast<long>(nRotation
) * -1))));
258 if (nHoriOrient
!= 0 && xPropertySet
.is())
259 xPropertySet
->setPropertyValue("HoriOrient", uno::makeAny(nHoriOrient
));
260 if (nVertOrient
!= 0 && xPropertySet
.is())
261 xPropertySet
->setPropertyValue("VertOrient", uno::makeAny(nVertOrient
));
262 if (!boost::logic::indeterminate(obFitShapeToText
) && xPropertySet
.is())
264 xPropertySet
->setPropertyValue(
265 "SizeType", uno::makeAny(obFitShapeToText
? text::SizeType::MIN
: text::SizeType::FIX
));
266 xPropertySet
->setPropertyValue("FrameIsAutomaticHeight",
267 uno::makeAny(static_cast<bool>(obFitShapeToText
)));
269 if (!bFilled
&& xPropertySet
.is())
272 xPropertySet
->setPropertyValue("BackColorTransparency", uno::makeAny(sal_Int32(100)));
274 xPropertySet
->setPropertyValue("FillStyle", uno::makeAny(drawing::FillStyle_NONE
));
278 int RTFSdrImport::initShape(uno::Reference
<drawing::XShape
>& o_xShape
,
279 uno::Reference
<beans::XPropertySet
>& o_xPropSet
, bool& o_rIsCustomShape
,
280 RTFShape
const& rShape
, bool const bClose
,
281 ShapeOrPict
const shapeOrPict
)
283 assert(!o_xShape
.is());
284 assert(!o_xPropSet
.is());
285 o_rIsCustomShape
= false;
288 // first, find the shape type
290 auto iter
= std::find_if(rShape
.getProperties().begin(), rShape
.getProperties().end(),
291 [](const std::pair
<OUString
, OUString
>& rProperty
) {
292 return rProperty
.first
== "shapeType";
295 if (iter
== rShape
.getProperties().end())
297 if (SHAPE
== shapeOrPict
)
299 // The spec doesn't state what is the default for shapeType,
300 // Word seems to implement it as a rectangle.
301 nType
= ESCHER_ShpInst_Rectangle
;
305 // pict is picture by default but can be a rectangle too fdo#79319
306 nType
= ESCHER_ShpInst_PictureFrame
;
311 nType
= iter
->second
.toInt32();
312 if (PICT
== shapeOrPict
&& ESCHER_ShpInst_PictureFrame
!= nType
)
320 case ESCHER_ShpInst_PictureFrame
:
321 createShape("com.sun.star.drawing.GraphicObjectShape", o_xShape
, o_xPropSet
);
322 m_bTextGraphicObject
= true;
324 case ESCHER_ShpInst_Line
:
325 createShape("com.sun.star.drawing.LineShape", o_xShape
, o_xPropSet
);
327 case ESCHER_ShpInst_Rectangle
:
328 case ESCHER_ShpInst_TextBox
:
329 // If we're inside a groupshape, can't use text frames.
330 if (!bClose
&& m_aParents
.size() == 1)
332 createShape("com.sun.star.text.TextFrame", o_xShape
, o_xPropSet
);
334 std::vector
<beans::PropertyValue
> aDefaults
= getTextFrameDefaults(true);
335 for (const beans::PropertyValue
& i
: aDefaults
)
336 o_xPropSet
->setPropertyValue(i
.Name
, i
.Value
);
341 createShape("com.sun.star.drawing.CustomShape", o_xShape
, o_xPropSet
);
342 o_rIsCustomShape
= true;
347 if (o_xPropSet
.is() && !m_bTextFrame
)
349 o_xPropSet
->setPropertyValue(
351 uno::makeAny(sal_uInt32(0xffffff))); // White in Word, kind of blue in Writer.
352 o_xPropSet
->setPropertyValue("VertOrient", uno::makeAny(text::VertOrientation::NONE
));
358 void RTFSdrImport::resolve(RTFShape
& rShape
, bool bClose
, ShapeOrPict
const shapeOrPict
)
361 m_bTextFrame
= false;
362 m_bTextGraphicObject
= false;
364 uno::Reference
<drawing::XShape
> xShape
;
365 uno::Reference
<beans::XPropertySet
> xPropertySet
;
367 beans::PropertyValue aPropertyValue
;
368 awt::Rectangle aViewBox
;
369 std::vector
<beans::PropertyValue
> aPath
;
370 // Default line color is black in Word, blue in Writer.
371 uno::Any aLineColor
= uno::makeAny(COL_BLACK
);
372 // Default line width is 0.75 pt (26 mm100) in Word, 0 in Writer.
373 uno::Any aLineWidth
= uno::makeAny(sal_Int32(26));
374 sal_Int16 eWritingMode
= text::WritingMode2::LR_TB
;
375 // Groupshape support
376 boost::optional
<sal_Int32
> oGroupLeft
;
377 boost::optional
<sal_Int32
> oGroupTop
;
378 boost::optional
<sal_Int32
> oGroupRight
;
379 boost::optional
<sal_Int32
> oGroupBottom
;
380 boost::optional
<sal_Int32
> oRelLeft
;
381 boost::optional
<sal_Int32
> oRelTop
;
382 boost::optional
<sal_Int32
> oRelRight
;
383 boost::optional
<sal_Int32
> oRelBottom
;
385 // Importing these are not trivial, let the VML import do the hard work.
386 oox::vml::FillModel aFillModel
; // Gradient.
387 oox::vml::ShadowModel aShadowModel
; // Shadow.
391 boost::optional
<sal_Int16
> oRelativeWidth
;
392 boost::optional
<sal_Int16
> oRelativeHeight
;
393 sal_Int16 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
394 sal_Int16 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
395 boost::logic::tribool
obRelFlipV(boost::logic::indeterminate
);
396 boost::logic::tribool
obFlipH(boost::logic::indeterminate
);
397 boost::logic::tribool
obFlipV(boost::logic::indeterminate
);
399 OUString aShapeText
= "";
400 OUString aFontFamily
= "";
401 float nFontSize
= 1.0;
404 int const nType
= initShape(xShape
, xPropertySet
, bCustom
, rShape
, bClose
, shapeOrPict
);
406 for (auto& rProperty
: rShape
.getProperties())
408 if (rProperty
.first
== "shapeType")
410 continue; // ignore: already handled by initShape
412 if (rProperty
.first
== "wzName")
416 uno::Reference
<container::XNamed
> xNamed(xShape
, uno::UNO_QUERY
);
417 xNamed
->setName(rProperty
.second
);
420 xPropertySet
->setPropertyValue("Name", uno::makeAny(rProperty
.second
));
422 else if (rProperty
.first
== "wzDescription")
423 xPropertySet
->setPropertyValue("Description", uno::makeAny(rProperty
.second
));
424 else if (rProperty
.first
== "gtextUNICODE")
425 aShapeText
= rProperty
.second
;
426 else if (rProperty
.first
== "gtextFont")
427 aFontFamily
= rProperty
.second
;
428 else if (rProperty
.first
== "gtextSize")
430 // RTF size is multiplied by 2^16
431 nFontSize
= static_cast<float>(rProperty
.second
.toUInt32()) / RTF_MULTIPLIER
;
433 else if (rProperty
.first
== "pib")
435 m_rImport
.setDestinationText(rProperty
.second
);
438 else if (rProperty
.first
== "fillColor" && xPropertySet
.is())
440 aAny
<<= msfilter::util::BGRToRGB(rProperty
.second
.toUInt32());
442 xPropertySet
->setPropertyValue("BackColor", aAny
);
444 xPropertySet
->setPropertyValue("FillColor", aAny
);
446 // fillType will decide, possible it'll be the start color of a gradient.
447 aFillModel
.moColor
.set(
448 "#" + OUString::fromUtf8(msfilter::util::ConvertColor(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
.set("#"
453 + OUString::fromUtf8(msfilter::util::ConvertColor(
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 OUString aToken
= rProperty
.second
.getToken(0, ';', nCharIndex
);
491 nSize
= aToken
.toInt32();
493 nCount
= aToken
.toInt32();
494 else if (aToken
.getLength())
496 // The coordinates are in an (x,y) form.
497 aToken
= aToken
.copy(1, aToken
.getLength() - 2);
499 sal_Int32 nX
= aToken
.getToken(0, ',', nI
).toInt32();
500 sal_Int32 nY
= (nI
>= 0) ? aToken
.getToken(0, ',', nI
).toInt32() : 0;
501 drawing::EnhancedCustomShapeParameterPair aPair
;
502 aPair
.First
.Value
<<= nX
;
503 aPair
.Second
.Value
<<= nY
;
504 aCoordinates
.push_back(aPair
);
506 } while (nCharIndex
>= 0);
507 aPropertyValue
.Name
= "Coordinates";
508 aPropertyValue
.Value
<<= comphelper::containerToSequence(aCoordinates
);
509 aPath
.push_back(aPropertyValue
);
511 else if (rProperty
.first
== "pSegmentInfo")
513 std::vector
<drawing::EnhancedCustomShapeSegment
> aSegments
;
515 sal_Int32 nCount
= 0;
516 sal_Int32 nCharIndex
= 0;
519 sal_Int32 nSeg
= rProperty
.second
.getToken(0, ';', nCharIndex
).toInt32();
526 sal_Int32 nPoints
= 1;
527 if (nSeg
>= 0x2000 && nSeg
< 0x20FF)
529 nPoints
= nSeg
& 0x0FFF;
533 drawing::EnhancedCustomShapeSegment aSegment
;
536 case 0x0001: // lineto
537 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
538 aSegment
.Count
= sal_Int32(1);
539 aSegments
.push_back(aSegment
);
541 case 0x4000: // moveto
542 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::MOVETO
;
543 aSegment
.Count
= sal_Int32(1);
544 aSegments
.push_back(aSegment
);
546 case 0x2000: // curveto
547 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::CURVETO
;
548 aSegment
.Count
= nPoints
;
549 aSegments
.push_back(aSegment
);
551 case 0xb300: // arcto
552 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::ARCTO
;
553 aSegment
.Count
= sal_Int32(0);
554 aSegments
.push_back(aSegment
);
557 case 0xaa00: // nofill
558 case 0xab00: // nostroke
559 case 0x6001: // close
563 = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH
;
564 aSegment
.Count
= sal_Int32(0);
565 aSegments
.push_back(aSegment
);
567 default: // given number of lineto elements
568 aSegment
.Command
= drawing::EnhancedCustomShapeSegmentCommand::LINETO
;
569 aSegment
.Count
= nSeg
;
570 aSegments
.push_back(aSegment
);
574 } while (nCharIndex
>= 0);
575 aPropertyValue
.Name
= "Segments";
576 aPropertyValue
.Value
<<= comphelper::containerToSequence(aSegments
);
577 aPath
.push_back(aPropertyValue
);
579 else if (rProperty
.first
== "geoLeft")
580 aViewBox
.X
= rProperty
.second
.toInt32();
581 else if (rProperty
.first
== "geoTop")
582 aViewBox
.Y
= rProperty
.second
.toInt32();
583 else if (rProperty
.first
== "geoRight")
584 aViewBox
.Width
= rProperty
.second
.toInt32();
585 else if (rProperty
.first
== "geoBottom")
586 aViewBox
.Height
= rProperty
.second
.toInt32();
587 else if (rProperty
.first
== "dhgt")
589 // dhgt is Word 2007, \shpz is Word 97-2003, the later has priority.
591 resolveDhgt(xPropertySet
, rProperty
.second
.toInt32(), /*bOldStyle=*/false);
593 // These are in EMU, convert to mm100.
594 else if (rProperty
.first
== "dxTextLeft")
596 if (xPropertySet
.is())
597 xPropertySet
->setPropertyValue("LeftBorderDistance",
598 uno::makeAny(rProperty
.second
.toInt32() / 360));
600 else if (rProperty
.first
== "dyTextTop")
602 if (xPropertySet
.is())
603 xPropertySet
->setPropertyValue("TopBorderDistance",
604 uno::makeAny(rProperty
.second
.toInt32() / 360));
606 else if (rProperty
.first
== "dxTextRight")
608 if (xPropertySet
.is())
609 xPropertySet
->setPropertyValue("RightBorderDistance",
610 uno::makeAny(rProperty
.second
.toInt32() / 360));
612 else if (rProperty
.first
== "dyTextBottom")
614 if (xPropertySet
.is())
615 xPropertySet
->setPropertyValue("BottomBorderDistance",
616 uno::makeAny(rProperty
.second
.toInt32() / 360));
618 else if (rProperty
.first
== "dxWrapDistLeft")
620 if (m_bTextGraphicObject
)
621 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distL
,
622 new RTFValue(rProperty
.second
.toInt32()));
623 else if (xPropertySet
.is())
624 xPropertySet
->setPropertyValue("LeftMargin",
625 uno::makeAny(rProperty
.second
.toInt32() / 360));
627 else if (rProperty
.first
== "dyWrapDistTop")
629 if (m_bTextGraphicObject
)
630 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distT
,
631 new RTFValue(rProperty
.second
.toInt32()));
632 else if (xPropertySet
.is())
633 xPropertySet
->setPropertyValue("TopMargin",
634 uno::makeAny(rProperty
.second
.toInt32() / 360));
636 else if (rProperty
.first
== "dxWrapDistRight")
638 if (m_bTextGraphicObject
)
639 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distR
,
640 new RTFValue(rProperty
.second
.toInt32()));
641 else if (xPropertySet
.is())
642 xPropertySet
->setPropertyValue("RightMargin",
643 uno::makeAny(rProperty
.second
.toInt32() / 360));
645 else if (rProperty
.first
== "dyWrapDistBottom")
647 if (m_bTextGraphicObject
)
648 rShape
.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distB
,
649 new RTFValue(rProperty
.second
.toInt32()));
650 else if (xPropertySet
.is())
651 xPropertySet
->setPropertyValue("BottomMargin",
652 uno::makeAny(rProperty
.second
.toInt32() / 360));
654 else if (rProperty
.first
== "fillType")
656 switch (rProperty
.second
.toInt32())
658 case 7: // Shade using the fillAngle
659 aFillModel
.moType
.set(oox::XML_gradient
);
662 SAL_INFO("writerfilter",
663 "TODO handle fillType value '" << rProperty
.second
<< "'");
667 else if (rProperty
.first
== "fillFocus")
668 aFillModel
.moFocus
.set(rProperty
.second
.toDouble() / 100); // percent
669 else if (rProperty
.first
== "fShadow" && xPropertySet
.is())
671 if (rProperty
.second
.toInt32() == 1)
672 aShadowModel
.mbHasShadow
= true;
674 else if (rProperty
.first
== "shadowColor")
675 aShadowModel
.moColor
.set("#"
676 + OUString::fromUtf8(msfilter::util::ConvertColor(
677 msfilter::util::BGRToRGB(rProperty
.second
.toInt32()))));
678 else if (rProperty
.first
== "shadowOffsetX")
680 aShadowModel
.moOffset
.set(OUString::number(rProperty
.second
.toDouble() / 12700) + "pt");
681 else if (rProperty
.first
== "posh" || rProperty
.first
== "posv"
682 || rProperty
.first
== "fFitShapeToText" || rProperty
.first
== "fFilled"
683 || rProperty
.first
== "rotation")
684 applyProperty(xShape
, rProperty
.first
, rProperty
.second
);
685 else if (rProperty
.first
== "posrelh")
687 switch (rProperty
.second
.toInt32())
690 rShape
.setHoriOrientRelation(text::RelOrientation::PAGE_FRAME
);
696 else if (rProperty
.first
== "posrelv")
698 switch (rProperty
.second
.toInt32())
701 rShape
.setVertOrientRelation(text::RelOrientation::PAGE_FRAME
);
707 else if (rProperty
.first
== "groupLeft")
708 oGroupLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
709 else if (rProperty
.first
== "groupTop")
710 oGroupTop
= convertTwipToMm100(rProperty
.second
.toInt32());
711 else if (rProperty
.first
== "groupRight")
712 oGroupRight
= convertTwipToMm100(rProperty
.second
.toInt32());
713 else if (rProperty
.first
== "groupBottom")
714 oGroupBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
715 else if (rProperty
.first
== "relLeft")
716 oRelLeft
= convertTwipToMm100(rProperty
.second
.toInt32());
717 else if (rProperty
.first
== "relTop")
718 oRelTop
= convertTwipToMm100(rProperty
.second
.toInt32());
719 else if (rProperty
.first
== "relRight")
720 oRelRight
= convertTwipToMm100(rProperty
.second
.toInt32());
721 else if (rProperty
.first
== "relBottom")
722 oRelBottom
= convertTwipToMm100(rProperty
.second
.toInt32());
723 else if (rProperty
.first
== "fBehindDocument")
724 bOpaque
= !rProperty
.second
.toInt32();
725 else if (rProperty
.first
== "pctHoriz" || rProperty
.first
== "pctVert")
727 sal_Int16 nPercentage
= rtl::math::round(rProperty
.second
.toDouble() / 10);
730 boost::optional
<sal_Int16
>& rPercentage
731 = rProperty
.first
== "pctHoriz" ? oRelativeWidth
: oRelativeHeight
;
732 rPercentage
= nPercentage
;
735 else if (rProperty
.first
== "sizerelh")
737 if (xPropertySet
.is())
739 switch (rProperty
.second
.toInt32())
742 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
745 nRelativeWidthRelation
= text::RelOrientation::PAGE_FRAME
;
748 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: "
749 << rProperty
.second
);
754 else if (rProperty
.first
== "sizerelv")
756 if (xPropertySet
.is())
758 switch (rProperty
.second
.toInt32())
761 nRelativeHeightRelation
= text::RelOrientation::FRAME
;
764 nRelativeHeightRelation
= text::RelOrientation::PAGE_FRAME
;
767 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: "
768 << rProperty
.second
);
773 else if (rProperty
.first
== "fHorizRule") // TODO: what does "fStandardHR" do?
775 // horizontal rule: relative width defaults to 100% of paragraph
776 // TODO: does it have a default height?
779 oRelativeWidth
= 100;
781 nRelativeWidthRelation
= text::RelOrientation::FRAME
;
782 sal_Int16
const nVertOrient
= text::VertOrientation::CENTER
;
783 if (xPropertySet
.is())
785 xPropertySet
->setPropertyValue("VertOrient", uno::makeAny(nVertOrient
));
788 else if (rProperty
.first
== "pctHR")
790 // horizontal rule relative width in permille
791 oRelativeWidth
= rProperty
.second
.toInt32() / 10;
793 else if (rProperty
.first
== "dxHeightHR")
795 // horizontal rule height
796 sal_uInt32
const nHeight(convertTwipToMm100(rProperty
.second
.toInt32()));
797 rShape
.setBottom(rShape
.getTop() + nHeight
);
799 else if (rProperty
.first
== "dxWidthHR")
801 // horizontal rule width
802 sal_uInt32
const nWidth(convertTwipToMm100(rProperty
.second
.toInt32()));
803 rShape
.setRight(rShape
.getLeft() + nWidth
);
805 else if (rProperty
.first
== "alignHR")
807 // horizontal orientation *for horizontal rule*
808 sal_Int16 nHoriOrient
= text::HoriOrientation::NONE
;
809 switch (rProperty
.second
.toInt32())
812 nHoriOrient
= text::HoriOrientation::LEFT
;
815 nHoriOrient
= text::HoriOrientation::CENTER
;
818 nHoriOrient
= text::HoriOrientation::RIGHT
;
821 if (xPropertySet
.is() && text::HoriOrientation::NONE
!= nHoriOrient
)
823 xPropertySet
->setPropertyValue("HoriOrient", uno::makeAny(nHoriOrient
));
826 else if (rProperty
.first
== "pWrapPolygonVertices")
828 RTFSprms aPolygonSprms
;
829 sal_Int32 nSize
= 0; // Size of a token
830 sal_Int32 nCount
= 0; // Number of tokens
831 sal_Int32 nCharIndex
= 0; // Character index
834 OUString aToken
= rProperty
.second
.getToken(0, ';', nCharIndex
);
836 nSize
= aToken
.toInt32();
838 nCount
= aToken
.toInt32();
839 else if (aToken
.getLength())
841 // The coordinates are in an (x,y) form.
842 aToken
= aToken
.copy(1, aToken
.getLength() - 2);
844 sal_Int32 nX
= aToken
.getToken(0, ',', nI
).toInt32();
845 sal_Int32 nY
= (nI
>= 0) ? aToken
.getToken(0, ',', nI
).toInt32() : 0;
846 RTFSprms aPathAttributes
;
847 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_x
, new RTFValue(nX
));
848 aPathAttributes
.set(NS_ooxml::LN_CT_Point2D_y
, new RTFValue(nY
));
849 aPolygonSprms
.set(NS_ooxml::LN_CT_WrapPath_lineTo
,
850 new RTFValue(aPathAttributes
), RTFOverwrite::NO_APPEND
);
852 } while (nCharIndex
>= 0);
853 rShape
.getWrapPolygonSprms() = aPolygonSprms
;
855 else if (rProperty
.first
== "fRelFlipV")
856 obRelFlipV
= rProperty
.second
.toInt32() == 1;
857 else if (rProperty
.first
== "fFlipH")
858 obFlipH
= rProperty
.second
.toInt32() == 1;
859 else if (rProperty
.first
== "fFlipV")
860 obFlipV
= rProperty
.second
.toInt32() == 1;
862 SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty
.first
<< "':'"
863 << rProperty
.second
<< "'");
866 if (xPropertySet
.is())
868 resolveLineColorAndWidth(m_bTextFrame
, xPropertySet
, aLineColor
, aLineWidth
);
871 bool bOldStyle
= m_aParents
.size() > 1;
872 resolveDhgt(xPropertySet
, rShape
.getZ(), bOldStyle
);
875 xPropertySet
->setPropertyValue("WritingMode", uno::makeAny(eWritingMode
));
877 // Only Writer textframes implement text::WritingMode2.
878 xPropertySet
->setPropertyValue("TextWritingMode",
879 uno::makeAny(text::WritingMode(eWritingMode
)));
882 if (!m_aParents
.empty() && m_aParents
.top().is() && !m_bTextFrame
)
883 m_aParents
.top()->add(xShape
);
887 m_rImport
.resolvePict(false, xShape
);
890 if (nType
== ESCHER_ShpInst_PictureFrame
) // picture frame
892 assert(!m_bTextFrame
);
893 if (!bPib
) // ??? not sure if the early return should be removed on else?
895 m_xShape
= xShape
; // store it for later resolvePict call
898 // Handle horizontal flip.
899 if (obFlipH
== true && xPropertySet
.is())
900 xPropertySet
->setPropertyValue("IsMirrored", uno::makeAny(true));
904 if (bCustom
&& xShape
.is() && !bPib
)
906 uno::Reference
<drawing::XEnhancedCustomShapeDefaulter
> xDefaulter(xShape
, uno::UNO_QUERY
);
907 xDefaulter
->createCustomShapeDefaults(OUString::number(nType
));
911 if (bCustom
&& !aShapeText
.isEmpty())
913 uno::Reference
<text::XTextRange
> xTextRange(xShape
, uno::UNO_QUERY
);
915 xTextRange
->setString(aShapeText
);
917 xPropertySet
->setPropertyValue("CharFontName", uno::makeAny(aFontFamily
));
918 xPropertySet
->setPropertyValue("CharHeight", uno::makeAny(nFontSize
));
921 // Creating CustomShapeGeometry property
922 std::vector
<beans::PropertyValue
> aGeometry
;
923 if (aViewBox
.X
|| aViewBox
.Y
|| aViewBox
.Width
|| aViewBox
.Height
)
925 aViewBox
.Width
-= aViewBox
.X
;
926 aViewBox
.Height
-= aViewBox
.Y
;
927 aPropertyValue
.Name
= "ViewBox";
928 aPropertyValue
.Value
<<= aViewBox
;
929 aGeometry
.push_back(aPropertyValue
);
933 aPropertyValue
.Name
= "Path";
934 aPropertyValue
.Value
<<= comphelper::containerToSequence(aPath
);
935 aGeometry
.push_back(aPropertyValue
);
937 if (!aGeometry
.empty() && xPropertySet
.is() && !m_bTextFrame
)
938 xPropertySet
->setPropertyValue("CustomShapeGeometry",
939 uno::Any(comphelper::containerToSequence(aGeometry
)));
941 if (!aShapeText
.isEmpty())
943 auto aGeomPropSeq
= xPropertySet
->getPropertyValue("CustomShapeGeometry")
944 .get
<uno::Sequence
<beans::PropertyValue
>>();
946 = comphelper::sequenceToContainer
<std::vector
<beans::PropertyValue
>>(aGeomPropSeq
);
947 uno::Sequence
<beans::PropertyValue
> aPropertyValues(comphelper::InitPropertySequence({
948 { "TextPath", uno::makeAny(true) },
950 auto it
= std::find_if(
951 aGeomPropVec
.begin(), aGeomPropVec
.end(),
952 [](const beans::PropertyValue
& rValue
) { return rValue
.Name
== "TextPath"; });
953 if (it
== aGeomPropVec
.end())
954 aGeomPropVec
.push_back(comphelper::makePropertyValue("TextPath", aPropertyValues
));
956 it
->Value
<<= aPropertyValues
;
958 xPropertySet
->setPropertyValue("CustomShapeGeometry",
959 uno::makeAny(comphelper::containerToSequence(aGeomPropVec
)));
960 xPropertySet
->setPropertyValue("TextAutoGrowHeight", uno::makeAny(false));
961 xPropertySet
->setPropertyValue("TextAutoGrowWidth", uno::makeAny(false));
964 if (!boost::logic::indeterminate(obRelFlipV
) && xPropertySet
.is())
966 if (nType
== ESCHER_ShpInst_Line
)
968 // Line shape inside group shape: get the polygon sequence and transform it.
969 uno::Sequence
<uno::Sequence
<awt::Point
>> aPolyPolySequence
;
970 if ((xPropertySet
->getPropertyValue("PolyPolygon") >>= aPolyPolySequence
)
971 && aPolyPolySequence
.hasElements())
973 uno::Sequence
<awt::Point
>& rPolygon
= aPolyPolySequence
[0];
974 basegfx::B2DPolygon aPoly
;
975 for (sal_Int32 i
= 0; i
< rPolygon
.getLength(); ++i
)
977 const awt::Point
& rPoint
= rPolygon
[i
];
978 aPoly
.insert(i
, basegfx::B2DPoint(rPoint
.X
, rPoint
.Y
));
980 basegfx::B2DHomMatrix aTransformation
;
981 aTransformation
.scale(1.0, obRelFlipV
? -1.0 : 1.0);
982 aPoly
.transform(aTransformation
);
983 for (sal_Int32 i
= 0; i
< rPolygon
.getLength(); ++i
)
985 basegfx::B2DPoint
aPoint(aPoly
.getB2DPoint(i
));
987 = awt::Point(static_cast<sal_Int32
>(convertMm100ToTwip(aPoint
.getX())),
988 static_cast<sal_Int32
>(convertMm100ToTwip(aPoint
.getY())));
990 xPropertySet
->setPropertyValue("PolyPolygon", uno::makeAny(aPolyPolySequence
));
995 // Set position and size
998 sal_Int32 nLeft
= rShape
.getLeft();
999 sal_Int32 nTop
= rShape
.getTop();
1001 bool bInShapeGroup
= oGroupLeft
&& oGroupTop
&& oGroupRight
&& oGroupBottom
&& oRelLeft
1002 && oRelTop
&& oRelRight
&& oRelBottom
;
1006 // See lclGetAbsPoint() in the VML import: rShape is the group shape, oGroup is its coordinate system, oRel is the relative child shape.
1007 sal_Int32 nShapeWidth
= rShape
.getRight() - rShape
.getLeft();
1008 sal_Int32 nShapeHeight
= rShape
.getBottom() - rShape
.getTop();
1009 sal_Int32 nCoordSysWidth
= *oGroupRight
- *oGroupLeft
;
1010 sal_Int32 nCoordSysHeight
= *oGroupBottom
- *oGroupTop
;
1011 double fWidthRatio
= static_cast<double>(nShapeWidth
) / nCoordSysWidth
;
1012 double fHeightRatio
= static_cast<double>(nShapeHeight
) / nCoordSysHeight
;
1013 nLeft
= static_cast<sal_Int32
>(rShape
.getLeft()
1014 + fWidthRatio
* (*oRelLeft
- *oGroupLeft
));
1015 nTop
= static_cast<sal_Int32
>(rShape
.getTop() + fHeightRatio
* (*oRelTop
- *oGroupTop
));
1017 // See lclGetAbsRect() in the VML import.
1018 aSize
.Width
= std::lround(fWidthRatio
* (*oRelRight
- *oRelLeft
));
1019 aSize
.Height
= std::lround(fHeightRatio
* (*oRelBottom
- *oRelTop
));
1024 xPropertySet
->setPropertyValue("HoriOrientPosition", uno::makeAny(nLeft
));
1025 xPropertySet
->setPropertyValue("VertOrientPosition", uno::makeAny(nTop
));
1028 xShape
->setPosition(awt::Point(nLeft
, nTop
));
1031 xShape
->setSize(aSize
);
1033 xShape
->setSize(awt::Size(rShape
.getRight() - rShape
.getLeft(),
1034 rShape
.getBottom() - rShape
.getTop()));
1036 if (obFlipH
== true || obFlipV
== true)
1038 // Line shapes have no CustomShapeGeometry.
1039 if (nType
!= ESCHER_ShpInst_Line
)
1041 // This has to be set after position and size is set, otherwise flip will affect the position.
1042 comphelper::SequenceAsHashMap
aCustomShapeGeometry(
1043 xPropertySet
->getPropertyValue("CustomShapeGeometry"));
1044 if (obFlipH
== true)
1045 aCustomShapeGeometry
["MirroredX"] <<= true;
1046 if (obFlipV
== true)
1047 aCustomShapeGeometry
["MirroredY"] <<= true;
1048 xPropertySet
->setPropertyValue(
1049 "CustomShapeGeometry",
1050 uno::makeAny(aCustomShapeGeometry
.getAsConstPropertyValueList()));
1052 else if (SdrObject
* pObject
= GetSdrObjectFromXShape(xShape
))
1054 Point aRef1
= pObject
->GetSnapRect().Center();
1056 if (obFlipH
== true)
1058 // Horizontal mirror means a vertical reference line.
1061 if (obFlipV
== true)
1063 // Vertical mirror means a horizontal reference line.
1066 pObject
->Mirror(aRef1
, aRef2
);
1070 if (rShape
.getHoriOrientRelation() != 0)
1071 xPropertySet
->setPropertyValue("HoriOrientRelation",
1072 uno::makeAny(rShape
.getHoriOrientRelation()));
1073 if (rShape
.getVertOrientRelation() != 0)
1074 xPropertySet
->setPropertyValue("VertOrientRelation",
1075 uno::makeAny(rShape
.getVertOrientRelation()));
1076 if (rShape
.getWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
)
1077 xPropertySet
->setPropertyValue("Surround", uno::makeAny(rShape
.getWrap()));
1078 oox::ModelObjectHelper
aModelObjectHelper(m_rImport
.getModelFactory());
1079 if (aFillModel
.moType
.has())
1081 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1082 aFillModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1083 // Sets the FillStyle and FillGradient UNO properties.
1084 oox::PropertySet(xShape
).setProperties(aPropMap
);
1087 if (aShadowModel
.mbHasShadow
)
1089 oox::drawingml::ShapePropertyMap
aPropMap(aModelObjectHelper
);
1090 aShadowModel
.pushToPropMap(aPropMap
, m_rImport
.getGraphicHelper());
1091 // Sets the ShadowFormat UNO property.
1092 oox::PropertySet(xShape
).setProperties(aPropMap
);
1094 xPropertySet
->setPropertyValue("AnchorType",
1095 uno::makeAny(text::TextContentAnchorType_AT_CHARACTER
));
1096 xPropertySet
->setPropertyValue("Opaque", uno::makeAny(bOpaque
));
1099 xPropertySet
->setPropertyValue("RelativeWidth", uno::makeAny(*oRelativeWidth
));
1100 xPropertySet
->setPropertyValue("RelativeWidthRelation",
1101 uno::makeAny(nRelativeWidthRelation
));
1103 if (oRelativeHeight
)
1105 xPropertySet
->setPropertyValue("RelativeHeight", uno::makeAny(*oRelativeHeight
));
1106 xPropertySet
->setPropertyValue("RelativeHeightRelation",
1107 uno::makeAny(nRelativeHeightRelation
));
1111 if (m_rImport
.isInBackground())
1113 RTFSprms aAttributes
;
1114 aAttributes
.set(NS_ooxml::LN_CT_Background_color
,
1115 new RTFValue(xPropertySet
->getPropertyValue("FillColor").get
<sal_Int32
>()));
1116 m_rImport
.Mapper().props(new RTFReferenceProperties(aAttributes
));
1118 uno::Reference
<lang::XComponent
> xComponent(xShape
, uno::UNO_QUERY
);
1119 xComponent
->dispose();
1123 // Send it to dmapper
1126 m_rImport
.Mapper().startShape(xShape
);
1129 m_rImport
.Mapper().endShape();
1135 void RTFSdrImport::close() { m_rImport
.Mapper().endShape(); }
1137 void RTFSdrImport::append(const OUString
& aKey
, const OUString
& aValue
)
1139 applyProperty(m_xShape
, aKey
, aValue
);
1142 void RTFSdrImport::appendGroupProperty(const OUString
& aKey
, const OUString
& aValue
)
1144 if (m_aParents
.empty())
1146 uno::Reference
<drawing::XShape
> xShape(m_aParents
.top(), uno::UNO_QUERY
);
1148 applyProperty(xShape
, aKey
, aValue
);
1151 } // namespace rtftok
1152 } // namespace writerfilter
1154 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */