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/namespacemap.hxx>
32 #include <xmloff/xmlement.hxx>
33 #include <xmloff/xmlexp.hxx>
34 #include <xmloff/xmlimp.hxx>
35 #include <xmloff/xmlnamespace.hxx>
36 #include <xmloff/xmltkmap.hxx>
37 #include <xmloff/xmluconv.hxx>
38 #include <docmodel/uno/UnoGradientTools.hxx>
40 using namespace ::com::sun::star
;
42 using namespace ::xmloff::token
;
44 SvXMLEnumMapEntry
<awt::GradientStyle
> const pXML_GradientStyle_Enum
[] =
46 { XML_LINEAR
, awt::GradientStyle_LINEAR
},
47 { XML_GRADIENTSTYLE_AXIAL
, awt::GradientStyle_AXIAL
},
48 { XML_GRADIENTSTYLE_RADIAL
, awt::GradientStyle_RADIAL
},
49 { XML_GRADIENTSTYLE_ELLIPSOID
, awt::GradientStyle_ELLIPTICAL
},
50 { XML_GRADIENTSTYLE_SQUARE
, awt::GradientStyle_SQUARE
},
51 { XML_GRADIENTSTYLE_RECTANGULAR
, awt::GradientStyle_RECT
},
52 { XML_TOKEN_INVALID
, awt::GradientStyle(0) }
57 XMLTransGradientStyleImport::XMLTransGradientStyleImport( SvXMLImport
& rImp
)
62 void XMLTransGradientStyleImport::importXML(
63 const uno::Reference
< xml::sax::XFastAttributeList
>& xAttrList
,
67 OUString aDisplayName
;
69 awt::Gradient2 aGradient
;
70 aGradient
.XOffset
= 0;
71 aGradient
.YOffset
= 0;
72 aGradient
.StartIntensity
= 100;
73 aGradient
.EndIntensity
= 100;
77 for (auto &aIter
: sax_fastparser::castToFastAttributeList( xAttrList
))
81 switch( aIter
.getToken() )
83 case XML_ELEMENT(DRAW
, XML_NAME
):
85 rStrName
= aIter
.toString();
88 case XML_ELEMENT(DRAW
, XML_DISPLAY_NAME
):
90 aDisplayName
= aIter
.toString();
93 case XML_ELEMENT(DRAW
, XML_STYLE
):
95 SvXMLUnitConverter::convertEnum( aGradient
.Style
, aIter
.toView(), pXML_GradientStyle_Enum
);
98 case XML_ELEMENT(DRAW
, XML_CX
):
99 ::sax::Converter::convertPercent( nTmpValue
, aIter
.toView() );
100 aGradient
.XOffset
= sal::static_int_cast
< sal_Int16
>(nTmpValue
);
102 case XML_ELEMENT(DRAW
, XML_CY
):
103 ::sax::Converter::convertPercent( nTmpValue
, aIter
.toView() );
104 aGradient
.YOffset
= sal::static_int_cast
< sal_Int16
>(nTmpValue
);
106 case XML_ELEMENT(DRAW
, XML_START
):
108 sal_Int32 aStartTransparency
;
109 ::sax::Converter::convertPercent( aStartTransparency
, aIter
.toView() );
111 sal_uInt8 n
= sal::static_int_cast
< sal_uInt8
>(
112 ( (100 - aStartTransparency
) * 255 ) / 100 );
114 Color
aColor( n
, n
, n
);
115 aGradient
.StartColor
= static_cast<sal_Int32
>( aColor
);
118 case XML_ELEMENT(DRAW
, XML_END
):
120 sal_Int32 aEndTransparency
;
121 ::sax::Converter::convertPercent( aEndTransparency
, aIter
.toView() );
123 sal_uInt8 n
= sal::static_int_cast
< sal_uInt8
>(
124 ( (100 - aEndTransparency
) * 255 ) / 100 );
126 Color
aColor( n
, n
, n
);
127 aGradient
.EndColor
= static_cast<sal_Int32
>( aColor
);
130 case XML_ELEMENT(DRAW
, XML_GRADIENT_ANGLE
):
132 auto const cmp12(rImport
.GetODFVersion().compareTo(ODFVER_012_TEXT
));
133 bool const bSuccess
=
134 ::sax::Converter::convertAngle(aGradient
.Angle
, aIter
.toView(),
135 // tdf#89475 try to detect borked OOo angles
136 (cmp12
< 0) || (cmp12
== 0
137 && (rImport
.isGeneratorVersionOlderThan(SvXMLImport::AOO_4x
, SvXMLImport::LO_7x
)
138 // also for AOO 4.x, assume there won't ever be a 4.2
139 || rImport
.getGeneratorVersion() == SvXMLImport::AOO_4x
)));
140 SAL_INFO_IF(!bSuccess
, "xmloff.style", "failed to import draw:angle");
143 case XML_ELEMENT(DRAW
, XML_BORDER
):
144 ::sax::Converter::convertPercent( nTmpValue
, aIter
.toView() );
145 aGradient
.Border
= sal::static_int_cast
< sal_Int16
>(nTmpValue
);
149 XMLOFF_WARN_UNKNOWN("xmloff.style", aIter
);
153 rValue
<<= aGradient
;
155 if( !aDisplayName
.isEmpty() )
157 rImport
.AddStyleDisplayName( XmlStyleFamily::SD_GRADIENT_ID
, rStrName
,
159 rStrName
= aDisplayName
;
165 XMLTransGradientStyleExport::XMLTransGradientStyleExport( SvXMLExport
& rExp
)
170 void XMLTransGradientStyleExport::exportXML(
171 const OUString
& rStrName
,
172 const uno::Any
& rValue
)
174 // MCGR: We try to write the gradient so, that applications without multi-color gradient support
175 // can render it as best as possible.
176 // This is similar to XMLGradientStyleExport::exportXML(). For details see there.
177 if( rStrName
.isEmpty() )
179 if (!rValue
.has
<css::awt::Gradient2
>() && !rValue
.has
<css::awt::Gradient
>())
182 basegfx::BGradient aGradient
= model::gradient::getFromAny(rValue
);
184 aGradient
.tryToConvertToAxial();
186 aGradient
.tryToRecreateBorder(nullptr);
192 if (!SvXMLUnitConverter::convertEnum(aOut
, aGradient
.GetGradientStyle(),
193 pXML_GradientStyle_Enum
))
197 bool bEncoded
= false;
198 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_NAME
,
199 rExport
.EncodeStyleName( rStrName
,
202 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_DISPLAY_NAME
,
205 aStrValue
= aOut
.makeStringAndClear();
206 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_STYLE
, aStrValue
);
209 if (awt::GradientStyle_LINEAR
!= aGradient
.GetGradientStyle()
210 && awt::GradientStyle_AXIAL
!= aGradient
.GetGradientStyle())
212 ::sax::Converter::convertPercent(aOut
, aGradient
.GetXOffset());
213 aStrValue
= aOut
.makeStringAndClear();
214 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_CX
, aStrValue
);
216 ::sax::Converter::convertPercent(aOut
, aGradient
.GetYOffset());
217 aStrValue
= aOut
.makeStringAndClear();
218 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_CY
, aStrValue
);
221 // LO uses a gray color as transparency. ODF uses opacity in range [0%,100%].
222 // Default 100% opacity.
223 double fOpacityStartPerc
= 100.0;
224 double fOpacityEndPerc
= 100.0;
225 if (!aGradient
.GetColorStops().empty())
228 = (1.0 - aGradient
.GetColorStops().front().getStopColor().getRed()) * 100.0;
229 fOpacityEndPerc
= (1.0 - aGradient
.GetColorStops().back().getStopColor().getRed()) * 100.0;
233 ::sax::Converter::convertPercent(aOut
, static_cast<sal_Int32
>(std::lround(fOpacityStartPerc
)));
234 aStrValue
= aOut
.makeStringAndClear();
235 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_START
, aStrValue
);
238 ::sax::Converter::convertPercent( aOut
, static_cast<sal_Int32
>(std::lround(fOpacityEndPerc
)));
239 aStrValue
= aOut
.makeStringAndClear();
240 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_END
, aStrValue
);
243 if (awt::GradientStyle_RADIAL
!= aGradient
.GetGradientStyle())
245 ::sax::Converter::convertAngle(aOut
, aGradient
.GetAngle().get(),
246 rExport
.getSaneDefaultVersion());
247 aStrValue
= aOut
.makeStringAndClear();
248 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_GRADIENT_ANGLE
, aStrValue
);
252 ::sax::Converter::convertPercent(aOut
, aGradient
.GetBorder());
253 aStrValue
= aOut
.makeStringAndClear();
254 rExport
.AddAttribute( XML_NAMESPACE_DRAW
, XML_BORDER
, aStrValue
);
256 // ctor writes start tag. End-tag is written by destructor at block end.
257 SvXMLElementExport
rElem(rExport
, XML_NAMESPACE_DRAW
, XML_OPACITY
, true, false);
259 // Write child elements <loext:opacity-stop>
260 // Do not export in standard ODF 1.3 or older.
261 if ((rExport
.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED
) == 0)
263 if (aGradient
.GetColorStops().empty())
266 double fPreviousOffset
= 0.0;
267 for (auto& aCandidate
: aGradient
.GetColorStops())
269 // Attribute svg:offset. Make sure offsets are increasing.
270 double fOffset
= std::clamp
<double>(aCandidate
.getStopOffset(), 0.0, 1.0);
271 if (fOffset
< fPreviousOffset
)
272 fOffset
= fPreviousOffset
;
273 rExport
.AddAttribute(XML_NAMESPACE_SVG
, XML_OFFSET
, OUString::number(fOffset
));
274 fPreviousOffset
= fOffset
;
276 // Attribute svg:stop-opacity, data type zeroToOneDecimal
277 double fOpacity
= std::clamp
<double>(1.0 - aCandidate
.getStopColor().getRed(), 0.0, 1.0);
278 rExport
.AddAttribute(XML_NAMESPACE_SVG
, XML_STOP_OPACITY
, OUString::number(fOpacity
));
280 // write opacity stop element
281 SvXMLElementExport
aStopElement(rExport
, XML_NAMESPACE_LO_EXT
, XML_OPACITY_STOP
, true, true);
285 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */