1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <TransGradientStyle.hxx>
22 #include <com/sun/star/awt/Gradient2.hpp>
24 #include <basegfx/utils/bgradient.hxx>
25 #include <comphelper/documentconstants.hxx>
26 #include <rtl/ustrbuf.hxx>
27 #include <rtl/ustring.hxx>
28 #include <sal/log.hxx>
29 #include <sax/tools/converter.hxx>
30 #include <tools/color.hxx>
31 #include <xmloff/xmlement.hxx>
32 #include <xmloff/xmlexp.hxx>
33 #include <xmloff/xmlimp.hxx>
34 #include <xmloff/xmlnamespace.hxx>
35 #include <xmloff/xmluconv.hxx>
36 #include <docmodel/uno/UnoGradientTools.hxx>
38 using namespace ::com::sun::star
;
40 using namespace ::xmloff::token
;
42 SvXMLEnumMapEntry
<awt::GradientStyle
> const pXML_GradientStyle_Enum
[] =
44 { XML_LINEAR
, awt::GradientStyle_LINEAR
},
45 { XML_GRADIENTSTYLE_AXIAL
, awt::GradientStyle_AXIAL
},
46 { XML_GRADIENTSTYLE_RADIAL
, awt::GradientStyle_RADIAL
},
47 { XML_GRADIENTSTYLE_ELLIPSOID
, awt::GradientStyle_ELLIPTICAL
},
48 { XML_GRADIENTSTYLE_SQUARE
, awt::GradientStyle_SQUARE
},
49 { XML_GRADIENTSTYLE_RECTANGULAR
, awt::GradientStyle_RECT
},
50 { XML_TOKEN_INVALID
, awt::GradientStyle(0) }
55 XMLTransGradientStyleImport::XMLTransGradientStyleImport( SvXMLImport
& rImp
)
60 void XMLTransGradientStyleImport::importXML(
61 const uno::Reference
< xml::sax::XFastAttributeList
>& xAttrList
,
65 OUString aDisplayName
;
67 awt::Gradient2 aGradient
;
68 aGradient
.XOffset
= 0;
69 aGradient
.YOffset
= 0;
70 aGradient
.StartIntensity
= 100;
71 aGradient
.EndIntensity
= 100;
75 for (auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
))
79 switch( aIter
.getToken() )
81 case XML_ELEMENT(DRAW
, XML_NAME
):
83 rStrName
= aIter
.toString();
86 case XML_ELEMENT(DRAW
, XML_DISPLAY_NAME
):
88 aDisplayName
= aIter
.toString();
91 case XML_ELEMENT(DRAW
, XML_STYLE
):
93 SvXMLUnitConverter::convertEnum( aGradient
.Style
, aIter
.toView(), pXML_GradientStyle_Enum
);
96 case XML_ELEMENT(DRAW
, XML_CX
):
97 ::sax::Converter::convertPercent( nTmpValue
, aIter
.toView() );
98 aGradient
.XOffset
= sal::static_int_cast
< sal_Int16
>(nTmpValue
);
100 case XML_ELEMENT(DRAW
, XML_CY
):
101 ::sax::Converter::convertPercent( nTmpValue
, aIter
.toView() );
102 aGradient
.YOffset
= sal::static_int_cast
< sal_Int16
>(nTmpValue
);
104 case XML_ELEMENT(DRAW
, XML_START
):
106 sal_Int32 aStartTransparency
;
107 ::sax::Converter::convertPercent( aStartTransparency
, aIter
.toView() );
109 sal_uInt8 n
= sal::static_int_cast
< sal_uInt8
>(
110 ( (100 - aStartTransparency
) * 255 ) / 100 );
112 Color
aColor( n
, n
, n
);
113 aGradient
.StartColor
= static_cast<sal_Int32
>( aColor
);
116 case XML_ELEMENT(DRAW
, XML_END
):
118 sal_Int32 aEndTransparency
;
119 ::sax::Converter::convertPercent( aEndTransparency
, aIter
.toView() );
121 sal_uInt8 n
= sal::static_int_cast
< sal_uInt8
>(
122 ( (100 - aEndTransparency
) * 255 ) / 100 );
124 Color
aColor( n
, n
, n
);
125 aGradient
.EndColor
= static_cast<sal_Int32
>( aColor
);
128 case XML_ELEMENT(DRAW
, XML_GRADIENT_ANGLE
):
130 auto const cmp12(rImport
.GetODFVersion().compareTo(ODFVER_012_TEXT
));
131 // tdf#89475 try to detect borked OOo angles
132 bool const bIsWrongOOo10thDegAngle(
135 && (rImport
.isGeneratorVersionOlderThan(SvXMLImport::AOO_4x
,
137 // also for AOO 4.x, assume there won't ever be a 4.2
138 || rImport
.getGeneratorVersion() == SvXMLImport::AOO_4x
)));
140 bool const bSuccess
= ::sax::Converter::convert10thDegAngle(
141 nAngle
, aIter
.toView(), bIsWrongOOo10thDegAngle
);
143 { // limit to valid range [0..3600[
144 nAngle
= nAngle
% 3600;
147 aGradient
.Angle
= nAngle
;
149 SAL_INFO_IF(!bSuccess
, "xmloff.style", "failed to import draw:angle");
152 case XML_ELEMENT(DRAW
, XML_BORDER
):
153 ::sax::Converter::convertPercent( nTmpValue
, aIter
.toView() );
154 aGradient
.Border
= sal::static_int_cast
< sal_Int16
>(nTmpValue
);
158 XMLOFF_WARN_UNKNOWN("xmloff.style", aIter
);
162 rValue
<<= aGradient
;
164 if( !aDisplayName
.isEmpty() )
166 rImport
.AddStyleDisplayName( XmlStyleFamily::SD_GRADIENT_ID
, rStrName
,
168 rStrName
= aDisplayName
;
174 XMLTransGradientStyleExport::XMLTransGradientStyleExport( SvXMLExport
& rExp
)
179 void XMLTransGradientStyleExport::exportXML(
180 const OUString
& rStrName
,
181 const uno::Any
& rValue
)
183 // MCGR: We try to write the gradient so, that applications without multi-color gradient support
184 // can render it as best as possible.
185 // This is similar to XMLGradientStyleExport::exportXML(). For details see there.
186 if( rStrName
.isEmpty() )
188 if (!rValue
.has
<css::awt::Gradient2
>() && !rValue
.has
<css::awt::Gradient
>())
191 basegfx::BGradient aGradient
= model::gradient::getFromAny(rValue
);
193 aGradient
.tryToConvertToAxial();
195 aGradient
.tryToRecreateBorder(nullptr);
201 if (!SvXMLUnitConverter::convertEnum(aOut
, aGradient
.GetGradientStyle(),
202 pXML_GradientStyle_Enum
))
206 bool bEncoded
= false;
207 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_NAME
,
208 rExport
.EncodeStyleName( rStrName
,
211 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_DISPLAY_NAME
,
214 aStrValue
= aOut
.makeStringAndClear();
215 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_STYLE
, aStrValue
);
218 if (awt::GradientStyle_LINEAR
!= aGradient
.GetGradientStyle()
219 && awt::GradientStyle_AXIAL
!= aGradient
.GetGradientStyle())
221 ::sax::Converter::convertPercent(aOut
, aGradient
.GetXOffset());
222 aStrValue
= aOut
.makeStringAndClear();
223 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_CX
, aStrValue
);
225 ::sax::Converter::convertPercent(aOut
, aGradient
.GetYOffset());
226 aStrValue
= aOut
.makeStringAndClear();
227 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_CY
, aStrValue
);
230 // LO uses a gray color as transparency. ODF uses opacity in range [0%,100%].
231 // Default 100% opacity.
232 double fOpacityStartPerc
= 100.0;
233 double fOpacityEndPerc
= 100.0;
234 if (!aGradient
.GetColorStops().empty())
237 = (1.0 - aGradient
.GetColorStops().front().getStopColor().getRed()) * 100.0;
238 fOpacityEndPerc
= (1.0 - aGradient
.GetColorStops().back().getStopColor().getRed()) * 100.0;
242 ::sax::Converter::convertPercent(aOut
, static_cast<sal_Int32
>(std::lround(fOpacityStartPerc
)));
243 aStrValue
= aOut
.makeStringAndClear();
244 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_START
, aStrValue
);
247 ::sax::Converter::convertPercent( aOut
, static_cast<sal_Int32
>(std::lround(fOpacityEndPerc
)));
248 aStrValue
= aOut
.makeStringAndClear();
249 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_END
, aStrValue
);
252 if (awt::GradientStyle_RADIAL
!= aGradient
.GetGradientStyle())
254 // true: wrong, but backward compatible with OOo/LO < 4.4
255 // false: OFFICE-3774 tdf#89475 write valid ODF 1.2 angle; needs LO 4.4 to import
256 bool bIsWrongOOo10thDegAngle(rExport
.getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012
257 || rExport
.getSaneDefaultVersion()
258 == SvtSaveOptions::ODFSVER_012_EXT_COMPAT
);
259 ::sax::Converter::convert10thDegAngle(aOut
, aGradient
.GetAngle().get(),
260 bIsWrongOOo10thDegAngle
);
261 aStrValue
= aOut
.makeStringAndClear();
262 rExport
.AddAttribute(XML_NAMESPACE_DRAW
, XML_GRADIENT_ANGLE
, aStrValue
);
266 ::sax::Converter::convertPercent(aOut
, aGradient
.GetBorder());
267 aStrValue
= aOut
.makeStringAndClear();
268 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_BORDER
, aStrValue
);
270 // ctor writes start tag. End-tag is written by destructor at block end.
271 SvXMLElementExport
rElem(rExport
, XML_NAMESPACE_DRAW
, XML_OPACITY
, true, false);
273 // Write child elements <loext:opacity-stop>
274 // Do not export in standard ODF 1.3 or older.
275 if ((rExport
.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED
) == 0)
277 if (aGradient
.GetColorStops().empty())
280 double fPreviousOffset
= 0.0;
281 for (auto& aCandidate
: aGradient
.GetColorStops())
283 // Attribute svg:offset. Make sure offsets are increasing.
284 double fOffset
= std::clamp
<double>(aCandidate
.getStopOffset(), 0.0, 1.0);
285 if (fOffset
< fPreviousOffset
)
286 fOffset
= fPreviousOffset
;
287 rExport
.AddAttribute(XML_NAMESPACE_SVG
, XML_OFFSET
, OUString::number(fOffset
));
288 fPreviousOffset
= fOffset
;
290 // Attribute svg:stop-opacity, data type zeroToOneDecimal
291 double fOpacity
= std::clamp
<double>(1.0 - aCandidate
.getStopColor().getRed(), 0.0, 1.0);
292 rExport
.AddAttribute(XML_NAMESPACE_SVG
, XML_STOP_OPACITY
, OUString::number(fOpacity
));
294 // write opacity stop element
295 SvXMLElementExport
aStopElement(rExport
, XML_NAMESPACE_LO_EXT
, XML_OPACITY_STOP
, true, true);
299 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */