basegfx/source/tools/bgradient.cxx                           |  206 
 basegfx/source/tools/gradienttools.cxx                       |   99 
 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx   |  205 
 drawinglayer/source/tools/emfphelperdata.cxx                 |   56 
 drawinglayer/source/tools/emfphelperdata.hxx                 |    2 
 drawinglayer/source/tools/emfppath.cxx                       |  126 
 drawinglayer/source/tools/emfppath.hxx                       |    5 
 emfio/qa/cppunit/emf/EmfImportTest.cxx                       |  186 
 emfio/qa/cppunit/emf/data/TestEmfPlusFillClosedCurve.emf     |binary
 emfio/qa/cppunit/emf/data/TestEmfPlusRecordTypeDrawCurve.emf |binary
 include/basegfx/utils/bgradient.hxx                          |   29 
 include/basegfx/utils/gradienttools.hxx                      |   21 
 include/svx/sdr/contact/viewobjectcontact.hxx                |    3 
 include/xmloff/GradientStyle.hxx                             |    4 
 include/xmloff/HatchStyle.hxx                                |    4 
 oox/source/drawingml/fillproperties.cxx                      |   97 
 oox/source/export/drawingml.cxx                              |   61 
 readlicense_oo/license/CREDITS.fodt                          | 3438 +++++------
 sc/qa/uitest/calc_tests9/tdf154174.py                        |   35 
 sc/source/core/data/column3.cxx                              |   23 
 sc/source/core/data/conditio.cxx                             |    2 
 sc/source/core/data/dociter.cxx                              |    6 
 sc/source/ui/view/viewfunc.cxx                               |    2 
 sd/source/ui/dlg/navigatr.cxx                                |    1 
 sd/source/ui/dlg/sdtreelb.cxx                                |   35 
 svtools/source/misc/sampletext.cxx                           |    5 
 svx/source/sdr/contact/viewobjectcontact.cxx                 |   11 
 svx/source/xoutdev/xattr.cxx                                 |    7 
 sw/qa/extras/htmlexport/data/listsWithNumFormat.fodt         |   74 
 sw/qa/extras/htmlexport/data/tableRight.fodt                 |   22 
 sw/qa/extras/htmlexport/htmlexport.cxx                       |   31 
 sw/qa/extras/uiwriter/data/pageBreakWithPageStyle.fodt       |   17 
 sw/qa/extras/uiwriter/uiwriter8.cxx                          |   32 
 sw/qa/extras/ww8export/data/tdf104704_mangledFooter.odt      |binary
 sw/qa/extras/ww8export/ww8export4.cxx                        |    5 
 sw/source/core/doc/docfmt.cxx                                |   60 
 sw/source/core/doc/notxtfrm.cxx                              |    3 
 sw/source/core/text/porlay.cxx                               |   24 
 sw/source/filter/html/htmlnumwriter.cxx                      |    5 
 sw/source/filter/html/htmltabw.cxx                           |   19 
 sw/source/filter/ww8/ww8atr.cxx                              |    5 
 sw/source/uibase/uiview/viewmdi.cxx                          |   13 
 vcl/inc/osx/a11ywrapper.h                                    |    3 
 vcl/osx/a11ytextattributeswrapper.mm                         |    1 
 vcl/osx/a11ywrapper.mm                                       |   72 
 vcl/osx/salframeview.mm                                      |    8 
 vcl/qa/cppunit/pdfexport/data/tdf155190.odt                  |binary
 vcl/qa/cppunit/pdfexport/pdfexport.cxx                       |   96 
 vcl/quartz/salgdi.cxx                                        |    6 
 vcl/source/gdi/CommonSalLayout.cxx                           |    2 
 vcl/source/gdi/impglyphitem.cxx                              |    5 
 vcl/source/gdi/sallayout.cxx                                 |   13 
 vcl/source/outdev/font.cxx                                   |    8 
 xmloff/qa/unit/data/MCGR_Border_restoration.pptx             |binary
 xmloff/qa/unit/data/MCGR_TransparencyBorder_restoration.pptx |binary
 xmloff/qa/unit/style.cxx                                     |   76 
 xmloff/source/style/GradientStyle.cxx                        |  116 
 xmloff/source/style/HatchStyle.cxx                           |   26 
 xmloff/source/style/TransGradientStyle.cxx                   |   72 
 59 files changed, 3224 insertions(+), 2259 deletions(-)

New commits:
commit dd2c45a12218f63be5121ce113a0e75bd317aba1
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Thu Jun 1 19:01:00 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:12 2023 +0200

    MCGR: Use tryToRecreateBorder too for transparency
    
    This is similar to 'Use tryToRecreateBorder for better BW comp with LO'
    (see commit 8259a99f41367a1d8326c9157fe1902915715879), but now for
    transparency gradients.
    
    Change-Id: I1c2e11562fa998c364896d517f4ed3bfe92f6c15
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152508
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.hensc...@t-online.de>

diff --git a/xmloff/qa/unit/data/MCGR_TransparencyBorder_restoration.pptx 
b/xmloff/qa/unit/data/MCGR_TransparencyBorder_restoration.pptx
new file mode 100644
index 000000000000..f32c4d5e2b21
Binary files /dev/null and 
b/xmloff/qa/unit/data/MCGR_TransparencyBorder_restoration.pptx differ
diff --git a/xmloff/qa/unit/style.cxx b/xmloff/qa/unit/style.cxx
index 1a291a2fba84..372cf003f613 100644
--- a/xmloff/qa/unit/style.cxx
+++ b/xmloff/qa/unit/style.cxx
@@ -559,6 +559,43 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, 
testBorderRestoration)
     SetODFDefaultVersion(nCurrentODFVersion);
 }
 
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testTransparencyBorderRestoration)
+{
+    // Load document. It has a shape with transparency gradient build from 
transparency 100% at
+    // offset 0, transparency 100% at offset 0.4 and transparency 10% at 
offset 1.0. For better
+    // backward compatibility such gradient is exported with a border of 40% 
in the transparency
+    // gradient. The color itself is the same for all gradient stops.
+    // When transparency gradient-stops are integrated in ODF strict, the test 
needs to be adapted.
+    loadFromURL(u"MCGR_TransparencyBorder_restoration.pptx");
+
+    // Backup original ODF default version
+    const SvtSaveOptions::ODFDefaultVersion 
nCurrentODFVersion(GetODFDefaultVersion());
+
+    // Save to ODF_LATEST which is currently ODF 1.3 extended. Make sure 
transparency gradient-stop
+    //elements are written with offset 0 and 1, and border is written as 40%.
+    SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_LATEST);
+    save("impress8");
+    xmlDocUniquePtr pXmlDoc = parseExport("styles.xml");
+    OString sPath = "/office:document-styles/office:styles/draw:opacity[1]";
+    assertXPath(pXmlDoc, sPath + "/loext:opacity-stop[2]", "stop-opacity", 
"0.9");
+    assertXPath(pXmlDoc, sPath + "/loext:opacity-stop[2]", "offset", "1");
+    assertXPath(pXmlDoc, sPath + "/loext:opacity-stop[1]", "stop-opacity", 
"0");
+    assertXPath(pXmlDoc, sPath + "/loext:opacity-stop[1]", "offset", "0");
+    assertXPath(pXmlDoc, sPath, "border", "40%");
+
+    // Save to ODF 1.3 strict and make sure border, start and end opacity are 
suitable set.
+    SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_013);
+    save("impress8");
+    pXmlDoc = parseExport("styles.xml");
+    assertXPath(pXmlDoc, sPath + "/loext:opacity-stop", 0);
+    assertXPath(pXmlDoc, sPath, "start", "0%");
+    assertXPath(pXmlDoc, sPath, "end", "90%");
+    assertXPath(pXmlDoc, sPath, "border", "40%");
+
+    // Set back to original ODF default version.
+    SetODFDefaultVersion(nCurrentODFVersion);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmloff/source/style/TransGradientStyle.cxx 
b/xmloff/source/style/TransGradientStyle.cxx
index d89348bbe0e8..9c268a21ff85 100644
--- a/xmloff/source/style/TransGradientStyle.cxx
+++ b/xmloff/source/style/TransGradientStyle.cxx
@@ -21,6 +21,7 @@
 
 #include <com/sun/star/awt/Gradient2.hpp>
 
+#include <basegfx/utils/bgradient.hxx>
 #include <comphelper/documentconstants.hxx>
 #include <rtl/ustrbuf.hxx>
 #include <rtl/ustring.hxx>
@@ -169,19 +170,26 @@ void XMLTransGradientStyleExport::exportXML(
     const OUString& rStrName,
     const uno::Any& rValue )
 {
-    awt::Gradient2 aGradient;
-
+    // MCGR: We try to write the gradient so, that applications without 
multi-color gradient support
+    // can render it as best as possible.
+    // This is similar to XMLGradientStyleExport::exportXML(). For details see 
there.
     if( rStrName.isEmpty() )
         return;
-
-    if( !(rValue >>= aGradient) )
+    if (!rValue.has<css::awt::Gradient2>() && 
!rValue.has<css::awt::Gradient>())
         return;
 
+    basegfx::BGradient aGradient(rValue);
+
+    // ToDo: aGradient.tryToConvertToAxial();
+
+    aGradient.tryToRecreateBorder(nullptr);
+
     OUString aStrValue;
     OUStringBuffer aOut;
 
     // Style
-    if( !SvXMLUnitConverter::convertEnum( aOut, aGradient.Style, 
pXML_GradientStyle_Enum ) )
+    if (!SvXMLUnitConverter::convertEnum(aOut, aGradient.GetGradientStyle(),
+                                         pXML_GradientStyle_Enum))
         return;
 
     // Name
@@ -197,71 +205,75 @@ void XMLTransGradientStyleExport::exportXML(
     rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE, aStrValue );
 
     // Center x/y
-    if( aGradient.Style != awt::GradientStyle_LINEAR &&
-        aGradient.Style != awt::GradientStyle_AXIAL   )
+    if (awt::GradientStyle_LINEAR != aGradient.GetGradientStyle()
+        && awt::GradientStyle_AXIAL != aGradient.GetGradientStyle())
     {
-        ::sax::Converter::convertPercent(aOut, aGradient.XOffset);
+        ::sax::Converter::convertPercent(aOut, aGradient.GetXOffset());
         aStrValue = aOut.makeStringAndClear();
         rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CX, aStrValue );
 
-        ::sax::Converter::convertPercent(aOut, aGradient.YOffset);
+        ::sax::Converter::convertPercent(aOut, aGradient.GetYOffset());
         aStrValue = aOut.makeStringAndClear();
         rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CY, aStrValue );
     }
 
-    // Transparency start
-    Color aColor(ColorTransparency, aGradient.StartColor);
-    sal_Int32 aStartValue = 100 - static_cast<sal_Int32>(((aColor.GetRed() + 
1) * 100) / 255);
-    ::sax::Converter::convertPercent( aOut, aStartValue );
+    // LO uses a gray color as transparency. ODF uses opacity in range 
[0%,100%].
+    // Default 100% opacity.
+    double fOpacityStartPerc = 100.0;
+    double fOpacityEndPerc = 100.0;
+    if (!aGradient.GetColorStops().empty())
+    {
+        fOpacityStartPerc
+            = (1.0 - 
aGradient.GetColorStops().front().getStopColor().getRed()) * 100.0;
+        fOpacityEndPerc = (1.0 - 
aGradient.GetColorStops().back().getStopColor().getRed()) * 100.0;
+    }
+
+    // Opacity start
+    ::sax::Converter::convertPercent(aOut, 
static_cast<sal_Int32>(std::lround(fOpacityStartPerc)));
     aStrValue = aOut.makeStringAndClear();
     rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START, aStrValue );
 
-    // Transparency end
-    aColor = Color(ColorTransparency, aGradient.EndColor);
-    sal_Int32 aEndValue = 100 - static_cast<sal_Int32>(((aColor.GetRed() + 1) 
* 100) / 255);
-    ::sax::Converter::convertPercent( aOut, aEndValue );
+    // Opacity end
+    ::sax::Converter::convertPercent( aOut, 
static_cast<sal_Int32>(std::lround(fOpacityEndPerc)));
     aStrValue = aOut.makeStringAndClear();
     rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END, aStrValue );
 
     // Angle
-    if( aGradient.Style != awt::GradientStyle_RADIAL )
+    if (awt::GradientStyle_RADIAL != aGradient.GetGradientStyle())
     {
-        ::sax::Converter::convertAngle(aOut, aGradient.Angle, 
rExport.getSaneDefaultVersion());
+        ::sax::Converter::convertAngle(aOut, aGradient.GetAngle().get(),
+                                       rExport.getSaneDefaultVersion());
         aStrValue = aOut.makeStringAndClear();
         rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_GRADIENT_ANGLE, 
aStrValue );
     }
 
     // Border
-    ::sax::Converter::convertPercent( aOut, aGradient.Border );
+    ::sax::Converter::convertPercent(aOut, aGradient.GetBorder());
     aStrValue = aOut.makeStringAndClear();
     rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_BORDER, aStrValue );
 
     // ctor writes start tag. End-tag is written by destructor at block end.
-    SvXMLElementExport rElem( rExport,
-                              XML_NAMESPACE_DRAW, XML_OPACITY,
-                              true, false );
+    SvXMLElementExport rElem(rExport, XML_NAMESPACE_DRAW, XML_OPACITY, true, 
false);
 
     // Write child elements <loext:opacity-stop>
     // 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)
+    if (aGradient.GetColorStops().empty())
         return;
+
     double fPreviousOffset = 0.0;
-    for (auto& aCandidate : aGradient.ColorStops)
+    for (auto& aCandidate : aGradient.GetColorStops())
     {
         // Attribute svg:offset. Make sure offsets are increasing.
-        double fOffset = std::clamp<double>(aCandidate.StopOffset, 0.0, 1.0);
+        double fOffset = std::clamp<double>(aCandidate.getStopOffset(), 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<double>(1.0 - aDecimalColor.Red, 0.0, 
1.0);
+        double fOpacity = std::clamp<double>(1.0 - 
aCandidate.getStopColor().getRed(), 0.0, 1.0);
         rExport.AddAttribute(XML_NAMESPACE_SVG, XML_STOP_OPACITY, 
OUString::number(fOpacity));
 
         // write opacity stop element
commit f770cc4ec9407e0c47b40f1cb6e4b8c8fb47a9ae
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Wed May 31 15:16:25 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:12 2023 +0200

    MCGR: Unit test for 'Use tryToRecreateBorder for...'
    
    Unit test for commit 8259a99f41367a1d8326c9157fe1902915715879.
    
    Change-Id: Iba3367fe42b5014b98cf575f53006fc09a79d6ee
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152428
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.hensc...@t-online.de>

diff --git a/xmloff/qa/unit/data/MCGR_Border_restoration.pptx 
b/xmloff/qa/unit/data/MCGR_Border_restoration.pptx
new file mode 100644
index 000000000000..b43e15145f89
Binary files /dev/null and b/xmloff/qa/unit/data/MCGR_Border_restoration.pptx 
differ
diff --git a/xmloff/qa/unit/style.cxx b/xmloff/qa/unit/style.cxx
index 2d7706bf7b9e..1a291a2fba84 100644
--- a/xmloff/qa/unit/style.cxx
+++ b/xmloff/qa/unit/style.cxx
@@ -22,6 +22,7 @@
 
 #include <officecfg/Office/Common.hxx>
 #include <rtl/character.hxx>
+#include <unotools/saveopt.hxx>
 
 using namespace ::com::sun::star;
 
@@ -520,6 +521,44 @@ CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testMCGR_threeStops)
     CPPUNIT_ASSERT_EQUAL(0.0, aColorStop.StopColor.Blue);
 }
 
+CPPUNIT_TEST_FIXTURE(XmloffStyleTest, testBorderRestoration)
+{
+    // Load document. It has a shape with color gradient build from color stop 
yellow at offset 0.5
+    // and color stop red at offset 1.0. For better backward compatibility 
such gradient has to be
+    // exported to ODF with a border of 50%.
+    // When gradient-stops are integrated in ODF strict, the test needs to be 
adapted.
+
+    loadFromURL(u"MCGR_Border_restoration.pptx");
+
+    // Backup original ODF default version
+    const SvtSaveOptions::ODFDefaultVersion 
nCurrentODFVersion(GetODFDefaultVersion());
+
+    // Save to ODF_LATEST which is currently ODF 1.3 extended. Make sure 
gradient-stop elements have
+    // offsets 0 and 1, and border is written as 50%.
+    SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_LATEST);
+    save("impress8");
+    xmlDocUniquePtr pXmlDoc = parseExport("styles.xml");
+    OString sPath
+        = 
"/office:document-styles/office:styles/draw:gradient[@draw:name='Gradient_20_1']";
+    assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[2]", "color-value", 
"#ff0000");
+    assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[2]", "offset", "1");
+    assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[1]", "color-value", 
"#ffff00");
+    assertXPath(pXmlDoc, sPath + "/loext:gradient-stop[1]", "offset", "0");
+    assertXPath(pXmlDoc, sPath, "border", "50%");
+
+    // Save to ODF 1.3 strict and make sure border, start-color and end-color 
are suitable set.
+    SetODFDefaultVersion(SvtSaveOptions::ODFDefaultVersion::ODFVER_013);
+    save("impress8");
+    pXmlDoc = parseExport("styles.xml");
+    assertXPath(pXmlDoc, sPath + "/loext:gradient-stop", 0);
+    assertXPath(pXmlDoc, sPath, "start-color", "#ffff00");
+    assertXPath(pXmlDoc, sPath, "end-color", "#ff0000");
+    assertXPath(pXmlDoc, sPath, "border", "50%");
+
+    // Set back to original ODF default version.
+    SetODFDefaultVersion(nCurrentODFVersion);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 8e3ae003634fcf3f36d7202e604be4ba134cf526
Author:     Andrea Gelmini <andrea.gelm...@gelma.net>
AuthorDate: Wed May 31 19:26:21 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:11 2023 +0200

    Fix typo
    
    Change-Id: I9c58980de73dc2f00802f0d589d0c6fafe11d16a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152460
    Tested-by: Julien Nabet <serval2...@yahoo.fr>
    Reviewed-by: Julien Nabet <serval2...@yahoo.fr>

diff --git a/xmloff/source/style/GradientStyle.cxx 
b/xmloff/source/style/GradientStyle.cxx
index fb1fc68fb077..7598074fc409 100644
--- a/xmloff/source/style/GradientStyle.cxx
+++ b/xmloff/source/style/GradientStyle.cxx
@@ -231,7 +231,7 @@ void XMLGradientStyleExport::exportXML(
     // 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
-    // no Start/EndColor at 0.0 resp. 1.0 is explicitely needed. Since we
+    // no Start/EndColor at 0.0 resp. 1.0 is explicitly needed. Since we
     // (unfortunately need to) internally continue to support border
     // anyways it does no harm to fallback to use the border value - if
     // there is an equivalent representation as this helper checks for.
commit 5e93d1d53631b1c188977b35c7c6d25416dcad3d
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Tue May 30 16:48:38 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:11 2023 +0200

    MCGR: Use tryToRecreateBorder for better BW comp with LO
    
    For better compatibility with LO versions before MCGR, try
    to re-create a 'border' value based on the existing GradientSteps.
    
    With MCGR we do not need 'border' anymore in quite some cases since
    no Start/EndColor at 0.0 resp. 1.0 is explicitely needed. Since we
    (unfortunately need to) internally continue to support border
    anyways it does no harm to fallback to use the border value - if
    there is an equivalent representation as this helper checks for.
    
    For exports that do not support 'border' this will be adapted as
    needed (see tryToApplyBorder())
    
    Change-Id: If98c64039ff97143d4b5c92ac2a950e70f5bb70a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152395
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/include/basegfx/utils/bgradient.hxx 
b/include/basegfx/utils/bgradient.hxx
index ab70d05880de..b7c2cf8c9e3f 100644
--- a/include/basegfx/utils/bgradient.hxx
+++ b/include/basegfx/utils/bgradient.hxx
@@ -335,7 +335,7 @@ public:
     css::awt::Gradient2 getAsGradient2() const;
 
     /// Tooling to handle border correction/integration and StartStopIntensity
-    void tryToRecreateBorder(basegfx::BColorStops* 
pAssociatedTransparencyStops);
+    void tryToRecreateBorder(basegfx::BColorStops* 
pAssociatedTransparencyStops = nullptr);
     void tryToApplyBorder();
     void tryToApplyStartEndIntensity();
 };
diff --git a/xmloff/source/style/GradientStyle.cxx 
b/xmloff/source/style/GradientStyle.cxx
index fcc371c89ffb..fb1fc68fb077 100644
--- a/xmloff/source/style/GradientStyle.cxx
+++ b/xmloff/source/style/GradientStyle.cxx
@@ -34,6 +34,7 @@
 #include <xmloff/xmltkmap.hxx>
 #include <xmloff/xmltoken.hxx>
 #include <xmloff/xmluconv.hxx>
+#include <basegfx/utils/bgradient.hxx>
 
 using namespace ::com::sun::star;
 using namespace ::xmloff::token;
@@ -219,19 +220,30 @@ void XMLGradientStyleExport::exportXML(
     const OUString& rStrName,
     const uno::Any& rValue )
 {
-    awt::Gradient2 aGradient;
-
     if( rStrName.isEmpty() )
         return;
 
-    if( !(rValue >>= aGradient) )
+    if (!rValue.has<css::awt::Gradient2>() && 
!rValue.has<css::awt::Gradient>())
         return;
 
+    basegfx::BGradient aGradient(rValue);
+
+    // 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
+    // no Start/EndColor at 0.0 resp. 1.0 is explicitely needed. Since we
+    // (unfortunately need to) internally continue to support border
+    // anyways it does no harm to fallback to use the border value - if
+    // there is an equivalent representation as this helper checks for.
+    // For exports that do not support 'border' this will be adapted as
+    // needed (see tryToApplyBorder()).
+    aGradient.tryToRecreateBorder(nullptr);
+
     OUString aStrValue;
     OUStringBuffer aOut;
 
     // Style
-    if( !SvXMLUnitConverter::convertEnum( aOut, aGradient.Style, 
pXML_GradientStyle_Enum ) )
+    if( !SvXMLUnitConverter::convertEnum( aOut, aGradient.GetGradientStyle(), 
pXML_GradientStyle_Enum ) )
         return;
 
     // Name
@@ -247,47 +259,57 @@ void XMLGradientStyleExport::exportXML(
     m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE, aStrValue );
 
     // Center x/y
-    if( aGradient.Style != awt::GradientStyle_LINEAR &&
-        aGradient.Style != awt::GradientStyle_AXIAL   )
+    if( aGradient.GetGradientStyle() != awt::GradientStyle_LINEAR &&
+        aGradient.GetGradientStyle() != awt::GradientStyle_AXIAL   )
     {
-        ::sax::Converter::convertPercent(aOut, aGradient.XOffset);
+        ::sax::Converter::convertPercent(aOut, aGradient.GetXOffset());
         aStrValue = aOut.makeStringAndClear();
         m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CX, aStrValue );
-        ::sax::Converter::convertPercent(aOut, aGradient.YOffset);
+        ::sax::Converter::convertPercent(aOut, aGradient.GetYOffset());
         aStrValue = aOut.makeStringAndClear();
         m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CY, aStrValue );
     }
 
+    // prep Start/EndColor, default black
+    basegfx::BColor aStartColor;
+    basegfx::BColor aEndColor;
+
+    if (!aGradient.GetColorStops().empty())
+    {
+        aStartColor = aGradient.GetColorStops().front().getStopColor();
+        aEndColor = aGradient.GetColorStops().back().getStopColor();
+    }
+
     // Color start
-    ::sax::Converter::convertColor(aOut, aGradient.StartColor);
+    ::sax::Converter::convertColor(aOut, Color(aStartColor));
     aStrValue = aOut.makeStringAndClear();
     m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START_COLOR, aStrValue );
 
     // Color end
-    ::sax::Converter::convertColor(aOut, aGradient.EndColor);
+    ::sax::Converter::convertColor(aOut, Color(aEndColor));
     aStrValue = aOut.makeStringAndClear();
     m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END_COLOR, aStrValue );
 
     // Intensity start
-    ::sax::Converter::convertPercent(aOut, aGradient.StartIntensity);
+    ::sax::Converter::convertPercent(aOut, aGradient.GetStartIntens());
     aStrValue = aOut.makeStringAndClear();
     m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START_INTENSITY, aStrValue 
);
 
     // Intensity end
-    ::sax::Converter::convertPercent(aOut, aGradient.EndIntensity);
+    ::sax::Converter::convertPercent(aOut, aGradient.GetEndIntens());
     aStrValue = aOut.makeStringAndClear();
     m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END_INTENSITY, aStrValue );
 
     // Angle
-    if( aGradient.Style != awt::GradientStyle_RADIAL )
+    if( aGradient.GetGradientStyle() != awt::GradientStyle_RADIAL )
     {
-        ::sax::Converter::convertAngle(aOut, aGradient.Angle, 
m_rExport.getSaneDefaultVersion());
+        ::sax::Converter::convertAngle(aOut, 
static_cast<sal_Int16>(aGradient.GetAngle()), 
m_rExport.getSaneDefaultVersion());
         aStrValue = aOut.makeStringAndClear();
         m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_GRADIENT_ANGLE, 
aStrValue );
     }
 
     // Border
-    ::sax::Converter::convertPercent( aOut, aGradient.Border );
+    ::sax::Converter::convertPercent( aOut, aGradient.GetBorder() );
     aStrValue = aOut.makeStringAndClear();
     m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_BORDER, aStrValue );
 
@@ -299,15 +321,15 @@ void XMLGradientStyleExport::exportXML(
     // Do not export in standard ODF 1.3 or older.
     if ((m_rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) 
== 0)
         return;
-    sal_Int32 nCount = aGradient.ColorStops.getLength();
-    if (nCount == 0)
+
+    if (aGradient.GetColorStops().empty())
         return;
 
     double fPreviousOffset = 0.0;
-    for (auto& aCandidate : aGradient.ColorStops)
+    for (const auto& aCandidate : aGradient.GetColorStops())
     {
         // Attribute svg:offset. Make sure offsets are increasing.
-        double fOffset = std::clamp<double>(aCandidate.StopOffset, 0.0, 1.0);
+        double fOffset = std::clamp<double>(aCandidate.getStopOffset(), 0.0, 
1.0);
         if (fOffset < fPreviousOffset)
             fOffset = fPreviousOffset;
         m_rExport.AddAttribute(XML_NAMESPACE_SVG, XML_OFFSET, 
OUString::number(fOffset));
@@ -317,10 +339,10 @@ void XMLGradientStyleExport::exportXML(
         m_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<sal_uInt8>(std::round(aDecimalColor.Red 
* 255.0), 0, 255),
-                            
std::clamp<sal_uInt8>(std::round(aDecimalColor.Green * 255.0), 0, 255),
-                            
std::clamp<sal_uInt8>(std::round(aDecimalColor.Blue * 255.0), 0, 255));
+        const basegfx::BColor aDecimalColor(aCandidate.getStopColor());
+        ::Color 
aToolsColor(std::clamp<sal_uInt8>(std::round(aDecimalColor.getRed() * 255.0), 
0, 255),
+                            
std::clamp<sal_uInt8>(std::round(aDecimalColor.getGreen() * 255.0), 0, 255),
+                            
std::clamp<sal_uInt8>(std::round(aDecimalColor.getBlue() * 255.0), 0, 255));
         m_rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_COLOR_VALUE,
                              rtl::OUStringChar('#') + 
aToolsColor.AsRGBHexString());
 
commit 5b6ae81ec086e4b120bc7b66938f17a485a7d8aa
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon May 22 08:27:57 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:11 2023 +0200

    sw: prefix members of XMLGradientStyleExport, XMLGradientStyleImport, ...
    
    ... XMLHatchStyleExport and XMLHatchStyleImport
    
    See tdf#94879 for motivation.
    
    Change-Id: I2dd4c97c6e234447190e46bd6f6a6354e16911bf
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152077
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/include/xmloff/GradientStyle.hxx b/include/xmloff/GradientStyle.hxx
index 04ad2832d997..58975644fb3d 100644
--- a/include/xmloff/GradientStyle.hxx
+++ b/include/xmloff/GradientStyle.hxx
@@ -37,7 +37,7 @@ namespace com::sun::star {
 
 class XMLOFF_DLLPUBLIC XMLGradientStyleImport
 {
-    SvXMLImport& rImport;
+    SvXMLImport& m_rImport;
 
 public:
     XMLGradientStyleImport( SvXMLImport& rImport );
@@ -60,7 +60,7 @@ public:
 
 class XMLOFF_DLLPUBLIC XMLGradientStyleExport
 {
-    SvXMLExport& rExport;
+    SvXMLExport& m_rExport;
 
 public:
     XMLGradientStyleExport( SvXMLExport& rExport );
diff --git a/include/xmloff/HatchStyle.hxx b/include/xmloff/HatchStyle.hxx
index 9ded654e53e2..73c07e643a2c 100644
--- a/include/xmloff/HatchStyle.hxx
+++ b/include/xmloff/HatchStyle.hxx
@@ -35,7 +35,7 @@ namespace com::sun::star {
 
 class XMLOFF_DLLPUBLIC XMLHatchStyleImport
 {
-    SvXMLImport& rImport;
+    SvXMLImport& m_rImport;
 
 public:
     XMLHatchStyleImport( SvXMLImport& rImport );
@@ -48,7 +48,7 @@ public:
 
 class XMLOFF_DLLPUBLIC XMLHatchStyleExport
 {
-    SvXMLExport& rExport;
+    SvXMLExport& m_rExport;
 
 public:
     XMLHatchStyleExport( SvXMLExport& rExport );
diff --git a/xmloff/source/style/GradientStyle.cxx 
b/xmloff/source/style/GradientStyle.cxx
index 3ab6c422b2c8..fcc371c89ffb 100644
--- a/xmloff/source/style/GradientStyle.cxx
+++ b/xmloff/source/style/GradientStyle.cxx
@@ -52,7 +52,7 @@ SvXMLEnumMapEntry<awt::GradientStyle> const 
pXML_GradientStyle_Enum[] =
 // Import
 XMLGradientStyleImport::XMLGradientStyleImport(
     SvXMLImport& rImp )
-    : rImport(rImp)
+    : m_rImport(rImp)
 {
 }
 
@@ -114,14 +114,14 @@ void XMLGradientStyleImport::importXML(
             break;
         case XML_ELEMENT(DRAW, XML_GRADIENT_ANGLE):
             {
-                auto const 
cmp12(rImport.GetODFVersion().compareTo(ODFVER_012_TEXT));
+                auto const 
cmp12(m_rImport.GetODFVersion().compareTo(ODFVER_012_TEXT));
                 bool const bSuccess =
                     ::sax::Converter::convertAngle(aGradient.Angle, 
aIter.toView(),
                         // tdf#89475 try to detect borked OOo angles
                         (cmp12 < 0) || (cmp12 == 0
-                            && 
(rImport.isGeneratorVersionOlderThan(SvXMLImport::AOO_4x, SvXMLImport::LO_7x)
+                            && 
(m_rImport.isGeneratorVersionOlderThan(SvXMLImport::AOO_4x, SvXMLImport::LO_7x)
                                 // also for AOO 4.x, assume there won't ever 
be a 4.2
-                                || rImport.getGeneratorVersion() == 
SvXMLImport::AOO_4x)));
+                                || m_rImport.getGeneratorVersion() == 
SvXMLImport::AOO_4x)));
                 SAL_INFO_IF(!bSuccess, "xmloff.style", "failed to import 
draw:angle");
             }
             break;
@@ -139,7 +139,7 @@ void XMLGradientStyleImport::importXML(
 
     if( !aDisplayName.isEmpty() )
     {
-        rImport.AddStyleDisplayName( XmlStyleFamily::SD_GRADIENT_ID, rStrName,
+        m_rImport.AddStyleDisplayName( XmlStyleFamily::SD_GRADIENT_ID, 
rStrName,
                                      aDisplayName );
         rStrName = aDisplayName;
     }
@@ -211,7 +211,7 @@ XMLGradientStopContext::~XMLGradientStopContext()
 
 XMLGradientStyleExport::XMLGradientStyleExport(
     SvXMLExport& rExp )
-    : rExport(rExp)
+    : m_rExport(rExp)
 {
 }
 
@@ -236,15 +236,15 @@ void XMLGradientStyleExport::exportXML(
 
     // Name
     bool bEncoded = false;
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME,
-                          rExport.EncodeStyleName( rStrName,
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME,
+                          m_rExport.EncodeStyleName( rStrName,
                                                     &bEncoded ) );
     if( bEncoded )
-        rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_DISPLAY_NAME,
+        m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_DISPLAY_NAME,
                                 rStrName );
 
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE, aStrValue );
 
     // Center x/y
     if( aGradient.Style != awt::GradientStyle_LINEAR &&
@@ -252,52 +252,52 @@ void XMLGradientStyleExport::exportXML(
     {
         ::sax::Converter::convertPercent(aOut, aGradient.XOffset);
         aStrValue = aOut.makeStringAndClear();
-        rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CX, aStrValue );
+        m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CX, aStrValue );
         ::sax::Converter::convertPercent(aOut, aGradient.YOffset);
         aStrValue = aOut.makeStringAndClear();
-        rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CY, aStrValue );
+        m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_CY, aStrValue );
     }
 
     // Color start
     ::sax::Converter::convertColor(aOut, aGradient.StartColor);
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START_COLOR, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START_COLOR, aStrValue );
 
     // Color end
     ::sax::Converter::convertColor(aOut, aGradient.EndColor);
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END_COLOR, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END_COLOR, aStrValue );
 
     // Intensity start
     ::sax::Converter::convertPercent(aOut, aGradient.StartIntensity);
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START_INTENSITY, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_START_INTENSITY, aStrValue 
);
 
     // Intensity end
     ::sax::Converter::convertPercent(aOut, aGradient.EndIntensity);
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END_INTENSITY, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_END_INTENSITY, aStrValue );
 
     // Angle
     if( aGradient.Style != awt::GradientStyle_RADIAL )
     {
-        ::sax::Converter::convertAngle(aOut, aGradient.Angle, 
rExport.getSaneDefaultVersion());
+        ::sax::Converter::convertAngle(aOut, aGradient.Angle, 
m_rExport.getSaneDefaultVersion());
         aStrValue = aOut.makeStringAndClear();
-        rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_GRADIENT_ANGLE, 
aStrValue );
+        m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_GRADIENT_ANGLE, 
aStrValue );
     }
 
     // Border
     ::sax::Converter::convertPercent( aOut, aGradient.Border );
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_BORDER, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_BORDER, aStrValue );
 
     // ctor writes start tag. End-tag is written by destructor at block end.
-    SvXMLElementExport aElem( rExport, XML_NAMESPACE_DRAW, XML_GRADIENT,
+    SvXMLElementExport aElem( m_rExport, XML_NAMESPACE_DRAW, XML_GRADIENT,
                           true, false );
 
     // Write child elements <loext:gradient-stop>
     // Do not export in standard ODF 1.3 or older.
-    if ((rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) 
== 0)
+    if ((m_rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) 
== 0)
         return;
     sal_Int32 nCount = aGradient.ColorStops.getLength();
     if (nCount == 0)
@@ -310,22 +310,22 @@ void XMLGradientStyleExport::exportXML(
         double fOffset = std::clamp<double>(aCandidate.StopOffset, 0.0, 1.0);
         if (fOffset < fPreviousOffset)
             fOffset = fPreviousOffset;
-        rExport.AddAttribute(XML_NAMESPACE_SVG, XML_OFFSET, 
OUString::number(fOffset));
+        m_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");
+        m_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<sal_uInt8>(std::round(aDecimalColor.Red 
* 255.0), 0, 255),
                             
std::clamp<sal_uInt8>(std::round(aDecimalColor.Green * 255.0), 0, 255),
                             
std::clamp<sal_uInt8>(std::round(aDecimalColor.Blue * 255.0), 0, 255));
-        rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_COLOR_VALUE,
+        m_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);
+        SvXMLElementExport aStopElement(m_rExport, XML_NAMESPACE_LO_EXT, 
XML_GRADIENT_STOP, true, true);
     }
 }
 
diff --git a/xmloff/source/style/HatchStyle.cxx 
b/xmloff/source/style/HatchStyle.cxx
index a70ee506ed3b..2a0bc2259757 100644
--- a/xmloff/source/style/HatchStyle.cxx
+++ b/xmloff/source/style/HatchStyle.cxx
@@ -50,7 +50,7 @@ SvXMLEnumMapEntry<drawing::HatchStyle> const 
pXML_HatchStyle_Enum[] =
 // Import
 
 XMLHatchStyleImport::XMLHatchStyleImport( SvXMLImport& rImp )
-    : rImport(rImp)
+    : m_rImport(rImp)
 {
 }
 
@@ -67,7 +67,7 @@ void XMLHatchStyleImport::importXML(
     aHatch.Distance = 0;
     aHatch.Angle = 0;
 
-    SvXMLUnitConverter& rUnitConverter = rImport.GetMM100UnitConverter();
+    SvXMLUnitConverter& rUnitConverter = m_rImport.GetMM100UnitConverter();
 
     for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
     {
@@ -110,7 +110,7 @@ void XMLHatchStyleImport::importXML(
 
     if( !aDisplayName.isEmpty() )
     {
-        rImport.AddStyleDisplayName( XmlStyleFamily::SD_HATCH_ID, rStrName,
+        m_rImport.AddStyleDisplayName( XmlStyleFamily::SD_HATCH_ID, rStrName,
                                      aDisplayName );
         rStrName = aDisplayName;
     }
@@ -119,7 +119,7 @@ void XMLHatchStyleImport::importXML(
 // Export
 
 XMLHatchStyleExport::XMLHatchStyleExport( SvXMLExport& rExp )
-    : rExport(rExp)
+    : m_rExport(rExp)
 {
 }
 
@@ -139,7 +139,7 @@ void XMLHatchStyleExport::exportXML(
     OUStringBuffer aOut;
 
     SvXMLUnitConverter& rUnitConverter =
-        rExport.GetMM100UnitConverter();
+        m_rExport.GetMM100UnitConverter();
 
     // Style
     if( !SvXMLUnitConverter::convertEnum( aOut, aHatch.Style, 
pXML_HatchStyle_Enum ) )
@@ -147,31 +147,31 @@ void XMLHatchStyleExport::exportXML(
 
     // Name
     bool bEncoded = false;
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME,
-                          rExport.EncodeStyleName( rStrName,
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME,
+                          m_rExport.EncodeStyleName( rStrName,
                                                     &bEncoded ) );
     if( bEncoded )
-        rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_DISPLAY_NAME,
+        m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_DISPLAY_NAME,
                                 rStrName );
 
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_STYLE, aStrValue );
 
     // Color
     ::sax::Converter::convertColor(aOut, aHatch.Color);
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_COLOR, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_COLOR, aStrValue );
 
     // Distance
     rUnitConverter.convertMeasureToXML( aOut, aHatch.Distance );
     aStrValue = aOut.makeStringAndClear();
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_DISTANCE, aStrValue );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_DISTANCE, aStrValue );
 
     // Angle
-    rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_ROTATION, 
OUString::number(aHatch.Angle) );
+    m_rExport.AddAttribute( XML_NAMESPACE_DRAW, XML_ROTATION, 
OUString::number(aHatch.Angle) );
 
     // Do Write
-    SvXMLElementExport rElem( rExport, XML_NAMESPACE_DRAW, XML_HATCH,
+    SvXMLElementExport rElem( m_rExport, XML_NAMESPACE_DRAW, XML_HATCH,
                               true, false );
 }
 
commit 4dd9fbf0b27a82d002237cf8fe25a843926500a8
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Tue May 30 15:16:08 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:11 2023 +0200

    MCGR: tdf#155437 handling of TransparencePrimitive2D for metafiles
    
    In VclMetafileProcessor2D we need to take care of changed
    circumstances for gradients with the MCGR adaptions. The
    method processTransparencePrimitive2D tries to detect the
    special case that the trapsparency part of the handled
    TransparencePrimitive2D is a single TransparencyGradient.
    
    If detected, this is handed directly to vcl using a
    MetaFloatTransparentAction which contains a class
    'Gradient', but the limited form from vcl (see
    include/vcl/gradient.hxx). This class can only in very
    limited scenarios directly handle/hold a gradient from
    the model (even before MCGR).
    
    For that case there is the helper method
    'cannotBeHandledByVCL' that is already used to decide
    if limited direct rendering using vcl can be used.
    This has also be used here.
    
    Also reworked the conversion to Bitmap as needed, the
    existing versions created slight errors. For more
    details see additional comments in the code.
    
    Change-Id: If9af8b1423df5354eaf9ba8ca6243a1b3ad1b965
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152392
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 2c353a64c5f6..2d78ef686264 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -64,6 +64,8 @@
 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
 #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
 #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> // for 
Title/Description metadata
+#include <drawinglayer/converters.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
 
 #include <com/sun/star/awt/XControl.hpp>
 #include <com/sun/star/i18n/BreakIterator.hpp>
@@ -2276,28 +2278,59 @@ void 
VclMetafileProcessor2D::processTransparencePrimitive2D(
     // FillGradientPrimitive2D and reconstruct the gradient.
     // If that detection goes wrong, I have to create a transparence-blended 
bitmap. Eventually
     // do that in stripes, else RenderTransparencePrimitive2D may just be used
-    const primitive2d::Primitive2DContainer& rContent = 
rTransparenceCandidate.getChildren();
-    const primitive2d::Primitive2DContainer& rTransparence
-        = rTransparenceCandidate.getTransparence();
+    const primitive2d::Primitive2DContainer& 
rContent(rTransparenceCandidate.getChildren());
+    const primitive2d::Primitive2DContainer& rTransparence(
+        rTransparenceCandidate.getTransparence());
 
     if (rContent.empty() || rTransparence.empty())
         return;
 
     // try to identify a single FillGradientPrimitive2D in the
-    // transparence part of the primitive
-    const primitive2d::FillGradientPrimitive2D* pFiGradient = nullptr;
+    // transparence part of the primitive. The hope is to handle
+    // the more specific case in a better way than the general
+    // TransparencePrimitive2D which has strongly seperated
+    // definitions for transparency and content, both completely
+    // free definable by primitives
+    const primitive2d::FillGradientPrimitive2D* pFiGradient(nullptr);
     static bool bForceToBigTransparentVDev(false); // loplugin:constvars:ignore
 
+    // check for single FillGradientPrimitive2D
     if (!bForceToBigTransparentVDev && 1 == rTransparence.size())
     {
-        const primitive2d::Primitive2DReference xReference(rTransparence[0]);
-        pFiGradient = dynamic_cast<const 
primitive2d::FillGradientPrimitive2D*>(xReference.get());
+        pFiGradient
+            = dynamic_cast<const 
primitive2d::FillGradientPrimitive2D*>(rTransparence[0].get());
+
+        // check also for correct ID to exclude derived implementations
+        if (pFiGradient
+            && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D != 
pFiGradient->getPrimitive2DID())
+            pFiGradient = nullptr;
     }
 
-    // Check also for correct ID to exclude derived implementations
-    if (pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == 
pFiGradient->getPrimitive2DID())
+    // MCGR: tdf#155437 If we have identified a transparency gradient,
+    // check if VCL is able to handle it at all
+    if (nullptr != pFiGradient && 
pFiGradient->getFillGradient().cannotBeHandledByVCL())
     {
-        // various content, create content-metafile
+        // If not, reset the pointer and do not make use of this special case.
+        // Adding a gradient in incomplete state that canot be handled by vcl
+        // makes no sense and will knowingly lead to errors, especially with
+        // MCGR extended possibilities. I checked what happens with the
+        // MetaFloatTransparentAction added by OutputDevice::DrawTransparent, 
but
+        // in most cases it gets converted to bitmap or even ignored, see e.g.
+        // - vcl/source/gdi/pdfwriter_impl2.cxx for PDF export
+        // - vcl/source/filter/wmf/wmfwr.cxx -> does ignore 
TransparenceGradient completely
+        //   - vcl/source/filter/wmf/emfwr.cxx -> same
+        //   - vcl/source/filter/eps/eps.cxx -> same
+        // NOTE: Theoretically it would be possible to make the new extended 
Gradient data
+        // available in metafiles, with the known limitiations (not backward 
comp, all
+        // places using it would need adaption, ...), but combined with 
knowing that nearly
+        // all usages ignore or render it locally anyways makes that a 
non-option.
+        pFiGradient = nullptr;
+    }
+
+    if (nullptr != pFiGradient)
+    {
+        // this combination of Gradient can be expressed/handled by
+        // vcl/metafile, so add it directly. various content, create 
content-metafile
         GDIMetaFile aContentMetafile;
         const tools::Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, 
aContentMetafile));
 
@@ -2306,101 +2339,71 @@ void 
VclMetafileProcessor2D::processTransparencePrimitive2D(
         impConvertFillGradientAttributeToVCLGradient(aVCLGradient, 
pFiGradient->getFillGradient(),
                                                      true);
 
-        // render it to VCL
+        // render it to VCL (creates MetaFloatTransparentAction)
         mpOutputDevice->DrawTransparent(aContentMetafile, 
aPrimitiveRectangle.TopLeft(),
                                         aPrimitiveRectangle.GetSize(), 
aVCLGradient);
+        return;
     }
-    else
-    {
-        // sub-transparence group. Draw to VDev first.
-        // this may get refined to tiling when resolution is too big here
-
-        // need to avoid switching off MapMode stuff here; maybe need another
-        // tooling class, cannot just do the same as with the pixel renderer.
-        // Need to experiment...
-
-        // Okay, basic implementation finished and tested. The DPI stuff was 
hard
-        // and not easy to find out that it's needed.
-        // Since this will not yet happen normally (as long as no one 
constructs
-        // transparence primitives with non-trivial transparence content) i 
will for now not
-        // refine to tiling here.
-
-        basegfx::B2DRange 
aViewRange(rContent.getB2DRange(getViewInformation2D()));
-        aViewRange.transform(maCurrentTransformation);
-        const tools::Rectangle 
aRectLogic(static_cast<sal_Int32>(floor(aViewRange.getMinX())),
-                                          
static_cast<sal_Int32>(floor(aViewRange.getMinY())),
-                                          
static_cast<sal_Int32>(ceil(aViewRange.getMaxX())),
-                                          
static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
-        const tools::Rectangle 
aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic));
-        Size aSizePixel(aRectPixel.GetSize());
-        ScopedVclPtrInstance<VirtualDevice> aBufferDevice;
-        const sal_uInt32 nMaxSquarePixels(500000);
-        const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * 
aSizePixel.getHeight());
-        double fReduceFactor(1.0);
-
-        if (nViewVisibleArea > nMaxSquarePixels)
-        {
-            // reduce render size
-            fReduceFactor = sqrt(double(nMaxSquarePixels) / 
static_cast<double>(nViewVisibleArea));
-            aSizePixel = Size(
-                basegfx::fround(static_cast<double>(aSizePixel.getWidth()) * 
fReduceFactor),
-                basegfx::fround(static_cast<double>(aSizePixel.getHeight()) * 
fReduceFactor));
-        }
-
-        if (aBufferDevice->SetOutputSizePixel(aSizePixel))
-        {
-            // create and set MapModes for target devices
-            MapMode aNewMapMode(mpOutputDevice->GetMapMode());
-            aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), 
-aRectLogic.Top()));
-            aBufferDevice->SetMapMode(aNewMapMode);
-
-            // prepare view transformation for target renderers
-            // ATTENTION! Need to apply another scaling because of the 
potential DPI differences
-            // between Printer and VDev (mpOutputDevice and aBufferDevice 
here).
-            // To get the DPI, LogicToPixel from (1,1) from MapUnit::MapInch 
needs to be used.
-            basegfx::B2DHomMatrix 
aViewTransform(aBufferDevice->GetViewTransformation());
-            const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), 
MapMode(MapUnit::MapInch)));
-            const Size aDPINew(aBufferDevice->LogicToPixel(Size(1, 1), 
MapMode(MapUnit::MapInch)));
-            const double fDPIXChange(static_cast<double>(aDPIOld.getWidth())
-                                     / 
static_cast<double>(aDPINew.getWidth()));
-            const double fDPIYChange(static_cast<double>(aDPIOld.getHeight())
-                                     / 
static_cast<double>(aDPINew.getHeight()));
-
-            if (!basegfx::fTools::equal(fDPIXChange, 1.0)
-                || !basegfx::fTools::equal(fDPIYChange, 1.0))
-            {
-                aViewTransform.scale(fDPIXChange, fDPIYChange);
-            }
 
