LibreOffice Module xmloff (master) 1
GradientStyle.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21
22#include <com/sun/star/awt/Gradient2.hpp>
23
25#include <rtl/ustrbuf.hxx>
26#include <rtl/ustring.hxx>
27#include <sal/log.hxx>
30#include <xmloff/xmlement.hxx>
31#include <xmloff/xmlexp.hxx>
32#include <xmloff/xmlimp.hxx>
34#include <xmloff/xmltkmap.hxx>
35#include <xmloff/xmltoken.hxx>
36#include <xmloff/xmluconv.hxx>
38
39using namespace ::com::sun::star;
40using namespace ::xmloff::token;
41
43{
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) }
51};
52
53// Import
55 SvXMLImport& rImp )
56 : m_rImport(rImp)
57{
58}
59
61 const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
62 uno::Any& rValue,
63 OUString& rStrName )
64{
65 OUString aDisplayName;
66
67 awt::Gradient2 aGradient;
68 aGradient.Style = css::awt::GradientStyle_LINEAR;
69 aGradient.StartColor = 0;
70 aGradient.EndColor = 0;
71 aGradient.Angle = 0;
72 aGradient.Border = 0;
73 aGradient.XOffset = 0;
74 aGradient.YOffset = 0;
75 aGradient.StartIntensity = 100;
76 aGradient.EndIntensity = 100;
77 aGradient.StepCount = 0;
78
79 for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
80 {
81 sal_Int32 nTmpValue(0);
82
83 switch( aIter.getToken() )
84 {
86 rStrName = aIter.toString();
87 break;
89 aDisplayName = aIter.toString();
90 break;
92 SvXMLUnitConverter::convertEnum( aGradient.Style, aIter.toView(), pXML_GradientStyle_Enum );
93 break;
94 case XML_ELEMENT(DRAW, XML_CX):
95 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
96 aGradient.XOffset = static_cast< sal_Int16 >( nTmpValue );
97 break;
98 case XML_ELEMENT(DRAW, XML_CY):
99 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
100 aGradient.YOffset = static_cast< sal_Int16 >( nTmpValue );
101 break;
103 ::sax::Converter::convertColor(aGradient.StartColor, aIter.toView());
104 break;
106 ::sax::Converter::convertColor(aGradient.EndColor, aIter.toView());
107 break;
109 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
110 aGradient.StartIntensity = static_cast< sal_Int16 >( nTmpValue );
111 break;
113 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
114 aGradient.EndIntensity = static_cast< sal_Int16 >( nTmpValue );
115 break;
117 {
118 auto const cmp12(m_rImport.GetODFVersion().compareTo(ODFVER_012_TEXT));
119 bool const bSuccess =
120 ::sax::Converter::convertAngle(aGradient.Angle, aIter.toView(),
121 // tdf#89475 try to detect borked OOo angles
122 (cmp12 < 0) || (cmp12 == 0
123 && (m_rImport.isGeneratorVersionOlderThan(SvXMLImport::AOO_4x, SvXMLImport::LO_7x)
124 // also for AOO 4.x, assume there won't ever be a 4.2
125 || m_rImport.getGeneratorVersion() == SvXMLImport::AOO_4x)));
126 SAL_INFO_IF(!bSuccess, "xmloff.style", "failed to import draw:angle");
127 }
128 break;
130 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
131 aGradient.Border = static_cast< sal_Int16 >( nTmpValue );
132 break;
133
134 default:
135 XMLOFF_WARN_UNKNOWN("xmloff.style", aIter);
136 }
137 }
138
139 rValue <<= aGradient;
140
141 if( !aDisplayName.isEmpty() )
142 {
143 m_rImport.AddStyleDisplayName( XmlStyleFamily::SD_GRADIENT_ID, rStrName,
144 aDisplayName );
145 rStrName = aDisplayName;
146 }
147}
148
150 SvXMLImport& rImport, sal_Int32 nElement,
151 const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
152 std::vector<awt::ColorStop>& rColorStopVec)
153: SvXMLImportContext(rImport)
154{
155 if(nElement != XML_ELEMENT(LO_EXT, xmloff::token::XML_GRADIENT_STOP))
156 return;
157
158 double fOffset = -1.0;
159 OUString sColorType;
160 OUString sColorValue;
161 // First collect all attributes
162 for (auto &aIter : sax_fastparser::castToFastAttributeList(xAttrList))
163 {
164 switch(aIter.getToken())
165 {
166 case XML_ELEMENT(SVG, xmloff::token::XML_OFFSET): // needed??
167 case XML_ELEMENT(SVG_COMPAT, xmloff::token::XML_OFFSET):
168 if (!::sax::Converter::convertDouble(fOffset, aIter.toView()))
169 return;
170 break;
172 sColorValue = aIter.toString();
173 if (sColorValue.isEmpty())
174 return;
175 break;
177 sColorType = aIter.toString();
178 if (sColorType.isEmpty())
179 return;
180 break;
181 default:
182 XMLOFF_WARN_UNKNOWN("xmloff.style", aIter);
183 }
184 }
185
186 // As of LO 7.6.0 only "rgb" is implemented.
187 if (sColorType != u"rgb")
188 return;
189
190 // Type "rgb" requires kind color-value="#rrggbb".
191 ::Color aColor;
192 if (!::sax::Converter::convertColor(aColor, sColorValue))
193 return;
194
195 // All attribute values OK. Generate ColorStop.
196 css::rendering::RGBColor aRGBColor;
197 aRGBColor.Red = aColor.GetRed() / 255.0;
198 aRGBColor.Green = aColor.GetGreen() / 255.0;
199 aRGBColor.Blue = aColor.GetBlue() / 255.0;
200
201 awt::ColorStop aColorStop;
202 aColorStop.StopOffset = fOffset;
203 aColorStop.StopColor = aRGBColor;
204 rColorStopVec.push_back(aColorStop);
205}
206
208{
209}
210
211// Export
212
214 SvXMLExport& rExp )
215 : m_rExport(rExp)
216{
217}
218
220 const OUString& rStrName,
221 const uno::Any& rValue )
222{
223 if( rStrName.isEmpty() )
224 return;
225
226 if (!rValue.has<css::awt::Gradient2>() && !rValue.has<css::awt::Gradient>())
227 return;
228
229 basegfx::BGradient aGradient(rValue);
230
231 // Export of axial gradient to OOXML produces a symmetrical linear multi-color gradient. Import
232 // does not regenerate it as 'axial' because that is not needed for MCGR. For export to ODF we
233 // try to regenerate 'axial' for to get a better compatibility with LO versions before MCGR.
234 aGradient.tryToConvertToAxial();
235
236 // MCGR: For better compatibility with LO versions before MCGR, try
237 // to re-create a 'border' value based on the existing gradient stops.
238 // With MCGR we do not need 'border' anymore in quite some cases since
239 // no Start/EndColor at 0.0 resp. 1.0 is explicitly needed. Since we
240 // (unfortunately need to) internally continue to support border
241 // anyways it does no harm to fallback to use the border value - if
242 // there is an equivalent representation as this helper checks for.
243 // For exports that do not support 'border' this will be adapted as
244 // needed (see tryToApplyBorder()).
245 aGradient.tryToRecreateBorder(nullptr);
246
247 OUString aStrValue;
248 OUStringBuffer aOut;
249
250 // Style
252 return;
253
254 // Name
255 bool bEncoded = false;
257 m_rExport.EncodeStyleName( rStrName,
258 &bEncoded ) );
259 if( bEncoded )
261 rStrName );
262
263 aStrValue = aOut.makeStringAndClear();
265
266 // Center x/y
267 if( aGradient.GetGradientStyle() != awt::GradientStyle_LINEAR &&
268 aGradient.GetGradientStyle() != awt::GradientStyle_AXIAL )
269 {
271 aStrValue = aOut.makeStringAndClear();
274 aStrValue = aOut.makeStringAndClear();
276 }
277
278 // prep Start/EndColor, default black
279 basegfx::BColor aStartColor;
280 basegfx::BColor aEndColor;
281
282 if (!aGradient.GetColorStops().empty())
283 {
284 aStartColor = aGradient.GetColorStops().front().getStopColor();
285 aEndColor = aGradient.GetColorStops().back().getStopColor();
286 }
287
288 // Color start
289 ::sax::Converter::convertColor(aOut, Color(aStartColor));
290 aStrValue = aOut.makeStringAndClear();
292
293 // Color end
294 ::sax::Converter::convertColor(aOut, Color(aEndColor));
295 aStrValue = aOut.makeStringAndClear();
297
298 // Intensity start
300 aStrValue = aOut.makeStringAndClear();
302
303 // Intensity end
305 aStrValue = aOut.makeStringAndClear();
307
308 // Angle
309 if( aGradient.GetGradientStyle() != awt::GradientStyle_RADIAL )
310 {
311 ::sax::Converter::convertAngle(aOut, static_cast<sal_Int16>(aGradient.GetAngle()), m_rExport.getSaneDefaultVersion());
312 aStrValue = aOut.makeStringAndClear();
314 }
315
316 // Border
317 ::sax::Converter::convertPercent( aOut, aGradient.GetBorder() );
318 aStrValue = aOut.makeStringAndClear();
320
321 // ctor writes start tag. End-tag is written by destructor at block end.
323 true, false );
324
325 // Write child elements <loext:gradient-stop>
326 // Do not export in standard ODF 1.3 or older.
328 return;
329
330 if (aGradient.GetColorStops().empty())
331 return;
332
333 double fPreviousOffset = 0.0;
334 for (const auto& aCandidate : aGradient.GetColorStops())
335 {
336 // Attribute svg:offset. Make sure offsets are increasing.
337 double fOffset = std::clamp<double>(aCandidate.getStopOffset(), 0.0, 1.0);
338 if (fOffset < fPreviousOffset)
339 fOffset = fPreviousOffset;
340 m_rExport.AddAttribute(XML_NAMESPACE_SVG, XML_OFFSET, OUString::number(fOffset));
341 fPreviousOffset = fOffset;
342
343 // As of LO 7.6.0 only color-type="rgb" is implemented.
345
346 // Attribute loext:color-value, data type color, that is #rrggbb.
347 const basegfx::BColor aDecimalColor(aCandidate.getStopColor());
348 ::Color aToolsColor(std::clamp<sal_uInt8>(std::round(aDecimalColor.getRed() * 255.0), 0, 255),
349 std::clamp<sal_uInt8>(std::round(aDecimalColor.getGreen() * 255.0), 0, 255),
350 std::clamp<sal_uInt8>(std::round(aDecimalColor.getBlue() * 255.0), 0, 255));
352 rtl::OUStringChar('#') + aToolsColor.AsRGBHexString());
353
354 // write gradient stop element
356 }
357}
358
359/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SvXMLEnumMapEntry< awt::GradientStyle > const pXML_GradientStyle_Enum[]
const SvXMLImport & m_rImport
sal_uInt8 GetBlue() const
OUString AsRGBHexString() const
sal_uInt8 GetRed() const
sal_uInt8 GetGreen() const
void AddAttribute(sal_uInt16 nPrefix, const OUString &rName, const OUString &rValue)
Definition: xmlexp.cxx:907
SvtSaveOptions::ODFSaneDefaultVersion getSaneDefaultVersion() const
returns the deterministic version for odf export
Definition: xmlexp.cxx:2264
OUString EncodeStyleName(const OUString &rName, bool *pEncoded=nullptr) const
Definition: xmlexp.cxx:1934
This class deliberately does not support XWeak, to improve performance when loading large documents.
Definition: xmlictxt.hxx:48
static bool convertEnum(EnumT &rEnum, std::u16string_view rValue, const SvXMLEnumMapEntry< EnumT > *pMap)
convert string to enum using given enum map, if the enum is not found in the map, this method will re...
Definition: xmluconv.hxx:145
virtual ~XMLGradientStopContext() override
XMLGradientStopContext(SvXMLImport &rImport, sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList, std::vector< css::awt::ColorStop > &rColorStopVec)
void exportXML(const OUString &rStrName, const css::uno::Any &rValue)
XMLGradientStyleExport(SvXMLExport &rExport)
XMLGradientStyleImport(SvXMLImport &rImport)
void importXML(const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList, css::uno::Any &rValue, OUString &rStrName)
double getBlue() const
double getRed() const
double getGreen() const
void tryToRecreateBorder(basegfx::BColorStops *pAssociatedTransparencyStops=nullptr)
sal_uInt16 GetBorder() const
sal_uInt16 GetStartIntens() const
sal_uInt16 GetXOffset() const
const basegfx::BColorStops & GetColorStops() const
sal_uInt16 GetEndIntens() const
void tryToConvertToAxial()
Degree10 GetAngle() const
css::awt::GradientStyle GetGradientStyle() const
sal_uInt16 GetYOffset() const
static void convertDouble(OUStringBuffer &rBuffer, double fNumber, bool bWriteUnits, sal_Int16 nSourceUnit, sal_Int16 nTargetUnit)
static bool convertPercent(sal_Int32 &rValue, std::u16string_view rString)
static bool convertColor(sal_Int32 &rColor, std::u16string_view rValue)
static void convertAngle(OUStringBuffer &rBuffer, sal_Int16 nAngle, SvtSaveOptions::ODFSaneDefaultVersion nVersion)
constexpr OUStringLiteral ODFVER_012_TEXT
float u
DRAW
#define SAL_INFO_IF(condition, area, stream)
FastAttributeList & castToFastAttributeList(const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList)
Handling of tokens in XML:
@ XML_GRADIENTSTYLE_ELLIPSOID
Definition: xmltoken.hxx:985
@ XML_GRADIENTSTYLE_AXIAL
Definition: xmltoken.hxx:984
@ XML_GRADIENTSTYLE_SQUARE
Definition: xmltoken.hxx:988
@ XML_GRADIENTSTYLE_RECTANGULAR
Definition: xmltoken.hxx:987
@ XML_GRADIENTSTYLE_RADIAL
Definition: xmltoken.hxx:986
#define XMLOFF_WARN_UNKNOWN(area, rIter)
Definition: xmlictxt.hxx:114
#define XML_ELEMENT(prefix, name)
Definition: xmlimp.hxx:97
constexpr sal_uInt16 XML_NAMESPACE_DRAW
constexpr sal_uInt16 XML_NAMESPACE_SVG
constexpr sal_uInt16 XML_NAMESPACE_LO_EXT