lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / writerfilter / source / rtftok / rtfsdrimport.cxx
blob7cfd76f21a14a13a934efa5269681fbef00b15de
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(
288 rShape.aProperties.begin(), rShape.aProperties.end(),
289 [](std::pair<OUString, OUString> aProperty) { return aProperty.first == "shapeType"; });
291 if (iter == rShape.aProperties.end())
293 if (SHAPE == shapeOrPict)
295 // The spec doesn't state what is the default for shapeType,
296 // Word seems to implement it as a rectangle.
297 nType = ESCHER_ShpInst_Rectangle;
299 else
301 // pict is picture by default but can be a rectangle too fdo#79319
302 nType = ESCHER_ShpInst_PictureFrame;
305 else
307 nType = iter->second.toInt32();
308 if (PICT == shapeOrPict && ESCHER_ShpInst_PictureFrame != nType)
310 m_bFakePict = true;
314 switch (nType)
316 case ESCHER_ShpInst_PictureFrame:
317 createShape("com.sun.star.drawing.GraphicObjectShape", o_xShape, o_xPropSet);
318 m_bTextGraphicObject = true;
319 break;
320 case ESCHER_ShpInst_Line:
321 createShape("com.sun.star.drawing.LineShape", o_xShape, o_xPropSet);
322 break;
323 case ESCHER_ShpInst_Rectangle:
324 case ESCHER_ShpInst_TextBox:
325 // If we're inside a groupshape, can't use text frames.
326 if (!bClose && m_aParents.size() == 1)
328 createShape("com.sun.star.text.TextFrame", o_xShape, o_xPropSet);
329 m_bTextFrame = true;
330 std::vector<beans::PropertyValue> aDefaults = getTextFrameDefaults(true);
331 for (beans::PropertyValue& i : aDefaults)
332 o_xPropSet->setPropertyValue(i.Name, i.Value);
333 break;
335 SAL_FALLTHROUGH;
336 default:
337 createShape("com.sun.star.drawing.CustomShape", o_xShape, o_xPropSet);
338 o_rIsCustomShape = true;
339 break;
342 // Defaults
343 if (o_xPropSet.is() && !m_bTextFrame)
345 o_xPropSet->setPropertyValue(
346 "FillColor",
347 uno::makeAny(sal_uInt32(0xffffff))); // White in Word, kind of blue in Writer.
348 o_xPropSet->setPropertyValue("VertOrient", uno::makeAny(text::VertOrientation::NONE));
351 return nType;
354 void RTFSdrImport::resolve(RTFShape& rShape, bool bClose, ShapeOrPict const shapeOrPict)
356 bool bPib = false;
357 m_bTextFrame = false;
358 m_bTextGraphicObject = false;
360 uno::Reference<drawing::XShape> xShape;
361 uno::Reference<beans::XPropertySet> xPropertySet;
362 uno::Any aAny;
363 beans::PropertyValue aPropertyValue;
364 awt::Rectangle aViewBox;
365 std::vector<beans::PropertyValue> aPath;
366 // Default line color is black in Word, blue in Writer.
367 uno::Any aLineColor = uno::makeAny(COL_BLACK);
368 // Default line width is 0.75 pt (26 mm100) in Word, 0 in Writer.
369 uno::Any aLineWidth = uno::makeAny(sal_Int32(26));
370 sal_Int16 eWritingMode = text::WritingMode2::LR_TB;
371 // Groupshape support
372 boost::optional<sal_Int32> oGroupLeft, oGroupTop, oGroupRight, oGroupBottom;
373 boost::optional<sal_Int32> oRelLeft, oRelTop, oRelRight, oRelBottom;
375 // Importing these are not trivial, let the VML import do the hard work.
376 oox::vml::FillModel aFillModel; // Gradient.
377 oox::vml::ShadowModel aShadowModel; // Shadow.
379 bool bOpaque = true;
381 boost::optional<sal_Int16> oRelativeWidth, oRelativeHeight;
382 sal_Int16 nRelativeWidthRelation = text::RelOrientation::PAGE_FRAME;
383 sal_Int16 nRelativeHeightRelation = text::RelOrientation::PAGE_FRAME;
384 boost::logic::tribool obRelFlipV(boost::logic::indeterminate);
385 boost::logic::tribool obFlipH(boost::logic::indeterminate);
386 boost::logic::tribool obFlipV(boost::logic::indeterminate);
388 OUString aShapeText = "";
389 OUString aFontFamily = "";
390 float nFontSize = 1.0;
392 bool bCustom(false);
393 int const nType = initShape(xShape, xPropertySet, bCustom, rShape, bClose, shapeOrPict);
395 for (auto& rProperty : rShape.aProperties)
397 if (rProperty.first == "shapeType")
399 continue; // ignore: already handled by initShape
401 if (rProperty.first == "wzName")
403 if (m_bTextFrame)
405 uno::Reference<container::XNamed> xNamed(xShape, uno::UNO_QUERY);
406 xNamed->setName(rProperty.second);
408 else
409 xPropertySet->setPropertyValue("Name", uno::makeAny(rProperty.second));
411 else if (rProperty.first == "wzDescription")
412 xPropertySet->setPropertyValue("Description", uno::makeAny(rProperty.second));
413 else if (rProperty.first == "gtextUNICODE")
414 aShapeText = rProperty.second;
415 else if (rProperty.first == "gtextFont")
416 aFontFamily = rProperty.second;
417 else if (rProperty.first == "gtextSize")
419 // RTF size is multiplied by 2^16
420 nFontSize = static_cast<float>(rProperty.second.toUInt32()) / RTF_MULTIPLIER;
422 else if (rProperty.first == "pib")
424 m_rImport.setDestinationText(rProperty.second);
425 bPib = true;
427 else if (rProperty.first == "fillColor" && xPropertySet.is())
429 aAny <<= msfilter::util::BGRToRGB(rProperty.second.toUInt32());
430 if (m_bTextFrame)
431 xPropertySet->setPropertyValue("BackColor", aAny);
432 else
433 xPropertySet->setPropertyValue("FillColor", aAny);
435 // fillType will decide, possible it'll be the start color of a gradient.
436 aFillModel.moColor.set(
437 "#" + OUString::fromUtf8(msfilter::util::ConvertColor(aAny.get<sal_Int32>())));
439 else if (rProperty.first == "fillBackColor")
440 // fillType will decide, possible it'll be the end color of a gradient.
441 aFillModel.moColor2.set("#"
442 + OUString::fromUtf8(msfilter::util::ConvertColor(
443 msfilter::util::BGRToRGB(rProperty.second.toInt32()))));
444 else if (rProperty.first == "lineColor")
445 aLineColor <<= msfilter::util::BGRToRGB(rProperty.second.toInt32());
446 else if (rProperty.first == "lineBackColor")
447 ; // Ignore: complementer of lineColor
448 else if (rProperty.first == "txflTextFlow" && xPropertySet.is())
450 switch (rProperty.second.toInt32())
452 case 1: // Top to bottom ASCII font
453 case 3: // Top to bottom non-ASCII font
454 eWritingMode = text::WritingMode2::TB_RL;
455 break;
456 case 2: // Bottom to top non-ASCII font
457 eWritingMode = text::WritingMode2::BT_LR;
458 break;
461 else if (rProperty.first == "fLine" && xPropertySet.is())
462 resolveFLine(xPropertySet, rProperty.second.toInt32());
463 else if (rProperty.first == "fillOpacity" && xPropertySet.is())
465 int opacity = 100 - (rProperty.second.toInt32()) * 100 / RTF_MULTIPLIER;
466 xPropertySet->setPropertyValue("FillTransparence", uno::Any(sal_uInt32(opacity)));
468 else if (rProperty.first == "lineWidth")
469 aLineWidth <<= rProperty.second.toInt32() / 360;
470 else if (rProperty.first == "pVerticies")
472 std::vector<drawing::EnhancedCustomShapeParameterPair> aCoordinates;
473 sal_Int32 nSize = 0; // Size of a token (its value is hardwired in the exporter)
474 sal_Int32 nCount = 0; // Number of tokens
475 sal_Int32 nCharIndex = 0; // Character index
478 OUString aToken = rProperty.second.getToken(0, ';', nCharIndex);
479 if (!nSize)
480 nSize = aToken.toInt32();
481 else if (!nCount)
482 nCount = aToken.toInt32();
483 else if (aToken.getLength())
485 // The coordinates are in an (x,y) form.
486 aToken = aToken.copy(1, aToken.getLength() - 2);
487 sal_Int32 nI = 0;
488 sal_Int32 nX = aToken.getToken(0, ',', nI).toInt32();
489 sal_Int32 nY = (nI >= 0) ? aToken.getToken(0, ',', nI).toInt32() : 0;
490 drawing::EnhancedCustomShapeParameterPair aPair;
491 aPair.First.Value <<= nX;
492 aPair.Second.Value <<= nY;
493 aCoordinates.push_back(aPair);
495 } while (nCharIndex >= 0);
496 aPropertyValue.Name = "Coordinates";
497 aPropertyValue.Value <<= comphelper::containerToSequence(aCoordinates);
498 aPath.push_back(aPropertyValue);
500 else if (rProperty.first == "pSegmentInfo")
502 std::vector<drawing::EnhancedCustomShapeSegment> aSegments;
503 sal_Int32 nSize = 0;
504 sal_Int32 nCount = 0;
505 sal_Int32 nCharIndex = 0;
508 sal_Int32 nSeg = rProperty.second.getToken(0, ';', nCharIndex).toInt32();
509 if (!nSize)
510 nSize = nSeg;
511 else if (!nCount)
512 nCount = nSeg;
513 else
515 sal_Int32 nPoints = 1;
516 if (nSeg >= 0x2000 && nSeg < 0x20FF)
518 nPoints = nSeg & 0x0FFF;
519 nSeg &= 0xFF00;
522 drawing::EnhancedCustomShapeSegment aSegment;
523 switch (nSeg)
525 case 0x0001: // lineto
526 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
527 aSegment.Count = sal_Int32(1);
528 aSegments.push_back(aSegment);
529 break;
530 case 0x4000: // moveto
531 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
532 aSegment.Count = sal_Int32(1);
533 aSegments.push_back(aSegment);
534 break;
535 case 0x2000: // curveto
536 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::CURVETO;
537 aSegment.Count = nPoints;
538 aSegments.push_back(aSegment);
539 break;
540 case 0xb300: // arcto
541 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::ARCTO;
542 aSegment.Count = sal_Int32(0);
543 aSegments.push_back(aSegment);
544 break;
545 case 0xac00:
546 case 0xaa00: // nofill
547 case 0xab00: // nostroke
548 case 0x6001: // close
549 break;
550 case 0x8000: // end
551 aSegment.Command
552 = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
553 aSegment.Count = sal_Int32(0);
554 aSegments.push_back(aSegment);
555 break;
556 default: // given number of lineto elements
557 aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
558 aSegment.Count = nSeg;
559 aSegments.push_back(aSegment);
560 break;
563 } while (nCharIndex >= 0);
564 aPropertyValue.Name = "Segments";
565 aPropertyValue.Value <<= comphelper::containerToSequence(aSegments);
566 aPath.push_back(aPropertyValue);
568 else if (rProperty.first == "geoLeft")
569 aViewBox.X = rProperty.second.toInt32();
570 else if (rProperty.first == "geoTop")
571 aViewBox.Y = rProperty.second.toInt32();
572 else if (rProperty.first == "geoRight")
573 aViewBox.Width = rProperty.second.toInt32();
574 else if (rProperty.first == "geoBottom")
575 aViewBox.Height = rProperty.second.toInt32();
576 else if (rProperty.first == "dhgt")
578 // dhgt is Word 2007, \shpz is Word 97-2003, the later has priority.
579 if (!rShape.oZ)
580 resolveDhgt(xPropertySet, rProperty.second.toInt32(), /*bOldStyle=*/false);
582 // These are in EMU, convert to mm100.
583 else if (rProperty.first == "dxTextLeft")
585 if (xPropertySet.is())
586 xPropertySet->setPropertyValue("LeftBorderDistance",
587 uno::makeAny(rProperty.second.toInt32() / 360));
589 else if (rProperty.first == "dyTextTop")
591 if (xPropertySet.is())
592 xPropertySet->setPropertyValue("TopBorderDistance",
593 uno::makeAny(rProperty.second.toInt32() / 360));
595 else if (rProperty.first == "dxTextRight")
597 if (xPropertySet.is())
598 xPropertySet->setPropertyValue("RightBorderDistance",
599 uno::makeAny(rProperty.second.toInt32() / 360));
601 else if (rProperty.first == "dyTextBottom")
603 if (xPropertySet.is())
604 xPropertySet->setPropertyValue("BottomBorderDistance",
605 uno::makeAny(rProperty.second.toInt32() / 360));
607 else if (rProperty.first == "dxWrapDistLeft")
609 if (m_bTextGraphicObject)
610 rShape.aAnchorAttributes.set(NS_ooxml::LN_CT_Anchor_distL,
611 new RTFValue(rProperty.second.toInt32()));
612 else if (xPropertySet.is())
613 xPropertySet->setPropertyValue("LeftMargin",
614 uno::makeAny(rProperty.second.toInt32() / 360));
616 else if (rProperty.first == "dyWrapDistTop")
618 if (m_bTextGraphicObject)
619 rShape.aAnchorAttributes.set(NS_ooxml::LN_CT_Anchor_distT,
620 new RTFValue(rProperty.second.toInt32()));
621 else if (xPropertySet.is())
622 xPropertySet->setPropertyValue("TopMargin",
623 uno::makeAny(rProperty.second.toInt32() / 360));
625 else if (rProperty.first == "dxWrapDistRight")
627 if (m_bTextGraphicObject)
628 rShape.aAnchorAttributes.set(NS_ooxml::LN_CT_Anchor_distR,
629 new RTFValue(rProperty.second.toInt32()));
630 else if (xPropertySet.is())
631 xPropertySet->setPropertyValue("RightMargin",
632 uno::makeAny(rProperty.second.toInt32() / 360));
634 else if (rProperty.first == "dyWrapDistBottom")
636 if (m_bTextGraphicObject)
637 rShape.aAnchorAttributes.set(NS_ooxml::LN_CT_Anchor_distB,
638 new RTFValue(rProperty.second.toInt32()));
639 else if (xPropertySet.is())
640 xPropertySet->setPropertyValue("BottomMargin",
641 uno::makeAny(rProperty.second.toInt32() / 360));
643 else if (rProperty.first == "fillType")
645 switch (rProperty.second.toInt32())
647 case 7: // Shade using the fillAngle
648 aFillModel.moType.set(oox::XML_gradient);
649 break;
650 default:
651 SAL_INFO("writerfilter",
652 "TODO handle fillType value '" << rProperty.second << "'");
653 break;
656 else if (rProperty.first == "fillFocus")
657 aFillModel.moFocus.set(rProperty.second.toDouble() / 100); // percent
658 else if (rProperty.first == "fShadow" && xPropertySet.is())
660 if (rProperty.second.toInt32() == 1)
661 aShadowModel.mbHasShadow = true;
663 else if (rProperty.first == "shadowColor")
664 aShadowModel.moColor.set("#"
665 + OUString::fromUtf8(msfilter::util::ConvertColor(
666 msfilter::util::BGRToRGB(rProperty.second.toInt32()))));
667 else if (rProperty.first == "shadowOffsetX")
668 // EMUs to points
669 aShadowModel.moOffset.set(OUString::number(rProperty.second.toDouble() / 12700) + "pt");
670 else if (rProperty.first == "posh" || rProperty.first == "posv"
671 || rProperty.first == "fFitShapeToText" || rProperty.first == "fFilled"
672 || rProperty.first == "rotation")
673 applyProperty(xShape, rProperty.first, rProperty.second);
674 else if (rProperty.first == "posrelh")
676 switch (rProperty.second.toInt32())
678 case 1:
679 rShape.nHoriOrientRelation = text::RelOrientation::PAGE_FRAME;
680 break;
681 default:
682 break;
685 else if (rProperty.first == "posrelv")
687 switch (rProperty.second.toInt32())
689 case 1:
690 rShape.nVertOrientRelation = text::RelOrientation::PAGE_FRAME;
691 break;
692 default:
693 break;
696 else if (rProperty.first == "groupLeft")
697 oGroupLeft = convertTwipToMm100(rProperty.second.toInt32());
698 else if (rProperty.first == "groupTop")
699 oGroupTop = convertTwipToMm100(rProperty.second.toInt32());
700 else if (rProperty.first == "groupRight")
701 oGroupRight = convertTwipToMm100(rProperty.second.toInt32());
702 else if (rProperty.first == "groupBottom")
703 oGroupBottom = convertTwipToMm100(rProperty.second.toInt32());
704 else if (rProperty.first == "relLeft")
705 oRelLeft = convertTwipToMm100(rProperty.second.toInt32());
706 else if (rProperty.first == "relTop")
707 oRelTop = convertTwipToMm100(rProperty.second.toInt32());
708 else if (rProperty.first == "relRight")
709 oRelRight = convertTwipToMm100(rProperty.second.toInt32());
710 else if (rProperty.first == "relBottom")
711 oRelBottom = convertTwipToMm100(rProperty.second.toInt32());
712 else if (rProperty.first == "fBehindDocument")
713 bOpaque = !rProperty.second.toInt32();
714 else if (rProperty.first == "pctHoriz" || rProperty.first == "pctVert")
716 sal_Int16 nPercentage = rtl::math::round(rProperty.second.toDouble() / 10);
717 if (nPercentage)
719 boost::optional<sal_Int16>& rPercentage
720 = rProperty.first == "pctHoriz" ? oRelativeWidth : oRelativeHeight;
721 rPercentage = nPercentage;
724 else if (rProperty.first == "sizerelh")
726 if (xPropertySet.is())
728 switch (rProperty.second.toInt32())
730 case 0: // margin
731 nRelativeWidthRelation = text::RelOrientation::FRAME;
732 break;
733 case 1: // page
734 nRelativeWidthRelation = text::RelOrientation::PAGE_FRAME;
735 break;
736 default:
737 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: "
738 << rProperty.second);
739 break;
743 else if (rProperty.first == "sizerelv")
745 if (xPropertySet.is())
747 switch (rProperty.second.toInt32())
749 case 0: // margin
750 nRelativeHeightRelation = text::RelOrientation::FRAME;
751 break;
752 case 1: // page
753 nRelativeHeightRelation = text::RelOrientation::PAGE_FRAME;
754 break;
755 default:
756 SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: "
757 << rProperty.second);
758 break;
762 else if (rProperty.first == "fHorizRule") // TODO: what does "fStandardHR" do?
764 // horizontal rule: relative width defaults to 100% of paragraph
765 // TODO: does it have a default height?
766 if (!oRelativeWidth)
768 oRelativeWidth = 100;
770 nRelativeWidthRelation = text::RelOrientation::FRAME;
771 sal_Int16 const nVertOrient = text::VertOrientation::CENTER;
772 if (xPropertySet.is())
774 xPropertySet->setPropertyValue("VertOrient", uno::makeAny(nVertOrient));
777 else if (rProperty.first == "pctHR")
779 // horizontal rule relative width in permille
780 oRelativeWidth = rProperty.second.toInt32() / 10;
782 else if (rProperty.first == "dxHeightHR")
784 // horizontal rule height
785 sal_uInt32 const nHeight(convertTwipToMm100(rProperty.second.toInt32()));
786 rShape.nBottom = rShape.nTop + nHeight;
788 else if (rProperty.first == "dxWidthHR")
790 // horizontal rule width
791 sal_uInt32 const nWidth(convertTwipToMm100(rProperty.second.toInt32()));
792 rShape.nRight = rShape.nLeft + nWidth;
794 else if (rProperty.first == "alignHR")
796 // horizontal orientation *for horizontal rule*
797 sal_Int16 nHoriOrient = text::HoriOrientation::NONE;
798 switch (rProperty.second.toInt32())
800 case 0:
801 nHoriOrient = text::HoriOrientation::LEFT;
802 break;
803 case 1:
804 nHoriOrient = text::HoriOrientation::CENTER;
805 break;
806 case 2:
807 nHoriOrient = text::HoriOrientation::RIGHT;
808 break;
810 if (xPropertySet.is() && text::HoriOrientation::NONE != nHoriOrient)
812 xPropertySet->setPropertyValue("HoriOrient", uno::makeAny(nHoriOrient));
815 else if (rProperty.first == "pWrapPolygonVertices")
817 RTFSprms aPolygonSprms;
818 sal_Int32 nSize = 0; // Size of a token
819 sal_Int32 nCount = 0; // Number of tokens
820 sal_Int32 nCharIndex = 0; // Character index
823 OUString aToken = rProperty.second.getToken(0, ';', nCharIndex);
824 if (!nSize)
825 nSize = aToken.toInt32();
826 else if (!nCount)
827 nCount = aToken.toInt32();
828 else if (aToken.getLength())
830 // The coordinates are in an (x,y) form.
831 aToken = aToken.copy(1, aToken.getLength() - 2);
832 sal_Int32 nI = 0;
833 sal_Int32 nX = aToken.getToken(0, ',', nI).toInt32();
834 sal_Int32 nY = (nI >= 0) ? aToken.getToken(0, ',', nI).toInt32() : 0;
835 RTFSprms aPathAttributes;
836 aPathAttributes.set(NS_ooxml::LN_CT_Point2D_x, new RTFValue(nX));
837 aPathAttributes.set(NS_ooxml::LN_CT_Point2D_y, new RTFValue(nY));
838 aPolygonSprms.set(NS_ooxml::LN_CT_WrapPath_lineTo,
839 new RTFValue(aPathAttributes), RTFOverwrite::NO_APPEND);
841 } while (nCharIndex >= 0);
842 rShape.aWrapPolygonSprms = aPolygonSprms;
844 else if (rProperty.first == "fRelFlipV")
845 obRelFlipV = rProperty.second.toInt32() == 1;
846 else if (rProperty.first == "fFlipH")
847 obFlipH = rProperty.second.toInt32() == 1;
848 else if (rProperty.first == "fFlipV")
849 obFlipV = rProperty.second.toInt32() == 1;
850 else
851 SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty.first << "':'"
852 << rProperty.second << "'");
855 if (xPropertySet.is())
857 resolveLineColorAndWidth(m_bTextFrame, xPropertySet, aLineColor, aLineWidth);
858 if (rShape.oZ)
860 bool bOldStyle = m_aParents.size() > 1;
861 resolveDhgt(xPropertySet, *rShape.oZ, bOldStyle);
863 if (m_bTextFrame)
864 xPropertySet->setPropertyValue("WritingMode", uno::makeAny(eWritingMode));
865 else
866 // Only Writer textframes implement text::WritingMode2.
867 xPropertySet->setPropertyValue("TextWritingMode",
868 uno::makeAny(text::WritingMode(eWritingMode)));
871 if (!m_aParents.empty() && m_aParents.top().is() && !m_bTextFrame)
872 m_aParents.top()->add(xShape);
874 if (bPib)
876 m_rImport.resolvePict(false, xShape);
879 if (nType == ESCHER_ShpInst_PictureFrame) // picture frame
881 assert(!m_bTextFrame);
882 if (!bPib) // ??? not sure if the early return should be removed on else?
884 m_xShape = xShape; // store it for later resolvePict call
887 // Handle horizontal flip.
888 if (obFlipH == true && xPropertySet.is())
889 xPropertySet->setPropertyValue("IsMirrored", uno::makeAny(true));
890 return;
893 if (bCustom && xShape.is() && !bPib)
895 uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY);
896 xDefaulter->createCustomShapeDefaults(OUString::number(nType));
899 // Set shape text
900 if (bCustom && !aShapeText.isEmpty())
902 uno::Reference<text::XTextRange> xTextRange(xShape, uno::UNO_QUERY);
903 if (xTextRange.is())
904 xTextRange->setString(aShapeText);
906 xPropertySet->setPropertyValue("CharFontName", uno::makeAny(aFontFamily));
907 xPropertySet->setPropertyValue("CharHeight", uno::makeAny(nFontSize));
910 // Creating CustomShapeGeometry property
911 std::vector<beans::PropertyValue> aGeometry;
912 if (aViewBox.X || aViewBox.Y || aViewBox.Width || aViewBox.Height)
914 aViewBox.Width -= aViewBox.X;
915 aViewBox.Height -= aViewBox.Y;
916 aPropertyValue.Name = "ViewBox";
917 aPropertyValue.Value <<= aViewBox;
918 aGeometry.push_back(aPropertyValue);
920 if (!aPath.empty())
922 aPropertyValue.Name = "Path";
923 aPropertyValue.Value <<= comphelper::containerToSequence(aPath);
924 aGeometry.push_back(aPropertyValue);
926 if (!aGeometry.empty() && xPropertySet.is() && !m_bTextFrame)
927 xPropertySet->setPropertyValue("CustomShapeGeometry",
928 uno::Any(comphelper::containerToSequence(aGeometry)));
930 if (!aShapeText.isEmpty())
932 auto aGeomPropSeq = xPropertySet->getPropertyValue("CustomShapeGeometry")
933 .get<uno::Sequence<beans::PropertyValue>>();
934 auto aGeomPropVec
935 = comphelper::sequenceToContainer<std::vector<beans::PropertyValue>>(aGeomPropSeq);
936 uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence({
937 { "TextPath", uno::makeAny(true) },
938 }));
939 auto it = std::find_if(
940 aGeomPropVec.begin(), aGeomPropVec.end(),
941 [](const beans::PropertyValue& rValue) { return rValue.Name == "TextPath"; });
942 if (it == aGeomPropVec.end())
943 aGeomPropVec.push_back(comphelper::makePropertyValue("TextPath", aPropertyValues));
944 else
945 it->Value <<= aPropertyValues;
947 xPropertySet->setPropertyValue("CustomShapeGeometry",
948 uno::makeAny(comphelper::containerToSequence(aGeomPropVec)));
949 xPropertySet->setPropertyValue("TextAutoGrowHeight", uno::makeAny(false));
950 xPropertySet->setPropertyValue("TextAutoGrowWidth", uno::makeAny(false));
953 if (!boost::logic::indeterminate(obRelFlipV) && xPropertySet.is())
955 if (nType == ESCHER_ShpInst_Line)
957 // Line shape inside group shape: get the polygon sequence and transform it.
958 uno::Sequence<uno::Sequence<awt::Point>> aPolyPolySequence;
959 if ((xPropertySet->getPropertyValue("PolyPolygon") >>= aPolyPolySequence)
960 && aPolyPolySequence.hasElements())
962 uno::Sequence<awt::Point>& rPolygon = aPolyPolySequence[0];
963 basegfx::B2DPolygon aPoly;
964 for (sal_Int32 i = 0; i < rPolygon.getLength(); ++i)
966 const awt::Point& rPoint = rPolygon[i];
967 aPoly.insert(i, basegfx::B2DPoint(rPoint.X, rPoint.Y));
969 basegfx::B2DHomMatrix aTransformation;
970 aTransformation.scale(1.0, obRelFlipV ? -1.0 : 1.0);
971 aPoly.transform(aTransformation);
972 for (sal_Int32 i = 0; i < rPolygon.getLength(); ++i)
974 basegfx::B2DPoint aPoint(aPoly.getB2DPoint(i));
975 rPolygon[i]
976 = awt::Point(static_cast<sal_Int32>(convertMm100ToTwip(aPoint.getX())),
977 static_cast<sal_Int32>(convertMm100ToTwip(aPoint.getY())));
979 xPropertySet->setPropertyValue("PolyPolygon", uno::makeAny(aPolyPolySequence));
984 // Set position and size
985 if (xShape.is())
987 sal_Int32 nLeft = rShape.nLeft;
988 sal_Int32 nTop = rShape.nTop;
990 bool bInShapeGroup = oGroupLeft && oGroupTop && oGroupRight && oGroupBottom && oRelLeft
991 && oRelTop && oRelRight && oRelBottom;
992 awt::Size aSize;
993 if (bInShapeGroup)
995 // See lclGetAbsPoint() in the VML import: rShape is the group shape, oGroup is its coordinate system, oRel is the relative child shape.
996 sal_Int32 nShapeWidth = rShape.nRight - rShape.nLeft;
997 sal_Int32 nShapeHeight = rShape.nBottom - rShape.nTop;
998 sal_Int32 nCoordSysWidth = *oGroupRight - *oGroupLeft;
999 sal_Int32 nCoordSysHeight = *oGroupBottom - *oGroupTop;
1000 double fWidthRatio = static_cast<double>(nShapeWidth) / nCoordSysWidth;
1001 double fHeightRatio = static_cast<double>(nShapeHeight) / nCoordSysHeight;
1002 nLeft = static_cast<sal_Int32>(rShape.nLeft + fWidthRatio * (*oRelLeft - *oGroupLeft));
1003 nTop = static_cast<sal_Int32>(rShape.nTop + fHeightRatio * (*oRelTop - *oGroupTop));
1005 // See lclGetAbsRect() in the VML import.
1006 aSize.Width = std::lround(fWidthRatio * (*oRelRight - *oRelLeft));
1007 aSize.Height = std::lround(fHeightRatio * (*oRelBottom - *oRelTop));
1010 if (m_bTextFrame)
1012 xPropertySet->setPropertyValue("HoriOrientPosition", uno::makeAny(nLeft));
1013 xPropertySet->setPropertyValue("VertOrientPosition", uno::makeAny(nTop));
1015 else
1016 xShape->setPosition(awt::Point(nLeft, nTop));
1018 if (bInShapeGroup)
1019 xShape->setSize(aSize);
1020 else
1021 xShape->setSize(awt::Size(rShape.nRight - rShape.nLeft, rShape.nBottom - rShape.nTop));
1023 if (obFlipH == true || obFlipV == true)
1025 // Line shapes have no CustomShapeGeometry.
1026 if (nType != ESCHER_ShpInst_Line)
1028 // This has to be set after position and size is set, otherwise flip will affect the position.
1029 comphelper::SequenceAsHashMap aCustomShapeGeometry(
1030 xPropertySet->getPropertyValue("CustomShapeGeometry"));
1031 if (obFlipH == true)
1032 aCustomShapeGeometry["MirroredX"] <<= true;
1033 if (obFlipV == true)
1034 aCustomShapeGeometry["MirroredY"] <<= true;
1035 xPropertySet->setPropertyValue(
1036 "CustomShapeGeometry",
1037 uno::makeAny(aCustomShapeGeometry.getAsConstPropertyValueList()));
1041 if (rShape.nHoriOrientRelation != 0)
1042 xPropertySet->setPropertyValue("HoriOrientRelation",
1043 uno::makeAny(rShape.nHoriOrientRelation));
1044 if (rShape.nVertOrientRelation != 0)
1045 xPropertySet->setPropertyValue("VertOrientRelation",
1046 uno::makeAny(rShape.nVertOrientRelation));
1047 if (rShape.nWrap != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE)
1048 xPropertySet->setPropertyValue("Surround",
1049 uno::makeAny(text::WrapTextMode(rShape.nWrap)));
1050 oox::ModelObjectHelper aModelObjectHelper(m_rImport.getModelFactory());
1051 if (aFillModel.moType.has())
1053 oox::drawingml::ShapePropertyMap aPropMap(aModelObjectHelper);
1054 aFillModel.pushToPropMap(aPropMap, m_rImport.getGraphicHelper());
1055 // Sets the FillStyle and FillGradient UNO properties.
1056 oox::PropertySet(xShape).setProperties(aPropMap);
1059 if (aShadowModel.mbHasShadow)
1061 oox::drawingml::ShapePropertyMap aPropMap(aModelObjectHelper);
1062 aShadowModel.pushToPropMap(aPropMap, m_rImport.getGraphicHelper());
1063 // Sets the ShadowFormat UNO property.
1064 oox::PropertySet(xShape).setProperties(aPropMap);
1066 xPropertySet->setPropertyValue("AnchorType",
1067 uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
1068 xPropertySet->setPropertyValue("Opaque", uno::makeAny(bOpaque));
1069 if (oRelativeWidth)
1071 xPropertySet->setPropertyValue("RelativeWidth", uno::makeAny(*oRelativeWidth));
1072 xPropertySet->setPropertyValue("RelativeWidthRelation",
1073 uno::makeAny(nRelativeWidthRelation));
1075 if (oRelativeHeight)
1077 xPropertySet->setPropertyValue("RelativeHeight", uno::makeAny(*oRelativeHeight));
1078 xPropertySet->setPropertyValue("RelativeHeightRelation",
1079 uno::makeAny(nRelativeHeightRelation));
1083 if (m_rImport.isInBackground())
1085 RTFSprms aAttributes;
1086 aAttributes.set(NS_ooxml::LN_CT_Background_color,
1087 new RTFValue(xPropertySet->getPropertyValue("FillColor").get<sal_Int32>()));
1088 m_rImport.Mapper().props(new RTFReferenceProperties(aAttributes));
1090 uno::Reference<lang::XComponent> xComponent(xShape, uno::UNO_QUERY);
1091 xComponent->dispose();
1092 return;
1095 // Send it to dmapper
1096 m_rImport.Mapper().startShape(xShape);
1097 if (bClose)
1099 m_rImport.Mapper().endShape();
1101 m_xShape = xShape;
1104 void RTFSdrImport::close() { m_rImport.Mapper().endShape(); }
1106 void RTFSdrImport::append(const OUString& aKey, const OUString& aValue)
1108 applyProperty(m_xShape, aKey, aValue);
1111 void RTFSdrImport::appendGroupProperty(const OUString& aKey, const OUString& aValue)
1113 if (m_aParents.empty())
1114 return;
1115 uno::Reference<drawing::XShape> xShape(m_aParents.top(), uno::UNO_QUERY);
1116 if (xShape.is())
1117 applyProperty(xShape, aKey, aValue);
1120 } // namespace rtftok
1121 } // namespace writerfilter
1123 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */