bump product version to 7.6.3.2-android
[LibreOffice.git] / xmloff / source / style / GradientStyle.cxx
blob29a277a79befe0b229d104ef0c360730bbc4b357
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <xmloff/GradientStyle.hxx>
22 #include <com/sun/star/awt/Gradient2.hpp>
24 #include <comphelper/documentconstants.hxx>
25 #include <rtl/ustrbuf.hxx>
26 #include <rtl/ustring.hxx>
27 #include <sal/log.hxx>
28 #include <sax/tools/converter.hxx>
29 #include <xmloff/namespacemap.hxx>
30 #include <xmloff/xmlement.hxx>
31 #include <xmloff/xmlexp.hxx>
32 #include <xmloff/xmlimp.hxx>
33 #include <xmloff/xmlnamespace.hxx>
34 #include <xmloff/xmltkmap.hxx>
35 #include <xmloff/xmltoken.hxx>
36 #include <xmloff/xmluconv.hxx>
37 #include <basegfx/utils/bgradient.hxx>
38 #include <docmodel/uno/UnoGradientTools.hxx>
40 using namespace ::com::sun::star;
41 using namespace ::xmloff::token;
43 SvXMLEnumMapEntry<awt::GradientStyle> const pXML_GradientStyle_Enum[] =
45 { XML_LINEAR, awt::GradientStyle_LINEAR },
46 { XML_GRADIENTSTYLE_AXIAL, awt::GradientStyle_AXIAL },
47 { XML_GRADIENTSTYLE_RADIAL, awt::GradientStyle_RADIAL },
48 { XML_GRADIENTSTYLE_ELLIPSOID, awt::GradientStyle_ELLIPTICAL },
49 { XML_GRADIENTSTYLE_SQUARE, awt::GradientStyle_SQUARE },
50 { XML_GRADIENTSTYLE_RECTANGULAR, awt::GradientStyle_RECT },
51 { XML_TOKEN_INVALID, awt::GradientStyle(0) }
54 // Import
55 XMLGradientStyleImport::XMLGradientStyleImport(
56 SvXMLImport& rImp )
57 : m_rImport(rImp)
61 void XMLGradientStyleImport::importXML(
62 const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
63 uno::Any& rValue,
64 OUString& rStrName )
66 OUString aDisplayName;
68 awt::Gradient2 aGradient;
69 aGradient.Style = css::awt::GradientStyle_LINEAR;
70 aGradient.StartColor = 0;
71 aGradient.EndColor = 0;
72 aGradient.Angle = 0;
73 aGradient.Border = 0;
74 aGradient.XOffset = 0;
75 aGradient.YOffset = 0;
76 aGradient.StartIntensity = 100;
77 aGradient.EndIntensity = 100;
78 aGradient.StepCount = 0;
80 for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
82 sal_Int32 nTmpValue(0);
84 switch( aIter.getToken() )
86 case XML_ELEMENT(DRAW, XML_NAME):
87 rStrName = aIter.toString();
88 break;
89 case XML_ELEMENT(DRAW, XML_DISPLAY_NAME):
90 aDisplayName = aIter.toString();
91 break;
92 case XML_ELEMENT(DRAW, XML_STYLE):
93 SvXMLUnitConverter::convertEnum( aGradient.Style, aIter.toView(), pXML_GradientStyle_Enum );
94 break;
95 case XML_ELEMENT(DRAW, XML_CX):
96 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
97 aGradient.XOffset = static_cast< sal_Int16 >( nTmpValue );
98 break;
99 case XML_ELEMENT(DRAW, XML_CY):
100 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
101 aGradient.YOffset = static_cast< sal_Int16 >( nTmpValue );
102 break;
103 case XML_ELEMENT(DRAW, XML_START_COLOR):
104 ::sax::Converter::convertColor(aGradient.StartColor, aIter.toView());
105 break;
106 case XML_ELEMENT(DRAW, XML_END_COLOR):
107 ::sax::Converter::convertColor(aGradient.EndColor, aIter.toView());
108 break;
109 case XML_ELEMENT(DRAW, XML_START_INTENSITY):
110 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
111 aGradient.StartIntensity = static_cast< sal_Int16 >( nTmpValue );
112 break;
113 case XML_ELEMENT(DRAW, XML_END_INTENSITY):
114 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
115 aGradient.EndIntensity = static_cast< sal_Int16 >( nTmpValue );
116 break;
117 case XML_ELEMENT(DRAW, XML_GRADIENT_ANGLE):
119 auto const cmp12(m_rImport.GetODFVersion().compareTo(ODFVER_012_TEXT));
120 bool const bSuccess =
121 ::sax::Converter::convertAngle(aGradient.Angle, aIter.toView(),
122 // tdf#89475 try to detect borked OOo angles
123 (cmp12 < 0) || (cmp12 == 0
124 && (m_rImport.isGeneratorVersionOlderThan(SvXMLImport::AOO_4x, SvXMLImport::LO_7x)
125 // also for AOO 4.x, assume there won't ever be a 4.2
126 || m_rImport.getGeneratorVersion() == SvXMLImport::AOO_4x)));
127 SAL_INFO_IF(!bSuccess, "xmloff.style", "failed to import draw:angle");
129 break;
130 case XML_ELEMENT(DRAW, XML_BORDER):
131 ::sax::Converter::convertPercent( nTmpValue, aIter.toView() );
132 aGradient.Border = static_cast< sal_Int16 >( nTmpValue );
133 break;
135 default:
136 XMLOFF_WARN_UNKNOWN("xmloff.style", aIter);
140 rValue <<= aGradient;
142 if( !aDisplayName.isEmpty() )
144 m_rImport.AddStyleDisplayName( XmlStyleFamily::SD_GRADIENT_ID, rStrName,
145 aDisplayName );
146 rStrName = aDisplayName;
150 XMLGradientStopContext::XMLGradientStopContext(
151 SvXMLImport& rImport, sal_Int32 nElement,
152 const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
153 std::vector<awt::ColorStop>& rColorStopVec)
154 : SvXMLImportContext(rImport)
156 if(nElement != XML_ELEMENT(LO_EXT, xmloff::token::XML_GRADIENT_STOP))
157 return;
159 double fOffset = -1.0;
160 OUString sColorType;
161 OUString sColorValue;
162 // First collect all attributes
163 for (auto &aIter : sax_fastparser::castToFastAttributeList(xAttrList))
165 switch(aIter.getToken())
167 case XML_ELEMENT(SVG, xmloff::token::XML_OFFSET): // needed??
168 case XML_ELEMENT(SVG_COMPAT, xmloff::token::XML_OFFSET):
169 if (!::sax::Converter::convertDouble(fOffset, aIter.toView()))
170 return;
171 break;
172 case XML_ELEMENT(LO_EXT, xmloff::token::XML_COLOR_VALUE):
173 sColorValue = aIter.toString();
174 if (sColorValue.isEmpty())
175 return;
176 break;
177 case XML_ELEMENT(LO_EXT, xmloff::token::XML_COLOR_TYPE):
178 sColorType = aIter.toString();
179 if (sColorType.isEmpty())
180 return;
181 break;
182 default:
183 XMLOFF_WARN_UNKNOWN("xmloff.style", aIter);
187 // As of LO 7.6.0 only "rgb" is implemented.
188 if (sColorType != u"rgb")
189 return;
191 // Type "rgb" requires kind color-value="#rrggbb".
192 ::Color aColor;
193 if (!::sax::Converter::convertColor(aColor, sColorValue))
194 return;
196 // All attribute values OK. Generate ColorStop.
197 css::rendering::RGBColor aRGBColor;
198 aRGBColor.Red = aColor.GetRed() / 255.0;
199 aRGBColor.Green = aColor.GetGreen() / 255.0;
200 aRGBColor.Blue = aColor.GetBlue() / 255.0;
202 awt::ColorStop aColorStop;
203 aColorStop.StopOffset = fOffset;
204 aColorStop.StopColor = aRGBColor;
205 rColorStopVec.push_back(aColorStop);
208 XMLGradientStopContext::~XMLGradientStopContext()
212 // Export
214 XMLGradientStyleExport::XMLGradientStyleExport(
215 SvXMLExport& rExp )
216 : m_rExport(rExp)
220 void XMLGradientStyleExport::exportXML(
221 const OUString& rStrName,
222 const uno::Any& rValue )
224 if( rStrName.isEmpty() )
225 return;
227 if (!rValue.has<css::awt::Gradient2>() && !rValue.has<css::awt::Gradient>())
228 return;
230 basegfx::BGradient aGradient = model::gradient::getFromAny(rValue);
232 // Export of axial gradient to OOXML produces a symmetrical linear multi-color gradient. Import
233 // does not regenerate it as 'axial' because that is not needed for MCGR. For export to ODF we
234 // try to regenerate 'axial' for to get a better compatibility with LO versions before MCGR.
235 aGradient.tryToConvertToAxial();
237 // MCGR: For better compatibility with LO versions before MCGR, try
238 // to re-create a 'border' value based on the existing gradient stops.
239 // With MCGR we do not need 'border' anymore in quite some cases since
240 // no Start/EndColor at 0.0 resp. 1.0 is explicitly needed. Since we
241 // (unfortunately need to) internally continue to support border
242 // anyways it does no harm to fallback to use the border value - if
243 // there is an equivalent representation as this helper checks for.
244 // For exports that do not support 'border' this will be adapted as
245 // needed (see tryToApplyBorder()).
246 aGradient.tryToRecreateBorder(nullptr);
248 OUString aStrValue;
249 OUStringBuffer aOut;
251 // Style
252 if( !SvXMLUnitConverter::convertEnum( aOut, aGradient.GetGradientStyle(), pXML_GradientStyle_Enum ) )
253 return;
255 // Name
256 bool bEncoded = false;
257 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME,
258 m_rExport.EncodeStyleName( rStrName,
259 &bEncoded ) );
260 if( bEncoded )
261 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_DISPLAY_NAME,
262 rStrName );
264 aStrValue = aOut.makeStringAndClear();
265 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE, aStrValue );
267 // Center x/y
268 if( aGradient.GetGradientStyle() != awt::GradientStyle_LINEAR &&
269 aGradient.GetGradientStyle() != awt::GradientStyle_AXIAL )
271 ::sax::Converter::convertPercent(aOut, aGradient.GetXOffset());
272 aStrValue = aOut.makeStringAndClear();
273 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CX, aStrValue );
274 ::sax::Converter::convertPercent(aOut, aGradient.GetYOffset());
275 aStrValue = aOut.makeStringAndClear();
276 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CY, aStrValue );
279 // prep Start/EndColor, default black
280 basegfx::BColor aStartColor;
281 basegfx::BColor aEndColor;
283 if (!aGradient.GetColorStops().empty())
285 aStartColor = aGradient.GetColorStops().front().getStopColor();
286 aEndColor = aGradient.GetColorStops().back().getStopColor();
289 // Color start
290 ::sax::Converter::convertColor(aOut, Color(aStartColor));
291 aStrValue = aOut.makeStringAndClear();
292 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START_COLOR, aStrValue );
294 // Color end
295 ::sax::Converter::convertColor(aOut, Color(aEndColor));
296 aStrValue = aOut.makeStringAndClear();
297 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END_COLOR, aStrValue );
299 // Intensity start
300 ::sax::Converter::convertPercent(aOut, aGradient.GetStartIntens());
301 aStrValue = aOut.makeStringAndClear();
302 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START_INTENSITY, aStrValue );
304 // Intensity end
305 ::sax::Converter::convertPercent(aOut, aGradient.GetEndIntens());
306 aStrValue = aOut.makeStringAndClear();
307 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END_INTENSITY, aStrValue );
309 // Angle
310 if( aGradient.GetGradientStyle() != awt::GradientStyle_RADIAL )
312 ::sax::Converter::convertAngle(aOut, static_cast<sal_Int16>(aGradient.GetAngle()), m_rExport.getSaneDefaultVersion());
313 aStrValue = aOut.makeStringAndClear();
314 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_GRADIENT_ANGLE, aStrValue );
317 // Border
318 ::sax::Converter::convertPercent( aOut, aGradient.GetBorder() );
319 aStrValue = aOut.makeStringAndClear();
320 m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_BORDER, aStrValue );
322 // ctor writes start tag. End-tag is written by destructor at block end.
323 SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_DRAW, XML_GRADIENT,
324 true, false );
326 // Write child elements <loext:gradient-stop>
327 // Do not export in standard ODF 1.3 or older.
328 if ((m_rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
329 return;
331 if (aGradient.GetColorStops().empty())
332 return;
334 double fPreviousOffset = 0.0;
335 for (const auto& aCandidate : aGradient.GetColorStops())
337 // Attribute svg:offset. Make sure offsets are increasing.
338 double fOffset = std::clamp<double>(aCandidate.getStopOffset(), 0.0, 1.0);
339 if (fOffset < fPreviousOffset)
340 fOffset = fPreviousOffset;
341 m_rExport.AddAttribute(XML_NAMESPACE_SVG, XML_OFFSET, OUString::number(fOffset));
342 fPreviousOffset = fOffset;
344 // As of LO 7.6.0 only color-type="rgb" is implemented.
345 m_rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_COLOR_TYPE, u"rgb");
347 // Attribute loext:color-value, data type color, that is #rrggbb.
348 const basegfx::BColor aDecimalColor(aCandidate.getStopColor());
349 ::Color aToolsColor(std::clamp<sal_uInt8>(std::round(aDecimalColor.getRed() * 255.0), 0, 255),
350 std::clamp<sal_uInt8>(std::round(aDecimalColor.getGreen() * 255.0), 0, 255),
351 std::clamp<sal_uInt8>(std::round(aDecimalColor.getBlue() * 255.0), 0, 255));
352 m_rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_COLOR_VALUE,
353 rtl::OUStringChar('#') + aToolsColor.AsRGBHexString());
355 // write gradient stop element
356 SvXMLElementExport aStopElement(m_rExport, XML_NAMESPACE_LO_EXT, XML_GRADIENT_STOP, true, true);
360 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */