summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--basegfx/source/tools/bgradient.cxx53
-rw-r--r--chart2/qa/extras/chart2geometry.cxx5
-rw-r--r--include/basegfx/utils/bgradient.hxx7
-rw-r--r--xmloff/qa/unit/data/tdf155549_MCGR_AxialGradientCompatible.odtbin0 -> 12891 bytes
-rw-r--r--xmloff/qa/unit/data/tdf155549_MCGR_AxialTransparencyCompatible.odtbin0 -> 22129 bytes
-rw-r--r--xmloff/qa/unit/style.cxx63
-rw-r--r--xmloff/source/style/GradientStyle.cxx5
-rw-r--r--xmloff/source/style/TransGradientStyle.cxx2
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
new file mode 100644
index 000000000000..ca9f49e9069f
--- /dev/null
+++ b/xmloff/qa/unit/data/tdf155549_MCGR_AxialGradientCompatible.odt
Binary files differ
diff --git a/xmloff/qa/unit/data/tdf155549_MCGR_AxialTransparencyCompatible.odt b/xmloff/qa/unit/data/tdf155549_MCGR_AxialTransparencyCompatible.odt
new file mode 100644
index 000000000000..5fda0c063ffa
--- /dev/null
+++ b/xmloff/qa/unit/data/tdf155549_MCGR_AxialTransparencyCompatible.odt
Binary files differ
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);