/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace
{ bool lcl_getTextPropsFromFrameText(const uno::Reference<text::XText>& xText,
std::vector<beans::PropertyValue>& rTextPropVec)
{ if (!xText.is()) returnfalse;
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()) returnfalse;
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)));
} returntrue;
}
} returnfalse;
}
// 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 parameter. If it does not find it, rValue is unchanged and // the method 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(u"attributes"_ustr) >>= aAttributesSeq)
&& aAttributesSeq.hasElements())) returnfalse;
comphelper::SequenceAsHashMap aAttributesMap(aAttributesSeq);
OUString sRet; if (!(aAttributesMap.getValue(rName) >>= sRet)) returnfalse;
rValue = sRet; returntrue;
}
// 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(u"attributes"_ustr) >>= aAttributesSeq)
&& aAttributesSeq.hasElements())) returnfalse;
comphelper::SequenceAsHashMap aAttributesMap(aAttributesSeq);
sal_Int32 nRet; if (!(aAttributesMap.getValue(rName) >>= nRet)) returnfalse;
rValue = nRet; returntrue;
}
// 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"_ustr) >>= aColorDetailSeq)
{
OUString sColorString;
bColorFound = lcl_getAttributeAsString(aColorDetailSeq, u"val"_ustr, sColorString); if (bColorFound)
{
sal_Int32 nColorToken = oox::AttributeConversion::decodeToken(sColorString);
rColor.setSchemeClr(nColorToken);
rColor.setSchemeName(sColorString);
}
} if (!bColorFound && (aPropMap.getValue(u"srgbClr"_ustr) >>= aColorDetailSeq))
{
OUString sColorString;
bColorFound = lcl_getAttributeAsString(aColorDetailSeq, u"val"_ustr, 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.contains(u"noFill"_ustr))
{
rFillProperties.moFillType = oox::XML_noFill; return;
}
if ((aTextFillMap.getValue(u"gradFill"_ustr) >>= aPropSeq) && aPropSeq.hasElements())
{
rFillProperties.moFillType = oox::XML_gradFill; // aPropSeq should have two items. One is "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(u"gsLst"_ustr) >>= 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"_ustr, 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(u"lin"_ustr) >>= aKindSeq)
{ // aKindSeq contains the attributes "ang" and "scaled"
sal_Int32 nAngle; // in 1/60000 deg if (lcl_getAttributeAsNumber(aKindSeq, u"ang"_ustr, nAngle))
rFillProperties.maGradientProps.moShadeAngle = nAngle;
OUString sScaledString; if (lcl_getAttributeAsString(aKindSeq, u"scaled"_ustr, sScaledString))
rFillProperties.maGradientProps.moShadeScaled
= sScaledString == u"1" || sScaledString == u"true"; return;
} if (aPropMap.getValue(u"path"_ustr) >>= 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, u"path"_ustr, sKind))
rFillProperties.maGradientProps.moGradientPath
= oox::AttributeConversion::decodeToken(sKind);
comphelper::SequenceAsHashMap aKindMap(aKindSeq);
uno::Sequence<beans::PropertyValue> aFillToRectSeq; if (aKindMap.getValue(u"fillToRect"_ustr) >>= 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"_ustr, aRect.X1))
aRect.X1 = 0; if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"t"_ustr, aRect.Y1))
aRect.Y1 = 0; if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"r"_ustr, aRect.X2))
aRect.X2 = 0; if (!lcl_getAttributeAsNumber(aFillToRectSeq, u"b"_ustr, 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.contains(u"bevel"_ustr))
rLineProperties.moLineJoint = oox::XML_bevel; elseif (aTextOutlineMap.contains(u"round"_ustr))
rLineProperties.moLineJoint = oox::XML_round; elseif (aTextOutlineMap.contains(u"miter"_ustr))
{ // 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(u"lim"_ustr, 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"_ustr) >>= aDashSeq)
{ // aDashSeq contains the attribute "val" with the kind of dash, e.g. "sysDot"
OUString sDashKind; if (lcl_getAttributeAsString(aDashSeq, u"val"_ustr, sDashKind))
rLineProperties.moPresetDash = oox::AttributeConversion::decodeToken(sDashKind);
}
OUString sCapKind; if (lcl_getAttributeAsString(rTextOutlineSeq, u"cap"_ustr, sCapKind))
rLineProperties.moLineCap = oox::AttributeConversion::decodeToken(sCapKind);
// Compound. LineProperties has a member for it, however Fontwork can currently only render "sng".
OUString sCompoundKind; if (lcl_getAttributeAsString(rTextOutlineSeq, u"cmpd"_ustr, sCompoundKind))
rLineProperties.moLineCompound = oox::AttributeConversion::decodeToken(sCompoundKind);
// Align. LineProperties has no member for attribute "algn".
// Get property "textOutline" from aTextPropMap
uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq; if (!(aTextPropMap.getValue(u"CharInteropGrabBag"_ustr) >>= aCharInteropGrabBagSeq)) return aLineProperties; if (!aCharInteropGrabBagSeq.hasElements()) return aLineProperties;
comphelper::SequenceAsHashMap aCharInteropGrabBagMap(aCharInteropGrabBagSeq);
beans::PropertyValue aProp; if (!(aCharInteropGrabBagMap.getValue(u"CharTextOutlineTextEffect"_ustr) >>= 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 // Theme color supersedes direct color. textFill supersedes theme color. Theme color and textFill // are in CharInteropGrabBag.
uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq; if ((rTextPropMap.getValue(u"CharInteropGrabBag"_ustr) >>= aCharInteropGrabBagSeq)
&& aCharInteropGrabBagSeq.hasElements())
{ // Handle case textFill
comphelper::SequenceAsHashMap aCharInteropGrabBagMap(aCharInteropGrabBagSeq);
beans::PropertyValue aProp; if (aCharInteropGrabBagMap.getValue(u"CharTextFillTextEffect"_ustr) >>= aProp)
{
uno::Sequence<beans::PropertyValue> aTextFillSeq; if (aProp.Name == "textFill" && (aProp.Value >>= aTextFillSeq)
&& aTextFillSeq.hasElements())
{ // Copy fill properties from aTextFillSeq to aFillProperties
lcl_getFillDetailsFromPropSeq(aTextFillSeq, aFillProperties); return aFillProperties;
}
}
// no textFill, look for theme color, tint and shade bool bColorFound(false);
OUString sColorString; if (aCharInteropGrabBagMap.getValue(u"CharThemeOriginalColor"_ustr) >>= sColorString)
{
sal_Int32 nThemeOrigColor = oox::AttributeConversion::decodeIntegerHex(sColorString);
aFillProperties.maFillColor.setSrgbClr(nThemeOrigColor);
bColorFound = true;
} if (aCharInteropGrabBagMap.getValue(u"CharThemeColor"_ustr) >>= sColorString)
{
sal_Int32 nColorToken = oox::AttributeConversion::decodeToken(sColorString);
aFillProperties.maFillColor.setSchemeClr(nColorToken);
aFillProperties.maFillColor.setSchemeName(sColorString);
bColorFound = true; // A character color has shade or tint, a shape color has lumMod and lumOff.
OUString sTransformString; if (aCharInteropGrabBagMap.getValue(u"CharThemeColorTint"_ustr) >>= 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));
} elseif (aCharInteropGrabBagMap.getValue(u"CharThemeColorShade"_ustr)
>>= 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));
}
} if (bColorFound) return aFillProperties;
}
// Neither textFill nor theme color. Look for direct color.
sal_Int32 aCharColor = 0; if (rTextPropMap.getValue(u"CharColor"_ustr) >>= aCharColor)
aFillProperties.maFillColor.setSrgbClr(aCharColor); else
aFillProperties.maFillColor.setUnused(); return aFillProperties;
}
oox::core::ContextHandlerRef WpsContext::onCreateContext(sal_Int32 nElementToken, const oox::AttributeList& rAttribs)
{ switch (getBaseToken(nElementToken))
{ case XML_wsp: break; case XML_cNvCnPr:
{ // It might be a connector shape in a wordprocessing canvas // Replace the custom shape with a connector shape. if (!mpShapePtr || !mpShapePtr->isInWordprocessingCanvas() || !mpMasterShapePtr) break; // Generate new shape
oox::drawingml::ShapePtr pShape = std::make_shared<oox::drawingml::Shape>(
u"com.sun.star.drawing.ConnectorShape"_ustr, false);
pShape->setConnectorShape(true);
pShape->setWps(true);
pShape->setWordprocessingCanvas(true); // ToDo: Can only copy infos from mpShapePtr to pShape for which getter available.
pShape->setName(mpShapePtr->getName());
pShape->setId(mpShapePtr->getId());
pShape->setWPGChild(mpShapePtr->isWPGChild()); // And actually replace the shape.
mpShapePtr = pShape;
mpMasterShapePtr->getChildren().pop_back();
mpMasterShapePtr->getChildren().push_back(pShape); returnnew oox::drawingml::ConnectorShapePropertiesContext(
*this, mpShapePtr, mpShapePtr->getConnectorShapeProperties());
} case XML_bodyPr: if (mxShape.is())
{ // no evaluation of attribute XML_rot, because Word ignores it, as of 2022-07.
uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY);
uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
sal_Int32 nVert = rAttribs.getToken(XML_vert, XML_horz); if (nVert == XML_eaVert)
{
xPropertySet->setPropertyValue(u"TextWritingMode"_ustr,
uno::Any(text::WritingMode_TB_RL));
xPropertySet->setPropertyValue(u"WritingMode"_ustr,
uno::Any(text::WritingMode2::TB_RL));
} elseif (nVert == XML_mongolianVert)
{
xPropertySet->setPropertyValue(u"WritingMode"_ustr,
uno::Any(text::WritingMode2::TB_LR));
} elseif (nVert == XML_wordArtVert || nVert == XML_wordArtVertRtl)
{ // Multiline wordArtVert is not implemented yet. // It will render all the text in 1 line. // Map 'wordArtVertRtl' to 'wordArtVert', as they are the same now.
xPropertySet->setPropertyValue(u"WritingMode"_ustr,
uno::Any(text::WritingMode2::STACKED));
} elseif (nVert != XML_horz) // cases XML_vert and XML_vert270
{ // Hack to get same rendering as after the fix for tdf#87924. If shape rotation // plus text direction results in upright text, use horizontal text direction. // Remove hack when frame is able to rotate.
// Need transformation matrix since RotateAngle does not contain flip.
drawing::HomogenMatrix3 aMatrix;
xPropertySet->getPropertyValue(u"Transformation"_ustr) >>= aMatrix;
basegfx::B2DHomMatrix aTransformation;
aTransformation.set(0, 0, aMatrix.Line1.Column1);
aTransformation.set(0, 1, aMatrix.Line1.Column2);
aTransformation.set(0, 2, aMatrix.Line1.Column3);
aTransformation.set(1, 0, aMatrix.Line2.Column1);
aTransformation.set(1, 1, aMatrix.Line2.Column2);
aTransformation.set(1, 2, aMatrix.Line2.Column3); // For this to be a valid 2D transform matrix, the last row must be [0,0,1]
assert(aMatrix.Line3.Column1 == 0);
assert(aMatrix.Line3.Column2 == 0);
assert(aMatrix.Line3.Column3 == 1);
basegfx::B2DTuple aScale;
basegfx::B2DTuple aTranslate; double fRotate = 0; double fShearX = 0;
aTransformation.decompose(aScale, aTranslate, fRotate, fShearX); auto nRotate(static_cast<sal_uInt16>(NormAngle360(basegfx::rad2deg(fRotate)))); if ((nVert == XML_vert && nRotate == 270)
|| (nVert == XML_vert270 && nRotate == 90))
{
xPropertySet->setPropertyValue(u"WritingMode"_ustr,
uno::Any(text::WritingMode2::LR_TB)); // ToDo: Remember original vert value and remove hack on export.
} elseif (nVert == XML_vert)
xPropertySet->setPropertyValue(u"WritingMode"_ustr,
uno::Any(text::WritingMode2::TB_RL90)); else// nVert == XML_vert270
xPropertySet->setPropertyValue(u"WritingMode"_ustr,
uno::Any(text::WritingMode2::BT_LR));
}
auto xPropertySetInfo = xPropertySet->getPropertySetInfo();
if (xServiceInfo.is())
{ // Handle inset attributes for Writer textframes.
sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns };
std::optional<sal_Int32> oInsets[4]; for (std::size_t i = 0; i < SAL_N_ELEMENTS(aInsets); ++i)
{
std::optional<OUString> oValue = rAttribs.getString(aInsets[i]); if (oValue.has_value())
oInsets[i] = oox::drawingml::GetCoordinate(oValue.value()); else // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
oInsets[i]
= (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
} const OUString aShapeProps[]
= { u"TextLeftDistance"_ustr, u"TextUpperDistance"_ustr,
u"TextRightDistance"_ustr, u"TextLowerDistance"_ustr }; for (std::size_t i = 0; i < SAL_N_ELEMENTS(aShapeProps); ++i)
{ if (!oInsets[i]) continue; if (xPropertySetInfo && xPropertySetInfo->hasPropertyByName(aShapeProps[i]))
xPropertySet->setPropertyValue(aShapeProps[i], uno::Any(*oInsets[i])); else
SAL_WARN("oox", "Property: " << aShapeProps[i] << " not supported");
}
}
// Handle text vertical adjustment inside a text frame if (rAttribs.hasAttribute(XML_anchor))
{ if (xPropertySetInfo
&& xPropertySetInfo->hasPropertyByName(u"TextVerticalAdjust"_ustr))
{
drawing::TextVerticalAdjust eAdjust = drawingml::GetTextVerticalAdjust(
rAttribs.getToken(XML_anchor, XML_t));
xPropertySet->setPropertyValue(u"TextVerticalAdjust"_ustr,
uno::Any(eAdjust));
} else
SAL_WARN("oox", "Property: TextVerticalAdjust not supported");
}
// Apply character color of the shape to the shape's textbox.
uno::Reference<text::XText> xText(mxShape, uno::UNO_QUERY); if (xText)
{
uno::Any xCharColor = xPropertySet->getPropertyValue(u"CharColor"_ustr);
Color aColor = COL_AUTO; if ((xCharColor >>= aColor) && aColor != COL_AUTO)
{ // tdf#135923 Apply character color of the shape to the textrun // when the character color of the textrun is default. // tdf#153791 But only if the run has no background color (shd element in OOXML) if (uno::Reference<container::XEnumerationAccess> paraEnumAccess{
xText, uno::UNO_QUERY })
{
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; if (uno::Reference<beans::XPropertySet> xParaPropSet{
xParagraph, uno::UNO_QUERY }) if ((xParaPropSet->getPropertyValue(u"ParaBackColor"_ustr)
>>= aColor)
&& aColor != COL_AUTO) continue;
while (runEnum->hasMoreElements())
{
uno::Reference<text::XTextRange> xRun(runEnum->nextElement(),
uno::UNO_QUERY); const uno::Reference<beans::XPropertyState> xRunState(
xRun, uno::UNO_QUERY); if (!xRunState
|| xRunState->getPropertyState(u"CharColor"_ustr)
== beans::PropertyState_DEFAULT_VALUE)
{
uno::Reference<beans::XPropertySet> xRunPropSet(
xRun, uno::UNO_QUERY); if (!xRunPropSet) continue; if ((xRunPropSet->getPropertyValue(u"CharBackColor"_ustr)
>>= aColor)
&& aColor != COL_AUTO) continue; if (!(xRunPropSet->getPropertyValue(u"CharColor"_ustr)
>>= aColor)
|| aColor == COL_AUTO)
xRunPropSet->setPropertyValue(u"CharColor"_ustr,
xCharColor);
}
}
}
}
}
}
if (xPropertySetInfo && xPropertySetInfo->hasPropertyByName(u"TextWordWrap"_ustr))
{ auto nWrappingType = rAttribs.getToken(XML_wrap, XML_square);
xPropertySet->setPropertyValue(u"TextWordWrap"_ustr,
uno::Any(nWrappingType == XML_square));
} else
SAL_WARN("oox", "Property: TextWordWrap not supported");
returnthis;
} elseif (m_bHasWPGParent && mpShapePtr)
{ // this WPS context has to be inside a WPG shape, so the <BodyPr> element // cannot be applied to mxShape member, use mpShape instead, and after the // the parent shape finished, apply it for its children.
mpShapePtr->setWPGChild(true);
oox::drawingml::TextBodyPtr pTextBody;
pTextBody.reset(new oox::drawingml::TextBody());
sal_Int32 aInsets[] = { XML_lIns, XML_tIns, XML_rIns, XML_bIns }; for (int i = 0; i < 4; ++i)
{ if (rAttribs.hasAttribute(XML_lIns))
{
std::optional<OUString> oValue = rAttribs.getString(aInsets[i]); if (oValue.has_value())
pTextBody->getTextProperties().moInsets[i]
= oox::drawingml::GetCoordinate(oValue.value()); else // Defaults from the spec: left/right: 91440 EMU, top/bottom: 45720 EMU
pTextBody->getTextProperties().moInsets[i]
= (aInsets[i] == XML_lIns || aInsets[i] == XML_rIns) ? 254 : 127;
}
}
mpShapePtr->setTextBody(pTextBody);
} break; case XML_noAutofit: case XML_spAutoFit:
{
uno::Reference<lang::XServiceInfo> xServiceInfo(mxShape, uno::UNO_QUERY); // We can't use oox::drawingml::TextBodyPropertiesContext here, as this // is a child context of bodyPr, so the shape is already sent: we need // to alter the XShape directly.
uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY); if (xPropertySet.is())
{ if (xServiceInfo->supportsService(u"com.sun.star.text.TextFrame"_ustr))
xPropertySet->setPropertyValue(
u"FrameIsAutomaticHeight"_ustr,
uno::Any(getBaseToken(nElementToken) == XML_spAutoFit)); else
{ auto xPropertySetInfo = xPropertySet->getPropertySetInfo(); if (xPropertySetInfo
&& xPropertySetInfo->hasPropertyByName(u"TextAutoGrowHeight"_ustr))
{
xPropertySet->setPropertyValue(
u"TextAutoGrowHeight"_ustr,
uno::Any(getBaseToken(nElementToken) == XML_spAutoFit));
} else
SAL_WARN("oox", "Property: TextAutoGrowHeight not supported");
}
}
} break; case XML_prstTxWarp: if (rAttribs.hasAttribute(XML_prst))
{
uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY); if (xPropertySet.is())
{
std::optional<OUString> presetShapeName = rAttribs.getString(XML_prst); const OUString& preset = presetShapeName.value();
comphelper::SequenceAsHashMap aCustomShapeGeometry(
xPropertySet->getPropertyValue(u"CustomShapeGeometry"_ustr));
aCustomShapeGeometry[u"PresetTextWarp"_ustr] <<= preset;
xPropertySet->setPropertyValue(
u"CustomShapeGeometry"_ustr,
uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList()));
}
} returnnew oox::drawingml::PresetTextShapeContext(
*this, rAttribs, *(getShape()->getCustomShapeProperties())); case XML_txbx:
{
mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
mpShapePtr->setTextBox(true); //in case if the textbox is linked, save the attributes //for further processing. if (rAttribs.hasAttribute(XML_id))
{
std::optional<OUString> id = rAttribs.getString(XML_id); if (id.has_value())
{
oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
linkedTxtBoxAttr.id = id.value().toInt32();
mpShapePtr->setTxbxHasLinkedTxtBox(true);
mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
}
} returnthis;
} break; case XML_linkedTxbx:
{ //in case if the textbox is linked, save the attributes //for further processing.
mpShapePtr->getCustomShapeProperties()->setShapeTypeOverride(true);
mpShapePtr->setTextBox(true);
std::optional<OUString> id = rAttribs.getString(XML_id);
std::optional<OUString> seq = rAttribs.getString(XML_seq); if (id.has_value() && seq.has_value())
{
oox::drawingml::LinkedTxbxAttr linkedTxtBoxAttr;
linkedTxtBoxAttr.id = id.value().toInt32();
linkedTxtBoxAttr.seq = seq.value().toInt32();
mpShapePtr->setTxbxHasLinkedTxtBox(true);
mpShapePtr->setLinkedTxbxAttributes(linkedTxtBoxAttr);
}
} break; default: return ShapeContext::onCreateContext(nElementToken, rAttribs);
} 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;
// 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[u"Type"_ustr] >>= 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(u"TextBox"_ustr, 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);
// 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(u"TextAutoGrowHeight"_ustr, uno::Any(false));
xShapePropertySet->setPropertyValue(u"TextAutoGrowWidth"_ustr, uno::Any(false));
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.