summaryrefslogtreecommitdiff
path: root/oox/source/shape/WpsContext.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'oox/source/shape/WpsContext.cxx')
-rw-r--r--oox/source/shape/WpsContext.cxx607
1 files changed, 598 insertions, 9 deletions
diff --git a/oox/source/shape/WpsContext.cxx b/oox/source/shape/WpsContext.cxx
index 99656195075b..86e02d24b146 100644
--- a/oox/source/shape/WpsContext.cxx
+++ b/oox/source/shape/WpsContext.cxx
@@ -11,30 +11,528 @@
#include "WpgContext.hxx"
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/tuple/b2dtuple.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
#include <comphelper/sequenceashashmap.hxx>
+#include <drawingml/customshapegeometry.hxx>
#include <drawingml/customshapeproperties.hxx>
+#include <drawingml/fontworkhelpers.hxx>
+#include <drawingml/textbody.hxx>
+#include <drawingml/textbodyproperties.hxx>
+#include <oox/drawingml/color.hxx>
+#include <oox/drawingml/drawingmltypes.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <oox/drawingml/shapepropertymap.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <svx/svdoashp.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/HomogenMatrix3.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/geometry/IntegerRectangle2D.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/text/XTextCursor.hpp>
#include <com/sun/star/text/WritingMode.hpp>
#include <com/sun/star/text/WritingMode2.hpp>
-#include <oox/helper/attributelist.hxx>
-#include <oox/token/namespaces.hxx>
-#include <oox/token/tokens.hxx>
-#include <oox/drawingml/shape.hxx>
-#include <oox/drawingml/drawingmltypes.hxx>
-#include <drawingml/textbody.hxx>
-#include <drawingml/textbodyproperties.hxx>
-#include <tools/helpers.hxx>
#include <optional>
using namespace com::sun::star;
+namespace
+{
+bool lcl_getTextPropsFromFrameText(const uno::Reference<text::XText>& xText,
+ std::vector<beans::PropertyValue>& rTextPropVec)
+{
+ if (!xText.is())
+ return false;
+ uno::Reference<text::XTextCursor> xTextCursor = xText->createTextCursor();
+ xTextCursor->gotoStart(false);
+ xTextCursor->gotoEnd(true);
+ uno::Reference<container::XEnumerationAccess> paraEnumAccess(xText, uno::UNO_QUERY);
+ if (!paraEnumAccess.is())
+ return false;
+ uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
+ while (paraEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
+ if (!runEnumAccess.is())
+ continue;
+ uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
+ while (runEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun->getString().isEmpty())
+ continue;
+ uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
+ if (!xRunPropSet.is())
+ continue;
+ auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
+ if (!xRunPropSetInfo.is())
+ continue;
+
+ // We have found a non-empty run. Collect its properties.
+ auto aRunPropInfoSequence = xRunPropSetInfo->getProperties();
+ for (const beans::Property& aProp : aRunPropInfoSequence)
+ {
+ rTextPropVec.push_back(comphelper::makePropertyValue(
+ aProp.Name, xRunPropSet->getPropertyValue(aProp.Name)));
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// CharInteropGrabBag puts all attributes of an element into a property with Name="attributes" and
+// Value being a sequence of the attributes. This methods finds the value of an individual rName
+// attribute and puts it into rValue paramenter. If it does not find it, rValue is unchanged and
+// the methode returns false, otherwise it returns true.
+bool lcl_getAttributeAsString(const uno::Sequence<beans::PropertyValue>& aPropertyValueAsSeq,
+ const OUString& rName, OUString& rValue)
+{
+ comphelper::SequenceAsHashMap aPropertyValueAsMap(aPropertyValueAsSeq);
+ uno::Sequence<beans::PropertyValue> aAttributesSeq;
+ if (!((aPropertyValueAsMap.getValue("attributes") >>= aAttributesSeq)
+ && aAttributesSeq.hasElements()))
+ return false;
+ comphelper::SequenceAsHashMap aAttributesMap(aAttributesSeq);
+ OUString sRet;
+ if (!(aAttributesMap.getValue(rName) >>= sRet))
+ return false;
+ rValue = sRet;
+ return true;
+}
+
+// Same as above for a number as attribute value
+bool lcl_getAttributeAsNumber(const uno::Sequence<beans::PropertyValue>& rPropertyValueAsSeq,
+ const OUString& rName, sal_Int32& rValue)
+{
+ comphelper::SequenceAsHashMap aPropertyValueAsMap(rPropertyValueAsSeq);
+ uno::Sequence<beans::PropertyValue> aAttributesSeq;
+ if (!((aPropertyValueAsMap.getValue("attributes") >>= aAttributesSeq)
+ && aAttributesSeq.hasElements()))
+ return false;
+ comphelper::SequenceAsHashMap aAttributesMap(aAttributesSeq);
+ sal_Int32 nRet;
+ if (!(aAttributesMap.getValue(rName) >>= nRet))
+ return false;
+ rValue = nRet;
+ return true;
+}
+
+void lcl_getColorTransformationsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rPropSeq,
+ oox::drawingml::Color& rColor)
+{
+ auto isValidPropName = [](const OUString& rName) -> bool {
+ return rName == u"tint" || rName == u"shade" || rName == u"alpha" || rName == u"hueMod"
+ || rName == u"sat" || rName == u"satOff" || rName == u"satMod" || rName == u"lum"
+ || rName == u"lumOff" || rName == u"lumMod";
+ };
+ for (auto it = rPropSeq.begin(); it < rPropSeq.end(); ++it)
+ {
+ if (isValidPropName((*it).Name))
+ {
+ uno::Sequence<beans::PropertyValue> aValueSeq;
+ sal_Int32 nNumber(0); // dummy value to make compiler happy, "val" should exist
+ if (((*it).Value >>= aValueSeq) && lcl_getAttributeAsNumber(aValueSeq, u"val", nNumber))
+ {
+ // char w14:alpha contains transparency, whereas shape fill a:alpha contains opacity.
+ if ((*it).Name == u"alpha")
+ rColor.addTransformation(
+ oox::NMSP_dml | oox::AttributeConversion::decodeToken((*it).Name),
+ oox::drawingml::MAX_PERCENT - nNumber);
+ else
+ rColor.addTransformation(
+ oox::NMSP_w14 | oox::AttributeConversion::decodeToken((*it).Name), nNumber);
+ }
+ }
+ }
+}
+
+// Expected: rPropSeq contains a property "schemeClr" or a property "srgbClr".
+bool lcl_getColorFromPropSeq(const uno::Sequence<beans::PropertyValue>& rPropSeq,
+ oox::drawingml::Color& rColor)
+{
+ bool bColorFound = false;
+ comphelper::SequenceAsHashMap aPropMap(rPropSeq);
+ uno::Sequence<beans::PropertyValue> aColorDetailSeq;
+ if (aPropMap.getValue(u"schemeClr") >>= aColorDetailSeq)
+ {
+ OUString sColorString;
+ bColorFound = lcl_getAttributeAsString(aColorDetailSeq, u"val", sColorString);
+ if (bColorFound)
+ {
+ sal_Int32 nColorToken = oox::AttributeConversion::decodeToken(sColorString);
+ rColor.setSchemeClr(nColorToken);
+ rColor.setSchemeName(sColorString);
+ }
+ }
+ if (!bColorFound && (aPropMap.getValue(u"srgbClr") >>= aColorDetailSeq))
+ {
+ OUString sColorString;
+ bColorFound = lcl_getAttributeAsString(aColorDetailSeq, u"val", sColorString);
+ if (bColorFound)
+ {
+ sal_Int32 nColor = oox::AttributeConversion::decodeIntegerHex(sColorString);
+ rColor.setSrgbClr(nColor);
+ }
+ }
+ // Without color, color transformations are pointless.
+ if (bColorFound)
+ lcl_getColorTransformationsFromPropSeq(aColorDetailSeq, rColor);
+ return bColorFound;
+}
+
+void lcl_getFillDetailsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rTextFillSeq,
+ oox::drawingml::FillProperties& rFillProperties)
+{
+ // rTextFillSeq should have an item containing either "noFill" or "solidFill" or "gradFill"
+ // property.
+ if (!rTextFillSeq.hasElements())
+ return;
+ comphelper::SequenceAsHashMap aTextFillMap(rTextFillSeq);
+ if (aTextFillMap.find(u"noFill") != aTextFillMap.end())
+ {
+ rFillProperties.moFillType = oox::XML_noFill;
+ return;
+ }
+
+ uno::Sequence<beans::PropertyValue> aPropSeq;
+ if ((aTextFillMap.getValue(u"solidFill") >>= aPropSeq) && aPropSeq.hasElements())
+ {
+ rFillProperties.moFillType = oox::XML_solidFill;
+ lcl_getColorFromPropSeq(aPropSeq, rFillProperties.maFillColor);
+ return;
+ }
+
+ if ((aTextFillMap.getValue(u"gradFill") >>= aPropSeq) && aPropSeq.hasElements())
+ {
+ rFillProperties.moFillType = oox::XML_gradFill;
+ // aPropSeq should have two items. One ist "gsLst" for the stop colors, the other is
+ // either "lin" or "path" for the kind of gradient.
+ // First get stop colors
+ comphelper::SequenceAsHashMap aPropMap(aPropSeq);
+ uno::Sequence<beans::PropertyValue> aGsLstSeq;
+ if (aPropMap.getValue("gsLst") >>= aGsLstSeq)
+ {
+ for (auto it = aGsLstSeq.begin(); it < aGsLstSeq.end(); ++it)
+ {
+ // (*it) is a bean::PropertyValue with Name="gs". Its Value is a property sequence.
+ uno::Sequence<beans::PropertyValue> aColorStopSeq;
+ if ((*it).Value >>= aColorStopSeq)
+ {
+ // aColorStopSeq should have an item for the color and an item for the position
+ sal_Int32 nPos;
+ oox::drawingml::Color aColor;
+ if (lcl_getAttributeAsNumber(aColorStopSeq, u"pos", nPos)
+ && lcl_getColorFromPropSeq(aColorStopSeq, aColor))
+ {
+ // The position in maGradientStops is relative, thus in range [0.0;1.0].
+ double fPos = nPos / 100000.0;
+ rFillProperties.maGradientProps.maGradientStops.insert({ fPos, aColor });
+ }
+ }
+ }
+ }
+ // Now determine kind of gradient.
+ uno::Sequence<beans::PropertyValue> aKindSeq;
+ if (aPropMap.getValue("lin") >>= aKindSeq)
+ {
+ // aKindSeq contains the attributes "ang" and "scaled"
+ sal_Int32 nAngle; // in 1/60000 deg
+ if (lcl_getAttributeAsNumber(aKindSeq, "ang", nAngle))
+ rFillProperties.maGradientProps.moShadeAngle = nAngle;
+ OUString sScaledString;
+ if (lcl_getAttributeAsString(aKindSeq, "scaled", sScaledString))
+ rFillProperties.maGradientProps.moShadeScaled
+ = sScaledString == u"1" || sScaledString == u"true";
+ return;
+ }
+ if (aPropMap.getValue("path") >>= aKindSeq)
+ {
+ // aKindSeq contains the attribute "path" for the kind of path and a property "fillToRect"
+ // which defines the center rectangle of the gradient. The property "a:tileRect" known from
+ // fill of shapes does not exist in w14 namespace.
+ OUString sKind;
+ if (lcl_getAttributeAsString(aKindSeq, "path", sKind))
+ rFillProperties.maGradientProps.moGradientPath
+ = oox::AttributeConversion::decodeToken(sKind);
+ comphelper::SequenceAsHashMap aKindMap(aKindSeq);
+ uno::Sequence<beans::PropertyValue> aFillToRectSeq;
+ if (aKindMap.getValue("fillToRect") >>= aFillToRectSeq)
+ {
+ // The values l, t, r and b are not coordinates, but determine an offset from the
+ // edge of the bounding box of the shape. This unusual meaning of X1, Y1, X2 and
+ // Y2 is needed for method pushToPropMap() of FillProperties.
+ geometry::IntegerRectangle2D aRect;
+ if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"l", aRect.X1))
+ aRect.X1 = 0;
+ if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"t", aRect.Y1))
+ aRect.Y1 = 0;
+ if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"r", aRect.X2))
+ aRect.X2 = 0;
+ if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"b", aRect.Y2))
+ aRect.Y2 = 0;
+ rFillProperties.maGradientProps.moFillToRect = aRect;
+ }
+ }
+ return;
+ }
+}
+
+void lcl_getLineDetailsFromPropSeq(const uno::Sequence<beans::PropertyValue>& rTextOutlineSeq,
+ oox::drawingml::LineProperties& rLineProperties)
+{
+ if (!rTextOutlineSeq.hasElements())
+ {
+ rLineProperties.maLineFill.moFillType = oox::XML_noFill; // MS Office default
+ return;
+ }
+ // aTextOulineSeq contains e.g. "attributes" {w, cap, cmpd, ctr}, either
+ // "solidFill" or "gradFill or "noFill", and "prstDash" and "lineJoint" properties.
+
+ // Fill
+ lcl_getFillDetailsFromPropSeq(rTextOutlineSeq, rLineProperties.maLineFill);
+
+ // LineJoint
+ comphelper::SequenceAsHashMap aTextOutlineMap(rTextOutlineSeq);
+ if (aTextOutlineMap.find(u"bevel") != aTextOutlineMap.end())
+ rLineProperties.moLineJoint = oox::XML_bevel;
+ else if (aTextOutlineMap.find(u"round") != aTextOutlineMap.end())
+ rLineProperties.moLineJoint = oox::XML_round;
+ else if (aTextOutlineMap.find(u"miter") != aTextOutlineMap.end())
+ {
+ // LineProperties has no member to store a miter limit. Therefore some heuristic is
+ // added here. 0 is default for attribute "lim" in MS Office. It is rendered same as bevel.
+ sal_Int32 nMiterLimit = aTextOutlineMap.getUnpackedValueOrDefault("lim", sal_Int32(0));
+ if (nMiterLimit == 0)
+ rLineProperties.moLineJoint = oox::XML_bevel;
+ else
+ rLineProperties.moLineJoint = oox::XML_miter;
+ }
+
+ // Dash
+ uno::Sequence<beans::PropertyValue> aDashSeq;
+ if (aTextOutlineMap.getValue(u"prstDash") >>= aDashSeq)
+ {
+ // aDashSeq contains the attribute "val" with the kind of dash, e.g. "sysDot"
+ OUString sDashKind;
+ if (lcl_getAttributeAsString(aDashSeq, u"val", sDashKind))
+ rLineProperties.moPresetDash = oox::AttributeConversion::decodeToken(sDashKind);
+ }
+ OUString sCapKind;
+ if (lcl_getAttributeAsString(rTextOutlineSeq, u"cap", sCapKind))
+ rLineProperties.moLineCap = oox::AttributeConversion::decodeToken(sCapKind);
+
+ // Width
+ sal_Int32 nWidth; // EMU
+ if (lcl_getAttributeAsNumber(rTextOutlineSeq, u"w", nWidth))
+ rLineProperties.moLineWidth = nWidth;
+
+ // Compound. LineProperties has a member for it, however Fontwork can currently only render "sng".
+ OUString sCompoundKind;
+ if (lcl_getAttributeAsString(rTextOutlineSeq, u"cmpd", sCompoundKind))
+ rLineProperties.moLineCompound = oox::AttributeConversion::decodeToken(sCompoundKind);
+
+ // Align. LineProperties has no member for attribute "algn".
+
+ return;
+}
+
+oox::drawingml::LineProperties
+lcl_generateLinePropertiesFromTextProps(const comphelper::SequenceAsHashMap& aTextPropMap)
+{
+ oox::drawingml::LineProperties aLineProperties;
+ aLineProperties.maLineFill.moFillType = oox::XML_noFill; // default
+
+ // Get property "textOutline" from aTextPropMap
+ uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq;
+ if (!(aTextPropMap.getValue(u"CharInteropGrabBag") >>= aCharInteropGrabBagSeq))
+ return aLineProperties;
+ if (!aCharInteropGrabBagSeq.hasElements())
+ return aLineProperties;
+ comphelper::SequenceAsHashMap aCharInteropGrabBagMap(aCharInteropGrabBagSeq);
+ beans::PropertyValue aProp;
+ if (!(aCharInteropGrabBagMap.getValue(u"CharTextOutlineTextEffect") >>= aProp))
+ return aLineProperties;
+ uno::Sequence<beans::PropertyValue> aTextOutlineSeq;
+ if (!(aProp.Name == "textOutline" && (aProp.Value >>= aTextOutlineSeq)
+ && aTextOutlineSeq.hasElements()))
+ return aLineProperties;
+
+ // Copy line properties from aTextOutlineSeq to aLineProperties
+ lcl_getLineDetailsFromPropSeq(aTextOutlineSeq, aLineProperties);
+ return aLineProperties;
+}
+
+oox::drawingml::FillProperties
+lcl_generateFillPropertiesFromTextProps(const comphelper::SequenceAsHashMap& rTextPropMap)
+{
+ oox::drawingml::FillProperties aFillProperties;
+ aFillProperties.moFillType = oox::XML_solidFill; // default
+ sal_Int32 aCharColor = 0;
+ if (rTextPropMap.getValue(u"CharColor") >>= aCharColor)
+ aFillProperties.maFillColor.setSrgbClr(aCharColor);
+ else
+ aFillProperties.maFillColor.setUnused();
+
+ // Theme color superseds direct color. textFill superseds theme color. Theme color and textfill
+ // are in CharInteropGrabBag
+ uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq;
+ if (!((rTextPropMap.getValue(u"CharInteropGrabBag") >>= aCharInteropGrabBagSeq)
+ && aCharInteropGrabBagSeq.hasElements()))
+ return aFillProperties;
+ comphelper::SequenceAsHashMap aCharInteropGrabBagMap(aCharInteropGrabBagSeq);
+
+ // Handle theme color, tint and shade.
+ OUString sColorString;
+ if (aCharInteropGrabBagMap.getValue("CharThemeOriginalColor") >>= sColorString)
+ {
+ sal_Int32 nThemeOrigColor = oox::AttributeConversion::decodeIntegerHex(sColorString);
+ aFillProperties.maFillColor.setSrgbClr(nThemeOrigColor);
+ }
+ if (aCharInteropGrabBagMap.getValue("CharThemeColor") >>= sColorString)
+ {
+ sal_Int32 nColorToken = oox::AttributeConversion::decodeToken(sColorString);
+ aFillProperties.maFillColor.setSchemeClr(nColorToken);
+ aFillProperties.maFillColor.setSchemeName(sColorString);
+ // A character color has shade and tint, a shape color has lumMod and lumOff.
+ OUString sTransformString;
+ if (aCharInteropGrabBagMap.getValue("CharThemeColorTint") >>= sTransformString)
+ {
+ double fTint = oox::AttributeConversion::decodeIntegerHex(sTransformString);
+ fTint = fTint / 255.0 * oox::drawingml::MAX_PERCENT;
+ aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumMod),
+ static_cast<sal_Int32>(fTint + 0.5));
+ double fOff = oox::drawingml::MAX_PERCENT - fTint;
+ aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumOff),
+ static_cast<sal_Int32>(fOff + 0.5));
+ }
+ else if (aCharInteropGrabBagMap.getValue("CharThemeColorShade") >>= sTransformString)
+ {
+ double fShade = oox::AttributeConversion::decodeIntegerHex(sTransformString);
+ fShade = fShade / 255.0 * oox::drawingml::MAX_PERCENT;
+ aFillProperties.maFillColor.addTransformation(OOX_TOKEN(w14, lumMod),
+ static_cast<sal_Int32>(fShade + 0.5));
+ }
+ }
+
+ // Handle textFill
+ beans::PropertyValue aProp;
+ if (!(aCharInteropGrabBagMap.getValue(u"CharTextFillTextEffect") >>= aProp))
+ return aFillProperties;
+ uno::Sequence<beans::PropertyValue> aTextFillSeq;
+ if (!(aProp.Name == "textFill" && (aProp.Value >>= aTextFillSeq) && aTextFillSeq.hasElements()))
+ return aFillProperties;
+ // Copy fill properties from aTextFillSeq to aFillProperties
+ lcl_getFillDetailsFromPropSeq(aTextFillSeq, aFillProperties);
+ return aFillProperties;
+}
+
+void lcl_applyShapePropsToShape(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
+ const oox::drawingml::ShapePropertyMap& rShapeProps)
+{
+ for (const auto& rProp : rShapeProps.makePropertyValueSequence())
+ {
+ xShapePropertySet->setPropertyValue(rProp.Name, rProp.Value);
+ }
+}
+
+void lcl_setTextAnchorFromTextProps(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
+ const comphelper::SequenceAsHashMap& aTextPropMap)
+{
+ // Fontwork does not evaluate paragraph alignment but uses text anchor instead
+ auto eHorzAdjust(drawing::TextHorizontalAdjust_CENTER);
+ sal_Int16 nParaAlign = sal_Int16(drawing::TextHorizontalAdjust_CENTER);
+ aTextPropMap.getValue("ParaAdjust") >>= nParaAlign;
+ switch (nParaAlign)
+ {
+ case sal_Int16(style::ParagraphAdjust_LEFT):
+ eHorzAdjust = drawing::TextHorizontalAdjust_LEFT;
+ break;
+ case sal_Int16(style::ParagraphAdjust_RIGHT):
+ eHorzAdjust = drawing::TextHorizontalAdjust_RIGHT;
+ break;
+ default:
+ eHorzAdjust = drawing::TextHorizontalAdjust_CENTER;
+ }
+ xShapePropertySet->setPropertyValue("TextHorizontalAdjust", uno::Any(eHorzAdjust));
+ xShapePropertySet->setPropertyValue("TextVerticalAdjust",
+ uno::Any(drawing::TextVerticalAdjust_TOP));
+}
+
+void lcl_setTextPropsToShape(const uno::Reference<beans::XPropertySet>& xShapePropertySet,
+ std::vector<beans::PropertyValue>& aTextPropVec)
+{
+ auto xShapePropertySetInfo = xShapePropertySet->getPropertySetInfo();
+ if (!xShapePropertySetInfo.is())
+ return;
+ for (size_t i = 0; i < aTextPropVec.size(); ++i)
+ {
+ if (xShapePropertySetInfo->hasPropertyByName(aTextPropVec[i].Name)
+ && !(xShapePropertySetInfo->getPropertyByName(aTextPropVec[i].Name).Attributes
+ & beans::PropertyAttribute::READONLY)
+ && aTextPropVec[i].Name != u"CharInteropGrabBag")
+ {
+ xShapePropertySet->setPropertyValue(aTextPropVec[i].Name, aTextPropVec[i].Value);
+ }
+ }
+}
+
+void lcl_applyUsedTextPropsToAllTextRuns(uno::Reference<text::XText>& xText,
+ const std::vector<beans::PropertyValue>& aTextPropVec)
+{
+ if (!xText.is())
+ return;
+ uno::Reference<text::XTextCursor> xTextCursor = xText->createTextCursor();
+ xTextCursor->gotoStart(false);
+ xTextCursor->gotoEnd(true);
+ uno::Reference<container::XEnumerationAccess> paraEnumAccess(xText, uno::UNO_QUERY);
+ if (!paraEnumAccess.is())
+ return;
+ uno::Reference<container::XEnumeration> paraEnum(paraEnumAccess->createEnumeration());
+ while (paraEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xParagraph(paraEnum->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumerationAccess> runEnumAccess(xParagraph, uno::UNO_QUERY);
+ if (!runEnumAccess.is())
+ continue;
+ uno::Reference<container::XEnumeration> runEnum = runEnumAccess->createEnumeration();
+ while (runEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(runEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun->getString().isEmpty())
+ continue;
+ uno::Reference<beans::XPropertySet> xRunPropSet(xRun, uno::UNO_QUERY);
+ if (!xRunPropSet.is())
+ continue;
+ auto xRunPropSetInfo = xRunPropSet->getPropertySetInfo();
+ if (!xRunPropSetInfo.is())
+ continue;
+
+ for (size_t i = 0; i < aTextPropVec.size(); ++i)
+ {
+ if (xRunPropSetInfo->hasPropertyByName(aTextPropVec[i].Name)
+ && !(xRunPropSetInfo->getPropertyByName(aTextPropVec[i].Name).Attributes
+ & beans::PropertyAttribute::READONLY))
+ xRunPropSet->setPropertyValue(aTextPropVec[i].Name, aTextPropVec[i].Value);
+ }
+ }
+ }
+}
+} // anonymous namespace
+
namespace oox::shape
{
WpsContext::WpsContext(ContextHandler2Helper const& rParent, uno::Reference<drawing::XShape> xShape,
@@ -309,7 +807,8 @@ oox::core::ContextHandlerRef WpsContext::onCreateContext(sal_Int32 nElementToken
uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList()));
}
}
- break;
+ return new oox::drawingml::PresetTextShapeContext(
+ *this, rAttribs, *(getShape()->getCustomShapeProperties()));
case XML_txbx:
{
mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
@@ -353,6 +852,96 @@ oox::core::ContextHandlerRef WpsContext::onCreateContext(sal_Int32 nElementToken
}
return nullptr;
}
+
+void WpsContext::onEndElement()
+{
+ // Convert shape to Fontwork shape if necessary and meaningful.
+ // Only at end of bodyPr all needed info is available.
+
+ if (getBaseToken(getCurrentElement()) != XML_bodyPr)
+ return;
+
+ // Make sure all needed parts are available
+ auto* pCustomShape
+ = dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(mxShape));
+ if (!pCustomShape || !mpShapePtr || !mxShape.is())
+ return;
+ uno::Reference<beans::XPropertySet> xShapePropertySet(mxShape, uno::UNO_QUERY);
+ if (!xShapePropertySet.is())
+ return;
+ // This is the text in the frame, associated with the shape
+ uno::Reference<text::XText> xText(mxShape, uno::UNO_QUERY);
+ if (!xText.is())
+ return;
+
+ OUString sMSPresetType;
+ comphelper::SequenceAsHashMap aCustomShapeGeometry(
+ xShapePropertySet->getPropertyValue("CustomShapeGeometry"));
+ aCustomShapeGeometry["PresetTextWarp"] >>= sMSPresetType;
+ if (sMSPresetType.isEmpty() || sMSPresetType == u"textNoShape")
+ return;
+
+ // Word can combine its "abc Transform" with a lot of shape types. LibreOffice can only render
+ // the old kind WordArt, which is based on a rectangle. In case of non rectangular shape we keep
+ // the shape and do not convert the text to Fontwork.
+ OUString sType;
+ aCustomShapeGeometry["Type"] >>= sType;
+ if (sType != u"ooxml-rect")
+ return;
+
+ // Copy properties from frame text to have them available after the frame is removed.
+ std::vector<beans::PropertyValue> aTextPropVec;
+ if (!lcl_getTextPropsFromFrameText(xText, aTextPropVec))
+ return;
+ comphelper::SequenceAsHashMap aTextPropMap(comphelper::containerToSequence(aTextPropVec));
+
+ // Copy text content from frame to shape. Since Fontwork uses simple text anyway, we can use
+ // a string.
+ OUString sFrameContent(xText->getString());
+ pCustomShape->NbcSetText(sFrameContent);
+
+ // Setting the property "TextBox" to false includes removing the attached frame from the shape.
+ xShapePropertySet->setPropertyValue("TextBox", uno::Any(false));
+
+ // Set the shape into text path mode, so that the text is drawn as Fontwork. Word renders a legacy
+ // "text on path" without the legacy stretching, therefore use false for bFromWordArt.
+ mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
+ FontworkHelpers::putCustomShapeIntoTextPathMode(mxShape, getShape()->getCustomShapeProperties(),
+ sMSPresetType, /*bFromWordArt*/ false);
+
+ // Apply the text props to the fontwork shape
+ lcl_setTextPropsToShape(xShapePropertySet, aTextPropVec); // includes e.g. FontName
+ lcl_setTextAnchorFromTextProps(xShapePropertySet, aTextPropMap);
+
+ // Fontwork in LO uses fill and stroke of the shape and cannot style text portions individually.
+ // "abc Transform" in Word uses fill and outline of the characters.
+ // We need to copy the properties from a run to the shape.
+ oox::drawingml::ShapePropertyMap aStrokeShapeProps(getFilter().getModelObjectHelper());
+ oox::drawingml::LineProperties aCreatedLineProperties
+ = lcl_generateLinePropertiesFromTextProps(aTextPropMap);
+ aCreatedLineProperties.pushToPropMap(aStrokeShapeProps, getFilter().getGraphicHelper());
+ lcl_applyShapePropsToShape(xShapePropertySet, aStrokeShapeProps);
+
+ oox::drawingml::ShapePropertyMap aFillShapeProps(getFilter().getModelObjectHelper());
+ oox::drawingml::FillProperties aCreatedFillProperties
+ = lcl_generateFillPropertiesFromTextProps(aTextPropMap);
+ aCreatedFillProperties.pushToPropMap(aFillShapeProps, getFilter().getGraphicHelper(),
+ /*nShapeRotation*/ 0,
+ /*nPhClr*/ API_RGB_TRANSPARENT, /*nPhClrTheme*/ -1,
+ pCustomShape->IsMirroredX(), pCustomShape->IsMirroredY(),
+ /*bIsCustomShape*/ true);
+ lcl_applyShapePropsToShape(xShapePropertySet, aFillShapeProps);
+
+ // Copying the text content from frame to shape as string has lost the styles. Apply the used text
+ // properties back to all runs in the text.
+ uno::Reference<text::XText> xNewText(pCustomShape->getUnoShape(), uno::UNO_QUERY);
+ if (xNewText.is())
+ lcl_applyUsedTextPropsToAllTextRuns(xNewText, aTextPropVec);
+
+ // Fontwork stretches the text to the given path. So adapt shape size to text is nonsensical.
+ xShapePropertySet->setPropertyValue("TextAutoGrowHeight", uno::Any(false));
+ xShapePropertySet->setPropertyValue("TextAutoGrowWidth", uno::Any(false));
+}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */