diff options
-rw-r--r-- | basegfx/source/tools/bgradient.cxx | 53 | ||||
-rw-r--r-- | chart2/qa/extras/chart2geometry.cxx | 5 | ||||
-rw-r--r-- | include/basegfx/utils/bgradient.hxx | 7 | ||||
-rw-r--r-- | xmloff/qa/unit/data/tdf155549_MCGR_AxialGradientCompatible.odt | bin | 0 -> 12891 bytes | |||
-rw-r--r-- | xmloff/qa/unit/data/tdf155549_MCGR_AxialTransparencyCompatible.odt | bin | 0 -> 22129 bytes | |||
-rw-r--r-- | xmloff/qa/unit/style.cxx | 63 | ||||
-rw-r--r-- | xmloff/source/style/GradientStyle.cxx | 5 | ||||
-rw-r--r-- | xmloff/source/style/TransGradientStyle.cxx | 2 |
8 files changed, 130 insertions, 5 deletions
diff --git a/basegfx/source/tools/bgradient.cxx b/basegfx/source/tools/bgradient.cxx index b7ee0780a2cc..b56ef0540d17 100644 --- a/basegfx/source/tools/bgradient.cxx +++ b/basegfx/source/tools/bgradient.cxx @@ -657,6 +657,28 @@ double BColorStops::detectPossibleOffsetAtStart() const return aColorL->getStopOffset(); } +// checks whether the color stops are symmetrical in color and offset. +bool BColorStops::isSymmetrical() const +{ + if (empty()) + return false; + if (1 == size()) + return basegfx::fTools::equal(0.5, front().getStopOffset()); + + BColorStops::const_iterator aIter(begin()); // for going forward + BColorStops::const_iterator aRIter(end()); // for going backward + --aRIter; + // We have at least two elements, so aIter <= aRIter fails before iterators no longer point to + // an element. + while (aIter <= aRIter && aIter->getStopColor().equal(aRIter->getStopColor()) + && basegfx::fTools::equal(aIter->getStopOffset(), 1.0 - aRIter->getStopOffset())) + { + ++aIter; + --aRIter; + } + return aIter > aRIter; +} + std::string BGradient::GradientStyleToString(css::awt::GradientStyle eStyle) { switch (eStyle) @@ -917,7 +939,7 @@ void BGradient::tryToRecreateBorder(basegfx::BColorStops* pAssociatedTransparenc pAssociatedTransparencyStops->removeSpaceAtStart(fOffset); // ...and create border value - SetBorder(static_cast<sal_uInt16>(fOffset * 100.0)); + SetBorder(static_cast<sal_uInt16>(std::lround(fOffset * 100.0))); } if (bIsAxial) @@ -971,6 +993,35 @@ void BGradient::tryToApplyStartEndIntensity() SetStartIntens(100); SetEndIntens(100); } + +void BGradient::tryToConvertToAxial() +{ + if (css::awt::GradientStyle_LINEAR != GetGradientStyle() || 0 != GetBorder() + || GetColorStops().empty()) + return; + + if (!GetColorStops().isSymmetrical()) + return; + + SetGradientStyle(css::awt::GradientStyle_AXIAL); + + // Stretch the first half of the color stops to double width + // and collect them in a new color stops vector. + BColorStops aAxialColorStops; + aAxialColorStops.reserve(std::ceil(GetColorStops().size() / 2.0)); + BColorStops::const_iterator aIter(GetColorStops().begin()); + while (basegfx::fTools::lessOrEqual(aIter->getStopOffset(), 0.5)) + { + BColorStop aNextStop(std::clamp((*aIter).getStopOffset() * 2.0, 0.0, 1.0), + (*aIter).getStopColor()); + aAxialColorStops.push_back(aNextStop); + ++aIter; + } + // Axial gradients have outmost color as last color stop. + aAxialColorStops.reverseColorStops(); + + SetColorStops(aAxialColorStops); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/qa/extras/chart2geometry.cxx b/chart2/qa/extras/chart2geometry.cxx index d52633c80814..f560d46ba054 100644 --- a/chart2/qa/extras/chart2geometry.cxx +++ b/chart2/qa/extras/chart2geometry.cxx @@ -340,9 +340,8 @@ void Chart2GeometryTest::testTdf128345Legend_CS_TG_axial_import() const OString sAttribute("@draw:name='" + OU2O(sOUOpacityName) + "'"); const OString sStart("//office:document-styles/office:styles/draw:opacity[" + sAttribute); assertXPath(pXmlDoc2, sStart + "]", 1); - // MCGR: Needs odf im/export for MCGR, then adapt. - assertXPath(pXmlDoc2, sStart + " and @draw:style='linear']"); // MCGR: axial -> linear - assertXPath(pXmlDoc2, sStart + " and @draw:start='100%']"); // MCGR: 0% -> 100% + assertXPath(pXmlDoc2, sStart + " and @draw:style='axial']"); + assertXPath(pXmlDoc2, sStart + " and @draw:start='0%']"); assertXPath(pXmlDoc2, sStart + " and @draw:end='100%']"); } diff --git a/include/basegfx/utils/bgradient.hxx b/include/basegfx/utils/bgradient.hxx index 49598c8266fa..1f42b23c6321 100644 --- a/include/basegfx/utils/bgradient.hxx +++ b/include/basegfx/utils/bgradient.hxx @@ -273,6 +273,9 @@ public: // try to detect if an empty/no-color-change area exists // at the start and return offset to it. Returns 0.0 if not. double detectPossibleOffsetAtStart() const; + + // returns true if the color stops are symmetrical in color and offset, otherwise false. + bool isSymmetrical() const; }; class BASEGFX_DLLPUBLIC BGradient final @@ -338,6 +341,10 @@ public: void tryToRecreateBorder(basegfx::BColorStops* pAssociatedTransparencyStops = nullptr); void tryToApplyBorder(); void tryToApplyStartEndIntensity(); + + // If a linear gradient is symmetrical it is converted to an axial gradient. + // Does nothing in other cases and for other gradient types. + void tryToConvertToAxial(); }; } diff --git a/xmloff/qa/unit/data/tdf155549_MCGR_AxialGradientCompatible.odt b/xmloff/qa/unit/data/tdf155549_MCGR_AxialGradientCompatible.odt Binary files differnew file mode 100644 index 000000000000..ca9f49e9069f --- /dev/null +++ b/xmloff/qa/unit/data/tdf155549_MCGR_AxialGradientCompatible.odt diff --git a/xmloff/qa/unit/data/tdf155549_MCGR_AxialTransparencyCompatible.odt b/xmloff/qa/unit/data/tdf155549_MCGR_AxialTransparencyCompatible.odt Binary files differnew file mode 100644 index 000000000000..5fda0c063ffa --- /dev/null +++ b/xmloff/qa/unit/data/tdf155549_MCGR_AxialTransparencyCompatible.odt diff --git a/xmloff/qa/unit/style.cxx b/xmloff/qa/unit/style.cxx index 3c5c050c0226..bde981d38864 100644 --- a/xmloff/qa/unit/style.cxx +++ b/xmloff/qa/unit/style.cxx @@ -590,6 +590,69 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testTransparencyBorderRestoration) SetODFDefaultVersion(nCurrentODFVersion); } +CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testAxialGradientCompatible) +{ + // tdf#155549. An axial gradient with Border, StartColor A and EndColor B is exported to OOXML as + // symmetrical linear gradient with three stops, colors B A B. After the changes for multi-color + // gradients (MCGR) this is imported as linear gradient with colors B A B. So a consumer not able + // of MCGR would get a linear gradient with start and end color B. For better compatibility + // ODF export writes an axial gradient. with colors A and B. + // This test needs to be adapted when color stops are available in ODF strict and widely + // supported in even older LibreOffice versions. + loadFromURL(u"tdf155549_MCGR_AxialGradientCompatible.odt"); + + //Round-trip through OOXML. + // FixMe tdf#153183. Here "Attribute 'ID' is not allowed to appear in element 'v:rect'". + skipValidation(); + saveAndReload("Office Open XML Text"); + saveAndReload("writer8"); + + // Examine reloaded file + uno::Reference<drawing::XShape> xShape(getShape(0)); + CPPUNIT_ASSERT_MESSAGE("No shape", xShape.is()); + uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY); + + // Without fix these would have failed with Style=0 (=LINEAR), StartColor=0xFFFF00 and Border=0. + awt::Gradient2 aGradient; + xShapeProperties->getPropertyValue("FillGradient") >>= aGradient; + CPPUNIT_ASSERT_EQUAL_MESSAGE("gradient style", awt::GradientStyle_AXIAL, aGradient.Style); + CPPUNIT_ASSERT_EQUAL_MESSAGE("EndColor", sal_Int32(0xFFFF00), aGradient.EndColor); + CPPUNIT_ASSERT_EQUAL_MESSAGE("StartColor", sal_Int32(0x1E90FF), aGradient.StartColor); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Border", sal_Int16(20), aGradient.Border); +} + +CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testAxialTransparencyCompatible) +{ + // tdf#155549. The shape in the document has a solid color and an axial transparency gradient + // with 'Transition start 60%', 'Start value 10%' and 'End value 80%'. The gradient is exported + // to OOXML as linear symmetrical gradient with three gradient stops. After the changes for + // multi-color gradients (MCGR) this is imported as linear transparency gradient. For better + // compatibility with consumers not able to use MCGR, the ODF export writes the transparency as + // axial transparency gradient that is same as in the original document. + // This test needs to be adapted when color stops are available in ODF strict and widely + // supported in even older LibreOffice versions. + loadFromURL(u"tdf155549_MCGR_AxialTransparencyCompatible.odt"); + + //Round-trip through OOXML. + // FixMe tdf#153183, and error in charSpace and in CharacterSet + //skipValidation(); + saveAndReload("Office Open XML Text"); + saveAndReload("writer8"); + + // Examine reloaded file + uno::Reference<drawing::XShape> xShape(getShape(0)); + CPPUNIT_ASSERT(xShape.is()); + uno::Reference<beans::XPropertySet> xShapeProperties(xShape, uno::UNO_QUERY); + + // Without fix these would have failed with Style=LINEAR, StartColor=0xCCCCCC and wrong Border. + awt::Gradient2 aTransGradient; + xShapeProperties->getPropertyValue("FillTransparenceGradient") >>= aTransGradient; + CPPUNIT_ASSERT_EQUAL_MESSAGE("gradient style", awt::GradientStyle_AXIAL, aTransGradient.Style); + CPPUNIT_ASSERT_EQUAL_MESSAGE("EndColor", sal_Int32(0xCCCCCC), aTransGradient.EndColor); + CPPUNIT_ASSERT_EQUAL_MESSAGE("StartColor", sal_Int32(0x191919), aTransGradient.StartColor); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Border", sal_Int16(60), aTransGradient.Border); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/style/GradientStyle.cxx b/xmloff/source/style/GradientStyle.cxx index 7598074fc409..d80b3f866482 100644 --- a/xmloff/source/style/GradientStyle.cxx +++ b/xmloff/source/style/GradientStyle.cxx @@ -228,6 +228,11 @@ void XMLGradientStyleExport::exportXML( basegfx::BGradient aGradient(rValue); + // Export of axial gradient to OOXML produces a symmetrical linear multi-color gradient. Import + // does not regenerate it as 'axial' because that is not needed for MCGR. For export to ODF we + // try to regenerate 'axial' for to get a better compatibility with LO versions before MCGR. + aGradient.tryToConvertToAxial(); + // MCGR: For better compatibility with LO versions before MCGR, try // to re-create a 'border' value based on the existing gradient stops. // With MCGR we do not need 'border' anymore in quite some cases since diff --git a/xmloff/source/style/TransGradientStyle.cxx b/xmloff/source/style/TransGradientStyle.cxx index 9c268a21ff85..3e89edb683f5 100644 --- a/xmloff/source/style/TransGradientStyle.cxx +++ b/xmloff/source/style/TransGradientStyle.cxx @@ -180,7 +180,7 @@ void XMLTransGradientStyleExport::exportXML( basegfx::BGradient aGradient(rValue); - // ToDo: aGradient.tryToConvertToAxial(); + aGradient.tryToConvertToAxial(); aGradient.tryToRecreateBorder(nullptr); |