Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / writerfilter / source / rtftok / rtfsdrimport.cxx
blob85f61129b1f3e21c73d1d0787c53d3b77d08b05d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include "rtfsdrimport.hxx"
11 #include <cmath>
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
58 namespace rtftok
60 RTFSdrImport::RTFSdrImport(RTFDocumentImpl& rDocument,
61 uno::Reference<lang::XComponent> const& xDstDoc)
62 : m_rImport(rDocument)
63 , m_bTextFrame(false)
64 , m_bTextGraphicObject(false)
65 , m_bFakePict(false)
67 uno::Reference<drawing::XDrawPageSupplier> xDrawings(xDstDoc, uno::UNO_QUERY);
68 if (xDrawings.is())
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())
78 m_aParents.pop();
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);
106 if (!bNew)
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);
128 return aRet;
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())
142 m_aParents.pop();
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)
161 if (!bTextFrame)
163 xPropertySet->setPropertyValue("LineColor", rLineColor);
164 xPropertySet->setPropertyValue("LineWidth", rLineWidth);
166 else
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)
187 if (nFLine == 0)
188 xPropertySet->setPropertyValue("LineStyle", uno::makeAny(drawing::LineStyle_NONE));
189 else
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);
200 bool bFilled = true;
202 if (aKey == "posh")
204 switch (aValue.toInt32())
206 case 1:
207 nHoriOrient = text::HoriOrientation::LEFT;
208 break;
209 case 2:
210 nHoriOrient = text::HoriOrientation::CENTER;
211 break;
212 case 3:
213 nHoriOrient = text::HoriOrientation::RIGHT;
214 break;
215 case 4:
216 nHoriOrient = text::HoriOrientation::INSIDE;
217 break;
218 case 5:
219 nHoriOrient = text::HoriOrientation::OUTSIDE;
220 break;
221 default:
222 break;
225 else if (aKey == "posv")
227 switch (aValue.toInt32())
229 case 1:
230 nVertOrient = text::VertOrientation::TOP;
231 break;
232 case 2:
233 nVertOrient = text::VertOrientation::CENTER;
234 break;
235 case 3:
236 nVertOrient = text::VertOrientation::BOTTOM;
237 break;
238 default:
239 break;
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(
254 "RotateAngle",
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())
271 if (m_bTextFrame)
272 xPropertySet->setPropertyValue("BackColorTransparency", uno::makeAny(sal_Int32(100)));
273 else
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;
286 m_bFakePict = false;
288 // first, find the shape type
289 int nType = -1;
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;
303 else
305 // pict is picture by default but can be a rectangle too fdo#79319
306 nType = ESCHER_ShpInst_PictureFrame;
309 else
311 nType = iter->second.toInt32();
312 if (PICT == shapeOrPict && ESCHER_ShpInst_PictureFrame != nType)
314 m_bFakePict = true;
318 switch (nType)
320 case ESCHER_ShpInst_PictureFrame:
321 createShape("com.sun.star.drawing.GraphicObjectShape", o_xShape, o_xPropSet);
322 m_bTextGraphicObject = true;
323 break;
324 case ESCHER_ShpInst_Line:
325 createShape("com.sun.star.drawing.LineShape", o_xShape, o_xPropSet);
326 break;
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);
333 m_bTextFrame = true;
334 std::vector<beans::PropertyValue> aDefaults = getTextFrameDefaults(true);
335 for (const beans::PropertyValue& i : aDefaults)
336 o_xPropSet->setPropertyValue(i.Name, i.Value);
337 break;
339 [[fallthrough]];
340 default:
341 createShape("com.sun.star.drawing.CustomShape", o_xShape, o_xPropSet);
342 o_rIsCustomShape = true;
343 break;
346 // Defaults
347 if (o_xPropSet.is() && !m_bTextFrame)
349 o_xPropSet->setPropertyValue(
350 "FillColor",
351 uno::makeAny(sal_uInt32(0xffffff))); // White in Word, kind of blue in Writer.
352 o_xPropSet->setPropertyValue("VertOrient", uno::makeAny(text::VertOrientation::NONE));
355 return nType;
358 void RTFSdrImport::resolve(RTFShape& rShape, bool bClose, ShapeOrPict const shapeOrPict)
360 bool bPib = false;
361 m_bTextFrame = false;
362 m_bTextGraphicObject = false;
364 uno::Reference<drawing::XShape> xShape;
365 uno::Reference<beans::XPropertySet> xPropertySet;
366 uno::Any aAny;
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.
389 bool bOpaque = true;
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;
403 bool bCustom(false);
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")
414 if (m_bTextFrame)
416 uno::Reference<container::XNamed> xNamed(xShape, uno::UNO_QUERY);
417 xNamed->setName(rProperty.second);
419 else
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);
436 bPib = true;
438 else if (rProperty.first == "fillColor" && xPropertySet.is())
440 aAny <<= msfilter::util::BGRToRGB(rProperty.second.toUInt32());
441 if (m_bTextFrame)
442 xPropertySet->setPropertyValue("BackColor", aAny);
443 else
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;
466 break;
467 case 2: // Bottom to top non-ASCII font
468 eWritingMode = text::WritingMode2::BT_LR;
469 break;
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);
490 if (!nSize)
491 nSize = aToken.toInt32();
492 else if (!nCount)
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);
498 sal_Int32 nI = 0;
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;
514 sal_Int32 nSize = 0;
515 sal_Int32 nCount = 0;
516 sal_Int32 nCharIndex = 0;
519 sal_Int32 nSeg = rProperty.second.getToken(0, ';', nCharIndex).toInt32();
520 if (!nSize)
521 nSize = nSeg;
522 else if (!nCount)
523 nCount = nSeg;
524 else
526 sal_Int32 nPoints = 1;
527 if (nSeg >= 0x2000 && nSeg < 0x20FF)
529 nPoints = nSeg & 0x0FFF;
530 nSeg &= 0xFF00;
533 drawing::EnhancedCustomShapeSegment aSegment;
534 switch (nSeg)
536 case 0x0001: // lineto
537 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
538 aSegment.Count = sal_Int32(1);
539 aSegments.push_back(aSegment);
540 break;
541 case 0x4000: // moveto
542 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
543 aSegment.Count = sal_Int32(1);
544 aSegments.push_back(aSegment);
545 break;
546 case 0x2000: // curveto
547 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::CURVETO;
548 aSegment.Count = nPoints;
549 aSegments.push_back(aSegment);
550 break;
551 case 0xb300: // arcto
552 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::ARCTO;
553 aSegment.Count = sal_Int32(0);
554 aSegments.push_back(aSegment);
555 break;
556 case 0xac00:
557 case 0xaa00: // nofill
558 case 0xab00: // nostroke
559 case 0x6001: // close
560 break;
561 case 0x8000: // end
562 aSegment.Command
563 = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
564 aSegment.Count = sal_Int32(0);
565 aSegments.push_back(aSegment);
566 break;
567 default: // given number of lineto elements
568 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
569 aSegment.Count = nSeg;
570 aSegments.push_back(aSegment);
571 break;
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.
590 if (!rShape.hasZ())
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);
660 break;
661 default:
662 SAL_INFO("writerfilter",
663 "TODO handle fillType value '" << rProperty.second << "'");
664 break;
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")
679 // EMUs to points
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())
689 case 1:
690 rShape.setHoriOrientRelation(text::RelOrientation::PAGE_FRAME);
691 break;
692 default:
693 break;
696 else if (rProperty.first == "posrelv")
698 switch (rProperty.second.toInt32())
700 case 1:
701 rShape.setVertOrientRelation(text::RelOrientation::PAGE_FRAME);
702 break;
703 default:
704 break;
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);
728 if (nPercentage)
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())
741 case 0: // margin
742 nRelativeWidthRelation = text::RelOrientation::FRAME;
743 break;
744 case 1: // page
745 nRelativeWidthRelation = text::RelOrientation::PAGE_FRAME;
746 break;
747 default:
748 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: "
749 << rProperty.second);
750 break;
754 else if (rProperty.first == "sizerelv")
756 if (xPropertySet.is())
758 switch (rProperty.second.toInt32())
760 case 0: // margin
761 nRelativeHeightRelation = text::RelOrientation::FRAME;
762 break;
763 case 1: // page
764 nRelativeHeightRelation = text::RelOrientation::PAGE_FRAME;
765 break;
766 default:
767 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: "
768 << rProperty.second);
769 break;
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?
777 if (!oRelativeWidth)
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())
811 case 0:
812 nHoriOrient = text::HoriOrientation::LEFT;
813 break;
814 case 1:
815 nHoriOrient = text::HoriOrientation::CENTER;
816 break;
817 case 2:
818 nHoriOrient = text::HoriOrientation::RIGHT;
819 break;
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);
835 if (!nSize)
836 nSize = aToken.toInt32();
837 else if (!nCount)
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);
843 sal_Int32 nI = 0;
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;
861 else
862 SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty.first << "':'"
863 << rProperty.second << "'");
866 if (xPropertySet.is())
868 resolveLineColorAndWidth(m_bTextFrame, xPropertySet, aLineColor, aLineWidth);
869 if (rShape.hasZ())
871 bool bOldStyle = m_aParents.size() > 1;
872 resolveDhgt(xPropertySet, rShape.getZ(), bOldStyle);
874 if (m_bTextFrame)
875 xPropertySet->setPropertyValue("WritingMode", uno::makeAny(eWritingMode));
876 else
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);
885 if (bPib)
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));
901 return;
904 if (bCustom && xShape.is() && !bPib)
906 uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY);
907 xDefaulter->createCustomShapeDefaults(OUString::number(nType));
910 // Set shape text
911 if (bCustom && !aShapeText.isEmpty())
913 uno::Reference<text::XTextRange> xTextRange(xShape, uno::UNO_QUERY);
914 if (xTextRange.is())
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);
931 if (!aPath.empty())
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>>();
945 auto aGeomPropVec
946 = comphelper::sequenceToContainer<std::vector<beans::PropertyValue>>(aGeomPropSeq);
947 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence({
948 { "TextPath", uno::makeAny(true) },
949 }));
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));
955 else
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));
986 rPolygon[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
996 if (xShape.is())
998 sal_Int32 nLeft = rShape.getLeft();
999 sal_Int32 nTop = rShape.getTop();
1001 bool bInShapeGroup = oGroupLeft && oGroupTop && oGroupRight && oGroupBottom && oRelLeft
1002 && oRelTop && oRelRight && oRelBottom;
1003 awt::Size aSize;
1004 if (bInShapeGroup)
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));
1022 if (m_bTextFrame)
1024 xPropertySet->setPropertyValue("HoriOrientPosition", uno::makeAny(nLeft));
1025 xPropertySet->setPropertyValue("VertOrientPosition", uno::makeAny(nTop));
1027 else
1028 xShape->setPosition(awt::Point(nLeft, nTop));
1030 if (bInShapeGroup)
1031 xShape->setSize(aSize);
1032 else
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();
1055 Point aRef2(aRef1);
1056 if (obFlipH == true)
1058 // Horizontal mirror means a vertical reference line.
1059 aRef2.AdjustY(1);
1061 if (obFlipV == true)
1063 // Vertical mirror means a horizontal reference line.
1064 aRef2.AdjustX(1);
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));
1097 if (oRelativeWidth)
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();
1120 return;
1123 // Send it to dmapper
1124 if (xShape.is())
1126 m_rImport.Mapper().startShape(xShape);
1127 if (bClose)
1129 m_rImport.Mapper().endShape();
1132 m_xShape = xShape;
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())
1145 return;
1146 uno::Reference<drawing::XShape> xShape(m_aParents.top(), uno::UNO_QUERY);
1147 if (xShape.is())
1148 applyProperty(xShape, aKey, aValue);
1151 } // namespace rtftok
1152 } // namespace writerfilter
1154 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */