include/svx/ColorSets.hxx | 3 + include/tools/color.hxx | 8 +++ svx/CppunitTest_svx_styles.mk | 44 +++++++++++++++++ svx/Module_svx.mk | 1 svx/qa/unit/data/theme.pptx |binary svx/qa/unit/styles.cxx | 100 ++++++++++++++++++++++++++++++++++++++++ svx/source/styles/ColorSets.cxx | 59 +++++++++++++++++++++++ svx/source/svdraw/svdpage.cxx | 21 ++++++++ tools/qa/cppunit/test_color.cxx | 13 +++++ tools/source/generic/color.cxx | 32 ++++++++++++ 10 files changed, 280 insertions(+), 1 deletion(-)
New commits: commit 70ce55234f93e6dc11d61e7930398c4f16d6aaa8 Author: Miklos Vajna <[email protected]> AuthorDate: Tue Nov 30 08:44:02 2021 +0100 Commit: Miklos Vajna <[email protected]> CommitDate: Mon Jun 27 08:58:37 2022 +0200 svx: update objects of pages of a master page when the theme changes Only text color as a start, and without effects. (cherry picked from commit 48f0c5f73f99c919ec24deadc96c3cf5483c9314) Change-Id: I52b1c110870605134c414bcc94b50871cd49975b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136372 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/include/svx/ColorSets.hxx b/include/svx/ColorSets.hxx index e6c9f1b4e7ee..c4aa0ae2b9bc 100644 --- a/include/svx/ColorSets.hxx +++ b/include/svx/ColorSets.hxx @@ -19,6 +19,7 @@ #include <tools/color.hxx> typedef struct _xmlTextWriter* xmlTextWriterPtr; +class SdrPage; namespace svx { @@ -89,6 +90,8 @@ public: void ToAny(css::uno::Any& rVal) const; static std::unique_ptr<Theme> FromAny(const css::uno::Any& rVal); + + void UpdateSdrPage(SdrPage* pPage); }; } // end of namespace svx diff --git a/svx/CppunitTest_svx_styles.mk b/svx/CppunitTest_svx_styles.mk new file mode 100644 index 000000000000..f617668ad705 --- /dev/null +++ b/svx/CppunitTest_svx_styles.mk @@ -0,0 +1,44 @@ +# -*- 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,svx_styles)) + +$(eval $(call gb_CppunitTest_use_externals,svx_styles,\ + boost_headers \ +)) + +$(eval $(call gb_CppunitTest_add_exception_objects,svx_styles, \ + svx/qa/unit/styles \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,svx_styles, \ + comphelper \ + cppu \ + svx \ + sal \ + test \ + unotest \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,svx_styles)) + +$(eval $(call gb_CppunitTest_use_ure,svx_styles)) +$(eval $(call gb_CppunitTest_use_vcl,svx_styles)) + +$(eval $(call gb_CppunitTest_use_rdb,svx_styles,services)) + +$(eval $(call gb_CppunitTest_use_custom_headers,svx_styles,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_configuration,svx_styles)) + +# vim: set noet sw=4 ts=4: diff --git a/svx/Module_svx.mk b/svx/Module_svx.mk index 3891829c8108..3fc4e889173e 100644 --- a/svx/Module_svx.mk +++ b/svx/Module_svx.mk @@ -39,6 +39,7 @@ $(eval $(call gb_Module_add_check_targets,svx,\ CppunitTest_svx_unit \ CppunitTest_svx_gallery_test \ CppunitTest_svx_removewhichrange \ + CppunitTest_svx_styles \ )) # screenshots diff --git a/svx/qa/unit/data/theme.pptx b/svx/qa/unit/data/theme.pptx new file mode 100644 index 000000000000..397b6706ffa4 Binary files /dev/null and b/svx/qa/unit/data/theme.pptx differ diff --git a/svx/qa/unit/styles.cxx b/svx/qa/unit/styles.cxx new file mode 100644 index 000000000000..9cbae8f997ba --- /dev/null +++ b/svx/qa/unit/styles.cxx @@ -0,0 +1,100 @@ +/* -*- 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 <test/bootstrapfixture.hxx> +#include <unotest/macros_test.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/text/XTextRange.hpp> + +using namespace ::com::sun::star; + +namespace +{ +/// Tests for svx/source/styles/ code. +class Test : public test::BootstrapFixture, public unotest::MacrosTest +{ +private: + uno::Reference<lang::XComponent> mxComponent; + +public: + void setUp() override; + void tearDown() override; + uno::Reference<lang::XComponent>& getComponent() { return mxComponent; } +}; + +void Test::setUp() +{ + test::BootstrapFixture::setUp(); + + mxDesktop.set(frame::Desktop::create(mxComponentContext)); +} + +void Test::tearDown() +{ + if (mxComponent.is()) + mxComponent->dispose(); + + test::BootstrapFixture::tearDown(); +} + +constexpr OUStringLiteral DATA_DIRECTORY = u"/svx/qa/unit/data/"; + +CPPUNIT_TEST_FIXTURE(Test, testThemeChange) +{ + // Given a document, with a first slide and blue shape text from theme: + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "theme.pptx"; + getComponent() = loadFromDesktop(aURL); + uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY); + uno::Reference<drawing::XMasterPageTarget> xDrawPage( + xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY); + uno::Reference<drawing::XShapes> xDrawPageShapes(xDrawPage, uno::UNO_QUERY); + uno::Reference<text::XTextRange> xShape(xDrawPageShapes->getByIndex(0), uno::UNO_QUERY); + { + uno::Reference<container::XEnumerationAccess> xText(xShape->getText(), uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xPara( + xText->createEnumeration()->nextElement(), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xPortion(xPara->createEnumeration()->nextElement(), + uno::UNO_QUERY); + sal_Int32 nColor{}; + xPortion->getPropertyValue("CharColor") >>= nColor; + // Blue. + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0x4472c4), nColor); + } + + // When changing the master slide of slide 1 to use the theme of the second master slide: + uno::Reference<drawing::XMasterPageTarget> xDrawPage2( + xDrawPagesSupplier->getDrawPages()->getByIndex(1), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xMasterPage2(xDrawPage2->getMasterPage(), uno::UNO_QUERY); + uno::Any aTheme = xMasterPage2->getPropertyValue("Theme"); + uno::Reference<beans::XPropertySet> xMasterPage(xDrawPage->getMasterPage(), uno::UNO_QUERY); + xMasterPage->setPropertyValue("Theme", aTheme); + + // Then make sure the shape text color is now green: + uno::Reference<container::XEnumerationAccess> xText(xShape->getText(), uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xPara(xText->createEnumeration()->nextElement(), + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xPortion(xPara->createEnumeration()->nextElement(), + uno::UNO_QUERY); + sal_Int32 nColor{}; + xPortion->getPropertyValue("CharColor") >>= nColor; + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 9486886 (#90c226, green) + // - Actual : 4485828 (#4472c4, blue) + // i.e. shape text was not updated on theme change. + CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0x90c226), nColor); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/styles/ColorSets.cxx b/svx/source/styles/ColorSets.cxx index 773e7c414ef3..a5f8c6e7c548 100644 --- a/svx/source/styles/ColorSets.cxx +++ b/svx/source/styles/ColorSets.cxx @@ -16,14 +16,55 @@ #include <com/sun/star/beans/PropertyValues.hpp> #include <com/sun/star/util/Color.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> #include <comphelper/propertyvalue.hxx> #include <comphelper/sequenceashashmap.hxx> #include <comphelper/sequence.hxx> #include <sal/log.hxx> +#include <svx/svdpage.hxx> +#include <svx/svditer.hxx> +#include <editeng/unoprnms.hxx> using namespace com::sun::star; +namespace +{ +void UpdateSdrObject(svx::Theme* pTheme, SdrObject* pObject) +{ + svx::ColorSet* pColorSet = pTheme->GetColorSet(); + if (!pColorSet) + { + return; + } + + uno::Reference<text::XTextRange> xShape(pObject->getUnoShape(), uno::UNO_QUERY); + uno::Reference<container::XEnumerationAccess> xText(xShape->getText(), uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xParagraphs = xText->createEnumeration(); + while (xParagraphs->hasMoreElements()) + { + uno::Reference<container::XEnumerationAccess> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xPortions = xParagraph->createEnumeration(); + while (xPortions->hasMoreElements()) + { + uno::Reference<beans::XPropertySet> xPortion(xPortions->nextElement(), uno::UNO_QUERY); + sal_Int16 nCharColorTheme = -1; + xPortion->getPropertyValue(UNO_NAME_EDIT_CHAR_COLOR_THEME) >>= nCharColorTheme; + if (nCharColorTheme < 0 || nCharColorTheme > 11) + { + continue; + } + + Color aColor = pColorSet->getColor(nCharColorTheme); + xPortion->setPropertyValue(UNO_NAME_EDIT_CHAR_COLOR, uno::makeAny(static_cast<sal_Int32>(aColor))); + } + } +} +} + namespace svx { @@ -229,6 +270,24 @@ std::unique_ptr<Theme> Theme::FromAny(const css::uno::Any& rVal) return pTheme; } +void Theme::UpdateSdrPage(SdrPage* pPage) +{ + for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + UpdateSdrObject(this, pObject); + SdrObjList* pList = pObject->GetSubList(); + if (pList) + { + SdrObjListIter aIter(pList, SdrIterMode::DeepWithGroups); + while (aIter.IsMore()) + { + UpdateSdrObject(this, aIter.Next()); + } + } + } +} + } // end of namespace svx /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/svdraw/svdpage.cxx b/svx/source/svdraw/svdpage.cxx index 7354b86adbb8..b6ef5a450e5c 100644 --- a/svx/source/svdraw/svdpage.cxx +++ b/svx/source/svdraw/svdpage.cxx @@ -1277,7 +1277,26 @@ void SdrPageProperties::SetStyleSheet(SfxStyleSheet* pStyleSheet) ImpPageChange(*mpSdrPage); } -void SdrPageProperties::SetTheme(std::unique_ptr<svx::Theme> pTheme) { mpTheme = std::move(pTheme); } +void SdrPageProperties::SetTheme(std::unique_ptr<svx::Theme> pTheme) +{ + mpTheme = std::move(pTheme); + + if (mpTheme && mpSdrPage->IsMasterPage()) + { + SdrModel& rModel = mpSdrPage->getSdrModelFromSdrPage(); + sal_uInt16 nPageCount = rModel.GetPageCount(); + for (sal_uInt16 nPage = 0; nPage < nPageCount; ++nPage) + { + SdrPage* pPage = rModel.GetPage(nPage); + if (!pPage->TRG_HasMasterPage() || &pPage->TRG_GetMasterPage() != mpSdrPage) + { + continue; + } + + mpTheme->UpdateSdrPage(pPage); + } + } +} svx::Theme* SdrPageProperties::GetTheme() { return mpTheme.get(); } commit bad177d0604b76672af2612cf8ab39f8e8345dc7 Author: Miklos Vajna <[email protected]> AuthorDate: Mon Nov 29 08:28:28 2021 +0100 Commit: Miklos Vajna <[email protected]> CommitDate: Mon Jun 27 08:58:25 2022 +0200 tools Color: implement MSO-style luminance modulation/offset filter To be used when a filtered theme color will be applied on the UI, and not at PPTX import time. (cherry picked from commit 8662293d17a875f4389ea21be00e768e3de3d048) Change-Id: Ifb56e38e59b529ef436063c407ee156d76a77f9c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136371 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/include/tools/color.hxx b/include/tools/color.hxx index 555017a21dee..5fcf840edfe6 100644 --- a/include/tools/color.hxx +++ b/include/tools/color.hxx @@ -335,6 +335,14 @@ public: **/ void ApplyTintOrShade(sal_Int16 n100thPercent); + /** + * Apply luminance offset and/or modulation. + * + * The input values are in percentages (in 100th percents). 100% modulation and 0% offset + * results in no change. + */ + void ApplyLumModOff(sal_Int16 nMod, sal_Int16 nOff); + /** Inverts color. 1 and 0 are switched. * Note that the result will be the complementary color. * For example, if you have red, you will get cyan: FF0000 -> 00FFFF. diff --git a/tools/qa/cppunit/test_color.cxx b/tools/qa/cppunit/test_color.cxx index f3ffd9c692cd..3dd4225cb20f 100644 --- a/tools/qa/cppunit/test_color.cxx +++ b/tools/qa/cppunit/test_color.cxx @@ -22,6 +22,7 @@ public: void testVariables(); void test_asRGBColor(); void test_ApplyTintOrShade(); + void test_ApplyLumModOff(); void testGetColorError(); void testInvert(); void testBColor(); @@ -31,6 +32,7 @@ public: CPPUNIT_TEST(testVariables); CPPUNIT_TEST(test_asRGBColor); CPPUNIT_TEST(test_ApplyTintOrShade); + CPPUNIT_TEST(test_ApplyLumModOff); CPPUNIT_TEST(testGetColorError); CPPUNIT_TEST(testInvert); CPPUNIT_TEST(testBColor); @@ -163,6 +165,17 @@ void Test::test_ApplyTintOrShade() CPPUNIT_ASSERT_EQUAL(OUString("000000"), createTintShade(0x80, 0x80, 0x80, u"808080", -10000)); } +void Test::test_ApplyLumModOff() +{ + // Kind of blue. + Color aColor(0x44, 0x72, 0xC4); + + // PowerPoint calls this "Ligher 40%". + aColor.ApplyLumModOff(6000, 4000); + + CPPUNIT_ASSERT_EQUAL(OUString("8faadc"), aColor.AsRGBHexString()); +} + void Test::testGetColorError() { CPPUNIT_ASSERT_EQUAL(sal_uInt16(0), Color(0xAA, 0xBB, 0xCC).GetColorError(Color(0xAA, 0xBB, 0xCC))); diff --git a/tools/source/generic/color.cxx b/tools/source/generic/color.cxx index cf4e084b722f..5df32719eb2c 100644 --- a/tools/source/generic/color.cxx +++ b/tools/source/generic/color.cxx @@ -230,4 +230,36 @@ void Color::ApplyTintOrShade(sal_Int16 n100thPercent) B = sal_uInt8(std::lround(aBColor.getBlue() * 255.0)); } +void Color::ApplyLumModOff(sal_Int16 nMod, sal_Int16 nOff) +{ + if (nMod == 10000 && nOff == 0) + { + return; + } + // Switch to HSL, where applying these transforms is easier. + basegfx::BColor aBColor = basegfx::utils::rgb2hsl(getBColor()); + + // 50% is half luminance, 200% is double luminance. Unit is 100th percent. + aBColor.setBlue(std::clamp(aBColor.getBlue() * nMod / 10000, 0.0, 1.0)); + // If color changes to black or white, it will stay gray if luminance changes again. + if ((aBColor.getBlue() == 0.0) || (aBColor.getBlue() == 1.0)) + { + aBColor.setGreen(0.0); + } + + // Luminance offset means hue and saturation is left unchanged. Unit is 100th percent. + aBColor.setBlue(std::clamp(aBColor.getBlue() + static_cast<double>(nOff) / 10000, 0.0, 1.0)); + // If color changes to black or white, it will stay gray if luminance changes again. + if ((aBColor.getBlue() == 0.0) || (aBColor.getBlue() == 1.0)) + { + aBColor.setGreen(0.0); + } + + // Switch back to RGB. + aBColor = basegfx::utils::hsl2rgb(aBColor); + R = sal_uInt8(std::lround(aBColor.getRed() * 255.0)); + G = sal_uInt8(std::lround(aBColor.getGreen() * 255.0)); + B = sal_uInt8(std::lround(aBColor.getBlue() * 255.0)); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