-            // also take scaling from Size reduction into account
-            if (!basegfx::fTools::equal(fReduceFactor, 1.0))
-            {
-                aViewTransform.scale(fReduceFactor, fReduceFactor);
-            }
-
-            // create view information and pixel renderer. Reuse known 
ViewInformation
-            // except new transformation and range
-            geometry::ViewInformation2D aViewInfo(getViewInformation2D());
-            aViewInfo.setViewTransformation(aViewTransform);
-            aViewInfo.setViewport(aViewRange);
-
-            VclPixelProcessor2D aBufferProcessor(aViewInfo, *aBufferDevice);
-
-            // draw content using pixel renderer
-            const Point aEmptyPoint;
-            aBufferProcessor.process(rContent);
-            const Bitmap aBmContent(aBufferDevice->GetBitmap(aEmptyPoint, 
aSizePixel));
-
-            // draw transparence using pixel renderer
-            aBufferDevice->Erase();
-            aBufferProcessor.process(rTransparence);
-            const AlphaMask aBmAlpha(aBufferDevice->GetBitmap(aEmptyPoint, 
aSizePixel));
-
-            // paint
-            mpOutputDevice->DrawBitmapEx(aRectLogic.TopLeft(), 
aRectLogic.GetSize(),
-                                         BitmapEx(aBmContent, aBmAlpha));
-        }
-    }
+    // Here we need to create a correct replacement visualization for the
+    // TransparencePrimitive2D for the target metafile.
+    // I replaced the n'th iteration to convert-to-bitmap which was
+    // used here by using the existing tooling. The orig here was also 
producing
+    // transparency errors with test-file from tdf#155437 on the right part of 
the
+    // image.
+    // Just rely on existing tooling doing the right thing in one place, so 
also
+    // corrections/optimizations can be in one single place
+
+    // Start by getting logic range of content, transform object-to-world, 
then world-to-view
+    // to get to discrete values ('pixels'). Matrix multiplication is 
right-to-left (and not
+    // commutative)
+    basegfx::B2DRange 
aLogicRange(rTransparenceCandidate.getB2DRange(getViewInformation2D()));
+    aLogicRange.transform(mpOutputDevice->GetViewTransformation() * 
maCurrentTransformation);
+
+    // expand in discrete coordinates to next-bigger 'pixel' boundaries and 
remember
+    // created discrete range
+    aLogicRange.expand(
+        basegfx::B2DPoint(floor(aLogicRange.getMinX()), 
floor(aLogicRange.getMinY())));
+    aLogicRange.expand(basegfx::B2DPoint(ceil(aLogicRange.getMaxX()), 
ceil(aLogicRange.getMaxY())));
+    const basegfx::B2DRange aDiscreteRange(aLogicRange);
+
+    // transform back from discrete to world coordinates: this creates the
+    // pixel-boundaries extended logic range we need to cover all content
+    // reliably
+    aLogicRange.transform(mpOutputDevice->GetInverseViewTransformation());
+
+    // create transform embedding for renderer. Goal is to translate what we
+    // want to paint to top/left 0/0 and the calculated discrete size
+    basegfx::B2DHomMatrix 
aEmbedding(basegfx::utils::createTranslateB2DHomMatrix(
+        -aLogicRange.getMinX(), -aLogicRange.getMinY()));
+    const double fLogicWidth(
+        basegfx::fTools::equalZero(aLogicRange.getWidth()) ? 1.0 : 
aLogicRange.getWidth());
+    const double fLogicHeight(
+        basegfx::fTools::equalZero(aLogicRange.getHeight()) ? 1.0 : 
aLogicRange.getHeight());
+    aEmbedding.scale(aDiscreteRange.getWidth() / fLogicWidth,
+                     aDiscreteRange.getHeight() / fLogicHeight);
+
+    // use the whole TransparencePrimitive2D as input (no need to create a new
+    // one with the sub-contents, these are ref-counted) and add to embedding
+    // primitive2d::TransparencePrimitive2D& rTrCand();
+    primitive2d::Primitive2DContainer xEmbedSeq{ 
&const_cast<primitive2d::TransparencePrimitive2D&>(
+        rTransparenceCandidate) };
+    xEmbedSeq = primitive2d::Primitive2DContainer{ new 
primitive2d::TransformPrimitive2D(
+        aEmbedding, std::move(xEmbedSeq)) };
+
+    // use empty ViewInformation & a useful MaximumQuadraticPixels
+    // limitation to paint the content
+    const auto aViewInformation2D(geometry::createViewInformation2D({}));
+    const sal_uInt32 nMaximumQuadraticPixels(500000);
+    const BitmapEx aBitmapEx(convertToBitmapEx(
+        std::move(xEmbedSeq), aViewInformation2D, 
basegfx::fround(aDiscreteRange.getWidth()),
+        basegfx::fround(aDiscreteRange.getHeight()), nMaximumQuadraticPixels));
+
+    // add to target metafile (will create MetaFloatTransparentAction)
+    mpOutputDevice->DrawBitmapEx(
+        Point(basegfx::fround(aLogicRange.getMinX()), 
basegfx::fround(aLogicRange.getMinY())),
+        Size(basegfx::fround(aLogicRange.getWidth()), 
basegfx::fround(aLogicRange.getHeight())),
+        aBitmapEx);
 }
 
 void VclMetafileProcessor2D::processStructureTagPrimitive2D(
commit 10f8cdb5ba93953d9efcc3959c8b886f993ddbbb
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Wed May 24 12:31:48 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:11 2023 +0200

    MCGR: Border restoration support
    
    Due to tdf#155362 I added code to be able in case we
    would need it to convert a BGradient using added
    tooling from having offsets in the GradientSteps
    and no border to adapted GradientSteps and border.
    
    This is preferrable due to our GradientStyle_RECT
    (and GradientStyle_ELLIPTICAL too) use that 'non-
    linear' paint aka move-two-pixels-inside, someone
    else called it 'frame-paint'. This does not bode
    well with the border being applied 'linear' at the
    same time (argh). Thus - while in principle all is
    correct when re-importing a GradientStyle_RECT
    with a border after export to pptx, it looks
    slightly better ('correcter') wen doing so. That is
    because when being able to and restoring a border
    at least that border part *is* applied linearly.
    
    I took the chance to further apply tooling, move
    it to classes involved and instead of awt::Gradient2
    use more basegfx::BGradient since it can and does
    host tooling. This is also a preparation to be
    able to adapt (restore) border in case of turn-
    around in ODF where the importing instance is before
    MCGR existance and has to handle Start/EndColor.
    
    Needed to take more care with using BGradient instead
    of awt::Gradient2 for initialization, also better
    dividing/organization of tooling, already preparation
    to use for other purposes.
    
    Change-Id: I2d3a4240a5ac6fff9211b46642ee80366dc09e3d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152194
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/basegfx/source/tools/bgradient.cxx 
b/basegfx/source/tools/bgradient.cxx
index 1499b0c682fe..375b9685edc5 100644
--- a/basegfx/source/tools/bgradient.cxx
+++ b/basegfx/source/tools/bgradient.cxx
@@ -563,6 +563,100 @@ void BColorStops::reverseColorStops()
         candidate = BColorStop(1.0 - candidate.getStopOffset(), 
candidate.getStopColor());
 }
 
+// createSpaceAtStart creates fOffset space at start by
+// translating/scaling all entries to the right
+void BColorStops::createSpaceAtStart(double fOffset)
+{
+    // nothing to do if empty
+    if (empty())
+        return;
+
+    // correct offset to [0.0 .. 1.0]
+    fOffset = std::max(std::min(1.0, fOffset), 0.0);
+
+    // nothing to do if 0.0 == offset
+    if (basegfx::fTools::equalZero(fOffset))
+        return;
+
+    BColorStops aNewStops;
+
+    for (const auto& candidate : *this)
+    {
+        aNewStops.emplace_back(fOffset + (candidate.getStopOffset() * (1.0 - 
fOffset)),
+                               candidate.getStopColor());
+    }
+
+    *this = aNewStops;
+}
+
+// removeSpaceAtStart removes fOffset space from start by
+// translating/scaling entries more or equal to fOffset
+// to the left. Entries less than fOffset will be removed
+void BColorStops::removeSpaceAtStart(double fOffset)
+{
+    // nothing to do if empty
+    if (empty())
+        return;
+
+    // correct factor to [0.0 .. 1.0]
+    fOffset = std::max(std::min(1.0, fOffset), 0.0);
+
+    // nothing to do if fOffset == 0.0
+    if (basegfx::fTools::equalZero(fOffset))
+        return;
+
+    BColorStops aNewStops;
+    const double fMul(basegfx::fTools::equal(fOffset, 1.0) ? 1.0 : 1.0 / (1.0 
- fOffset));
+
+    for (const auto& candidate : *this)
+    {
+        if (basegfx::fTools::moreOrEqual(candidate.getStopOffset(), fOffset))
+        {
+            aNewStops.emplace_back((candidate.getStopOffset() - fOffset) * 
fMul,
+                                   candidate.getStopColor());
+        }
+    }
+
+    *this = aNewStops;
+}
+
+// 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 BColorStops::detectPossibleOffsetAtStart() const
+{
+    BColor aSingleColor;
+    const bool bSingleColor(isSingleColor(aSingleColor));
+
+    // no useful offset for single color
+    if (bSingleColor)
+        return 0.0;
+
+    // here we know that we have at least two colors, so we have a
+    // color change. Find colors left and right of that first color change
+    BColorStops::const_iterator aColorR(begin());
+    BColorStops::const_iterator aColorL(aColorR++);
+
+    // aColorR would 1st get equal to end(), so no need to also check aColorL
+    // for end(). Loop as long as same color. Since we *have* a color change
+    // not even aColorR can get equal to end() before color inequality, but
+    // keep for safety
+    while (aColorR != end() && aColorL->getStopColor() == 
aColorR->getStopColor())
+    {
+        aColorL++;
+        aColorR++;
+    }
+
+    // also for safety: access values at aColorL below *only*
+    // if not equal to end(), but can theoretically not happen
+    if (aColorL == end())
+    {
+        return 0.0;
+    }
+
+    // return offset (maybe 0.0 what is OK)
+    return aColorL->getStopOffset();
+}
+
 std::string BGradient::GradientStyleToString(css::awt::GradientStyle eStyle)
 {
     switch (eStyle)
@@ -757,7 +851,24 @@ css::awt::Gradient2 BGradient::getAsGradient2() const
     aRetval.StepCount = GetSteps();
 
     // for compatibility, still set StartColor/EndColor
-    // const basegfx::BColorStops& rColorStops(GetColorStops());
+    // NOTE: All code after adapting to multi color gradients works
+    //       using the ColorSteps, so in principle Start/EndColor might
+    //       be either
+    //        (a) ignored consequently everywhere or
+    //        (b) be set/added consequently everywhere
+    //       since this is - in principle - redundant data.
+    //       Be aware that e.g. cases like DrawingML::EqualGradients
+    //       and others would have to be identified and adapted (!)
+    //       Since awt::Gradient2 is UNO API data there might
+    //       be cases where just awt::Gradient is transferred, so (b)
+    //       is far better backwards compatible and thus more safe, so
+    //       all changes will make use of additionally using/setting
+    //       these additionally, but will only make use of the given
+    //       ColorSteps if these are not empty, assuming that these
+    //       already contain Start/EndColor.
+    //       In principle that redundancy and that it is conflict-free
+    //       could even be checked and asserted, but consequently using
+    //       (b) methodically should be safe.
     aRetval.StartColor
         = 
static_cast<sal_Int32>(ColorToBColorConverter(aColorStops.front().getStopColor()));
     aRetval.EndColor
@@ -765,10 +876,101 @@ css::awt::Gradient2 BGradient::getAsGradient2() const
 
     // fill ColorStops to extended Gradient2
     aRetval.ColorStops = aColorStops.getAsColorStopSequence();
-    // fillColorStopSequenceFromColorStops(rGradient2.ColorStops, rColorStops);
 
     return aRetval;
 }
