diff --git a/oox/CppunitTest_oox_mcgr.mk b/oox/CppunitTest_oox_mcgr.mk
new file mode 100644
index 000000000000..ea7692e161fc
--- /dev/null
+++ b/oox/CppunitTest_oox_mcgr.mk
@@ -0,0 +1,53 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,oox_mcgr))
+
+$(eval $(call gb_CppunitTest_use_externals,oox_mcgr,\
+ boost_headers \
+ libxml2 \
+))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,oox_mcgr, \
+ oox/qa/unit/mcgr \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,oox_mcgr, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ oox \
+ sal \
+ subsequenttest \
+ test \
+ unotest \
+ utl \
+ tl \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,oox_mcgr))
+
+$(eval $(call gb_CppunitTest_use_ure,oox_mcgr))
+$(eval $(call gb_CppunitTest_use_vcl,oox_mcgr))
+
+$(eval $(call gb_CppunitTest_use_rdb,oox_mcgr,services))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,oox_mcgr,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,oox_mcgr))
+
+$(eval $(call gb_CppunitTest_add_arguments,oox_mcgr, \
+ -env:arg-env=$(gb_Helper_LIBRARY_PATH_VAR)"$$$${$(gb_Helper_LIBRARY_PATH_VAR)+=$$$$$(gb_Helper_LIBRARY_PATH_VAR)}" \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/oox/Module_oox.mk b/oox/Module_oox.mk
index f868a126a61c..dc07ab913c56 100644
--- a/oox/Module_oox.mk
+++ b/oox/Module_oox.mk
@@ -34,6 +34,7 @@ $(eval $(call gb_Module_add_check_targets,oox,\
CppunitTest_oox_vml \
CppunitTest_oox_shape \
CppunitTest_oox_export \
+ CppunitTest_oox_mcgr \
))
endif
diff --git a/oox/qa/unit/data/MCGR_FontworkColorGradient.fodp b/oox/qa/unit/data/MCGR_FontworkColorGradient.fodp
new file mode 100644
index 000000000000..e82401442c05
--- /dev/null
+++ b/oox/qa/unit/data/MCGR_FontworkColorGradient.fodp
@@ -0,0 +1,662 @@
+
+
+
+ 2023-06-08T12:15:12.97800000024x16impressPT2M30S2B2020/24.2.0.0.alpha0$Windows_X86_64 LibreOffice_project/ca9341cf60f3f9350662d30b61f6eadefca24667Regina Henschel2023-06-08T12:17:40.279000000Regina Henschel
+
+
+ -245
+ -3142
+ 30369
+ 16510
+
+
+ view1
+ false
+ false
+ true
+ true
+ true
+ true
+ false
+ false
+ true
+ 1500
+ false
+ Hw==
+ Hw==
+
+ false
+ true
+ false
+ 0
+ 0
+ false
+ true
+ true
+ 4
+ 0
+ -231
+ -11545
+ 47915
+ 16742
+ 2000
+ 2000
+ 500
+ 500
+ 2000
+ 4
+ 2000
+ 4
+ false
+ 1500
+ true
+ false
+ true
+ false
+ false
+
+
+
+
+ true
+ 1250
+ EPSON6FC99C (WP-4025 Series)
+
+
+ de
+ DE
+
+
+
+
+
+ iAv+/0VQU09ONkZDOTlDIChXUC00MDI1IFNlcmllcykAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARVBTT04gV1AtNDAyNSBTZXJpZXMAAAAAAAAAAAAAAAAWAAEAWgoAAAAAAAAEAAhSAAAEdAAAM1ROVwAAAAAKAEUAUABTAE8ATgA2AEYAQwA5ADkAQwAgACgAVwBQAC0ANAAwADIANQAgAFMAZQByAGkAZQBzACkAAAAAAAAAAAABBAAB3AB0CQ+bgAcBAAkAmgs0CGQAAQAHAFgCAgABAFgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0CQAAAQEBAQABAAABAAAAAAAAAAAAAAA4AAAAfAgAALQIAABAAAAA9AgAAIAAAAAAAAAAAAAAAAMACQRFAFAAUwBPAE4AIABXAFAALQA0ADAAMgA1ACAAUwBlAHIAaQBlAHMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABYAgAAAAAAAAAAAAABAAAAAgAAAAAAAQBYAlgCBwAAAAAACQA0CJoLHgAeAB4AHgA0CJoLOwORBAEAAAAOABYAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAYAAAAAAAAAAAACAAAAAAIAAAMAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAABkAGQANAiaCx4AHgAeAB4ACQAAAAAAAAAAAAAA//8AAAAAAAAAAB4AHgABAAAAAwDgAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAQAAgAAAAAAAAAAAAEAMgAyANT+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYACoAAAAgAAEAAAAgAAAAQAAAAAYAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs/wAAAAAAAAAAAABCAAAAAQAAALAAAAAAAAAAAAAAAAAAAAAeAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBARIAQ09NUEFUX0RVUExFWF9NT0RFDwBEdXBsZXhNb2RlOjpPZmYMAFBSSU5URVJfTkFNRRwARVBTT042RkM5OUMgKFdQLTQwMjUgU2VyaWVzKQsARFJJVkVSX05BTUUUAEVQU09OIFdQLTQwMjUgU2VyaWVz
+ false
+ false
+ false
+ $(inst)/share/palette%3B$(user)/config/standard.sob
+ false
+ 0
+ 0
+ true
+ false
+ false
+ true
+ false
+ true
+ 0
+ $(inst)/share/palette/html.soc
+ $(inst)/share/palette%3B$(user)/config/standard.sod
+ $(inst)/share/palette%3B$(user)/config/standard.soe
+ $(inst)/share/palette%3B$(user)/config/standard.soh
+ $(inst)/share/palette%3B$(user)/config/standard.sog
+ true
+ 4
+ false
+ false
+ true
+ low-resolution
+ false
+ false
+ false
+ true
+ false
+ true
+ true
+ true
+ false
+ false
+ false
+ 6
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/oox/qa/unit/data/MCGR_FontworkColorGradient.fodt b/oox/qa/unit/data/MCGR_FontworkColorGradient.fodt
new file mode 100644
index 000000000000..17052a44b9bf
--- /dev/null
+++ b/oox/qa/unit/data/MCGR_FontworkColorGradient.fodt
@@ -0,0 +1,331 @@
+
+
+
+ 2023-06-08T12:08:34.452000000A6querPT2M6S2B2020/24.2.0.0.alpha0$Windows_X86_64 LibreOffice_project/ca9341cf60f3f9350662d30b61f6eadefca24667Regina Henschel2023-06-08T12:10:27.725000000Regina Henschel
+
+
+ 0
+ 0
+ 30249
+ 9082
+ true
+ false
+
+
+ view2
+ 8223
+ 1000
+ 0
+ 0
+ 30247
+ 9081
+ 0
+ 1
+ false
+ 100
+ false
+ false
+ false
+ true
+ false
+ false
+
+
+
+
+ true
+
+ false
+ false
+ false
+ false
+ true
+ 1
+ true
+ false
+ false
+ false
+
+ false
+
+ false
+ false
+ false
+
+ 0
+ false
+ true
+ true
+ false
+ false
+ false
+
+ 0
+
+ true
+ high-resolution
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ 1621794
+ 1306543
+ false
+ false
+ false
+ true
+ false
+ false
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ false
+ false
+ true
+ 0
+ true
+ false
+ false
+ true
+ false
+ 0
+ true
+ false
+ true
+ true
+ true
+ true
+ false
+ false
+ false
+ false
+ false
+ true
+
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Simple
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx
index 3922726306b3..9b62dc337c1b 100644
--- a/oox/qa/unit/export.cxx
+++ b/oox/qa/unit/export.cxx
@@ -1012,7 +1012,7 @@ CPPUNIT_TEST_FIXTURE(Test, testFontworkDistance)
CPPUNIT_TEST_FIXTURE(Test, testFontworkLinGradientRGBColor)
{
// The document has a Fontwork shape with UI settings: linear gradient fill with angle 330deg,
- // start color #ffff00 (Yellow) with 'Brightness' 80%, end color #4682B4 (Steel Blue), Transition
+ // start color #ffff00 (Yellow) with 'Intensity' 80%, end color #4682B4 (Steel Blue), Transition
// Start 25% and solid transparency 30%.
// Without fix the gradient was not exported at all.
loadFromURL(u"tdf51195_Fontwork_linearGradient.fodt");
@@ -1040,15 +1040,15 @@ CPPUNIT_TEST_FIXTURE(Test, testFontworkLinGradientRGBColor)
assertXPath(pXmlDoc, sElement + "w14:textFill/w14:gradFill/w14:lin", "scaled", "0");
// Make sure the color stops have correct position and color
+ // The 'intensity' property in the UI has a different algorithm than the 'lumMod' attribute in
+ // OOXML. Therefore it cannot be exported as 'lumMod' but need to be incorporated into the color.
sElement += "w14:textFill/w14:gradFill/w14:gsLst/";
assertXPath(pXmlDoc, sElement + "w14:gs[1]", "pos", "0");
- assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr", "val", "ffff00");
- assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr/w14:lumMod", "val", "80000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr", "val", "cccc00");
assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr/w14:alpha", "val", "30000");
assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "25000");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr", "val", "ffff00");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr/w14:lumMod", "val", "80000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr", "val", "cccc00");
assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr/w14:alpha", "val", "30000");
assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "100000");
@@ -1081,7 +1081,7 @@ CPPUNIT_TEST_FIXTURE(Test, testFontworkAxialGradientTransparency)
// Make sure w14:textFill and w14:gradFill elements exist with child elements
assertXPath(pXmlDoc, sElement + "w14:textFill/w14:gradFill/w14:gsLst", 1);
- assertXPath(pXmlDoc, sElement + "w14:textFill/w14:gradFill/w14:gsLst/w14:gs", 6);
+ assertXPath(pXmlDoc, sElement + "w14:textFill/w14:gradFill/w14:gsLst/w14:gs", 3);
// 160deg gradient rotation = 290deg (360deg-160deg+90deg) color transition direction
assertXPath(pXmlDoc, sElement + "w14:textFill/w14:gradFill/w14:lin", "ang", "17400000");
assertXPath(pXmlDoc, sElement + "w14:textFill/w14:gradFill/w14:lin", "scaled", "0");
@@ -1089,31 +1089,21 @@ CPPUNIT_TEST_FIXTURE(Test, testFontworkAxialGradientTransparency)
// Make sure the color stops have correct position and color
sElement += "w14:textFill/w14:gradFill/w14:gsLst/";
// gradient is in transparency, color is always the same.
- for (char ch = '1'; ch <= '6'; ++ch)
+ for (char ch = '1'; ch <= '3'; ++ch)
{
assertXPath(pXmlDoc, sElement + "w14:gs[" + OStringChar(ch) + "]/w14:schemeClr", "val",
"accent3");
assertXPath(pXmlDoc, sElement + "w14:gs[" + OStringChar(ch) + "]/w14:schemeClr/w14:lumMod",
"val", "75000");
}
- // outer transparency
- assertXPath(pXmlDoc, sElement + "w14:gs[1]", "pos", "0");
- assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:schemeClr/w14:alpha", "val", "90000");
- // border, same transparency
- assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "20000");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:schemeClr/w14:alpha", "val", "90000");
- // gradient to inner transparency at center
- assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "50000");
- assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:schemeClr/w14:alpha", "val", "5000");
- // from inner transparency at center
- assertXPath(pXmlDoc, sElement + "w14:gs[4]", "pos", "50000");
- assertXPath(pXmlDoc, sElement + "w14:gs[4]/w14:schemeClr/w14:alpha", "val", "5000");
- // mirrored gradient to outer transparency
- assertXPath(pXmlDoc, sElement + "w14:gs[5]", "pos", "80000");
- assertXPath(pXmlDoc, sElement + "w14:gs[5]/w14:schemeClr/w14:alpha", "val", "90000");
- // mirrored border
- assertXPath(pXmlDoc, sElement + "w14:gs[6]", "pos", "100000");
- assertXPath(pXmlDoc, sElement + "w14:gs[6]/w14:schemeClr/w14:alpha", "val", "90000");
+ // transparency values are not exactly like in UI because converting through rgb-color.
+ // border 40% in UI means 20% on each side.
+ assertXPath(pXmlDoc, sElement + "w14:gs[1]", "pos", "20000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:schemeClr/w14:alpha", "val", "89800");
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "50000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:schemeClr/w14:alpha", "val", "4710");
+ assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "80000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:schemeClr/w14:alpha", "val", "89800");
}
CPPUNIT_TEST_FIXTURE(Test, testFontworkRadialGradient)
@@ -1147,12 +1137,13 @@ CPPUNIT_TEST_FIXTURE(Test, testFontworkRadialGradient)
{ { "l", "75000" }, { "t", "20000" }, { "r", "25000" }, { "b", "80000" } });
// Make sure the color stops have correct position and color
+ // The first stop is duplicated to force Word to render the gradient same as LO.
sElement += "w14:textFill/w14:gradFill/w14:gsLst/";
assertXPath(pXmlDoc, sElement + "w14:gs[1]", "pos", "0");
assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr", "val", "ff0000");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "90000");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr", "val", "40e0d0");
- assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "100000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "0");
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr", "val", "ff0000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "90000");
assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:srgbClr", "val", "40e0d0");
}
@@ -1187,16 +1178,16 @@ CPPUNIT_TEST_FIXTURE(Test, testFontworkEllipticalGradient)
{ { "l", "50000" }, { "t", "50000" }, { "r", "50000" }, { "b", "50000" } });
// Make sure the color stops have correct position and color
+ // transparency values are not exactly like in UI because converting through rgb-color.
sElement += "w14:textFill/w14:gradFill/w14:gsLst/";
assertXPath(pXmlDoc, sElement + "w14:gs[1]", "pos", "0");
assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr", "val", "00008b");
- assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr/w14:alpha", 0);
- assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "50000");
+ // stop is duplicated to force Word to same rendering as LO does.
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "0");
assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr", "val", "00008b");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr/w14:alpha", "val", "70000");
- assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "100000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "50000");
assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:srgbClr", "val", "00008b");
- assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:srgbClr/w14:alpha", "val", "70000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:srgbClr/w14:alpha", "val", "69800");
}
CPPUNIT_TEST_FIXTURE(Test, testFontworkSquareGradient)
@@ -1230,16 +1221,15 @@ CPPUNIT_TEST_FIXTURE(Test, testFontworkSquareGradient)
{ { "l", "100000" }, { "t", "50000" }, { "r", "0" }, { "b", "50000" } });
// Make sure the color stops have correct position and color
+ // The 'intensity' property in the UI has a different algorithm than the 'lumMod' attribute in
+ // OOXML. Therefore it cannot be exported as 'lumMod' but need to be incorporated into the color.
sElement += "w14:textFill/w14:gradFill/w14:gsLst/";
assertXPath(pXmlDoc, sElement + "w14:gs[1]", "pos", "0");
- assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr", "val", "ffff6e");
- assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr/w14:lumMod", "val", "90000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:srgbClr", "val", "e6e663");
assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "0");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr", "val", "ffff6e");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr/w14:lumMod", "val", "90000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:srgbClr", "val", "e6e663");
assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "100000");
- assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:srgbClr", "val", "49b3ef");
- assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:srgbClr/w14:lumMod", "val", "40000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:srgbClr", "val", "1d4860");
}
CPPUNIT_TEST_FIXTURE(Test, testFontworkRectGradient)
@@ -1273,22 +1263,24 @@ CPPUNIT_TEST_FIXTURE(Test, testFontworkRectGradient)
{ { "l", "50000" }, { "t", "50000" }, { "r", "50000" }, { "b", "50000" } });
// Make sure the color stops have correct position and color
+ // transparency values are not exactly like in UI because converting through rgb-color.
sElement += "w14:textFill/w14:gradFill/w14:gsLst/";
assertXPath(pXmlDoc, sElement + "w14:gs[1]", "pos", "0");
assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:schemeClr", "val", "accent4");
assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:schemeClr/w14:lumMod", "val", "40000");
assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:schemeClr/w14:lumOff", "val", "60000");
- assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:schemeClr/w14:alpha", "val", "5000");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "90000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[1]/w14:schemeClr/w14:alpha", "val", "4710");
+ // The first stop is duplicated to force Word to render the gradient same as LO.
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]", "pos", "0");
assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:schemeClr", "val", "accent4");
assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:schemeClr/w14:lumMod", "val", "40000");
assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:schemeClr/w14:lumOff", "val", "60000");
- assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:schemeClr/w14:alpha", "val", "70000");
- assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "100000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[2]/w14:schemeClr/w14:alpha", "val", "4710");
+ assertXPath(pXmlDoc, sElement + "w14:gs[3]", "pos", "90000");
assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:schemeClr", "val", "accent4");
assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:schemeClr/w14:lumMod", "val", "40000");
assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:schemeClr/w14:lumOff", "val", "60000");
- assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:schemeClr/w14:alpha", "val", "70000");
+ assertXPath(pXmlDoc, sElement + "w14:gs[3]/w14:schemeClr/w14:alpha", "val", "69800");
}
CPPUNIT_TEST_FIXTURE(Test, testThemeColorTransparency)
diff --git a/oox/qa/unit/mcgr.cxx b/oox/qa/unit/mcgr.cxx
new file mode 100644
index 000000000000..b752f49115f4
--- /dev/null
+++ b/oox/qa/unit/mcgr.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Covers tests for multi-color gradient (MCGR) feature, available since LO 7.6.0.
+class TestMCGR : public UnoApiXmlTest
+{
+public:
+ TestMCGR()
+ : UnoApiXmlTest("/oox/qa/unit/data/")
+ {
+ }
+};
+
+CPPUNIT_TEST_FIXTURE(TestMCGR, testFontworkColorGradient)
+{
+ // Given a document with three-color gradient on a Fontwork.
+ loadFromURL(u"MCGR_FontworkColorGradient.fodp");
+ // Save it to PPTX
+ save("Impress Office Open XML");
+ // And make sure a multi-color gradient fill is exported.
+ xmlDocUniquePtr pXmlDoc = parseExport("ppt/slides/slide1.xml");
+ // linear gradient with 30deg angle
+ assertXPath(pXmlDoc, "//a:r/a:rPr/a:gradFill/a:lin", "ang", "3600000");
+ // three color stops, no transparency
+ const OString sPath = "//a:r/a:rPr/a:gradFill/a:gsLst/";
+ assertXPath(pXmlDoc, sPath + "a:gs", 3);
+ assertXPath(pXmlDoc, sPath + "a:gs[1]", "pos", "0");
+ assertXPath(pXmlDoc, sPath + "a:gs[1]/a:srgbClr", "val", "ff1493");
+ assertXPath(pXmlDoc, sPath + "a:gs[2]", "pos", "30000");
+ assertXPath(pXmlDoc, sPath + "a:gs[2]/a:srgbClr", "val", "ffff00");
+ assertXPath(pXmlDoc, sPath + "a:gs[3]", "pos", "100000");
+ assertXPath(pXmlDoc, sPath + "a:gs[3]/a:srgbClr", "val", "00ffff");
+}
+
+CPPUNIT_TEST_FIXTURE(TestMCGR, testFontworkColorGradientWord)
+{
+ // Fontwork is handled different in Word than in PowerPoint documents. So we need a separate
+ // test for a text document.
+ // Given a document with three-color gradient on a Fontwork.
+ loadFromURL(u"MCGR_FontworkColorGradient.fodt");
+ // Save it to DOCX
+ save("Office Open XML Text");
+ // And make sure a multi-color gradient fill is exported.
+ xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
+ // linear gradient with 30deg angle
+ assertXPath(pXmlDoc, "//w14:lin", "ang", "3600000");
+ // three color stops, no transparency
+ const OString sPath = "//w14:gradFill/w14:gsLst/";
+ assertXPath(pXmlDoc, sPath + "w14:gs", 3);
+ assertXPath(pXmlDoc, sPath + "w14:gs[1]", "pos", "0");
+ assertXPath(pXmlDoc, sPath + "w14:gs[1]/w14:srgbClr", "val", "ff1493");
+ assertXPath(pXmlDoc, sPath + "w14:gs[2]", "pos", "30000");
+ assertXPath(pXmlDoc, sPath + "w14:gs[2]/w14:srgbClr", "val", "ffff00");
+ assertXPath(pXmlDoc, sPath + "w14:gs[3]", "pos", "100000");
+ assertXPath(pXmlDoc, sPath + "w14:gs[3]/w14:srgbClr", "val", "00ffff");
+}
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file
diff --git a/oox/source/drawingml/fontworkhelpers.cxx b/oox/source/drawingml/fontworkhelpers.cxx
index 3c8d28d634c6..43f2b050cdd8 100644
--- a/oox/source/drawingml/fontworkhelpers.cxx
+++ b/oox/source/drawingml/fontworkhelpers.cxx
@@ -19,6 +19,8 @@
#include
+#include
+#include
#include
#include
#include
@@ -33,7 +35,7 @@
#include
#include
-#include
+#include
#include
#include
#include
@@ -1123,8 +1125,8 @@ void lcl_addColorTransformationToGrabBagStack(const model::ComplexColor& rComple
void lcl_getGradientsFromShape(const uno::Reference& rXPropSet,
const uno::Reference& rXPropSetInfo,
- awt::Gradient& rColorGradient, bool& rbHasColorGradient,
- awt::Gradient& rTransparenceGradient,
+ awt::Gradient2& rColorGradient, bool& rbHasColorGradient,
+ awt::Gradient2& rTransparenceGradient,
bool& rbHasTransparenceGradient)
{
OUString sColorGradientName;
@@ -1144,249 +1146,94 @@ void lcl_getGradientsFromShape(const uno::Reference& rXProp
&& (rXPropSet->getPropertyValue(u"FillTransparenceGradient") >>= rTransparenceGradient);
}
-// Returns color without transparency and without intensity. rnPos is position in gradient
-// definition from 0 (= 0%) to 100 (=100%), without considering the gradient type. The border is at
-// 0% side. The caller takes care to use a suitable position and gradient.
-::Color lcl_getColorFromColorGradient(const awt::Gradient& rColorGradient, const sal_Int32 rnPos)
-{
- sal_Int16 nBorder = rColorGradient.Border; // Border is in percent
- ::Color aStartColor(ColorTransparency, rColorGradient.StartColor);
- if (rnPos <= 0 || rnPos <= nBorder || nBorder >= 100)
- return aStartColor;
-
- ::Color aEndColor(ColorTransparency, rColorGradient.EndColor);
- if (rnPos >= 100)
- return aEndColor;
-
- // linear interpolation for nBorder < rnpos < 100 in each color component
- auto ColorInterpolate = [rnPos, nBorder](sal_uInt8 nStartC, sal_uInt8 nEndC) -> sal_uInt8 {
- return std::clamp(
- std::lround((nStartC * (100 - rnPos) + nEndC * (rnPos - nBorder)) / (100.0 - nBorder)),
- 0, 255);
- };
- sal_uInt8 nInterpolatedRed = ColorInterpolate(aStartColor.GetRed(), aEndColor.GetRed());
- sal_uInt8 nInterpolatedGreen = ColorInterpolate(aStartColor.GetGreen(), aEndColor.GetGreen());
- sal_uInt8 nInterpolatedBlue = ColorInterpolate(aStartColor.GetBlue(), aEndColor.GetBlue());
- return ::Color(nInterpolatedRed, nInterpolatedGreen, nInterpolatedBlue);
-}
-
-// returns intensity in percent. rnPos is position in gradient definition from
-// 0 (= 0%) to 100 (=100%), without considering the gradient type. The border is at 0% side.
-// The caller takes care to use a suitable position and gradient.
-sal_Int16 lcl_getIntensityFromColorGradient(const awt::Gradient& rColorGradient,
- const sal_Int32 rnPos)
-{
- sal_Int16 nBorder = rColorGradient.Border; // Border is in percent
- sal_Int16 nStartIntensity = rColorGradient.StartIntensity;
- if (rnPos <= 0 || rnPos <= nBorder || nBorder >= 100)
- return nStartIntensity;
-
- sal_Int32 nEndIntensity = rColorGradient.EndIntensity;
- if (rnPos >= 100)
- return nEndIntensity;
-
- // linear interpolation for nBorder < npos < 100
- return std::lround((nStartIntensity * (100 - rnPos) + nEndIntensity * (rnPos - nBorder))
- / (100.0 - nBorder));
-}
-
-// returns transparency in percent. rnPos is position in gradient definition from
-// 0 (= 0%) to 100 (=100%), without considering the gradient type. The border is at 0% side.
-// The caller takes care to use a suitable position and gradient.
-sal_Int16 lcl_getAlphaFromTransparenceGradient(const awt::Gradient& rTransparenceGradient,
- const sal_Int32 rnPos)
-{
- sal_Int16 nBorder = rTransparenceGradient.Border; // Border is in percent
- // The transparency is not in Start- or EndIntensity, but encoded into the Color as gray.
- ::Color aStartColor(ColorTransparency, rTransparenceGradient.StartColor);
- if (rnPos <= 0 || rnPos <= nBorder || nBorder >= 100)
- return std::lround(aStartColor.GetRed() * 100 / 255.0);
-
- ::Color aEndColor(ColorTransparency, rTransparenceGradient.EndColor);
- if (rnPos >= 100)
- return std::lround(aEndColor.GetRed() * 100 / 255.0);
-
- // linear interpolation for nBorder < npos < 100
- return std::lround(
- (aStartColor.GetRed() * (100 - rnPos) + aEndColor.GetRed() * (rnPos - nBorder))
- / (100.0 - nBorder) * 100 / 255.0);
-}
-
-GradientStopColor
-lcl_createGradientStopColor(const uno::Reference& rXPropSet,
- const uno::Reference& rXPropSetInfo,
- const awt::Gradient& rColorGradient, const bool& rbHasColorGradient,
- const awt::Gradient& rTransparenceGradient,
- const bool& rbHasTransparenceGradient, const sal_Int32& rnPos)
-{
- // Component mnValue of Transformation struct is in 1/100th percent (e.g 80% = 8000) in range
- // -10000 to +10000. Constants are used in converting from API values below.
- constexpr sal_Int16 nFactorToHthPerc = 100;
- constexpr sal_Int16 nMaxHthPerc = 10000;
- GradientStopColor aStopColor;
- if (rbHasTransparenceGradient)
- {
- // Color
- if (rbHasColorGradient)
- {
- // a color gradient is yet not enabled to use theme colors
- aStopColor.RGBColor = lcl_getColorFromColorGradient(rColorGradient, rnPos);
- sal_Int16 nIntensity = lcl_getIntensityFromColorGradient(rColorGradient, rnPos);
- if (nIntensity != 100)
- aStopColor.TTColor.addTransformation(
- { model::TransformationType::LumMod,
- std::clamp(nIntensity * nFactorToHthPerc, -nMaxHthPerc,
- nMaxHthPerc) });
- }
- else // solid color
- {
- // fill color might be a theme color
- if (!(FontworkHelpers::getThemeColorFromShape("FillComplexColor", rXPropSet,
- aStopColor.TTColor)))
- {
- // no theme color, use FillColor
- sal_Int32 nFillColor(0);
- if (rXPropSetInfo->hasPropertyByName("FillColor"))
- rXPropSet->getPropertyValue(u"FillColor") >>= nFillColor;
- aStopColor.RGBColor = ::Color(ColorTransparency, nFillColor);
- aStopColor.TTColor = model::ComplexColor();
- }
- }
-
- // transparency
- // Mixed gradient types for color and transparency are not possible in oox. For now we act as
- // if gradient geometries are identical. That is the case if we get the gradient from oox
- // import.
- sal_Int16 nAlpha = lcl_getAlphaFromTransparenceGradient(rTransparenceGradient, rnPos);
- // model::TransformationType::Alpha is designed to be used with a:alpha, which has opacity.
- // Therefore convert transparency to opacity.
- if (nAlpha > 0)
- aStopColor.TTColor.addTransformation(
- { model::TransformationType::Alpha,
- std::clamp(nMaxHthPerc - nAlpha * nFactorToHthPerc, -nMaxHthPerc,
- nMaxHthPerc) });
-
- return aStopColor;
- }
-
- // else solid transparency or no transparency
- // color
- if (rbHasColorGradient)
- {
- // a color gradient is yet not enabled to use theme colors
- aStopColor.RGBColor = lcl_getColorFromColorGradient(rColorGradient, rnPos);
- aStopColor.TTColor = model::ComplexColor();
- sal_Int16 nIntensity = lcl_getIntensityFromColorGradient(rColorGradient, rnPos);
- if (nIntensity != 100)
- aStopColor.TTColor.addTransformation(
- { model::TransformationType::LumMod,
- std::clamp(nIntensity * nFactorToHthPerc, -nMaxHthPerc,
- nMaxHthPerc) });
- }
- else
- {
- // solid color and solid transparency
- SAL_WARN("oox.drawingml", "method should not be called in this case");
- if (!(FontworkHelpers::getThemeColorFromShape("FillComplexColor", rXPropSet,
- aStopColor.TTColor)))
- {
- // no theme color, use FillColor
- sal_Int32 nFillColor(0);
- if (rXPropSetInfo->hasPropertyByName(u"FillColor"))
- rXPropSet->getPropertyValue(u"FillColor") >>= nFillColor;
- aStopColor.RGBColor = ::Color(ColorTransparency, nFillColor);
- aStopColor.TTColor = model::ComplexColor();
- }
- }
-
- // Maybe transparency from FillTransparence
- // model::TransformationType::Alpha is designed to be used with a:alpha, which has opacity.
- // Therefore convert transparency to opacity.
- sal_Int16 nAlpha(0);
- if (rXPropSetInfo->hasPropertyByName(u"FillTransparence")
- && (rXPropSet->getPropertyValue(u"FillTransparence") >>= nAlpha) && nAlpha > 0)
- aStopColor.TTColor.addTransformation(
- { model::TransformationType::Alpha,
- std::clamp(nMaxHthPerc - nAlpha * nFactorToHthPerc, -nMaxHthPerc,
- nMaxHthPerc) });
-
- return aStopColor;
-}
-
ColorMapType lcl_createColorMapFromShapeProps(
const uno::Reference& rXPropSet,
const uno::Reference& rXPropSetInfo,
- const awt::Gradient& rColorGradient, const bool& rbHasColorGradient,
- const awt::Gradient& rTransparenceGradient, const bool& rbHasTransparenceGradient)
+ const awt::Gradient2& rColorGradient, const bool& rbHasColorGradient,
+ const awt::Gradient2& rTransparenceGradient, const bool& rbHasTransparenceGradient)
{
- ColorMapType aColorMap;
- awt::Gradient aColorGradient = rColorGradient;
- awt::Gradient aTransparenceGradient = rTransparenceGradient;
- // AXIAL has reversed gradient direction. Change it so, that 'border' is at 'start'.
- if (rbHasColorGradient && aColorGradient.Style == awt::GradientStyle_AXIAL)
- {
- std::swap(aColorGradient.StartColor, aColorGradient.EndColor);
- std::swap(aColorGradient.StartIntensity, aColorGradient.EndIntensity);
- }
- if (rbHasTransparenceGradient && aTransparenceGradient.Style == awt::GradientStyle_AXIAL)
- {
- std::swap(aTransparenceGradient.StartColor, aTransparenceGradient.EndColor);
- std::swap(aTransparenceGradient.StartIntensity,
- aTransparenceGradient.EndIntensity);
- }
+ // LibreOffice can use color gradients and transparency gradients with different geometries.
+ // That is not possible in OOXML, so a fill might look different in Word. But a round-trip
+ // with gradients imported from Word, should work well.
- // A GradientStopColor includes color and transparency.
- // The key of aColorMap has same unit as the w14:pos attribute of element in oox.
- GradientStopColor aStartStopColor
- = lcl_createGradientStopColor(rXPropSet, rXPropSetInfo, aColorGradient, rbHasColorGradient,
- aTransparenceGradient, rbHasTransparenceGradient, 0);
- aColorMap.insert(std::pair{ 0, aStartStopColor });
- GradientStopColor aEndStopColor
- = lcl_createGradientStopColor(rXPropSet, rXPropSetInfo, aColorGradient, rbHasColorGradient,
- aTransparenceGradient, rbHasTransparenceGradient, 100);
- aColorMap.insert(std::pair{ 100000, aEndStopColor });
-
- // We add additional GradientStopColor in case of borders.
+ // Word has transparency not as separate gradient but as color transformation in a color
+ // gradient. Thus we synchronize the gradients. Then they have same offsets and count.
+ basegfx::BColor aSingleColor;
+ basegfx::BGradient aColorBGradient;
+ basegfx::BColorStops aColorStops;
if (rbHasColorGradient)
{
- // We only use the color border for now. If the transparency gradient has a total different
- // geometry than the color gradient, a description is not possible in oox.
- // ToDo: If geometries only differ in border, emulation is possible.
- sal_Int32 nBorderPos = aColorGradient.Border * 1000;
- if (nBorderPos > 0)
- aColorMap.insert(std::pair{ nBorderPos, aStartStopColor });
+ aColorBGradient = basegfx::BGradient(rColorGradient);
+ aColorBGradient.tryToApplyStartEndIntensity();
+ aColorBGradient.tryToApplyBorder();
+ aColorBGradient.tryToApplyAxial();
+ basegfx::utils::prepareColorStops(aColorBGradient, aColorStops, aSingleColor);
+ // All gradient styles but LINEAR and AXIAL (which is already converted to LINEAR) need the
+ // stops sequence reverse.
+ if (awt::GradientStyle_LINEAR != aColorBGradient.GetGradientStyle())
+ aColorStops.reverseColorStops();
}
- else if (rbHasTransparenceGradient)
+ else
{
- sal_Int32 nBorderPos = aTransparenceGradient.Border * 1000;
- if (nBorderPos > 0)
- aColorMap.insert(std::pair{ nBorderPos, aStartStopColor });
+ sal_Int32 nFillColor(0);
+ if (rXPropSetInfo->hasPropertyByName("FillColor"))
+ rXPropSet->getPropertyValue(u"FillColor") >>= nFillColor;
+ aSingleColor = ::Color(ColorTransparency, nFillColor).getBColor().clamp();
}
- // In case of AXIAL we compress the gradient to half wide and mirror it to the other half.
- if ((rbHasColorGradient && aColorGradient.Style == awt::GradientStyle_AXIAL)
- || (!rbHasColorGradient && rbHasTransparenceGradient
- && aTransparenceGradient.Style == awt::GradientStyle_AXIAL))
+ basegfx::BColor aSingleTrans;
+ basegfx::BGradient aTransBGradient;
+ basegfx::BColorStops aTransStops;
+ if (rbHasTransparenceGradient)
{
- ColorMapType aHelpColorMap(aColorMap);
- aColorMap.clear();
- for (auto it = aHelpColorMap.begin(); it != aHelpColorMap.end(); ++it)
- {
- aColorMap.insert(std::pair{ (*it).first / 2, (*it).second });
- aColorMap.insert(std::pair{ 100000 - (*it).first / 2, (*it).second });
- }
+ aTransBGradient = basegfx::BGradient(rTransparenceGradient);
+ aTransBGradient.tryToApplyStartEndIntensity(); // usually 100%, but might be set by macro
+ aTransBGradient.tryToApplyBorder();
+ aTransBGradient.tryToApplyAxial();
+ basegfx::utils::prepareColorStops(aTransBGradient, aTransStops, aSingleTrans);
+ // All gradient styles but LINEAR and AXIAL (which is already converted to LINEAR) need the
+ // stops sequence reverse.
+ if (awt::GradientStyle_LINEAR != aTransBGradient.GetGradientStyle())
+ aTransStops.reverseColorStops();
}
- else if ((rbHasColorGradient && aColorGradient.Style != awt::GradientStyle_LINEAR)
- || (!rbHasColorGradient && rbHasTransparenceGradient
- && aTransparenceGradient.Style != awt::GradientStyle_LINEAR))
+ else
{
- // only LINEAR has same direction as Word, the others are reverse.
- ColorMapType aHelpColorMap(aColorMap);
- aColorMap.clear();
- for (auto it = aHelpColorMap.begin(); it != aHelpColorMap.end(); ++it)
+ sal_Int16 nAPITrans(0);
+ if (rXPropSetInfo->hasPropertyByName(u"FillTransparence"))
+ rXPropSet->getPropertyValue(u"FillTransparence") >>= nAPITrans;
+ // API transparency is in range 0..100, BColor in range [0.0, 1.0].
+ aSingleTrans = basegfx::BColor(nAPITrans * 0.01).clamp();
+ }
+
+ basegfx::utils::synchronizeColorStops(aColorStops, aTransStops, aSingleColor, aSingleTrans);
+
+ ColorMapType aColorMap;
+
+ // If we have no color gradient, the fix fill color might be a theme color. In that case we use
+ // it instead of the color from the color stop.
+ GradientStopColor aFixColor;
+ bool bUseThemeColor(!rbHasColorGradient
+ && FontworkHelpers::getThemeColorFromShape("FillComplexColor", rXPropSet,
+ aFixColor.TTColor));
+
+ for (auto itC = aColorStops.begin(), itT = aTransStops.begin();
+ itC != aColorStops.end() && itT != aTransStops.end(); ++itC, ++itT)
+ {
+ GradientStopColor aNextStopColor = aFixColor;
+ if (!bUseThemeColor)
{
- aColorMap.insert(std::pair{ 100000 - (*it).first, (*it).second });
+ aNextStopColor.TTColor = model::ComplexColor();
+ aNextStopColor.RGBColor = ::Color((*itC).getStopColor());
}
+ // model::TransformationType::Alpha is opacity in range 0..10000,
+ // BColor is transparency in range [0.0, 1.0]
+ sal_Int16 nAlpha = std::clamp(
+ 10000 - std::lround((*itT).getStopColor().luminance() * 10000.0), 0, 10000);
+ if (nAlpha < 10000)
+ aNextStopColor.TTColor.addTransformation({ model::TransformationType::Alpha, nAlpha });
+ sal_Int32 nPosition
+ = static_cast(std::lround((*itC).getStopOffset() * 100000.0));
+ aColorMap.insert(std::pair{ nPosition, aNextStopColor });
}
// If a gradient has only two stops, MS Office renders it with a non-linear method which looks
@@ -1397,7 +1244,6 @@ ColorMapType lcl_createColorMapFromShapeProps(
auto it = aColorMap.begin();
aColorMap.insert(std::pair{ 0, (*it).second });
}
-
return aColorMap;
}
} // end namespace
@@ -1439,9 +1285,9 @@ void FontworkHelpers::createCharInteropGrabBagUpdatesFromShapeProps(
}
case drawing::FillStyle_GRADIENT:
{
- awt::Gradient aColorGradient;
+ awt::Gradient2 aColorGradient;
bool bHasColorGradient(false);
- awt::Gradient aTransparenceGradient;
+ awt::Gradient2 aTransparenceGradient;
bool bHasTransparenceGradient(false);
lcl_getGradientsFromShape(rXPropSet, xPropSetInfo, aColorGradient, bHasColorGradient,
aTransparenceGradient, bHasTransparenceGradient);
@@ -1475,8 +1321,8 @@ void FontworkHelpers::createCharInteropGrabBagUpdatesFromShapeProps(
}
lcl_addColorTransformationToGrabBagStack((*it).second.TTColor, pGrabBagStack);
- pGrabBagStack
- ->pop(); // maCurrentElement:'gs', maPropertyList:'attributes', 'srgbClr' or 'schemeClr'
+ pGrabBagStack->pop();
+ // maCurrentElement:'gs', maPropertyList:'attributes', 'srgbClr' or 'schemeClr'
pGrabBagStack->pop(); // maCurrentElement:'gsLst', maPropertyList: at least two 'gs'
}
pGrabBagStack->pop(); // maCurrentElement:'gradFill', maPropertyList: gsLst