From c95288aec4eb4d92a5ccfb9d8fc02a0185d8e8d0 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Tue, 7 Dec 2021 08:32:30 +0100 Subject: [PATCH] ODP export: write the theme of a master page Which requires describing the schema, which is really just a new element, the rest reuses the color-table markup, which wasn't used in ODF so far (but was used in our .soc files). Also make sure that we only do this in ODF extended mode (which is the default). Change-Id: I90eaad30f63946c441fe0c53caf6a47caf1714d5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/126466 Reviewed-by: Miklos Vajna Tested-by: Jenkins --- include/xmloff/xmltoken.hxx | 2 + .../OpenDocument-v1.3+libreoffice-schema.rng | 96 +++++++++++++++++++ xmloff/qa/unit/draw.cxx | 52 +++++++++- xmloff/source/core/xmltoken.cxx | 2 + xmloff/source/draw/sdxmlexp.cxx | 85 ++++++++++++++++ xmloff/source/draw/sdxmlexp_impl.hxx | 1 + xmloff/source/token/tokens.txt | 1 + 7 files changed, 234 insertions(+), 5 deletions(-) diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index 26a4c050f78a..dfdacf58c5ff 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -3440,6 +3440,8 @@ namespace xmloff::token { XML_LINKED_STYLE_NAME, + XML_THEME, + XML_TOKEN_END }; diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index 3919ba47f04f..6e0a301b047f 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -2743,4 +2743,100 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xmloff/qa/unit/draw.cxx b/xmloff/qa/unit/draw.cxx index 3b0cda0f0383..5d3854844ce9 100644 --- a/xmloff/qa/unit/draw.cxx +++ b/xmloff/qa/unit/draw.cxx @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include @@ -38,6 +41,7 @@ public: void tearDown() override; void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override; uno::Reference& getComponent() { return mxComponent; } + void save(const OUString& rFilterName, utl::TempFile& rTempFile); }; void XmloffDrawTest::setUp() @@ -60,6 +64,16 @@ void XmloffDrawTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) XmlTestTools::registerODFNamespaces(pXmlXpathCtx); } +void XmloffDrawTest::save(const OUString& rFilterName, utl::TempFile& rTempFile) +{ + uno::Reference xStorable(mxComponent, uno::UNO_QUERY); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= rFilterName; + rTempFile.EnableKillingFile(); + xStorable->storeToURL(rTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); + validate(rTempFile.GetFileName(), test::ODF); +} + CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTextBoxLoss) { // Load a document that has a shape with a textbox in it. Save it to ODF and reload. @@ -93,12 +107,8 @@ CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTdf141301_Extrusion_Angle) getComponent() = loadFromDesktop(aURL, "com.sun.star.comp.drawing.DrawingDocument"); // Prepare use of XPath - uno::Reference xStorable(getComponent(), uno::UNO_QUERY); utl::TempFile aTempFile; - aTempFile.EnableKillingFile(); - utl::MediaDescriptor aMediaDescriptor; - aMediaDescriptor["FilterName"] <<= OUString("draw8"); - xStorable->storeAsURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); + save("draw8", aTempFile); uno::Reference xNameAccess = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, aTempFile.GetURL()); uno::Reference xInputStream(xNameAccess->getByName("content.xml"), @@ -111,6 +121,38 @@ CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTdf141301_Extrusion_Angle) assertXPath(pXmlDoc, "//draw:enhanced-geometry", "extrusion-skew", "50 -135"); } +CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testThemeExport) +{ + // Create an Impress document which has a master page which has a theme associated with it. + getComponent() = loadFromDesktop("private:factory/simpress"); + uno::Reference xDrawPagesSupplier(getComponent(), uno::UNO_QUERY); + uno::Reference xDrawPage( + xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY); + uno::Reference xMasterPage(xDrawPage->getMasterPage(), uno::UNO_QUERY); + comphelper::SequenceAsHashMap aMap; + aMap["Name"] <<= OUString("mytheme"); + aMap["ColorSchemeName"] <<= OUString("mycolorscheme"); + uno::Sequence aColorScheme + = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb }; + aMap["ColorScheme"] <<= aColorScheme; + uno::Any aTheme = uno::makeAny(aMap.getAsConstPropertyValueList()); + xMasterPage->setPropertyValue("Theme", aTheme); + + // Export to ODP: + utl::TempFile aTempFile; + save("impress8", aTempFile); + + // Check if the 12 colors are written in the XML: + std::unique_ptr pStream = parseExportStream(aTempFile, "styles.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 12 + // - Actual : 0 + // - XPath '//style:master-page/loext:theme/loext:color-table/loext:color' number of nodes is incorrect + // i.e. the theme was lost on exporting to ODF. + assertXPath(pXmlDoc, "//style:master-page/loext:theme/loext:color-table/loext:color", 12); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 704f374a5026..caf15a395e03 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -3443,6 +3443,8 @@ namespace xmloff::token { TOKEN("linked-style-name", XML_LINKED_STYLE_NAME ), + TOKEN("theme", XML_THEME ), + #if OSL_DEBUG_LEVEL > 0 { 0, nullptr, std::nullopt, XML_TOKEN_END } diff --git a/xmloff/source/draw/sdxmlexp.cxx b/xmloff/source/draw/sdxmlexp.cxx index d02a5731ad25..9388cedb25e4 100644 --- a/xmloff/source/draw/sdxmlexp.cxx +++ b/xmloff/source/draw/sdxmlexp.cxx @@ -72,6 +72,9 @@ #include #include +#include + +#include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; @@ -2299,6 +2302,12 @@ void SdXMLExport::ExportMasterStyles_() // write optional office:forms exportFormsElement( xMasterPage ); + // write optional loext:theme + if (IsImpress()) + { + ExportThemeElement(xMasterPage); + } + // write graphic objects on this master page (if any) if(xMasterPage.is() && xMasterPage->getCount()) GetShapeExport()->exportShapes( xMasterPage ); @@ -2354,6 +2363,82 @@ void SdXMLExport::exportFormsElement( const Reference< XDrawPage >& xDrawPage ) } } +void SdXMLExport::ExportThemeElement(const uno::Reference& xDrawPage) +{ + uno::Reference xPropertySet(xDrawPage, uno::UNO_QUERY); + if (!xPropertySet.is()) + return; + + comphelper::SequenceAsHashMap aMap(xPropertySet->getPropertyValue("Theme")); + if (aMap.empty()) + { + return; + } + + if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0) + { + // Do not export in standard ODF 1.3 or older. + return; + } + + auto it = aMap.find("Name"); + if (it != aMap.end()) + { + OUString aName; + it->second >>= aName; + AddAttribute(XML_NAMESPACE_LO_EXT, XML_NAME, aName); + } + SvXMLElementExport aTheme(*this, XML_NAMESPACE_LO_EXT, XML_THEME, true, true); + + uno::Sequence aColors; + it = aMap.find("ColorScheme"); + if (it != aMap.end()) + { + it->second >>= aColors; + } + if (!aColors.hasElements()) + { + return; + } + + it = aMap.find("ColorSchemeName"); + if (it != aMap.end()) + { + OUString aName; + it->second >>= aName; + AddAttribute(XML_NAMESPACE_LO_EXT, XML_NAME, aName); + } + SvXMLElementExport aColorTable(*this, XML_NAMESPACE_LO_EXT, XML_COLOR_TABLE, true, true); + + static const std::u16string_view aColorNames[] = { + u"dk1", // Background 1 + u"lt1", // Text 1 + u"dk2", // Background 2 + u"lt2", // Text 2 + u"accent1", // Accent 1 + u"accent2", // Accent 2 + u"accent3", // Accent 3 + u"accent4", // Accent 4 + u"accent5", // Accent 5 + u"accent6", // Accent 6 + u"hlink", // Hyperlink + u"folHlink", // Followed hyperlink + }; + for (size_t nColor = 0; nColor < aColors.size(); ++nColor) + { + // Import goes via svx::Theme::FromAny(), which sanitizes user input. + assert(nColor < SAL_N_ELEMENTS(aColorNames)); + + AddAttribute(XML_NAMESPACE_LO_EXT, XML_NAME, OUString(aColorNames[nColor])); + + OUStringBuffer sValue; + sax::Converter::convertColor(sValue, aColors[nColor]); + AddAttribute(XML_NAMESPACE_LO_EXT, XML_COLOR, sValue.makeStringAndClear()); + + SvXMLElementExport aColor(*this, XML_NAMESPACE_LO_EXT, XML_COLOR, true, true); + } +} + void SdXMLExport::GetViewSettings(uno::Sequence& rProps) { Reference< beans::XPropertySet > xPropSet( GetModel(), UNO_QUERY ); diff --git a/xmloff/source/draw/sdxmlexp_impl.hxx b/xmloff/source/draw/sdxmlexp_impl.hxx index 799767f990b1..9808c738120e 100644 --- a/xmloff/source/draw/sdxmlexp_impl.hxx +++ b/xmloff/source/draw/sdxmlexp_impl.hxx @@ -135,6 +135,7 @@ class SdXMLExport : public SvXMLExport void ImplExportHeaderFooterDeclAttributes( const HeaderFooterPageSettingsImpl& aSettings ); void exportFormsElement( const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage ); + void ExportThemeElement(const css::uno::Reference& xDrawPage); void exportPresentationSettings(); // #82003# helper function for recursive object count diff --git a/xmloff/source/token/tokens.txt b/xmloff/source/token/tokens.txt index 6e34ec554fab..abc66bd73034 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -3187,4 +3187,5 @@ rspace rtl symmetric linked-style-name +theme TOKEN_END_DUMMY