/* -*- 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/. * * 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 .
*/
// Write the focus rectangle. Work with the focus point, and assume // that it extends 50% in all directions. The below // left/top/right/bottom values are percentages, where 0 means the // edge of the tile rectangle and 100% means the center of it.
rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList(
sax_fastparser::FastSerializerHelper::createAttrList());
sal_Int32 nLeftPercent = rBGradient.GetXOffset();
pAttributeList->add(XML_l, OString::number(nLeftPercent * PER_PERCENT));
sal_Int32 nTopPercent = rBGradient.GetYOffset();
pAttributeList->add(XML_t, OString::number(nTopPercent * PER_PERCENT));
sal_Int32 nRightPercent = 100 - rBGradient.GetXOffset();
pAttributeList->add(XML_r, OString::number(nRightPercent * PER_PERCENT));
sal_Int32 nBottomPercent = 100 - rBGradient.GetYOffset();
pAttributeList->add(XML_b, OString::number(nBottomPercent * PER_PERCENT));
pFS->singleElementNS(XML_a, XML_fillToRect, pAttributeList);
bool DrawingML::GetProperty( const Reference< XPropertySet >& rXPropertySet, const OUString& aName )
{ try
{
mAny = rXPropertySet->getPropertyValue(aName); if (mAny.hasValue()) returntrue;
} catch( const Exception& )
{ /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
} returnfalse;
}
bool DrawingML::GetPropertyAndState( const Reference< XPropertySet >& rXPropertySet, const Reference< XPropertyState >& rXPropertyState, const OUString& aName, PropertyState& eState )
{ try
{
mAny = rXPropertySet->getPropertyValue(aName); if (mAny.hasValue())
{
eState = rXPropertyState->getPropertyState(aName); returntrue;
}
} catch( const Exception& )
{ /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
} returnfalse;
}
namespace
{ /// Gets hexa value of color on string format.
OString getColorStr(const ::Color nColor)
{ // Transparency is a separate element.
OString sColor = OString::number(sal_uInt32(nColor) & 0x00FFFFFF, 16); if (sColor.getLength() < 6)
{
OStringBuffer sBuf("0"); int remains = 5 - sColor.getLength();
while (remains > 0)
{
sBuf.append("0");
remains--;
}
// OOXML has no separate transparence gradient but uses transparency in the gradient stops. // So we merge transparency and color and use gradient fill in such case.
basegfx::BGradient aTransparenceGradient;
OUString sFillTransparenceGradientName; bool bNeedGradientFill(false);
// we no longer need to 'guess' if FillTransparenceGradient is used by // comparing it's 1st color to COL_BLACK after having tested that the // FillTransparenceGradientName is set if (!bNeedGradientFill)
{ // Our alpha is a gray color value. const sal_uInt8 nRed(aSingleColor.getRed() * 255.0);
// drawingML alpha is a percentage on a 0..100000 scale.
nAlpha = (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
}
}
// write XML if (bNeedGradientFill)
{ // no longer create copy/PseudoColorGradient, use new API of // WriteGradientFill to express fix fill color
mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
WriteGradientFill(nullptr, nFillColor, &aTransparenceGradient);
mpFS->endElementNS( XML_a, XML_gradFill );
} elseif ( nFillColor != nOriginalColor )
{ // the user has set a different color for the shape if (!WriteSchemeColor(u"FillComplexColor"_ustr, rXPropSet))
{
WriteSolidFill(::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha);
}
} // tdf#91332 LO doesn't export the actual theme.xml in XLSX. elseif ( !sColorFillScheme.isEmpty() && GetDocumentType() != DOCUMENT_XLSX )
{ // the shape had a scheme color and the user didn't change it
WriteSolidFill( sColorFillScheme, aTransformations, nAlpha );
} else
{ // the shape had a custom color and the user didn't change it // tdf#124013
WriteSolidFill( ::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha );
}
}
// use BGradient constructor directly, it will take care of Gradient/Gradient2
basegfx::BGradient aGradient = model::gradient::getFromAny(mAny);
// get InteropGrabBag and search the relevant attributes
basegfx::BGradient aOriginalGradient;
Sequence< PropertyValue > aGradientStops; if ( GetProperty( rXPropSet, u"InteropGrabBag"_ustr ) )
{
Sequence< PropertyValue > aGrabBag;
mAny >>= aGrabBag; for (constauto& rProp : aGrabBag) if( rProp.Name == "GradFillDefinition" )
rProp.Value >>= aGradientStops; elseif( rProp.Name == "OriginalGradFill" )
aOriginalGradient = model::gradient::getFromAny(rProp.Value);
}
// check if an ooxml gradient had been imported and if the user has modified it // Gradient grab-bag depends on theme grab-bag, which is implemented // only for DOCX. if (aOriginalGradient == aGradient && GetDocumentType() == DOCUMENT_DOCX)
{ // If we have no gradient stops that means original gradient were defined by a theme. if( aGradientStops.hasElements() )
{
mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
WriteGrabBagGradientFill(aGradientStops, aGradient);
mpFS->endElementNS( XML_a, XML_gradFill );
}
} else
{
mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
if (GetProperty(rXPropSet, u"FillTransparenceGradientName"_ustr)
&& (mAny >>= sFillTransparenceGradientName)
&& !sFillTransparenceGradientName.isEmpty()
&& GetProperty(rXPropSet, u"FillTransparenceGradient"_ustr))
{ // TransparenceGradient is only used when name is not empty
aTransparenceGradient = model::gradient::getFromAny(mAny);
pTransparenceGradient = &aTransparenceGradient;
} elseif (GetProperty(rXPropSet, u"FillTransparence"_ustr))
{ // no longer create PseudoTransparencyGradient, use new API of // WriteGradientFill to express fix transparency
sal_Int32 nTransparency(0);
mAny >>= nTransparency; // nTransparency is [0..100]%
fTransparency = nTransparency * 0.01;
}
// tdf#155852 The gradient might wrongly have StepCount==0, as the draw:gradient-step-count // attribute in ODF does not belong to the gradient definition but is an attribute in // the graphic style of the shape. if (GetProperty(rXPropSet, u"FillGradientStepCount"_ustr))
{
sal_Int16 nStepCount = 0;
mAny >>= nStepCount;
aGradient.SetSteps(nStepCount);
}
if (nullptr != pColorGradient)
{ // extract and correct/process ColorStops
basegfx::utils::prepareColorStops(*pColorGradient, aColorStops, aSingleColor);
// tdf#155827 Convert 'axial' to 'linear' before synchronize and for each gradient separate. if (aColorStops.size() > 0 && awt::GradientStyle_AXIAL == pColorGradient->GetGradientStyle())
aColorStops.doApplyAxial();
} if (nullptr != pTransparenceGradient)
{ // remember basic Gradient definition to use // So we can get the gradient geometry in any case from pGradient. if (nullptr == pGradient)
{
pGradient = pTransparenceGradient;
}
// extract and correct/process AlphaStops
basegfx::utils::prepareColorStops(*pTransparenceGradient, aAlphaStops, aSingleAlpha); if (aAlphaStops.size() > 0
&& awt::GradientStyle_AXIAL == pTransparenceGradient->GetGradientStyle())
{
aAlphaStops.doApplyAxial();
}
}
if (nullptr == pGradient)
{ // an error - see comment in header - is to give neither pColorGradient // nor pTransparenceGradient
assert(false && "pColorGradient or pTransparenceGradient should be set"); return;
}
// Apply steps if used. That increases the number of stops and thus needs to be done before // synchronize. if (pGradient->GetSteps())
{
aColorStops.doApplySteps(pGradient->GetSteps()); // transparency gradients are always automatic, so do not have steps.
}
// synchronize ColorStops and AlphaStops as preparation to export // so also gradients 'coupled' indirectly using the 'FillTransparenceGradient' // method (at import time) will be exported again.
basegfx::utils::synchronizeColorStops(aColorStops, aAlphaStops, aSingleColor, aSingleAlpha);
if (aColorStops.size() != aAlphaStops.size())
{ // this is an error - synchronizeColorStops above *has* to create that // state, see description there (!)
assert(false && "oox::WriteGradientFill: non-synchronized gradients (!)"); return;
}
bool bLinearOrAxial(awt::GradientStyle_LINEAR == pGradient->GetGradientStyle()
|| awt::GradientStyle_AXIAL == pGradient->GetGradientStyle()); if (!bLinearOrAxial)
{ // case awt::GradientStyle_RADIAL: // case awt::GradientStyle_ELLIPTICAL: // case awt::GradientStyle_RECT: // case awt::GradientStyle_SQUARE: // all these types need the gradients to be mirrored
aColorStops.reverseColorStops();
aAlphaStops.reverseColorStops();
}
// If there were one stop, prepareColorStops() method would have cleared aColorStops, same for // aAlphaStops. In case of empty stops vectors synchronizeColorStops() method creates two stops // for each. So at this point we have at least two stops and can fulfill the requirement of // <gsLst> element to have at least two child elements.
if (bLinearOrAxial)
{ // CT_LinearShadeProperties, cases where gradient rotation has to be exported // 'scaled' does not exist in LO, so only 'ang'. const sal_Int16 nAngle(pGradient->GetAngle());
mpFS->singleElementNS(
XML_a, XML_lin, XML_ang,
OString::number(((3600 - static_cast<sal_Int32>(nAngle) + 900) * 6000) % 21600000));
} else
{ // CT_PathShadeProperties, cases where gradient path has to be exported // Concentric fill is not yet implemented, therefore no type 'shape', only 'circle' or 'rect' constbool bCircle(pGradient->GetGradientStyle() == awt::GradientStyle_RADIAL ||
pGradient->GetGradientStyle() == awt::GradientStyle_ELLIPTICAL);
WriteGradientPath(*pGradient, mpFS, bCircle);
}
}
switch( nArrowLength )
{ case ESCHER_LineShortArrow:
len = "sm"; break; default: case ESCHER_LineMediumLenArrow:
len = "med"; break; case ESCHER_LineLongArrow:
len = "lg"; break;
}
switch( eLineEnd )
{ default: case ESCHER_LineNoEnd:
type = "none"; break; case ESCHER_LineArrowEnd:
type = "triangle"; break; case ESCHER_LineArrowStealthEnd:
type = "stealth"; break; case ESCHER_LineArrowDiamondEnd:
type = "diamond"; break; case ESCHER_LineArrowOvalEnd:
type = "oval"; break; case ESCHER_LineArrowOpenEnd:
type = "arrow"; break;
}
switch( nArrowWidth )
{ case ESCHER_LineNarrowArrow:
width = "sm"; break; default: case ESCHER_LineMediumWidthArrow:
width = "med"; break; case ESCHER_LineWideArrow:
width = "lg"; break;
}
[[fallthrough]]; case drawing::LineStyle_SOLID: default: if (GetProperty(rXPropSet, u"LineColor"_ustr))
{
nColor = ::Color(ColorTransparency, mAny.get<sal_uInt32>() & 0xffffff);
bColorSet = true;
} if (GetProperty(rXPropSet, u"LineTransparence"_ustr))
{
nColorAlpha = MAX_PERCENT - (mAny.get<sal_Int16>() * PER_PERCENT);
} if (aLineCap == LineCap_ROUND)
cap = "rnd"; elseif (aLineCap == LineCap_SQUARE)
cap = "sq"; break;
}
// if the line-width was not modified after importing then the original EMU value will be exported to avoid unexpected conversion (rounding) error if (nEmuLineWidth == 0 || static_cast<sal_uInt32>(oox::drawingml::convertEmuToHmm(nEmuLineWidth)) != nLineWidth)
nEmuLineWidth = oox::drawingml::convertHmmToEmu(nLineWidth);
mpFS->startElementNS( XML_a, XML_ln,
XML_cap, cap,
XML_w, sax_fastparser::UseIf(OString::number(nEmuLineWidth),
nLineWidth == 0 || GetDocumentType() == DOCUMENT_XLSX // tdf#119565 LO doesn't export the actual theme.xml in XLSX.
|| (nLineWidth > 1 && nStyleLineWidth != nLineWidth)));
if( bColorSet )
{ if( nColor != nOriginalColor )
{ // the user has set a different color for the line if (!WriteSchemeColor(u"LineComplexColor"_ustr, rXPropSet))
WriteSolidFill(nColor, nColorAlpha);
} elseif( !sColorFillScheme.isEmpty() )
{ // the line had a scheme color and the user didn't change it
WriteSolidFill( aResolvedColorFillScheme, aTransformations );
} else
{
WriteSolidFill( nColor, nColorAlpha );
}
}
if( bDashSet && aStyleLineStyle != drawing::LineStyle_DASH )
{ // Try to detect if it might come from ms preset line style import. // MS Office styles are always relative, both binary and OOXML. // "dot" is always the first dash and "dash" the second one. All OOXML presets linestyles // start with the longer one. Definitions are in OOXML part 1, 20.1.10.49 // The tests are strict, for to not catch styles from standard.sod (as of Aug 2019). bool bIsConverted = false;
bool bIsRelative(aLineDash.Style == DashStyle_RECTRELATIVE || aLineDash.Style == DashStyle_ROUNDRELATIVE); if ( bIsRelative && aLineDash.Dots == 1)
{ // The length were tweaked on import in case of prstDash. Revert it here.
sal_uInt32 nDotLen = aLineDash.DotLen;
sal_uInt32 nDashLen = aLineDash.DashLen;
sal_uInt32 nDistance = aLineDash.Distance; if (aLineCap != 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 && aLineDash.Dashes > 0)
nDashLen = 100;
bIsConverted = true; if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot");
} elseif (nDotLen == 400 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
} elseif (nDotLen == 400 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dashDot");
} elseif (nDotLen == 800 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDash");
} elseif (nDotLen == 800 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDot");
} elseif (nDotLen == 800 && aLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 300)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDotDot");
} elseif (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot");
} elseif (nDotLen == 300 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash");
} elseif (nDotLen == 300 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 100)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDot");
} elseif (nDotLen == 300 && aLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 100)
{
mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot");
} else
bIsConverted = false;
} // Do not map our own line styles to OOXML prstDash values, because custDash gives better results. if (!bIsConverted)
{
mpFS->startElementNS(XML_a, XML_custDash); // In case of hairline we would need the current pixel size. Instead use a reasonable // ersatz for it. The value is the same as SMALLEST_DASH_WIDTH in xattr.cxx. // (And it makes sure fLineWidth is not zero in below division.) double fLineWidth = nLineWidth > 0 ? nLineWidth : 26.95; int i; double fSp = bIsRelative ? aLineDash.Distance : aLineDash.Distance * 100.0 / fLineWidth; // LO uses line width, in case Distance is zero. MS Office would use a space of zero length. // So set 100% explicitly. if (aLineDash.Distance <= 0)
fSp = 100.0; // In case of custDash, round caps are included in dash length in MS Office. Square caps are added // to dash length, same as in ODF. Change the length values accordingly. if (aLineCap == LineCap_ROUND && fSp > 99.0)
fSp -= 99.0;
if (aLineDash.Dots > 0)
{ double fD = bIsRelative ? aLineDash.DotLen : aLineDash.DotLen * 100.0 / fLineWidth; // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended. if (aLineDash.DotLen == 0)
fD = 100.0; // Tweak dash length, see above. if (aLineCap == LineCap_ROUND && fSp > 99.0)
fD += 99.0;
for( i = 0; i < aLineDash.Dots; i ++ )
{
mpFS->singleElementNS( XML_a, XML_ds,
XML_d , write1000thOfAPercent(fD),
XML_sp, write1000thOfAPercent(fSp) );
}
} if ( aLineDash.Dashes > 0 )
{ double fD = bIsRelative ? aLineDash.DashLen : aLineDash.DashLen * 100.0 / fLineWidth; // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended. if (aLineDash.DashLen == 0)
fD = 100.0; // Tweak dash length, see above. if (aLineCap == LineCap_ROUND && fSp > 99.0)
fD += 99.0;
for( i = 0; i < aLineDash.Dashes; i ++ )
{
mpFS->singleElementNS( XML_a , XML_ds,
XML_d , write1000thOfAPercent(fD),
XML_sp, write1000thOfAPercent(fSp) );
}
}
// tdf#119565 LO doesn't export the actual theme.xml in XLSX. if (aStyleLineJoint == LineJoint_NONE || GetDocumentType() == DOCUMENT_XLSX
|| aStyleLineJoint != eLineJoint)
{ // style-defined line joint does not exist, or is different from the shape's joint switch( eLineJoint )
{ case LineJoint_NONE: case LineJoint_BEVEL:
mpFS->singleElementNS(XML_a, XML_bevel); break; default: case LineJoint_MIDDLE: case LineJoint_MITER:
mpFS->singleElementNS(XML_a, XML_miter); break; case LineJoint_ROUND:
mpFS->singleElementNS(XML_a, XML_round); break;
}
}
}
// #i15508# added BMP type for better exports // export not yet active, so adding for reference (not checked) case GfxLinkType::NativeBmp:
sMediaType = u"image/bmp"_ustr;
aExtension = u"bmp"_ustr; break;
/*Earlier, even in case of unhandled graphic types we were proceeding to write the image, which would eventually write an empty image with a zero size, and return a valid relationID, which is incorrect.
*/ return OUString();
}
namespace
{
BitmapChecksum makeChecksumUniqueForSVG(BitmapChecksum aChecksum)
{ // need to modify the checksum so we know it's for SVG - just invert it return ~aChecksum;
}
// extension
OUString aExtension; const OUString& rURL(pMediaObj->getURL()); int nLastDot = rURL.lastIndexOf('.'); if (nLastDot >= 0)
aExtension = rURL.copy(nLastDot).replace(':', '_'); // Colons are not allowed in Zip entry file names, see OStorageHelper::IsValidZipEntryFileName
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.