From f2eac63dcca642efc80a171a986aecc482f9cbc6 Mon Sep 17 00:00:00 2001 From: Regina Henschel Date: Wed, 5 Apr 2023 18:10:55 +0200 Subject: WIP ODF import and export for MCGR Current state uses: Element loext:gradient-stop with the attributes svg:offset, loext:color-type with value 'rgb', and loext:color-value with values of kind #rrggbb. Element loext:opacity-stop with the attributes svg:offset and svg:stop-opacity, both with datatype double. With MCGR enabled testColorGradientWithTransparencyDOCX in CppunitTest_chart_export3 has the value 90000 instead of 90196. That is same value as in original file. Thus I have adapted the test. Change-Id: I976934f9b8fb79be4f74adb180b3285486dce31f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150060 Tested-by: Jenkins Reviewed-by: Armin Le Grand --- chart2/qa/extras/chart2export3.cxx | 2 +- include/xmloff/xmltoken.hxx | 5 + .../OpenDocument-v1.3+libreoffice-schema.rng | 72 ++++- xmloff/qa/unit/data/MCGR_OldToNew.odg | Bin 0 -> 14577 bytes xmloff/qa/unit/data/MCGR_OldToNew_opacity.odg | Bin 0 -> 16806 bytes xmloff/qa/unit/data/MCGR_threeStops.fodt | 317 +++++++++++++++++++++ xmloff/qa/unit/style.cxx | 206 ++++++++++++- xmloff/source/core/xmltoken.cxx | 5 + xmloff/source/style/FillStyleContext.cxx | 165 ++++++++++- xmloff/source/style/FillStyleContext.hxx | 38 +++ xmloff/source/style/GradientStyle.cxx | 56 +++- xmloff/source/style/TransGradientStyle.cxx | 48 +++- xmloff/source/token/tokens.txt | 4 + 13 files changed, 888 insertions(+), 30 deletions(-) create mode 100644 xmloff/qa/unit/data/MCGR_OldToNew.odg create mode 100644 xmloff/qa/unit/data/MCGR_OldToNew_opacity.odg create mode 100644 xmloff/qa/unit/data/MCGR_threeStops.fodt diff --git a/chart2/qa/extras/chart2export3.cxx b/chart2/qa/extras/chart2export3.cxx index dfb7f330d582..d22d5e51e545 100644 --- a/chart2/qa/extras/chart2export3.cxx +++ b/chart2/qa/extras/chart2export3.cxx @@ -110,7 +110,7 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testColorGradientWithTransparencyDOCX) // Test the transparency of the first color assertXPath(pXmlDoc, "/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser/c:spPr/a:gradFill/a:gsLst/a:gs[1]/a:srgbClr/a:alpha", "val", "60000"); // Test the transparency of the second color - assertXPath(pXmlDoc, "/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser/c:spPr/a:gradFill/a:gsLst/a:gs[2]/a:srgbClr/a:alpha", "val", "90196"); + assertXPath(pXmlDoc, "/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser/c:spPr/a:gradFill/a:gsLst/a:gs[2]/a:srgbClr/a:alpha", "val", "90000"); } CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testColorGradientWithTransparencyODS) diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 0c63a31d0818..3ea2e7ee8335 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -3519,6 +3519,11 @@ namespace xmloff::token { XML_MAY_BREAK_BETWEEN_PAGES, + XML_GRADIENT_STOP, // multi-color-gradient + XML_OPACITY_STOP, + XML_COLOR_VALUE, + XML_COLOR_TYPE, + XML_TOKEN_END }; diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index f772ce223f35..a3900e4f31c7 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -1974,7 +1974,28 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. - + + + + + + + + + + + + + + + + + + + + + + @@ -3532,4 +3553,53 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. + + + + + + rgb + + + + + + + + theme + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xmloff/qa/unit/data/MCGR_OldToNew.odg b/xmloff/qa/unit/data/MCGR_OldToNew.odg new file mode 100644 index 000000000000..7db627d0c23b Binary files /dev/null and b/xmloff/qa/unit/data/MCGR_OldToNew.odg differ diff --git a/xmloff/qa/unit/data/MCGR_OldToNew_opacity.odg b/xmloff/qa/unit/data/MCGR_OldToNew_opacity.odg new file mode 100644 index 000000000000..8b64a9037e73 Binary files /dev/null and b/xmloff/qa/unit/data/MCGR_OldToNew_opacity.odg differ diff --git a/xmloff/qa/unit/data/MCGR_threeStops.fodt b/xmloff/qa/unit/data/MCGR_threeStops.fodt new file mode 100644 index 000000000000..ac9c9978e299 --- /dev/null +++ b/xmloff/qa/unit/data/MCGR_threeStops.fodt @@ -0,0 +1,317 @@ + + + + Regina Henschel2023-04-18T23:09:21.4340000002023-04-18T23:28:52.397000000Regina HenschelPT9M47S3B2020/7.6.0.0.alpha0$Windows_X86_64 LibreOffice_project/bcf20273b7036cae9b58d8f452933c0c8d0b8e47 + + + 0 + 0 + 27349 + 9527 + true + false + + + view2 + 6562 + 2501 + 0 + 0 + 27347 + 9525 + 0 + 1 + false + 100 + true + false + false + false + false + false + + + + + true + + false + false + false + false + true + 1 + true + false + false + false + + false + + false + false + false + + 0 + false + true + true + false + false + false + + 0 + + true + high-resolution + false + false + false + false + true + false + false + true + false + false + true + true + false + false + false + true + false + true + false + false + true + false + false + false + false + false + false + 447710 + 238319 + false + false + false + true + false + false + true + true + false + true + true + false + false + false + false + false + true + false + true + false + false + false + false + false + true + 0 + true + false + false + false + true + false + 0 + true + false + true + true + true + true + false + false + false + false + false + true + + false + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dummy + + + \ No newline at end of file diff --git a/xmloff/qa/unit/style.cxx b/xmloff/qa/unit/style.cxx index 88e7177f3b91..9f67fb9d20d6 100644 --- a/xmloff/qa/unit/style.cxx +++ b/xmloff/qa/unit/style.cxx @@ -11,8 +11,13 @@ #include -#include +#include +#include #include +#include +#include +#include +#include #include #include @@ -25,6 +30,7 @@ class XmloffStyleTest : public UnoApiXmlTest { public: XmloffStyleTest(); + uno::Reference getShape(sal_uInt8 nShapeIndex); }; XmloffStyleTest::XmloffStyleTest() @@ -32,6 +38,17 @@ XmloffStyleTest::XmloffStyleTest() { } +uno::Reference XmloffStyleTest::getShape(sal_uInt8 nShapeIndex) +{ + uno::Reference xDrawPagesSupplier(mxComponent, + uno::UNO_QUERY_THROW); + uno::Reference xDrawPages(xDrawPagesSupplier->getDrawPages()); + uno::Reference xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY_THROW); + uno::Reference xShape(xDrawPage->getByIndex(nShapeIndex), + uno::UNO_QUERY_THROW); + return xShape; +} + CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testFillImageBase64) { // Load a flat ODG that has base64-encoded bitmap as a fill style. @@ -310,6 +327,193 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testPosRelTopMargin) } } +CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testMCGR_OldToNew) +{ + // The file contains a shape with linear gradient fill from red #ff0000 to yellow #ffff00, + // named 'red2yellow' + loadFromURL(u"MCGR_OldToNew.odg"); + + // saveAndReload includes validation and must not fail with the new elements and attributes. + saveAndReload("draw8"); + + // Examine file markup + // For compatibilty the file should still have the old attributes 'start-color' and 'end-color' + xmlDocUniquePtr pXmlDoc = parseExport("styles.xml"); + OString sPath = "/office:document-styles/office:styles/draw:gradient[@draw:name='red2yellow']"; + assertXPath(pXmlDoc, sPath, "start-color", "#ff0000"); + assertXPath(pXmlDoc, sPath, "end-color", "#ffff00"); + + // And it must have the new 'gradient-stop' elements. + // The prefix 'loext' needs to be adapted, when the element is available in ODF strict. + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[1]", "offset", "0"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[1]", "color-type", "rgb"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[1]", "color-value", "#ff0000"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[2]", "offset", "1"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[2]", "color-type", "rgb"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[2]", "color-value", "#ffff00"); + + // Examine reloaded file + uno::Reference xShape(getShape(0)); + CPPUNIT_ASSERT(xShape.is()); + uno::Reference xShapeProperties(xShape, uno::UNO_QUERY); + + // The old properties need to be still available, as they might be used in macros. + OUString sGradientName; + xShapeProperties->getPropertyValue("FillGradientName") >>= sGradientName; + CPPUNIT_ASSERT_EQUAL(OUString(u"red2yellow"), sGradientName); + awt::Gradient2 aGradient; + xShapeProperties->getPropertyValue("FillGradient") >>= aGradient; + CPPUNIT_ASSERT_EQUAL(sal_Int32(0xFF0000), aGradient.StartColor); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0xFFFF00), aGradient.EndColor); + + // Test new properties + auto aColorStopSeq = aGradient.ColorStops; + awt::ColorStop aColorStop = aColorStopSeq[0]; + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopOffset); + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopColor.Red); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Green); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Blue); + aColorStop = aColorStopSeq[1]; + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopOffset); + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopColor.Red); + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopColor.Green); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Blue); +} + +CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testMCGR_OldToNew_opacity) +{ + // The file contains a shape with solid fill and a radial transparency gradient with start 90%, + // end 0%, border 20% and center at 50%|50%. There is only one draw:opacity element in file. + loadFromURL(u"MCGR_OldToNew_opacity.odg"); + + // saveAndReload includes validation and must not fail with the new elements and attributes. + saveAndReload("draw8"); + + // Examine file markup + // For compatibilty the file should still have the old attributes. + xmlDocUniquePtr pXmlDoc = parseExport("styles.xml"); + OString sPath = "/office:document-styles/office:styles/draw:opacity"; + assertXPath(pXmlDoc, sPath, "start", "10%"); // UI 90% transparency + assertXPath(pXmlDoc, sPath, "end", "100%"); // UI 0% transparency + assertXPath(pXmlDoc, sPath, "border", "20%"); + assertXPath(pXmlDoc, sPath, "cx", "50%"); + assertXPath(pXmlDoc, sPath, "cy", "50%"); + assertXPath(pXmlDoc, sPath, "style", "radial"); + + // And it must have the new 'opacity-stop' elements. + // The prefix 'loext' needs to be adapted, when the element is available in ODF strict. + OString sFirstStop = sPath + "/loext:opacity-stop[1]"; + assertXPath(pXmlDoc, sFirstStop, "offset", "0"); + // Because of converting through color, the grade of opacity is not exact "0.1" + double fOpacity = getXPathContent(pXmlDoc, sFirstStop + "/@svg:stop-opacity").toDouble(); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.1, fOpacity, 0.002); + + assertXPath(pXmlDoc, sPath + "/loext:opacity-stop[2]", "offset", "1"); + assertXPath(pXmlDoc, sPath + "/loext:opacity-stop[2]", "stop-opacity", "1"); + + // Examine reloaded file + uno::Reference xShape(getShape(0)); + CPPUNIT_ASSERT(xShape.is()); + uno::Reference xShapeProperties(xShape, uno::UNO_QUERY); + + // The old properties need to be still available, as they might be used in macros. + awt::Gradient2 aGradient; + xShapeProperties->getPropertyValue("FillTransparenceGradient") >>= aGradient; + CPPUNIT_ASSERT_EQUAL(sal_Int32(0xE5E5E5), aGradient.StartColor); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aGradient.EndColor); + CPPUNIT_ASSERT_EQUAL(sal_Int16(20), aGradient.Border); + CPPUNIT_ASSERT_EQUAL(sal_Int16(50), aGradient.XOffset); + CPPUNIT_ASSERT_EQUAL(sal_Int16(50), aGradient.YOffset); + CPPUNIT_ASSERT_EQUAL(awt::GradientStyle_RADIAL, aGradient.Style); + + // Test new properties + auto aColorStopSeq = aGradient.ColorStops; + awt::ColorStop aColorStop = aColorStopSeq[0]; + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopOffset); + // Rounding error because of converting through color: 90% => 0.9 * 255 => 229 + // 299.0 / 255.0 = 0.898039215686275 + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.9, aColorStop.StopColor.Red, 0.002); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.9, aColorStop.StopColor.Green, 0.002); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.9, aColorStop.StopColor.Blue, 0.002); + aColorStop = aColorStopSeq[1]; + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopOffset); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Red); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Green); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Blue); +} + +CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testMCGR_threeStops) +{ + // The file contains a shape with square gradient fill from red #ff0000 over teal #0099bb to + // yellow #ffff00, named 'threeStops'. It has 45deg rotation, center 0%|50%, border 10%. + loadFromURL(u"MCGR_threeStops.fodt"); + + // saveAndReload includes validation and must not fail with the new elements and attributes. + saveAndReload("draw8"); + + // Examine file markup + // For compatibilty the file should still have the old attributes 'start-color' and 'end-color' + xmlDocUniquePtr pXmlDoc = parseExport("styles.xml"); + OString sPath = "/office:document-styles/office:styles/draw:gradient[@draw:name='threeStops']"; + assertXPath(pXmlDoc, sPath, "start-color", "#ff0000"); + assertXPath(pXmlDoc, sPath, "end-color", "#ffff00"); + assertXPath(pXmlDoc, sPath, "style", "square"); + assertXPath(pXmlDoc, sPath, "cx", "0%"); + assertXPath(pXmlDoc, sPath, "cy", "50%"); + assertXPath(pXmlDoc, sPath, "angle", "45deg"); + assertXPath(pXmlDoc, sPath, "border", "10%"); + + // And it must have the new 'gradient-stop' elements. + // The prefix 'loext' needs to be adapted, when the element is available in ODF strict. + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[1]", "offset", "0"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[1]", "color-type", "rgb"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[1]", "color-value", "#ff0000"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[2]", "offset", "0.3"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[2]", "color-type", "rgb"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[2]", "color-value", "#0099bb"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[3]", "offset", "1"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[3]", "color-type", "rgb"); + assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[3]", "color-value", "#ffff00"); + + // Examine reloaded file + uno::Reference xShape(getShape(0)); + CPPUNIT_ASSERT(xShape.is()); + uno::Reference xShapeProperties(xShape, uno::UNO_QUERY); + + // The old properties need to be still available, as they might be used in macros. + OUString sGradientName; + xShapeProperties->getPropertyValue("FillGradientName") >>= sGradientName; + CPPUNIT_ASSERT_EQUAL(OUString(u"threeStops"), sGradientName); + awt::Gradient2 aGradient; + xShapeProperties->getPropertyValue("FillGradient") >>= aGradient; + CPPUNIT_ASSERT_EQUAL(sal_Int32(0xFF0000), aGradient.StartColor); + CPPUNIT_ASSERT_EQUAL(sal_Int32(0xFFFF00), aGradient.EndColor); + CPPUNIT_ASSERT_EQUAL(awt::GradientStyle_SQUARE, aGradient.Style); + CPPUNIT_ASSERT_EQUAL(sal_Int16(0), aGradient.XOffset); + CPPUNIT_ASSERT_EQUAL(sal_Int16(50), aGradient.YOffset); + CPPUNIT_ASSERT_EQUAL(sal_Int16(450), aGradient.Angle); + CPPUNIT_ASSERT_EQUAL(sal_Int16(10), aGradient.Border); + + // Test new properties + auto aColorStopSeq = aGradient.ColorStops; + awt::ColorStop aColorStop = aColorStopSeq[0]; + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopOffset); + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopColor.Red); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Green); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Blue); + aColorStop = aColorStopSeq[1]; + CPPUNIT_ASSERT_EQUAL(0.3, aColorStop.StopOffset); + // 0x99 = 153 => 153/255 = 0.6, 0xbb = 187 => 187/255 = 0.733... + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Red); + CPPUNIT_ASSERT_EQUAL(0.6, aColorStop.StopColor.Green); + CPPUNIT_ASSERT_DOUBLES_EQUAL(0.733333333333333, aColorStop.StopColor.Blue, 0.0000001); + aColorStop = aColorStopSeq[2]; + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopOffset); + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopColor.Red); + CPPUNIT_ASSERT_EQUAL(1.0, aColorStop.StopColor.Green); + CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Blue); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index b42aa9c43f42..30ea348588ce 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -3522,6 +3522,11 @@ namespace xmloff::token { TOKEN("may-break-between-pages", XML_MAY_BREAK_BETWEEN_PAGES), + TOKEN("gradient-stop", XML_GRADIENT_STOP), + TOKEN("opacity-stop", XML_OPACITY_STOP), + TOKEN("color-value", XML_COLOR_VALUE), + TOKEN("color-type", XML_COLOR_TYPE), + #if OSL_DEBUG_LEVEL > 0 { 0, nullptr, std::nullopt, XML_TOKEN_END } #else diff --git a/xmloff/source/style/FillStyleContext.cxx b/xmloff/source/style/FillStyleContext.cxx index 8cd99a25b261..cae1bcd0313b 100644 --- a/xmloff/source/style/FillStyleContext.cxx +++ b/xmloff/source/style/FillStyleContext.cxx @@ -17,21 +17,31 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ +#include "FillStyleContext.hxx" + +#include + +#include +#include +#include #include #include -#include -#include "FillStyleContext.hxx" +#include + +#include +#include #include #include #include #include -#include #include #include #include #include #include +#include + using namespace ::com::sun::star; @@ -48,10 +58,29 @@ XMLGradientStyleContext::~XMLGradientStyleContext() { } +css::uno::Reference XMLGradientStyleContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference& xAttrList) +{ + if (nElement == XML_ELEMENT(LO_EXT, xmloff::token::XML_GRADIENT_STOP)) + return new XMLGradientStopContext(GetImport(), nElement, xAttrList, maColorStopVec); + + return nullptr; +} + void XMLGradientStyleContext::endFastElement(sal_Int32 ) { - uno::Reference< container::XNameContainer > xGradient( GetImport().GetGradientHelper() ); + // correcting invalid StopOffset values is done at the model. Therefore we import them here + // without any change. + if (!maColorStopVec.empty()) + { + awt::Gradient2 aGradient; + maAny >>= aGradient; + aGradient.ColorStops = comphelper::containerToSequence(maColorStopVec); + maAny <<= aGradient; + } + uno::Reference< container::XNameContainer > xGradient( GetImport().GetGradientHelper() ); try { if(xGradient.is()) @@ -75,6 +104,67 @@ bool XMLGradientStyleContext::IsTransient() const return true; } +XMLGradientStopContext::XMLGradientStopContext( + SvXMLImport& rImport, sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList, + std::vector& rColorStopVec) +: SvXMLStyleContext(rImport) +{ + if(nElement != XML_ELEMENT(LO_EXT, xmloff::token::XML_GRADIENT_STOP)) + return; + + double fOffset = -1.0; + OUString sColorType; + OUString sColorValue; + // First collect all attributes + for (auto &aIter : sax_fastparser::castToFastAttributeList(xAttrList)) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(SVG, xmloff::token::XML_OFFSET): // needed?? + case XML_ELEMENT(SVG_COMPAT, xmloff::token::XML_OFFSET): + if (!::sax::Converter::convertDouble(fOffset, aIter.toView())) + return; + break; + case XML_ELEMENT(LO_EXT, xmloff::token::XML_COLOR_VALUE): + sColorValue = aIter.toString(); + if (sColorValue.isEmpty()) + return; + break; + case XML_ELEMENT(LO_EXT, xmloff::token::XML_COLOR_TYPE): + sColorType = aIter.toString(); + if (sColorType.isEmpty()) + return; + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff.style", aIter); + } + } + + // As of LO 7.6.0 only "rgb" is implemented. + if (sColorType != u"rgb") + return; + + // Type "rgb" requires kind color-value="#rrggbb". + ::Color aColor; + if (!::sax::Converter::convertColor(aColor, sColorValue)) + return; + + // All attribute values OK. Generate ColorStop. + css::rendering::RGBColor aRGBColor; + aRGBColor.Red = aColor.GetRed() / 255.0; + aRGBColor.Green = aColor.GetGreen() / 255.0; + aRGBColor.Blue = aColor.GetBlue() / 255.0; + + awt::ColorStop aColorStop; + aColorStop.StopOffset = fOffset; + aColorStop.StopColor = aRGBColor; + rColorStopVec.push_back(aColorStop); +} + +XMLGradientStopContext::~XMLGradientStopContext() +{ +} XMLHatchStyleContext::XMLHatchStyleContext( SvXMLImport& rImport, sal_Int32 /*nElement*/, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList) @@ -205,10 +295,30 @@ XMLTransGradientStyleContext::~XMLTransGradientStyleContext() { } +css::uno::Reference XMLTransGradientStyleContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference& xAttrList) +{ + if (nElement == XML_ELEMENT(LO_EXT, xmloff::token::XML_OPACITY_STOP)) + return new XMLTransparencyStopContext(GetImport(), nElement, xAttrList, maColorStopVec); + + return nullptr; +} + void XMLTransGradientStyleContext::endFastElement(sal_Int32 ) { uno::Reference< container::XNameContainer > xTransGradient( GetImport().GetTransGradientHelper() ); + // correcting invalid StopOffset values is done at the model. Therefore we import them here + // without any change. + if (!maColorStopVec.empty()) + { + awt::Gradient2 aGradient; + maAny >>= aGradient; + aGradient.ColorStops = comphelper::containerToSequence(maColorStopVec); + maAny <<= aGradient; + } + try { if(xTransGradient.is()) @@ -232,6 +342,53 @@ bool XMLTransGradientStyleContext::IsTransient() const return true; } +XMLTransparencyStopContext::XMLTransparencyStopContext( + SvXMLImport& rImport, sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList, + std::vector& rColorStopVec) +: SvXMLStyleContext(rImport) +{ + if(nElement != XML_ELEMENT(LO_EXT, xmloff::token::XML_OPACITY_STOP)) + return; + + double fOffset = -1.0; + css::rendering::RGBColor aRGBColor; // transparency is handled as gray color + for (auto &aIter : sax_fastparser::castToFastAttributeList(xAttrList)) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(SVG, xmloff::token::XML_OFFSET): // needed?? + case XML_ELEMENT(SVG_COMPAT, xmloff::token::XML_OFFSET): + if (!::sax::Converter::convertDouble(fOffset, aIter.toView())) + return; + break; + case XML_ELEMENT(SVG, xmloff::token::XML_STOP_OPACITY): + case XML_ELEMENT(SVG_COMPAT, xmloff::token::XML_STOP_OPACITY): + { + double fOpacity = 1.0; + if (!::sax::Converter::convertDouble(fOpacity, aIter.toView())) + return; + // Transparency is gray, full transparent is (1|1|1). + double fGrayComponent = std::clamp(1.0 - fOpacity, 0.0, 1.0); + aRGBColor.Red = fGrayComponent; + aRGBColor.Green = fGrayComponent; + aRGBColor.Blue = fGrayComponent; + } + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff.style", aIter); + } + } + + awt::ColorStop aColorStop; + aColorStop.StopOffset = fOffset; + aColorStop.StopColor = aRGBColor; + rColorStopVec.push_back(aColorStop); +} + +XMLTransparencyStopContext::~XMLTransparencyStopContext() +{ +} XMLMarkerStyleContext::XMLMarkerStyleContext( SvXMLImport& rImport, sal_Int32 /*nElement*/, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList) diff --git a/xmloff/source/style/FillStyleContext.hxx b/xmloff/source/style/FillStyleContext.hxx index 3b17c29c8239..aee8f6d81038 100644 --- a/xmloff/source/style/FillStyleContext.hxx +++ b/xmloff/source/style/FillStyleContext.hxx @@ -19,9 +19,13 @@ #pragma once +#include #include #include #include +#include + +#include // draw:gradient context @@ -30,6 +34,7 @@ class XMLGradientStyleContext: public SvXMLStyleContext private: css::uno::Any maAny; OUString maStrName; + std::vector maColorStopVec; public: @@ -37,11 +42,27 @@ public: const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ); virtual ~XMLGradientStyleContext() override; + virtual css::uno::Reference SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference& AttrList) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; virtual bool IsTransient() const override; }; +class XMLGradientStopContext: public SvXMLStyleContext +{ +private: + +public: + + XMLGradientStopContext(SvXMLImport& rImport, sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList, + std::vector& rColorStopVec); + virtual ~XMLGradientStopContext() override; +}; + // draw:hatch context class XMLHatchStyleContext: public SvXMLStyleContext @@ -91,6 +112,7 @@ class XMLTransGradientStyleContext: public SvXMLStyleContext private: css::uno::Any maAny; OUString maStrName; + std::vector maColorStopVec; // Transparency is handled as color gray. public: @@ -98,11 +120,27 @@ public: const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ); virtual ~XMLTransGradientStyleContext() override; + virtual css::uno::Reference SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference& AttrList) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; virtual bool IsTransient() const override; }; +class XMLTransparencyStopContext: public SvXMLStyleContext +{ +private: + +public: + + XMLTransparencyStopContext(SvXMLImport& rImport, sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList, + std::vector& rColorStopVec); + virtual ~XMLTransparencyStopContext() override; +}; + // draw:marker context class XMLMarkerStyleContext: public SvXMLStyleContext diff --git a/xmloff/source/style/GradientStyle.cxx b/xmloff/source/style/GradientStyle.cxx index 81ccd84d2b55..9f2723078a89 100644 --- a/xmloff/source/style/GradientStyle.cxx +++ b/xmloff/source/style/GradientStyle.cxx @@ -19,22 +19,21 @@ #include -#include +#include -#include #include - -#include -#include -#include -#include #include #include #include -#include +#include +#include +#include #include #include -#include +#include +#include +#include +#include using namespace ::com::sun::star; using namespace ::xmloff::token; @@ -64,7 +63,7 @@ void XMLGradientStyleImport::importXML( { OUString aDisplayName; - awt::Gradient aGradient; + awt::Gradient2 aGradient; aGradient.Style = css::awt::GradientStyle_LINEAR; aGradient.StartColor = 0; aGradient.EndColor = 0; @@ -158,7 +157,7 @@ void XMLGradientStyleExport::exportXML( const OUString& rStrName, const uno::Any& rValue ) { - awt::Gradient aGradient; + awt::Gradient2 aGradient; if( rStrName.isEmpty() ) return; @@ -230,9 +229,42 @@ void XMLGradientStyleExport::exportXML( aStrValue = aOut.makeStringAndClear(); rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_BORDER, aStrValue ); - // Do Write + // ctor writes start tag. End-tag is written by destructor at block end. SvXMLElementExport aElem( rExport, XML_NAMESPACE_DRAW, XML_GRADIENT, true, false ); + + // Write child elements + // Do not export in standard ODF 1.3 or older. + if ((rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0) + return; + sal_Int32 nCount = aGradient.ColorStops.getLength(); + if (nCount == 0) + return; + + double fPreviousOffset = 0.0; + for (auto& aCandidate : aGradient.ColorStops) + { + // Attribute svg:offset. Make sure offsets are increasing. + double fOffset = std::clamp(aCandidate.StopOffset, 0.0, 1.0); + if (fOffset < fPreviousOffset) + fOffset = fPreviousOffset; + rExport.AddAttribute(XML_NAMESPACE_SVG, XML_OFFSET, OUString::number(fOffset)); + fPreviousOffset = fOffset; + + // As of LO 7.6.0 only color-type="rgb" is implemented. + rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_COLOR_TYPE, u"rgb"); + + // Attribute loext:color-value, data type color, that is #rrggbb. + rendering::RGBColor aDecimalColor = aCandidate.StopColor; + ::Color aToolsColor(std::clamp(std::round(aDecimalColor.Red * 255.0), 0, 255), + std::clamp(std::round(aDecimalColor.Green * 255.0), 0, 255), + std::clamp(std::round(aDecimalColor.Blue * 255.0), 0, 255)); + rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_COLOR_VALUE, + rtl::OUStringChar('#') + aToolsColor.AsRGBHexString()); + + // write gradient stop element + SvXMLElementExport aStopElement(rExport, XML_NAMESPACE_LO_EXT, XML_GRADIENT_STOP, true, true); + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/style/TransGradientStyle.cxx b/xmloff/source/style/TransGradientStyle.cxx index 9b000c3eb448..d89348bbe0e8 100644 --- a/xmloff/source/style/TransGradientStyle.cxx +++ b/xmloff/source/style/TransGradientStyle.cxx @@ -19,22 +19,21 @@ #include -#include +#include -#include #include - -#include -#include -#include #include #include #include +#include #include -#include +#include +#include #include #include -#include +#include +#include +#include using namespace ::com::sun::star; @@ -65,7 +64,7 @@ void XMLTransGradientStyleImport::importXML( { OUString aDisplayName; - awt::Gradient aGradient; + awt::Gradient2 aGradient; aGradient.XOffset = 0; aGradient.YOffset = 0; aGradient.StartIntensity = 100; @@ -170,7 +169,7 @@ void XMLTransGradientStyleExport::exportXML( const OUString& rStrName, const uno::Any& rValue ) { - awt::Gradient aGradient; + awt::Gradient2 aGradient; if( rStrName.isEmpty() ) return; @@ -237,10 +236,37 @@ void XMLTransGradientStyleExport::exportXML( aStrValue = aOut.makeStringAndClear(); rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_BORDER, aStrValue ); - // Do Write + // ctor writes start tag. End-tag is written by destructor at block end. SvXMLElementExport rElem( rExport, XML_NAMESPACE_DRAW, XML_OPACITY, true, false ); + + // Write child elements + // Do not export in standard ODF 1.3 or older. + if ((rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0) + return; + sal_Int32 nCount = aGradient.ColorStops.getLength(); + if (nCount == 0) + return; + double fPreviousOffset = 0.0; + for (auto& aCandidate : aGradient.ColorStops) + { + // Attribute svg:offset. Make sure offsets are increasing. + double fOffset = std::clamp(aCandidate.StopOffset, 0.0, 1.0); + if (fOffset < fPreviousOffset) + fOffset = fPreviousOffset; + rExport.AddAttribute(XML_NAMESPACE_SVG, XML_OFFSET, OUString::number(fOffset)); + fPreviousOffset = fOffset; + + // Attribute svg:stop-opacity, data type zeroToOneDecimal + rendering::RGBColor aDecimalColor = aCandidate.StopColor; + // transparency is encoded as gray, 1.0 corresponds to full transparent + double fOpacity = std::clamp(1.0 - aDecimalColor.Red, 0.0, 1.0); + rExport.AddAttribute(XML_NAMESPACE_SVG, XML_STOP_OPACITY, OUString::number(fOpacity)); + + // write opacity stop element + SvXMLElementExport aStopElement(rExport, XML_NAMESPACE_LO_EXT, XML_OPACITY_STOP, true, true); + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 01b9515172e2..8a5615a0a790 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -3262,4 +3262,8 @@ alias tag fill-use-slide-background may-break-between-pages +gradient-stop +opacity-stop +color-value +color-type TOKEN_END_DUMMY -- cgit