+
+void BGradient::tryToRecreateBorder(basegfx::BColorStops* 
pAssociatedTransparencyStops)
+{
+    // border already set, do not try to recreate
+    if (0 != GetBorder())
+        return;
+
+    BColor aSingleColor;
+    const bool bSingleColor(GetColorStops().isSingleColor(aSingleColor));
+
+    // no need to recreate with single color
+    if (bSingleColor)
+        return;
+
+    const bool bIsAxial(css::awt::GradientStyle_AXIAL == GetGradientStyle());
+
+    if (bIsAxial)
+    {
+        // for axial due to reverse used gradient work reversed
+        aColorStops.reverseColorStops();
+        if (nullptr != pAssociatedTransparencyStops)
+            pAssociatedTransparencyStops->reverseColorStops();
+    }
+
+    // check if we have space at start of range [0.0 .. 1.0] that
+    // may be interpreted as 'border' -> same color. That may involve
+    // different scenarios, e.g. 1st index > 0.0, but also a non-zero
+    // number of same color entries, or a combination of both
+    const double fOffset(aColorStops.detectPossibleOffsetAtStart());
+
+    if (!basegfx::fTools::equalZero(fOffset))
+    {
+        // we have a border area, indeed re-create
+        aColorStops.removeSpaceAtStart(fOffset);
+        if (nullptr != pAssociatedTransparencyStops)
+            pAssociatedTransparencyStops->removeSpaceAtStart(fOffset);
+
+        // ...and create border value
+        SetBorder(static_cast<sal_uInt16>(fOffset * 100.0));
+    }
+
+    if (bIsAxial)
+    {
+        // take back reverse
+        aColorStops.reverseColorStops();
+        if (nullptr != pAssociatedTransparencyStops)
+            pAssociatedTransparencyStops->reverseColorStops();
+    }
+}
+
+void BGradient::tryToApplyBorder()
+{
+    // no border to apply, done
+    if (0 == GetBorder())
+        return;
+
+    // NOTE: no new start node is added. The new ColorStop
+    //       mechanism does not need entries at 0.0 and 1.0.
+    //       In case this is needed, do that in the caller
+    const double fOffset(GetBorder() * 0.01);
+
+    if (css::awt::GradientStyle_AXIAL == GetGradientStyle())
+    {
+        // for axial due to reverse used gradient work reversed
+        aColorStops.reverseColorStops();
+        aColorStops.createSpaceAtStart(fOffset);
+        aColorStops.reverseColorStops();
+    }
+    else
+    {
+        // apply border to GradientSteps
+        aColorStops.createSpaceAtStart(fOffset);
+    }
+
+    // set changed values
+    SetBorder(0);
+}
+
+void BGradient::tryToApplyStartEndIntensity()
+{
+    // already on default, nothing to apply
+    if (100 == GetStartIntens() && 100 == GetEndIntens())
+        return;
+
+    // apply 'old' blend stuff, blend against black
+    aColorStops.blendToIntensity(GetStartIntens() * 0.01, GetEndIntens() * 
0.01,
+                                 BColor()); // COL_BLACK
+
+    // set values to default
+    SetStartIntens(100);
+    SetEndIntens(100);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/basegfx/source/tools/gradienttools.cxx 
b/basegfx/source/tools/gradienttools.cxx
index 2cc455aa0342..9ccfbd71d605 100644
--- a/basegfx/source/tools/gradienttools.cxx
+++ b/basegfx/source/tools/gradienttools.cxx
@@ -265,28 +265,15 @@ namespace basegfx
 
     namespace utils
     {
-        /* Tooling method to extract data from given awt::Gradient2
-           to ColorStops, doing some corrections, partitally based
-           on given SingleColor.
-           This will do quite some preparations for the gradient
-           as follows:
-           - It will check for single color (resetting rSingleColor when
-             this is the case) and return with empty ColorStops
-           - It will blend ColorStops to Intensity if StartIntensity/
-             EndIntensity != 100 is set in awt::Gradient2, so applying
-             that value(s) to the gadient directly
-           - It will adapt to Border if Border != 0 is set at the
-             given awt::Gradient2, so applying that value to the gadient
-             directly
-        */
+        /* Tooling method to extract data from given BGradient
+           to ColorStops, doing some corrections, partially based
+           on given SingleColor */
         void prepareColorStops(
             const basegfx::BGradient& rGradient,
             BColorStops& rColorStops,
             BColor& rSingleColor)
         {
-            rColorStops = rGradient.GetColorStops();
-
-            if (rColorStops.isSingleColor(rSingleColor))
+            if (rGradient.GetColorStops().isSingleColor(rSingleColor))
             {
                 // when single color, preserve value in rSingleColor
                 // and clear the ColorStops, done.
@@ -294,73 +281,45 @@ namespace basegfx
                 return;
             }
 
-            if (100 != rGradient.GetStartIntens() || 100 != 
rGradient.GetEndIntens())
+            const bool bAdaptStartEndIntensity(100 != 
rGradient.GetStartIntens() || 100 != rGradient.GetEndIntens());
+            const bool bAdaptBorder(0 != rGradient.GetBorder());
+
+            if (!bAdaptStartEndIntensity && !bAdaptBorder)
             {
-                // apply 'old' blend stuff, blend against black
-                rColorStops.blendToIntensity(
-                    rGradient.GetStartIntens() * 0.01,
-                    rGradient.GetEndIntens() * 0.01,
-                    basegfx::BColor()); // COL_BLACK
-
-                // can lead to single color (e.g. both zero, so all black),
-                // so check again
-                if (rColorStops.isSingleColor(rSingleColor))
+                // copy unchanged ColorStops & done
+                rColorStops = rGradient.GetColorStops();
+                return;
+            }
+
+            // prepare a copy to work on
+            basegfx::BGradient aWorkCopy(rGradient);
+
+            if (bAdaptStartEndIntensity)
+            {
+                aWorkCopy.tryToApplyStartEndIntensity();
+
+                // this can again lead to single color (e.g. both zero, so
+                // all black), so check again for it
+                if (aWorkCopy.GetColorStops().isSingleColor(rSingleColor))
                 {
                     rColorStops.clear();
                     return;
                 }
             }
 
-            if (0 != rGradient.GetBorder())
+            if (bAdaptBorder)
             {
-                // apply Border if set
-                // NOTE: no new start node is added. The new ColorStop
-                //       mechanism does not need entries at 0.0 and 1.0.
-                //       In case this is needed, do that in the caller
-                const double fFactor(rGradient.GetBorder() * 0.01);
-                BColorStops aNewStops;
-
-                for (const auto& candidate : rColorStops)
-                {
-                    if (css::awt::GradientStyle_AXIAL == 
rGradient.GetGradientStyle())
-                    {
-                        // for axial add the 'gap' at the start due to reverse 
used gradient
-                        aNewStops.emplace_back((1.0 - fFactor) * 
candidate.getStopOffset(), candidate.getStopColor());
-                    }
-                    else
-                    {
-                        // css::awt::GradientStyle_LINEAR
-                        // case awt::GradientStyle_RADIAL
-                        // case awt::GradientStyle_ELLIPTICAL
-                        // case awt::GradientStyle_RECT
-                        // case awt::GradientStyle_SQUARE
-
-                        // for all others add the 'gap' at the end
-                        aNewStops.emplace_back(fFactor + 
(candidate.getStopOffset() * (1.0 - fFactor)), candidate.getStopColor());
-                    }
-                }
-
-                rColorStops = aNewStops;
+                aWorkCopy.tryToApplyBorder();
             }
+
+            // extract ColorStops, that's all we need here
+            rColorStops = aWorkCopy.GetColorStops();
         }
 
         /* Tooling method to synchronize the given ColorStops.
            The intention is that a color GradientStops and an
            alpha/transparence GradientStops gets synchronized
-           for export.
-           Fo the corrections the single values for color and
-           alpha may be used, e.g. when ColorStops is given
-           and not empty, but AlphaStops is empty, it will get
-           sycronized so that it will have the same number and
-           offsets in AlphaStops as in ColorStops, but with
-           the given SingleAlpha as value.
-           At return it guarantees that both have the same
-           number of entries with the same StopOffsets, so
-           that synchonized pair of ColorStops can e.g. be used
-           to export a Gradient with defined/adapted alpha
-           being 'coupled' indirectly using the
-           'FillTransparenceGradient' method (at import time).
-        */
+           for export. */
         void synchronizeColorStops(
             BColorStops& rColorStops,
             BColorStops& rAlphaStops,
diff --git a/include/basegfx/utils/bgradient.hxx 
b/include/basegfx/utils/bgradient.hxx
index 4f85ed85a407..ab70d05880de 100644
--- a/include/basegfx/utils/bgradient.hxx
+++ b/include/basegfx/utils/bgradient.hxx
@@ -40,13 +40,10 @@ namespace basegfx
 
         Offset is defined as:
         - being in the range of [0.0 .. 1.0] (unit range)
-          - 0.0 being reserved for StartColor
-          - 1.0 being reserved for EndColor
-        - in-between offsets thus being in the range of ]0.0 .. 1.0[
-        - no two equal offsets are allowed
-            - this is an error
-        - missing 1.0 entry (EndColor) is allowed
-          - it means that EndColor == StartColor
+          - offsets outside are an error
+        - lowest/1st value equivalent to StartColor
+        - highest/last value equivalent to EndColor
+        - missing 0.0/1.0 entries are allowed
         - at least one value (usually 0.0, StartColor) is required
             - this allows to avoid massive testing in all places where
               this data has to be accessed
@@ -263,6 +260,19 @@ public:
            When also mirroring offsets a valid sort keeps valid.
         */
     void reverseColorStops();
+
+    // createSpaceAtStart creates fOffset space at start by
+    // translating/scaling all entries to the right
+    void createSpaceAtStart(double fOffset);
+
+    // removeSpaceAtStart removes fOffset space from start by
+    // translating/scaling entries more or equal to fOffset
+    // to the left. Entries less than fOffset will be removed
+    void removeSpaceAtStart(double fOffset);
+
+    // 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;
 };
 
 class BASEGFX_DLLPUBLIC BGradient final
@@ -323,6 +333,11 @@ public:
 
     /// Tooling method to fill awt::Gradient2 from data contained in the given 
basegfx::BGradient
     css::awt::Gradient2 getAsGradient2() const;
+
+    /// Tooling to handle border correction/integration and StartStopIntensity
+    void tryToRecreateBorder(basegfx::BColorStops* 
pAssociatedTransparencyStops);
+    void tryToApplyBorder();
+    void tryToApplyStartEndIntensity();
 };
 }
 
