Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / writerfilter / source / rtftok / rtfsdrimport.cxx
blob2545b93c552c6a66c2a0a38be21ab82ffc6a2bdf
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 <dmapper/GraphicZOrderHelper.hxx>
49 #include "rtfdocumentimpl.hxx"
51 using namespace com::sun::star;
53 namespace writerfilter
55 namespace rtftok
57 RTFSdrImport::RTFSdrImport(RTFDocumentImpl& rDocument,
58 uno::Reference<lang::XComponent> const& xDstDoc)
59 : m_rImport(rDocument)
60 , m_bTextFrame(false)
61 , m_bTextGraphicObject(false)
62 , m_bFakePict(false)
64 uno::Reference<drawing::XDrawPageSupplier> xDrawings(xDstDoc, uno::UNO_QUERY);
65 if (xDrawings.is())
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())
75 m_aParents.pop();
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);
103 if (!bNew)
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);
125 return aRet;
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())
139 m_aParents.pop();
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)
158 if (!bTextFrame)
160 xPropertySet->setPropertyValue("LineColor", rLineColor);
161 xPropertySet->setPropertyValue("LineWidth", rLineWidth);
163 else
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)
184 if (nFLine == 0)
185 xPropertySet->setPropertyValue("LineStyle", uno::makeAny(drawing::LineStyle_NONE));
186 else
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);
197 bool bFilled = true;
199 if (aKey == "posh")
201 switch (aValue.toInt32())
203 case 1:
204 nHoriOrient = text::HoriOrientation::LEFT;
205 break;
206 case 2:
207 nHoriOrient = text::HoriOrientation::CENTER;
208 break;
209 case 3:
210 nHoriOrient = text::HoriOrientation::RIGHT;
211 break;
212 case 4:
213 nHoriOrient = text::HoriOrientation::INSIDE;
214 break;
215 case 5:
216 nHoriOrient = text::HoriOrientation::OUTSIDE;
217 break;
218 default:
219 break;
222 else if (aKey == "posv")
224 switch (aValue.toInt32())
226 case 1:
227 nVertOrient = text::VertOrientation::TOP;
228 break;
229 case 2:
230 nVertOrient = text::VertOrientation::CENTER;
231 break;
232 case 3:
233 nVertOrient = text::VertOrientation::BOTTOM;
234 break;
235 default:
236 break;
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(
251 "RotateAngle",
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())
268 if (m_bTextFrame)
269 xPropertySet->setPropertyValue("BackColorTransparency", uno::makeAny(sal_Int32(100)));
270 else
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;
283 m_bFakePict = false;
285 // first, find the shape type
286 int nType = -1;
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;
300 else
302 // pict is picture by default but can be a rectangle too fdo#79319
303 nType = ESCHER_ShpInst_PictureFrame;
306 else
308 nType = iter->second.toInt32();
309 if (PICT == shapeOrPict && ESCHER_ShpInst_PictureFrame != nType)
311 m_bFakePict = true;
315 switch (nType)
317 case ESCHER_ShpInst_PictureFrame:
318 createShape("com.sun.star.drawing.GraphicObjectShape", o_xShape, o_xPropSet);
319 m_bTextGraphicObject = true;
320 break;
321 case ESCHER_ShpInst_Line:
322 createShape("com.sun.star.drawing.LineShape", o_xShape, o_xPropSet);
323 break;
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);
330 m_bTextFrame = true;
331 std::vector<beans::PropertyValue> aDefaults = getTextFrameDefaults(true);
332 for (const beans::PropertyValue& i : aDefaults)
333 o_xPropSet->setPropertyValue(i.Name, i.Value);
334 break;
336 [[fallthrough]];
337 default:
338 createShape("com.sun.star.drawing.CustomShape", o_xShape, o_xPropSet);
339 o_rIsCustomShape = true;
340 break;
343 // Defaults
344 if (o_xPropSet.is() && !m_bTextFrame)
346 o_xPropSet->setPropertyValue(
347 "FillColor",
348 uno::makeAny(sal_uInt32(0xffffff))); // White in Word, kind of blue in Writer.
349 o_xPropSet->setPropertyValue("VertOrient", uno::makeAny(text::VertOrientation::NONE));
352 return nType;
355 void RTFSdrImport::resolve(RTFShape& rShape, bool bClose, ShapeOrPict const shapeOrPict)
357 bool bPib = false;
358 m_bTextFrame = false;
359 m_bTextGraphicObject = false;
361 uno::Reference<drawing::XShape> xShape;
362 uno::Reference<beans::XPropertySet> xPropertySet;
363 uno::Any aAny;
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.
386 bool bOpaque = true;
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;
400 bool bCustom(false);
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")
411 if (m_bTextFrame)
413 uno::Reference<container::XNamed> xNamed(xShape, uno::UNO_QUERY);
414 xNamed->setName(rProperty.second);
416 else
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);
433 bPib = true;
435 else if (rProperty.first == "fillColor" && xPropertySet.is())
437 aAny <<= msfilter::util::BGRToRGB(rProperty.second.toUInt32());
438 if (m_bTextFrame)
439 xPropertySet->setPropertyValue("BackColor", aAny);
440 else
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;
463 break;
464 case 2: // Bottom to top non-ASCII font
465 eWritingMode = text::WritingMode2::BT_LR;
466 break;
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);
487 if (!nSize)
488 nSize = aToken.toInt32();
489 else if (!nCount)
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);
495 sal_Int32 nI = 0;
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;
511 sal_Int32 nSize = 0;
512 sal_Int32 nCount = 0;
513 sal_Int32 nCharIndex = 0;
516 sal_Int32 nSeg = rProperty.second.getToken(0, ';', nCharIndex).toInt32();
517 if (!nSize)
518 nSize = nSeg;
519 else if (!nCount)
520 nCount = nSeg;
521 else
523 sal_Int32 nPoints = 1;
524 if (nSeg >= 0x2000 && nSeg < 0x20FF)
526 nPoints = nSeg & 0x0FFF;
527 nSeg &= 0xFF00;
530 drawing::EnhancedCustomShapeSegment aSegment;
531 switch (nSeg)
533 case 0x0001: // lineto
534 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
535 aSegment.Count = sal_Int32(1);
536 aSegments.push_back(aSegment);
537 break;
538 case 0x4000: // moveto
539 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
540 aSegment.Count = sal_Int32(1);
541 aSegments.push_back(aSegment);
542 break;
543 case 0x2000: // curveto
544 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::CURVETO;
545 aSegment.Count = nPoints;
546 aSegments.push_back(aSegment);
547 break;
548 case 0xb300: // arcto
549 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::ARCTO;
550 aSegment.Count = sal_Int32(0);
551 aSegments.push_back(aSegment);
552 break;
553 case 0xac00:
554 case 0xaa00: // nofill
555 case 0xab00: // nostroke
556 case 0x6001: // close
557 break;
558 case 0x8000: // end
559 aSegment.Command
560 = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
561 aSegment.Count = sal_Int32(0);
562 aSegments.push_back(aSegment);
563 break;
564 default: // given number of lineto elements
565 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
566 aSegment.Count = nSeg;
567 aSegments.push_back(aSegment);
568 break;
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.
587 if (!rShape.hasZ())
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);
657 break;
658 default:
659 SAL_INFO("writerfilter",
660 "TODO handle fillType value '" << rProperty.second << "'");
661 break;
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")
676 // EMUs to points
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())
686 case 1:
687 rShape.setHoriOrientRelation(text::RelOrientation::PAGE_FRAME);
688 break;
689 default:
690 break;
693 else if (rProperty.first == "posrelv")
695 switch (rProperty.second.toInt32())
697 case 1:
698 rShape.setVertOrientRelation(text::RelOrientation::PAGE_FRAME);
699 break;
700 default:
701 break;
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);
725 if (nPercentage)
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())
738 case 0: // margin
739 nRelativeWidthRelation = text::RelOrientation::FRAME;
740 break;
741 case 1: // page
742 nRelativeWidthRelation = text::RelOrientation::PAGE_FRAME;
743 break;
744 default:
745 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: "
746 << rProperty.second);
747 break;
751 else if (rProperty.first == "sizerelv")
753 if (xPropertySet.is())
755 switch (rProperty.second.toInt32())
757 case 0: // margin
758 nRelativeHeightRelation = text::RelOrientation::FRAME;
759 break;
760 case 1: // page
761 nRelativeHeightRelation = text::RelOrientation::PAGE_FRAME;
762 break;
763 default:
764 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: "
765 << rProperty.second);
766 break;
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?
774 if (!oRelativeWidth)
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())
808 case 0:
809 nHoriOrient = text::HoriOrientation::LEFT;
810 break;
811 case 1:
812 nHoriOrient = text::HoriOrientation::CENTER;
813 break;
814 case 2:
815 nHoriOrient = text::HoriOrientation::RIGHT;
816 break;
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);
832 if (!nSize)
833 nSize = aToken.toInt32();
834 else if (!nCount)
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);
840 sal_Int32 nI = 0;
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;
858 else
859 SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty.first << "':'"
860 << rProperty.second << "'");
863 if (xPropertySet.is())
865 resolveLineColorAndWidth(m_bTextFrame, xPropertySet, aLineColor, aLineWidth);
866 if (rShape.hasZ())
868 bool bOldStyle = m_aParents.size() > 1;
869 resolveDhgt(xPropertySet, rShape.getZ(), bOldStyle);
871 if (m_bTextFrame)
872 xPropertySet->setPropertyValue("WritingMode", uno::makeAny(eWritingMode));
873 else
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);
882 if (bPib)
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));
898 return;
901 if (bCustom && xShape.is() && !bPib)
903 uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY);
904 xDefaulter->createCustomShapeDefaults(OUString::number(nType));
907 // Set shape text
908 if (bCustom && !aShapeText.isEmpty())
910 uno::Reference<text::XTextRange> xTextRange(xShape, uno::UNO_QUERY);
911 if (xTextRange.is())
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);
928 if (!aPath.empty())
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>>();
942 auto aGeomPropVec
943 = comphelper::sequenceToContainer<std::vector<beans::PropertyValue>>(aGeomPropSeq);
944 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence({
945 { "TextPath", uno::makeAny(true) },
946 }));
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));
952 else
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));
983 rPolygon[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
993 if (xShape.is())
995 sal_Int32 nLeft = rShape.getLeft();
996 sal_Int32 nTop = rShape.getTop();
998 bool bInShapeGroup = oGroupLeft && oGroupTop && oGroupRight && oGroupBottom && oRelLeft
999 && oRelTop && oRelRight && oRelBottom;
1000 awt::Size aSize;
1001 if (bInShapeGroup)
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));
1019 if (m_bTextFrame)
1021 xPropertySet->setPropertyValue("HoriOrientPosition", uno::makeAny(nLeft));
1022 xPropertySet->setPropertyValue("VertOrientPosition", uno::makeAny(nTop));
1024 else
1025 xShape->setPosition(awt::Point(nLeft, nTop));
1027 if (bInShapeGroup)
1028 xShape->setSize(aSize);
1029 else
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));
1078 if (oRelativeWidth)
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();
1101 return;
1104 // Send it to dmapper
1105 if (xShape.is())
1107 m_rImport.Mapper().startShape(xShape);
1108 if (bClose)
1110 m_rImport.Mapper().endShape();
1113 m_xShape = xShape;
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())
1126 return;
1127 uno::Reference<drawing::XShape> xShape(m_aParents.top(), uno::UNO_QUERY);
1128 if (xShape.is())
1129 applyProperty(xShape, aKey, aValue);
1132 } // namespace rtftok
1133 } // namespace writerfilter
1135 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */