/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// Set attributes in CustomShapeGeometry
xDefaulter->createCustomShapeDefaults(sFontworkType);
auto aGeomPropSeq = xSet->getPropertyValue(u"CustomShapeGeometry"_ustr)
.get<uno::Sequence<beans::PropertyValue>>(); auto aGeomPropVec
= comphelper::sequenceToContainer<std::vector<beans::PropertyValue>>(aGeomPropSeq);
OString FontworkHelpers::GetVMLFontworkShapetypeMarkup(const MSO_SPT eShapeType)
{ // The markup is taken from VML in DOCX documents. Using the generated 'vml-shape-types' file // does not work.
void FontworkHelpers::createCharFillPropsFromShape( const uno::Reference<beans::XPropertySet>& rXPropSet,
std::vector<beans::PropertyValue>& rCharPropVec)
{ auto xPropSetInfo = rXPropSet->getPropertySetInfo(); if (!xPropSetInfo.is()) return; // CharColor contains the color including all color transformations // FillColor contains darken and lighten but not transparency
sal_Int32 nColorRGB = 0; if (xPropSetInfo->hasPropertyByName(u"FillColor"_ustr)
&& (rXPropSet->getPropertyValue(u"FillColor"_ustr) >>= nColorRGB))
{
::Color aColor(ColorTransparency, nColorRGB);
sal_Int16 nTransPercent = 0; if (xPropSetInfo->hasPropertyByName(u"FillTransparence"_ustr)
&& (rXPropSet->getPropertyValue(u"FillTransparence"_ustr) >>= nTransPercent))
{
sal_uInt8 nAlpha = 255 - sal_uInt8(std::lround(double(nTransPercent) * 2.55));
aColor.SetAlpha(nAlpha);
}
rCharPropVec.push_back(comphelper::makePropertyValue(u"CharColor"_ustr, sal_Int32(aColor)));
}
for (size_t i = 0; i < 5; i++)
{
OUString aPropertyName(aShapePropNames[i]); if (xPropSetInfo->hasPropertyByName(aPropertyName))
rCharPropVec.push_back(comphelper::makePropertyValue(
OUString(aCharPropNames[i]), rXPropSet->getPropertyValue(aPropertyName)));
}
}
bool bIsRelative(rLineDash.Style == drawing::DashStyle_RECTRELATIVE
|| rLineDash.Style == drawing::DashStyle_ROUNDRELATIVE); if (bIsRelative && rLineDash.Dots == 1)
{ // The length were tweaked on import in case of prstDash. Revert it here.
sal_uInt32 nDotLen = rLineDash.DotLen;
sal_uInt32 nDashLen = rLineDash.DashLen;
sal_uInt32 nDistance = rLineDash.Distance; if (rLineCap != drawing::LineCap_BUTT && nDistance >= 99)
{
nDistance -= 99;
nDotLen += 99; if (nDashLen > 0)
nDashLen += 99;
}
// LO uses length 0 for 100%, if the attribute is missing in ODF. // Other applications might write 100%. Make is unique for the conditions. if (nDotLen == 0)
nDotLen = 100; if (nDashLen == 0 && rLineDash.Dashes > 0)
nDashLen = 100;
namespace
{ // Contains information about one gradient stop. Each gradient has at least 2 of these. struct GradientStopColor
{ // RGBColor contains no transformations. In case TTColor has other type than // ThemeColorType::Unknown, it has precedence. The color transformations in TTColor are used // for RGBColor as well.
model::ComplexColor TTColor; // ThemeColorType and color transformations
::Color RGBColor;
};
}
// 'first' contains the position in the range 0 (=0%) to 100000 (=100%) in the gradient as needed for // the 'pos' attribute in <w14:gs> element in oox, 'second' contains color and color transformations // at this position. The map contains all information needed for a <w14:gsLst> element in oox. typedef std::multimap<sal_Int32, GradientStopColor> ColorMapType;
// Returns the string to be used in w14:schemeClr in case of w14:textOutline or w14:textFill
OUString lcl_getW14MarkupStringForThemeColor(const model::ComplexColor& rComplexColor)
{ const sal_uInt8 nClrNameIndex = std::clamp<sal_uInt8>(
sal_Int32(rComplexColor.getThemeColorType()), sal_Int32(model::ThemeColorType::Dark1),
sal_Int32(model::ThemeColorType::FollowedHyperlink)); return OUString(W14ColorNames[nClrNameIndex]);
}
// Returns the string to be used in w:themeColor. It is exported via CharThemeColor.
OUString lcl_getWMarkupStringForThemeColor(const model::ComplexColor& rComplexColor)
{ const sal_uInt8 nClrNameIndex = std::clamp<sal_uInt8>(
sal_Int32(rComplexColor.getThemeColorType()), sal_Int32(model::ThemeColorType::Dark1),
sal_Int32(model::ThemeColorType::FollowedHyperlink)); return OUString(WColorNames[nClrNameIndex]);
}
// Puts the value of the first occurrence of rType in rComplexColor into rValue and returns true. // If such does not exist, rValue is unchanged and the method returns false. bool lcl_getThemeColorTransformationValue(const model::ComplexColor& rComplexColor, const model::TransformationType& rType, sal_Int16& rValue)
{ const std::vector<model::Transformation>& aTransVec(rComplexColor.getTransformations()); auto bItemFound
= [rType](const model::Transformation& rTrans) { return rType == rTrans.meType; }; auto pIt = std::find_if(aTransVec.begin(), aTransVec.end(), bItemFound); if (pIt == aTransVec.end()) returnfalse;
rValue = (*pIt).mnValue; returntrue;
}
// Adds the child elements 'lumMod' and 'lumOff' to 'schemeClr' maCurrentElement of pGrabStack, // if such exist in rComplexColor. 'alpha' is contained in the maTransformations of rComplexColor // in case of gradient fill. void lcl_addColorTransformationToGrabBagStack( const model::ComplexColor& rComplexColor, const std::unique_ptr<oox::GrabBagStack>& pGrabBagStack)
{ if (pGrabBagStack == nullptr) return; for (autoconst& rColorTransform : rComplexColor.getTransformations())
{ switch (rColorTransform.meType)
{ case model::TransformationType::LumMod:
pGrabBagStack->push(u"lumMod"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addInt32(u"val"_ustr, rColorTransform.mnValue * 10);
pGrabBagStack->pop();
pGrabBagStack->pop(); break; case model::TransformationType::LumOff:
pGrabBagStack->push(u"lumOff"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addInt32(u"val"_ustr, rColorTransform.mnValue * 10);
pGrabBagStack->pop();
pGrabBagStack->pop(); break; case model::TransformationType::Alpha:
pGrabBagStack->push(u"alpha"_ustr);
pGrabBagStack->push(u"attributes"_ustr); // model::TransformationType::Alpha is designed to be used with a:alpha, which has // opacity. But w14:alpha uses transparency. So convert it here.
pGrabBagStack->addInt32(u"val"_ustr,
oox::drawingml::MAX_PERCENT - rColorTransform.mnValue * 10);
pGrabBagStack->pop();
pGrabBagStack->pop(); break; default: // other child elements can be added later if needed for Fontwork break;
}
}
}
ColorMapType lcl_createColorMapFromShapeProps( const uno::Reference<beans::XPropertySet>& rXPropSet, const uno::Reference<beans::XPropertySetInfo>& rXPropSetInfo, const awt::Gradient2& rColorGradient, constbool& rbHasColorGradient, const awt::Gradient2& rTransparenceGradient, constbool& rbHasTransparenceGradient)
{ // LibreOffice can use color gradients and transparency gradients with different geometries. // That is not possible in OOXML, so a fill might look different in Word. But a round-trip // with gradients imported from Word, should work well.
// Word has transparency not as separate gradient but as color transformation in a color // gradient. Thus we synchronize the gradients. Then they have same offsets and count.
basegfx::BColor aSingleColor;
basegfx::BGradient aColorBGradient;
basegfx::BColorStops aColorStops; if (rbHasColorGradient)
{
aColorBGradient = model::gradient::getFromUnoGradient2(rColorGradient);
aColorBGradient.tryToApplyStartEndIntensity();
aColorBGradient.tryToApplyBorder();
aColorBGradient.tryToApplyAxial();
basegfx::utils::prepareColorStops(aColorBGradient, aColorStops, aSingleColor); // All gradient styles but LINEAR and AXIAL (which is already converted to LINEAR) need the // stops sequence reverse. if (awt::GradientStyle_LINEAR != aColorBGradient.GetGradientStyle())
aColorStops.reverseColorStops();
} else
{
sal_Int32 nFillColor(0); if (rXPropSetInfo->hasPropertyByName(u"FillColor"_ustr))
rXPropSet->getPropertyValue(u"FillColor"_ustr) >>= nFillColor;
aSingleColor = ::Color(ColorTransparency, nFillColor).getBColor().clamp();
}
basegfx::BColor aSingleTrans;
basegfx::BGradient aTransBGradient;
basegfx::BColorStops aTransStops; if (rbHasTransparenceGradient)
{
aTransBGradient = model::gradient::getFromUnoGradient2(rTransparenceGradient);
aTransBGradient.tryToApplyStartEndIntensity(); // usually 100%, but might be set by macro
aTransBGradient.tryToApplyBorder();
aTransBGradient.tryToApplyAxial();
basegfx::utils::prepareColorStops(aTransBGradient, aTransStops, aSingleTrans); // All gradient styles but LINEAR and AXIAL (which is already converted to LINEAR) need the // stops sequence reverse. if (awt::GradientStyle_LINEAR != aTransBGradient.GetGradientStyle())
aTransStops.reverseColorStops();
} else
{
sal_Int16 nAPITrans(0); if (rXPropSetInfo->hasPropertyByName(u"FillTransparence"_ustr))
rXPropSet->getPropertyValue(u"FillTransparence"_ustr) >>= nAPITrans; // API transparency is in range 0..100, BColor in range [0.0, 1.0].
aSingleTrans = basegfx::BColor(nAPITrans * 0.01).clamp();
}
// If we have no color gradient, the fix fill color might be a theme color. In that case we use // it instead of the color from the color stop.
GradientStopColor aFixColor; bool bUseThemeColor(!rbHasColorGradient
&& FontworkHelpers::getThemeColorFromShape(u"FillComplexColor"_ustr,
rXPropSet, aFixColor.TTColor));
for (auto itC = aColorStops.begin(), itT = aTransStops.begin();
itC != aColorStops.end() && itT != aTransStops.end(); ++itC, ++itT)
{
GradientStopColor aNextStopColor = aFixColor; if (!bUseThemeColor)
{
aNextStopColor.TTColor = model::ComplexColor();
aNextStopColor.RGBColor = ::Color((*itC).getStopColor());
} // model::TransformationType::Alpha is opacity in range 0..10000, // BColor is transparency in range [0.0, 1.0]
sal_Int16 nAlpha = std::clamp<sal_Int16>(
10000 - std::lround((*itT).getStopColor().luminance() * 10000.0), 0, 10000); if (nAlpha < 10000)
aNextStopColor.TTColor.addTransformation({ model::TransformationType::Alpha, nAlpha });
sal_Int32 nPosition
= static_cast<sal_Int32>(std::lround((*itC).getStopOffset() * 100000.0));
aColorMap.insert(std::pair{ nPosition, aNextStopColor });
}
// If a gradient has only two stops, MS Office renders it with a non-linear method which looks // different than gradient in LibreOffice (see tdf#128795). For more than two stops rendering is // the same as in LibreOffice, even if two stops are identical. if (aColorMap.size() == 2)
{ auto it = aColorMap.begin();
aColorMap.insert(std::pair{ 0, (*it).second });
} return aColorMap;
}
} // end namespace
void FontworkHelpers::createCharInteropGrabBagUpdatesFromShapeProps( const uno::Reference<beans::XPropertySet>& rXPropSet,
std::vector<beans::PropertyValue>& rUpdatePropVec)
{ auto xPropSetInfo = rXPropSet->getPropertySetInfo(); if (!xPropSetInfo.is()) return;
// GrabBagStack is a special tool for handling the hierarchy in a GrabBag
std::unique_ptr<oox::GrabBagStack> pGrabBagStack;
// We might have a solid fill but a transparency gradient. That needs to be exported as gradFill // too, because Word has transparency not separated but in the color stops in a color gradient. // A gradient exists, if the GradientName is not empty.
OUString sTransparenceGradientName; if (eFillStyle == drawing::FillStyle_SOLID
&& xPropSetInfo->hasPropertyByName(u"FillTransparenceGradientName"_ustr)
&& (rXPropSet->getPropertyValue(u"FillTransparenceGradientName"_ustr)
>>= sTransparenceGradientName)
&& !sTransparenceGradientName.isEmpty())
eFillStyle = drawing::FillStyle_GRADIENT;
switch (eFillStyle)
{ case drawing::FillStyle_NONE:
{
pGrabBagStack->appendElement(u"noFill"_ustr, uno::Any()); break;
} case drawing::FillStyle_GRADIENT:
{
awt::Gradient2 aColorGradient; bool bHasColorGradient(false);
awt::Gradient2 aTransparenceGradient; bool bHasTransparenceGradient(false);
lcl_getGradientsFromShape(rXPropSet, xPropSetInfo, aColorGradient, bHasColorGradient,
aTransparenceGradient, bHasTransparenceGradient); // aColorMap contains the color stops suitable to generate gsLst
ColorMapType aColorMap = lcl_createColorMapFromShapeProps(
rXPropSet, xPropSetInfo, aColorGradient, bHasColorGradient, aTransparenceGradient,
bHasTransparenceGradient);
pGrabBagStack->push(u"gradFill"_ustr);
pGrabBagStack->push(u"gsLst"_ustr); for (auto it = aColorMap.begin(); it != aColorMap.end(); ++it)
{
pGrabBagStack->push(u"gs"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addInt32(u"pos"_ustr, (*it).first);
pGrabBagStack->pop(); if ((*it).second.TTColor.getThemeColorType() == model::ThemeColorType::Unknown)
{
pGrabBagStack->push(u"srgbClr"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addString(u"val"_ustr, (*it).second.RGBColor.AsRGBHexString());
pGrabBagStack->pop(); // maCurrentElement:'srgbClr', maPropertyList:'attributes'
} else
{
pGrabBagStack->push(u"schemeClr"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addString(
u"val"_ustr, lcl_getW14MarkupStringForThemeColor((*it).second.TTColor));
pGrabBagStack->pop(); // maCurrentElement:'schemeClr', maPropertyList:'attributes'
}
lcl_addColorTransformationToGrabBagStack((*it).second.TTColor, pGrabBagStack);
pGrabBagStack->pop(); // maCurrentElement:'gs', maPropertyList:'attributes', 'srgbClr' or 'schemeClr'
pGrabBagStack->pop(); // maCurrentElement:'gsLst', maPropertyList: at least two 'gs'
}
pGrabBagStack->pop(); // maCurrentElement:'gradFill', maPropertyList: gsLst
// Kind of gradient
awt::GradientStyle eGradientStyle = awt::GradientStyle_LINEAR; if (bHasColorGradient)
eGradientStyle = aColorGradient.Style; elseif (bHasTransparenceGradient)
eGradientStyle = aTransparenceGradient.Style; // write 'lin' or 'path'. LibreOffice has nothing which corresponds to 'shape'. if (eGradientStyle == awt::GradientStyle_LINEAR
|| eGradientStyle == awt::GradientStyle_AXIAL)
{ // API angle is in 1/10th deg and describes counter-clockwise rotation of line of // equal color. OOX angle is in 1/60000th deg and describes clockwise rotation of // color transition direction.
sal_Int32 nAngleOOX = 0; if (bHasColorGradient)
nAngleOOX = ((3600 - aColorGradient.Angle + 900) % 3600) * 6000; elseif (bHasTransparenceGradient)
nAngleOOX = ((3600 - aTransparenceGradient.Angle + 900) % 3600) * 6000;
pGrabBagStack->push(u"lin"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addInt32(u"ang"_ustr, nAngleOOX); // LibreOffice cannot scale a gradient to the shape size.
pGrabBagStack->addString(u"scaled"_ustr, u"0"_ustr);
} else
{ // Same rendering as in LibreOffice is not possible: // (1) The gradient type 'path' in Word has no rotation. // (2) To get the same size of gradient area, the element 'tileRect' is needed, but // that is not available for <w14:textFill> element. // So we can only set a reasonably suitable focus point.
pGrabBagStack->push(u"path"_ustr);
pGrabBagStack->push(u"attributes"_ustr); if (eGradientStyle == awt::GradientStyle_RADIAL
|| eGradientStyle == awt::GradientStyle_ELLIPTICAL)
pGrabBagStack->addString(u"path"_ustr, u"circle"_ustr); else
pGrabBagStack->addString(u"path"_ustr, u"rect"_ustr);
pGrabBagStack->pop();
pGrabBagStack->push(u"fillToRect"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
sal_Int32 nLeftPercent
= bHasColorGradient ? aColorGradient.XOffset : aTransparenceGradient.XOffset;
sal_Int32 nTopPercent
= bHasColorGradient ? aColorGradient.YOffset : aTransparenceGradient.YOffset;
pGrabBagStack->addInt32(u"l"_ustr, nLeftPercent * 1000);
pGrabBagStack->addInt32(u"t"_ustr, nTopPercent * 1000);
pGrabBagStack->addInt32(u"r"_ustr, (100 - nLeftPercent) * 1000);
pGrabBagStack->addInt32(u"b"_ustr, (100 - nTopPercent) * 1000);
} // all remaining pop() calls are in the final getRootProperty() method break;
} case drawing::FillStyle_SOLID:
{
pGrabBagStack->push(u"solidFill"_ustr);
model::ComplexColor aComplexColor; // It is either "schemeClr" or "srgbClr". if (FontworkHelpers::getThemeColorFromShape(u"FillComplexColor"_ustr, rXPropSet,
aComplexColor))
{
pGrabBagStack->push(u"schemeClr"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addString(u"val"_ustr,
lcl_getW14MarkupStringForThemeColor(aComplexColor));
pGrabBagStack->pop(); // maCurrentElement:'schemeClr', maPropertyList:'attributes'
lcl_addColorTransformationToGrabBagStack(aComplexColor, pGrabBagStack); // maCurrentElement:'schemeClr', maPropertyList:'attributes', maybe 'lumMod' and // maybe 'lumOff'
} else
{
pGrabBagStack->push(u"srgbClr"_ustr);
sal_Int32 nFillColor(0); if (xPropSetInfo->hasPropertyByName(u"FillColor"_ustr))
rXPropSet->getPropertyValue(u"FillColor"_ustr) >>= nFillColor;
pGrabBagStack->push(u"attributes"_ustr);
::Color aColor(ColorTransparency, nFillColor);
pGrabBagStack->addString(u"val"_ustr, aColor.AsRGBHexString());
pGrabBagStack->pop(); // maCurrentElement:'srgbClr', maPropertyList:'attributes'
}
sal_Int16 nFillTransparence(0); if (xPropSetInfo->hasPropertyByName(u"FillTransparence"_ustr))
rXPropSet->getPropertyValue(u"FillTransparence"_ustr) >>= nFillTransparence; if (nFillTransparence != 0)
{
pGrabBagStack->push(u"alpha"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addInt32(u"val"_ustr, nFillTransparence * 1000);
} // all remaining pop() calls are in the final getRootProperty() method break;
} default: // BITMAP is VML only export and does not arrive here. HATCH has to be VML only // export too, but is not yet implemented. break;
} // resolve the stack and put resulting PropertyValue into the update vector
beans::PropertyValue aCharTextFillTextEffect;
aCharTextFillTextEffect.Name = "CharTextFillTextEffect";
aCharTextFillTextEffect.Value <<= pGrabBagStack->getRootProperty();
rUpdatePropVec.push_back(aCharTextFillTextEffect);
// attributes
pGrabBagStack->push(u"attributes"_ustr); // line width
sal_Int32 nLineWidth(0); if (xPropSetInfo->hasPropertyByName(u"LineWidth"_ustr))
rXPropSet->getPropertyValue(u"LineWidth"_ustr) >>= nLineWidth;
pGrabBagStack->addInt32(u"w"_ustr, nLineWidth * 360); // cap for dashes
drawing::LineCap eLineCap = drawing::LineCap_BUTT; if (xPropSetInfo->hasPropertyByName(u"LineCap"_ustr))
rXPropSet->getPropertyValue(u"LineCap"_ustr) >>= eLineCap;
OUString sCap = u"flat"_ustr; if (eLineCap == drawing::LineCap_ROUND)
sCap = u"rnd"_ustr; elseif (eLineCap == drawing::LineCap_SQUARE)
sCap = u"sq"_ustr;
pGrabBagStack->addString(u"cap"_ustr, sCap); // LO has no compound lines and always centers the lines
pGrabBagStack->addString(u"cmpd"_ustr, u"sng"_ustr);
pGrabBagStack->addString(u"alng"_ustr, u"ctr"_ustr);
pGrabBagStack->pop(); // maCurrentElement:'textOutline', maPropertyList:'attributes'
// style
drawing::LineStyle eLineStyle = drawing::LineStyle_NONE; if (xPropSetInfo->hasPropertyByName(u"LineStyle"_ustr))
rXPropSet->getPropertyValue(u"LineStyle"_ustr) >>= eLineStyle; // 'dashed' is not a separate style in Word. Word has a style 'gradFill', but that is not yet // implemented in LO. So only 'noFill' and 'solidFill'. if (eLineStyle == drawing::LineStyle_NONE)
{
pGrabBagStack->appendElement(u"noFill"_ustr, uno::Any());
} else
{
pGrabBagStack->push(u"solidFill"_ustr); // It is either "schemeClr" or "srgbClr".
model::ComplexColor aComplexColor; if (FontworkHelpers::getThemeColorFromShape(u"LineComplexColor"_ustr, rXPropSet,
aComplexColor))
{
pGrabBagStack->push(u"schemeClr"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addString(u"val"_ustr,
lcl_getW14MarkupStringForThemeColor(aComplexColor));
pGrabBagStack->pop();
lcl_addColorTransformationToGrabBagStack(aComplexColor, pGrabBagStack); // maCurrentElement:'schemeClr', maPropertylist:'attributes'
} else// not a theme color
{
pGrabBagStack->push(u"srgbClr"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
sal_Int32 nLineColor(0); if (xPropSetInfo->hasPropertyByName(u"LineColor"_ustr))
rXPropSet->getPropertyValue(u"LineColor"_ustr) >>= nLineColor;
::Color aColor(ColorTransparency, nLineColor);
pGrabBagStack->addString(u"val"_ustr, aColor.AsRGBHexString());
pGrabBagStack->pop(); // maCurrentElement:'srgbClr', maPropertylist:'attributes'
}
sal_Int16 nLineTransparence(0); if (xPropSetInfo->hasPropertyByName(u"LineTransparence"_ustr))
rXPropSet->getPropertyValue(u"LineTransparence"_ustr) >>= nLineTransparence; if (nLineTransparence != 0)
{
pGrabBagStack->push(u"alpha"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addInt32(u"val"_ustr, nLineTransparence * 1000);
pGrabBagStack->pop(); // maCurrentElement: 'alpha'
pGrabBagStack->pop(); // maCurrentElement: 'srgbClr' or 'schemeClr'
}
pGrabBagStack->pop(); // maCurrentElement:'solidFill', maPropertyList:either 'srgbClr' or 'schemeClr
pGrabBagStack->pop();
} // maCurrentElement:'textOutline', maPropertyList:'attributes' and either 'noFill' or 'solidFill'
// prstDash if (eLineStyle == drawing::LineStyle_DASH)
{
pGrabBagStack->push(u"prstDash"_ustr);
OUString sPrstDash = u"sysDot"_ustr;
drawing::LineDash aLineDash; if (xPropSetInfo->hasPropertyByName(u"LineDash"_ustr)
&& (rXPropSet->getPropertyValue(u"LineDash"_ustr) >>= aLineDash))
{ // The outline of abc-transform in Word is not able to use custDash. But we know the line // is dashed. We keep "sysDot" as fallback in case no prstDash is detected.
FontworkHelpers::createPrstDashFromLineDash(aLineDash, eLineCap, sPrstDash);
} else
{ // ToDo: There may be a named dash style, but that is unlikely for Fontwork shapes. So // I skip it for now and use the "sysDot" fallback.
}
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addString(u"val"_ustr, sPrstDash);
pGrabBagStack->pop(); // maCurrentElement:'prstDash'
pGrabBagStack->pop(); // maCurrentElement:'textOutline'
} // maCurrentElement:'textOutline', maPropertyList:'attributes', either 'noFill' or 'solidFill', // and maybe 'prstDash'.
// LineJoint, can be 'round', 'bevel' or 'miter' in Word
drawing::LineJoint eLineJoint = drawing::LineJoint_NONE; if (xPropSetInfo->hasPropertyByName(u"LineJoint"_ustr))
rXPropSet->getPropertyValue(u"LineJoint"_ustr) >>= eLineJoint; if (eLineJoint == drawing::LineJoint_NONE || eLineJoint == drawing::LineJoint_BEVEL)
pGrabBagStack->appendElement(u"bevel"_ustr, uno::Any()); elseif (eLineJoint == drawing::LineJoint_ROUND)
pGrabBagStack->appendElement(u"round"_ustr, uno::Any()); else// MITER or deprecated MIDDLE
{
pGrabBagStack->push(u"miter"_ustr);
pGrabBagStack->push(u"attributes"_ustr);
pGrabBagStack->addInt32(u"lim"_ustr, 0); // As of Feb. 2023 LO cannot render other values.
pGrabBagStack->pop(); // maCurrentElement:'attributes'
pGrabBagStack->pop(); // maCurrentElement:'miter'
} // maCurrentElement:'textOutline', maPropertyList:'attributes', either 'noFill' or // 'solidFill', maybe 'prstDash', and either 'bevel', 'round' or 'miter'.
// resolve the stack and put resulting PropertyValue into the update vector
beans::PropertyValue aCharTextOutlineTextEffect;
aCharTextOutlineTextEffect.Name = "CharTextOutlineTextEffect";
aCharTextOutlineTextEffect.Value <<= pGrabBagStack->getRootProperty();
rUpdatePropVec.push_back(aCharTextOutlineTextEffect);
// CharThemeOriginalColor, CharThemeColor, and CharThemeColorShade or CharThemeColorTint will be // used for <w:color> element. That is evaluated by applications, which do not understand w14 // namespace, or if w14:textFill is omitted.
model::ComplexColor aComplexColor; if (FontworkHelpers::getThemeColorFromShape(u"FillComplexColor"_ustr, rXPropSet, aComplexColor))
{ // CharThemeColor
beans::PropertyValue aCharThemeColor;
aCharThemeColor.Name = u"CharThemeColor"_ustr;
aCharThemeColor.Value <<= lcl_getWMarkupStringForThemeColor(aComplexColor);
rUpdatePropVec.push_back(aCharThemeColor);
// CharThemeColorShade or CharThemeColorTint // MS Office uses themeTint and themeShade on the luminance in a HSL color space, see 2.1.72 // in [MS-OI29500]. That is different from OOXML specification. // We made two assumption here: (1) If LumOff exists and is not zero, it is a 'tint'. // (2) LumMod + LumOff == 10000;
sal_Int16 nLumMod; if (lcl_getThemeColorTransformationValue(aComplexColor, model::TransformationType::LumMod,
nLumMod))
{
sal_Int16 nLumOff; bool bIsTint = lcl_getThemeColorTransformationValue(
aComplexColor, model::TransformationType::LumOff, nLumOff)
&& nLumOff != 0;
sal_uInt8 nValue
= std::clamp<sal_uInt8>(lround(double(nLumMod) * 255.0 / 10000.0), 0, 255);
OUString sValue = OUString::number(nValue, 16);
beans::PropertyValue aCharThemeTintOrShade;
aCharThemeTintOrShade.Name = bIsTint ? u"CharThemeColorTint" : u"CharThemeColorShade";
aCharThemeTintOrShade.Value <<= sValue;
rUpdatePropVec.push_back(aCharThemeTintOrShade);
}
} // ToDo: Are FillColorLumMod, FillColorLumOff and FillColorTheme possible without // FillComplexColor? If yes, we need an 'else' part here.
void FontworkHelpers::applyUpdatesToCharInteropGrabBag( const std::vector<beans::PropertyValue>& rUpdatePropVec, const uno::Reference<text::XText>& rXText)
{ if (!rXText.is()) return;
uno::Reference<text::XTextCursor> rXTextCursor = rXText->createTextCursor();
rXTextCursor->gotoStart(false);
rXTextCursor->gotoEnd(true);
uno::Reference<container::XEnumerationAccess> paraEnumAccess(rXText, 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;
// Now apply the updates to the CharInteropGrabBag of this run
uno::Sequence<beans::PropertyValue> aCharInteropGrabBagSeq; if (xRunPropSetInfo->hasPropertyByName(u"CharInteropGrabBag"_ustr))
xRunPropSet->getPropertyValue(u"CharInteropGrabBag"_ustr)
>>= aCharInteropGrabBagSeq;
comphelper::SequenceAsHashMap aGrabBagMap(aCharInteropGrabBagSeq); for (constauto& rProp : rUpdatePropVec)
{
aGrabBagMap[rProp.Name] = rProp.Value; // [] inserts if not exists
}
xRunPropSet->setPropertyValue(u"CharInteropGrabBag"_ustr,
uno::Any(aGrabBagMap.getAsConstPropertyValueList()));
}
}
}
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.