diff --git a/include/basegfx/utils/gradienttools.hxx 
b/include/basegfx/utils/gradienttools.hxx
index 50d58e8a39c0..6fc51adc0e37 100644
--- a/include/basegfx/utils/gradienttools.hxx
+++ b/include/basegfx/utils/gradienttools.hxx
@@ -186,18 +186,23 @@ namespace basegfx
 
     namespace utils
     {
-        /* Tooling method to extract data from given awt::Gradient2
-           to ColorStops, doing some corrections, partitally based
+        /* Tooling method to extract data from given BGradient
+           to ColorStops, doing some corrections, partially based
            on given SingleColor.
+           This is used for export preparations in case these exports
+           do neither support Start/EndIntensity nor Border settings,
+           both will be elliminated if possible (see below).
+           The BGradient rGradient and BColorStops& rColorStops
+           are both return parameters and may be changed.
            This will do quite some preparations for the gradient
            as follows:
            - It will check for single color (resetting rSingleColor when
              this is the case) and return with empty ColorStops
            - It will blend ColorStops to Intensity if StartIntensity/
-             EndIntensity != 100 is set in awt::Gradient2, so applying
-             that value(s) to the gadient directly
+             EndIntensity != 100 is set in BGradient, so applying
+             that value(s) to the gradient directly
            - It will adapt to Border if Border != 0 is set at the
-             given awt::Gradient2, so applying that value to the gadient
+             given BGradient, so applying that value to the gradient
              directly
         */
         BASEGFX_DLLPUBLIC void prepareColorStops(
@@ -209,15 +214,15 @@ namespace basegfx
            The intention is that a color GradientStops and an
            alpha/transparence GradientStops gets synchronized
            for export.
-           Fo the corrections the single values for color and
+           For the corrections the single values for color and
            alpha may be used, e.g. when ColorStops is given
            and not empty, but AlphaStops is empty, it will get
-           sycronized so that it will have the same number and
+           synchronized so that it will have the same number and
            offsets in AlphaStops as in ColorStops, but with
            the given SingleAlpha as value.
            At return it guarantees that both have the same
            number of entries with the same StopOffsets, so
-           that synchonized pair of ColorStops can e.g. be used
+           that synchronized pair of ColorStops can e.g. be used
            to export a Gradient with defined/adapted alpha
            being 'coupled' indirectly using the
            'FillTransparenceGradient' method (at import time).
diff --git a/oox/source/drawingml/fillproperties.cxx 
b/oox/source/drawingml/fillproperties.cxx
index d8cfc19beff3..fe00d12cf078 100644
--- a/oox/source/drawingml/fillproperties.cxx
+++ b/oox/source/drawingml/fillproperties.cxx
@@ -457,18 +457,11 @@ void FillProperties::pushToPropMap( ShapePropertyMap& 
rPropMap,
             // do not create gradient struct if property is not supported...
             if( rPropMap.supportsProperty( ShapeProperty::FillGradient ) )
             {
-                // use awt::Gradient2, prepare ColorStops
-                awt::Gradient2 aGradient;
+                // prepare ColorStops
                 basegfx::BColorStops aColorStops;
                 basegfx::BColorStops aTransparencyStops;
                 bool bContainsTransparency(false);
 
-                // set defaults
-                aGradient.Angle = 900;
-                aGradient.StartIntensity = 100;
-                aGradient.EndIntensity = 100;
-                aGradient.Style = awt::GradientStyle_LINEAR;
-
                 // convert to BColorStops, check for contained transparency
                 for (const auto& rCandidate : maGradientProps.maGradientStops)
                 {
@@ -487,6 +480,20 @@ void FillProperties::pushToPropMap( ShapePropertyMap& 
rPropMap,
                     }
                 }
 
+                // prepare BGradient with some defaults
+                // CAUTION: This used awt::Gradient2 before who's empty 
constructor
+                //          (see 
workdir/UnoApiHeadersTarget/offapi/normal/com/sun/
+                //          star/awt/Gradient.hpp) initializes all to zeros, 
so reflect
+                //          this here. OTOH set all that were set, e.g. 
Start/EndIntens
+                //          were set to 100, so just use default of BGradient 
constructor
+                basegfx::BGradient aGradient(
+                    aColorStops,
+                    awt::GradientStyle_LINEAR,
+                    Degree10(900),
+                    0,  // border
+                    0,  // OfsX -> 0, not 50 (!)
+                    0); // OfsY -> 0, not 50 (!)
+
                 // "rotate with shape" set to false -> do not rotate
                 if (!maGradientProps.moRotateWithShape.value_or(true))
                 {
@@ -497,77 +504,61 @@ void FillProperties::pushToPropMap( ShapePropertyMap& 
rPropMap,
                 {
                     IntegerRectangle2D aFillToRect = 
maGradientProps.moFillToRect.value_or( IntegerRectangle2D( 0, 0, MAX_PERCENT, 
MAX_PERCENT ) );
                     sal_Int32 nCenterX = (MAX_PERCENT + aFillToRect.X1 - 
aFillToRect.X2) / 2;
-                    aGradient.XOffset = getLimitedValue<sal_Int16, sal_Int32>(
-                        nCenterX / PER_PERCENT, 0, 100);
+                    aGradient.SetXOffset(getLimitedValue<sal_Int16, sal_Int32>(
+                        nCenterX / PER_PERCENT, 0, 100));
                     sal_Int32 nCenterY = (MAX_PERCENT + aFillToRect.Y1 - 
aFillToRect.Y2) / 2;
-                    aGradient.YOffset = getLimitedValue<sal_Int16, sal_Int32>(
-                        nCenterY / PER_PERCENT, 0, 100);
+                    aGradient.SetYOffset(getLimitedValue<sal_Int16, sal_Int32>(
+                        nCenterY / PER_PERCENT, 0, 100));
 
                     if( maGradientProps.moGradientPath.value() == XML_circle )
                     {
                         // Style should be radial at least when the horizontal 
center is at 50%.
                         // Otherwise import as a linear gradient, because it 
is the most similar to the MSO radial style.
-                        // aGradient.Style = awt::GradientStyle_LINEAR;
-                        if( aGradient.XOffset == 100 && aGradient.YOffset == 
100 )
-                            aGradient.Angle = 450;
-                        else if( aGradient.XOffset == 0 && aGradient.YOffset 
== 100 )
-                            aGradient.Angle = 3150;
-                        else if( aGradient.XOffset == 100 && aGradient.YOffset 
== 0 )
-                            aGradient.Angle = 1350;
-                        else if( aGradient.XOffset == 0 && aGradient.YOffset 
== 0 )
-                            aGradient.Angle = 2250;
+                        // 
aGradient.SetGradientStyle(awt::GradientStyle_LINEAR);
+                        if( 100 == aGradient.GetXOffset() && 100 == 
aGradient.GetYOffset() )
+                            aGradient.SetAngle( Degree10(450) );
+                        else if( 0 == aGradient.GetXOffset() && 100 == 
aGradient.GetYOffset() )
+                            aGradient.SetAngle( Degree10(3150) );
+                        else if( 100 == aGradient.GetXOffset() && 0 == 
aGradient.GetYOffset() )
+                            aGradient.SetAngle( Degree10(1350) );
+                        else if( 0 == aGradient.GetXOffset() && 0 == 
aGradient.GetYOffset() )
+                            aGradient.SetAngle( Degree10(2250) );
                         else
-                            aGradient.Style = awt::GradientStyle_RADIAL;
+                            
aGradient.SetGradientStyle(awt::GradientStyle_RADIAL);
                     }
                     else
                     {
-                        aGradient.Style = awt::GradientStyle_RECT;
+                        aGradient.SetGradientStyle(awt::GradientStyle_RECT);
                     }
 
                     aColorStops.reverseColorStops();
+                    aGradient.SetColorStops(aColorStops);
                     aTransparencyStops.reverseColorStops();
                 }
                 else if (!maGradientProps.maGradientStops.empty())
                 {
-                    // aGradient.Style = awt::GradientStyle_LINEAR;
-                    sal_Int32 nShadeAngle = 
maGradientProps.moShadeAngle.value_or( 0 );
+                    // aGradient.SetGradientStyle(awt::GradientStyle_LINEAR);
+                    sal_Int32 
nShadeAngle(maGradientProps.moShadeAngle.value_or( 0 ));
                     // Adjust for flips
                     if ( bFlipH )
                         nShadeAngle = 180*60000 - nShadeAngle;
                     if ( bFlipV )
                         nShadeAngle = -nShadeAngle;
                     const sal_Int32 nDmlAngle = nShadeAngle + nShapeRotation;
+
                     // convert DrawingML angle (in 1/60000 degrees) to API 
angle (in 1/10 degrees)
-                    aGradient.Angle = static_cast< sal_Int16 >( (8100 - 
(nDmlAngle / (PER_DEGREE / 10))) % 3600 );
+                    aGradient.SetAngle(Degree10(static_cast< sal_Int16 >( 
(8100 - (nDmlAngle / (PER_DEGREE / 10))) % 3600 )));
                 }
 
-                // set BColorStops using UNO API
-                aGradient.ColorStops = aColorStops.getAsColorStopSequence();
-
-                // for compatibility, still set StartColor/EndColor
-                // NOTE: All code after adapting to multi color gradients works
-                //       using the ColorSteps, so in principle Start/EndColor 
might
-                //       be either
-                //        (a) ignored consequently everywhere or
-                //        (b) be set/added consequently everywhere
-                //       since this is - in principle - redundant data.
-                //       Be aware that e.g. cases like 
DrawingML::EqualGradients
-                //       and others would have to be identified and adapted (!)
-                //       Since awt::Gradient2 is UNO API data there might
-                //       be cases where just awt::Gradient is transferred, so 
(b)
-                //       is far better backwards compatible and thus more 
safe, so
-                //       all changes will make use of additionally 
using/setting
-                //       these additionally, but will only make use of the 
given
-                //       ColorSteps if these are not empty, assuming that these
-                //       already contain Start/EndColor.
-                //       In principle that redundancy and that it is 
conflict-free
-                //       could even be checked and asserted, but consequently 
using
-                //       (b) methodically should be safe.
-                aGradient.StartColor = 
static_cast<sal_Int32>(::Color(aColorStops.front().getStopColor()));
-                aGradient.EndColor = 
static_cast<sal_Int32>(::Color(aColorStops.back().getStopColor()));
+                if (awt::GradientStyle_RECT == aGradient.GetGradientStyle())
+                {
+                    // MCGR: tdf#155362: better support border
+                    // CAUTION: Need to handle TransparencyStops if used
+                    aGradient.tryToRecreateBorder(aTransparencyStops.empty() ? 
nullptr : &aTransparencyStops);
+                }
 
                 // push gradient or named gradient to property map
-                if (rPropMap.setProperty(ShapeProperty::FillGradient, 
aGradient))
+                if (rPropMap.setProperty(ShapeProperty::FillGradient, 
aGradient.getAsGradient2()))
                 {
                     eFillStyle = FillStyle_GRADIENT;
                 }
@@ -575,8 +566,8 @@ void FillProperties::pushToPropMap( ShapePropertyMap& 
rPropMap,
                 // push gradient transparency to property map if it exists
                 if (!aTransparencyStops.empty())
                 {
-                    aGradient.ColorStops = 
aTransparencyStops.getAsColorStopSequence();
-                    rPropMap.setProperty(ShapeProperty::GradientTransparency, 
aGradient);
+                    aGradient.SetColorStops(aTransparencyStops);
+                    rPropMap.setProperty(ShapeProperty::GradientTransparency, 
aGradient.getAsGradient2());
                 }
             }
         break;
commit c6b90b83ad6bfaf1e95ca600ef4c5559a2a864fe
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Mon May 22 12:13:25 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:11 2023 +0200

    MCGR: Check correctly for used FillTransparenceGradient
    
    To correctly check using UNO API if a FillTransparence-
    Gradient is used it is necessary to check if a Name for
    it is set. This corresponds to the IsEnabled() state
    of the XFillFloatTransparenceItem in the core.
    
    This was not consequently done that way and e.g. was
    done by checking if the FTG was 'default' in the sense
    that the StartColor was COL_BLACK. This was never
    sufficient and is not with MCGRs, too.
    
    Important in this case is the UnitTest checking for
    file fdo66688.docx - the re-export/roundtrip goes
    wrong when not doing this correctly.
    
    Change-Id: Iaf14c1e4481188124f044b4b3c8bcd6689c65aad
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152087
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 65798fa36cf6..7771caa7c92f 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -470,15 +470,22 @@ void DrawingML::WriteSolidFill( const Reference< 
XPropertySet >& rXPropSet )
     // OOXML has no separate transparence gradient but uses transparency in 
the gradient stops.
     // So we merge transparency and color and use gradient fill in such case.
     basegfx::BGradient aTransparenceGradient;
+    OUString sFillTransparenceGradientName;
     bool bNeedGradientFill(false);
 
-    if (GetProperty(rXPropSet, "FillTransparenceGradient"))
+    if (GetProperty(rXPropSet, "FillTransparenceGradientName")
+        && (mAny >>= sFillTransparenceGradientName)
+        && !sFillTransparenceGradientName.isEmpty()
+        && GetProperty(rXPropSet, "FillTransparenceGradient"))
     {
         aTransparenceGradient = basegfx::BGradient(mAny);
         basegfx::BColor aSingleColor;
         bNeedGradientFill = 
!aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor);
 
-        if (!bNeedGradientFill && aSingleColor != basegfx::BColor())
+        // we no longer need to 'guess' if FillTransparenceGradient is used by
+        // comparing it's 1st color to COL_BLACK after having tested that the
+        // FillTransparenceGradientName is set
+        if (!bNeedGradientFill)
         {
             // Our alpha is a gray color value.
             const sal_uInt8 nRed(aSingleColor.getRed() * 255.0);
@@ -625,13 +632,11 @@ void DrawingML::WriteGradientFill( const Reference< 
XPropertySet >& rXPropSet )
 
         if (GetProperty(rXPropSet, "FillTransparenceGradientName")
             && (mAny >>= sFillTransparenceGradientName)
-            && !sFillTransparenceGradientName.isEmpty())
+            && !sFillTransparenceGradientName.isEmpty()
+            && GetProperty(rXPropSet, "FillTransparenceGradient"))
         {
-            if (GetProperty(rXPropSet, "FillTransparenceGradient"))
-            {
-                aTransparenceGradient = basegfx::BGradient(mAny);
-            }
-
+            // TransparenceGradient is only used when name is not empty
+            aTransparenceGradient = basegfx::BGradient(mAny);
             pTransparenceGradient = &aTransparenceGradient;
         }
         else if (GetProperty(rXPropSet, "FillTransparence"))
@@ -5176,19 +5181,35 @@ void DrawingML::WriteFill( const Reference< 
XPropertySet >& xPropSet )
     xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle;
 
     // map full transparent background to no fill
-    if ( aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, 
"FillTransparence" ) )
-    {
-        sal_Int16 nVal = 0;
-        xPropSet->getPropertyValue( "FillTransparence" ) >>= nVal;
-        if ( nVal == 100 )
-            aFillStyle = FillStyle_NONE;
-    }
-    if (aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, 
"FillTransparenceGradient"))
+    if (aFillStyle == FillStyle_SOLID)
     {
-        awt::Gradient aTransparenceGradient;
-        mAny >>= aTransparenceGradient;
-        if (aTransparenceGradient.StartColor == 0xffffff && 
aTransparenceGradient.EndColor == 0xffffff)
-            aFillStyle = FillStyle_NONE;
+        OUString sFillTransparenceGradientName;
+
+        if (GetProperty(xPropSet, "FillTransparenceGradientName")
+            && (mAny >>= sFillTransparenceGradientName)
+            && !sFillTransparenceGradientName.isEmpty()
+            && GetProperty(xPropSet, "FillTransparenceGradient"))
+        {
+            // check if a fully transparent TransparenceGradient is used
+            // use BGradient constructor & tooling here now
+            const basegfx::BGradient aTransparenceGradient(mAny);
+            basegfx::BColor aSingleColor;
+            const bool 
bSingleColor(aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor));
+            const bool bCompletelyTransparent(bSingleColor && 
basegfx::fTools::equal(aSingleColor.luminance(), 1.0));
+
+            if (bCompletelyTransparent)
+            {
+                aFillStyle = FillStyle_NONE;
+            }
+        }
+        else if ( GetProperty( xPropSet, "FillTransparence" ) )
+        {
+            // check if a fully transparent FillTransparence is used
+            sal_Int16 nVal = 0;
+            xPropSet->getPropertyValue( "FillTransparence" ) >>= nVal;
+            if ( nVal == 100 )
+                aFillStyle = FillStyle_NONE;
+        }
     }
 
     bool bUseBackground(false);
diff --git a/svx/source/xoutdev/xattr.cxx b/svx/source/xoutdev/xattr.cxx
index 9a3567430278..4860373836a8 100644
--- a/svx/source/xoutdev/xattr.cxx
+++ b/svx/source/xoutdev/xattr.cxx
@@ -2434,6 +2434,13 @@ XFillFloatTransparenceItem* 
XFillFloatTransparenceItem::Clone( SfxItemPool* /*pP
 
 bool XFillFloatTransparenceItem::QueryValue( css::uno::Any& rVal, sal_uInt8 
nMemberId ) const
 {
+    if (!IsEnabled() && nMemberId == MID_NAME)
+    {
+        // make sure that we return empty string in case of query for
+        // "FillTransparenceGradientName" if the item is disabled
+        rVal <<= OUString();
+    }
+
     return XFillGradientItem::QueryValue( rVal, nMemberId );
 }
 
commit 08de3d302faf13cd7ff6a1927d07428651cafabf
Author:     Noel Grandin <noel.gran...@collabora.co.uk>
AuthorDate: Thu Jun 1 14:24:41 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:11 2023 +0200

    reduce memory overhead of AquaA11yWrapper
    
    we can allocate this internally, which reduces the number of
    heap allocations we need to do
    
    Change-Id: Id5298e70b6816cd65d8285ceea846dc8e9e4b39d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152489
    Reviewed-by: Patrick Luby <plub...@neooffice.org>
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>
    (cherry picked from commit 7acc7660a752497a546263d28e82a2e8f61fd702)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152513

diff --git a/vcl/inc/osx/a11ywrapper.h b/vcl/inc/osx/a11ywrapper.h
index f9b30eb98fcd..469e50820e19 100644
--- a/vcl/inc/osx/a11ywrapper.h
+++ b/vcl/inc/osx/a11ywrapper.h
@@ -52,7 +52,7 @@ struct ReferenceWrapper
 
 @interface AquaA11yWrapper : NSView
 {
-    ReferenceWrapper * mpReferenceWrapper;
+    ReferenceWrapper maReferenceWrapper;
     BOOL mActsAsRadioGroup;
     BOOL mIsTableCell;
 }
@@ -88,7 +88,6 @@ struct ReferenceWrapper
 -(NSWindow*)windowForParent;
 -(id)initWithAccessibleContext: (css::uno::Reference < 
css::accessibility::XAccessibleContext >) anAccessibleContext;
 -(void) setDefaults: (css::uno::Reference < 
css::accessibility::XAccessibleContext >) rxAccessibleContext;
--(void) dealloc;
 +(void)setPopupMenuOpen:(BOOL)popupMenuOpen;
 -(css::accessibility::XAccessibleAction *)accessibleAction;
 -(css::accessibility::XAccessibleContext *)accessibleContext;
diff --git a/vcl/osx/a11ywrapper.mm b/vcl/osx/a11ywrapper.mm
index 96de29e07f77..75763a65a131 100644
--- a/vcl/osx/a11ywrapper.mm
+++ b/vcl/osx/a11ywrapper.mm
@@ -79,34 +79,33 @@ static std::ostream &operator<<(std::ostream &s, NSObject 
*obj) {
 }
 
 -(void) setDefaults: (Reference < XAccessibleContext >) rxAccessibleContext {
-    mpReferenceWrapper = new ReferenceWrapper;
     mActsAsRadioGroup = NO;
-    mpReferenceWrapper -> rAccessibleContext = rxAccessibleContext;
+    maReferenceWrapper.rAccessibleContext = rxAccessibleContext;
     mIsTableCell = NO;
     // Querying all supported interfaces
     try {
         // XAccessibleComponent
-        mpReferenceWrapper->rAccessibleComponent.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleComponent.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleExtendedComponent
-        mpReferenceWrapper->rAccessibleExtendedComponent.set( 
rxAccessibleContext, UNO_QUERY );
+        maReferenceWrapper.rAccessibleExtendedComponent.set( 
rxAccessibleContext, UNO_QUERY );
         // XAccessibleSelection
-        mpReferenceWrapper->rAccessibleSelection.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleSelection.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleTable
-        mpReferenceWrapper->rAccessibleTable.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleTable.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleText
-        mpReferenceWrapper->rAccessibleText.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleText.set( rxAccessibleContext, UNO_QUERY 
);
         // XAccessibleEditableText
-        mpReferenceWrapper->rAccessibleEditableText.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleEditableText.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleValue
-        mpReferenceWrapper->rAccessibleValue.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleValue.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleAction
-        mpReferenceWrapper->rAccessibleAction.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleAction.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleTextAttributes
-        mpReferenceWrapper->rAccessibleTextAttributes.set( 
rxAccessibleContext, UNO_QUERY );
+        maReferenceWrapper.rAccessibleTextAttributes.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleMultiLineText
-        mpReferenceWrapper->rAccessibleMultiLineText.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleMultiLineText.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleTextMarkup
-        mpReferenceWrapper->rAccessibleTextMarkup.set( rxAccessibleContext, 
UNO_QUERY );
+        maReferenceWrapper.rAccessibleTextMarkup.set( rxAccessibleContext, 
UNO_QUERY );
         // XAccessibleEventBroadcaster
         #if 0
         /* #i102033# NSAccessibility does not seemt to know an equivalent for 
transient children.
@@ -137,13 +136,6 @@ static std::ostream &operator<<(std::ostream &s, NSObject 
*obj) {
     }
 }
 
--(void)dealloc {
-    if ( mpReferenceWrapper ) {
-        delete mpReferenceWrapper;
-    }
-    [ super dealloc ];
-}
-
 #pragma mark -
 #pragma mark Utility Section
 
@@ -1112,10 +1104,10 @@ static Reference < XAccessibleContext > hitTestRunner ( 
css::awt::Point point,
     }
     // nothing hit yet, so check ourself
     if ( ! hitChild.is() ) {
-        if ( !mpReferenceWrapper ) {
+        if ( !maReferenceWrapper.rAccessibleContext ) {
             [ self setDefaults: [ self accessibleContext ] ];
         }
-        hitChild = hitTestRunner ( hitPoint, mpReferenceWrapper -> 
rAccessibleContext );
+        hitChild = hitTestRunner ( hitPoint, 
maReferenceWrapper.rAccessibleContext );
     }
     if ( hitChild.is() ) {
         wrapper = [ AquaA11yFactory wrapperForAccessibleContext: hitChild ];
@@ -1130,51 +1122,51 @@ static Reference < XAccessibleContext > hitTestRunner ( 
css::awt::Point point,
 #pragma mark Access Methods
 
 -(XAccessibleAction *)accessibleAction {
-    return mpReferenceWrapper -> rAccessibleAction.get();
+    return maReferenceWrapper.rAccessibleAction.get();
 }
 
 -(XAccessibleContext *)accessibleContext {
-    return mpReferenceWrapper -> rAccessibleContext.get();
+    return maReferenceWrapper.rAccessibleContext.get();
 }
 
 -(XAccessibleComponent *)accessibleComponent {
-    return mpReferenceWrapper -> rAccessibleComponent.get();
+    return maReferenceWrapper.rAccessibleComponent.get();
 }
 
 -(XAccessibleExtendedComponent *)accessibleExtendedComponent {
-    return mpReferenceWrapper -> rAccessibleExtendedComponent.get();
+    return maReferenceWrapper.rAccessibleExtendedComponent.get();
 }
 
 -(XAccessibleSelection *)accessibleSelection {
-    return mpReferenceWrapper -> rAccessibleSelection.get();
+    return maReferenceWrapper.rAccessibleSelection.get();
 }
 
 -(XAccessibleTable *)accessibleTable {
-    return mpReferenceWrapper -> rAccessibleTable.get();
+    return maReferenceWrapper.rAccessibleTable.get();
 }
 
 -(XAccessibleText *)accessibleText {
-    return mpReferenceWrapper -> rAccessibleText.get();
+    return maReferenceWrapper.rAccessibleText.get();
 }
 
 -(XAccessibleEditableText *)accessibleEditableText {
-    return mpReferenceWrapper -> rAccessibleEditableText.get();
+    return maReferenceWrapper.rAccessibleEditableText.get();
 }
 
 -(XAccessibleValue *)accessibleValue {
-    return mpReferenceWrapper -> rAccessibleValue.get();
+    return maReferenceWrapper.rAccessibleValue.get();
 }
 
 -(XAccessibleTextAttributes *)accessibleTextAttributes {
-    return mpReferenceWrapper -> rAccessibleTextAttributes.get();
+    return maReferenceWrapper.rAccessibleTextAttributes.get();
 }
 
 -(XAccessibleMultiLineText *)accessibleMultiLineText {
-    return mpReferenceWrapper -> rAccessibleMultiLineText.get();
+    return maReferenceWrapper.rAccessibleMultiLineText.get();
 }
 
 -(XAccessibleTextMarkup *)accessibleTextMarkup {
-    return mpReferenceWrapper -> rAccessibleTextMarkup.get();
+    return maReferenceWrapper.rAccessibleTextMarkup.get();
 }
 
 -(NSWindow*)windowForParent {
diff --git a/vcl/osx/salframeview.mm b/vcl/osx/salframeview.mm
index 54a91571096a..08821800f5a9 100644
--- a/vcl/osx/salframeview.mm
+++ b/vcl/osx/salframeview.mm
@@ -619,7 +619,6 @@ static AquaSalFrame* getMouseContainerFrame()
         mpLastEvent = nil;
         mMarkedRange = NSMakeRange(NSNotFound, 0);
         mSelectedRange = NSMakeRange(NSNotFound, 0);
-        mpReferenceWrapper = nil;
         mpMouseEventListener = nil;
         mpLastSuperEvent = nil;
         mfLastMagnifyTime = 0.0;
@@ -2055,15 +2054,14 @@ static AquaSalFrame* getMouseContainerFrame()
 
 -(css::accessibility::XAccessibleContext *)accessibleContext
 {
-    if ( !mpReferenceWrapper ) {
+    if ( !maReferenceWrapper.rAccessibleContext ) {
         // some frames never become visible ..
         vcl::Window *pWindow = mpFrame -> GetWindow();
         if ( ! pWindow )
             return nil;
 
-        mpReferenceWrapper = new ReferenceWrapper;
-        mpReferenceWrapper -> rAccessibleContext =  pWindow -> 
/*GetAccessibleChildWindow( 0 ) ->*/ GetAccessible() -> getAccessibleContext();
-        [ AquaA11yFactory insertIntoWrapperRepository: self 
forAccessibleContext: mpReferenceWrapper -> rAccessibleContext ];
+        maReferenceWrapper.rAccessibleContext =  pWindow -> 
/*GetAccessibleChildWindow( 0 ) ->*/ GetAccessible() -> getAccessibleContext();
+        [ AquaA11yFactory insertIntoWrapperRepository: self 
forAccessibleContext: maReferenceWrapper.rAccessibleContext ];
     }
     return [ super accessibleContext ];
 }
commit cc9a6a47e47ef227cb2d93ec9d746e49c999eae2
Author:     Bartosz Kosiorek <gan...@poczta.onet.pl>
AuthorDate: Thu Jun 1 21:16:06 2023 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Sun Jun 4 16:34:10 2023 +0200

    tdf#143877 EMF+ Implement EmfPlusDrawCurve with cardinal spline
    
    Change-Id: I98d30b2a8ba63fdddc08668f453c5f0feeb452db
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152288
    Tested-by: Jenkins
    Reviewed-by: Bartosz Kosiorek <gan...@poczta.onet.pl>
    Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152552

diff --git a/drawinglayer/source/tools/emfphelperdata.cxx 
b/drawinglayer/source/tools/emfphelperdata.cxx
index 94c4c32f026f..03b270228628 100644
--- a/drawinglayer/source/tools/emfphelperdata.cxx
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -81,6 +81,8 @@ namespace emfplushelper
             case EmfPlusRecordTypeFillPolygon: return 
"EmfPlusRecordTypeFillPolygon";
             case EmfPlusRecordTypeDrawLines: return 
"EmfPlusRecordTypeDrawLines";
             case EmfPlusRecordTypeFillClosedCurve: return 
"EmfPlusRecordTypeFillClosedCurve";
+            case EmfPlusRecordTypeDrawClosedCurve: return 
"EmfPlusRecordTypeDrawClosedCurve";

... etc. - the rest is truncated

Reply via email to