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