include/xmloff/xmltoken.hxx | 1 schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng | 96 ++++++++++++ xmloff/qa/unit/draw.cxx | 66 +++++--- 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, 229 insertions(+), 23 deletions(-)
New commits: commit e207983a09cbb0139bcc693d63f3ea0980699b26 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Dec 7 08:32:30 2021 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Jun 28 08:15:14 2022 +0200 ODP export: write the theme of a master page Which requires describing the schema, which is really just a new <loext:theme> 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). (cherry picked from commit c95288aec4eb4d92a5ccfb9d8fc02a0185d8e8d0) Conflicts: include/xmloff/xmltoken.hxx xmloff/qa/unit/draw.cxx xmloff/source/core/xmltoken.cxx xmloff/source/token/tokens.txt Change-Id: I90eaad30f63946c441fe0c53caf6a47caf1714d5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136495 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/include/xmloff/xmltoken.hxx b/include/xmloff/xmltoken.hxx index b0e9003b0261..f26c4871ec35 100644 --- a/include/xmloff/xmltoken.hxx +++ b/include/xmloff/xmltoken.hxx @@ -3467,6 +3467,7 @@ namespace xmloff::token { XML_LINKED_STYLE_NAME, + XML_THEME, XML_CONTENT_CONTROL, XML_SHOWING_PLACE_HOLDER, diff --git a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng index 55bedf37b1bb..19a1edb7f3c0 100644 --- a/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.3+libreoffice-schema.rng @@ -2999,4 +2999,100 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. </rng:optional> </rng:define> + <rng:define name="loext-color-attlist"> + <rng:interleave> + <rng:optional> + <rng:attribute name="loext:name"> + <rng:ref name="string"/> + </rng:attribute> + </rng:optional> + <rng:optional> + <rng:attribute name="loext:color"> + <rng:ref name="color"/> + </rng:attribute> + </rng:optional> + </rng:interleave> + </rng:define> + <rng:define name="loext-color"> + <rng:element name="loext:color"> + <rng:ref name="loext-color-attlist"/> + <rng:empty/> + </rng:element> + </rng:define> + <rng:define name="loext-color-table-attlist"> + <rng:interleave> + <rng:optional> + <rng:attribute name="loext:name"> + <rng:ref name="string"/> + </rng:attribute> + </rng:optional> + </rng:interleave> + </rng:define> + <rng:define name="loext-color-table"> + <rng:element name="loext:color-table"> + <rng:ref name="loext-color-table-attlist"/> + <rng:zeroOrMore> + <rng:ref name="loext-color"/> + </rng:zeroOrMore> + </rng:element> + </rng:define> + <rng:define name="loext-theme-attlist"> + <rng:interleave> + <rng:optional> + <rng:attribute name="loext:name"> + <rng:ref name="string"/> + </rng:attribute> + </rng:optional> + </rng:interleave> + </rng:define> + <rng:define name="loext-theme"> + <rng:element name="loext:theme"> + <rng:ref name="loext-theme-attlist"/> + <rng:optional> + <rng:ref name="loext-color-table"/> + </rng:optional> + </rng:element> + </rng:define> + <rng:define name="style-master-page" combine="choice"> + <rng:element name="style:master-page"> + <rng:ref name="style-master-page-attlist"/> + <rng:optional> + <rng:ref name="style-header"/> + <rng:optional> + <rng:ref name="style-header-left"/> + </rng:optional> + <rng:optional> + <rng:ref name="style-header-first"/> + </rng:optional> + </rng:optional> + <rng:optional> + <rng:ref name="style-footer"/> + <rng:optional> + <rng:ref name="style-footer-left"/> + </rng:optional> + <rng:optional> + <rng:ref name="style-footer-first"/> + </rng:optional> + </rng:optional> + <rng:optional> + <rng:ref name="draw-layer-set"/> + </rng:optional> + <rng:optional> + <rng:ref name="office-forms"/> + </rng:optional> + <rng:optional> + <!-- TODO no proposal --> + <rng:ref name="loext-theme"/> + </rng:optional> + <rng:zeroOrMore> + <rng:ref name="shape"/> + </rng:zeroOrMore> + <rng:optional> + <rng:ref name="animation-element"/> + </rng:optional> + <rng:optional> + <rng:ref name="presentation-notes"/> + </rng:optional> + </rng:element> + </rng:define> </rng:grammar> diff --git a/xmloff/qa/unit/draw.cxx b/xmloff/qa/unit/draw.cxx index 9a753d184f34..fd32c59d90b8 100644 --- a/xmloff/qa/unit/draw.cxx +++ b/xmloff/qa/unit/draw.cxx @@ -18,6 +18,9 @@ #include <com/sun/star/packages/zip/ZipFileAccess.hpp> #include <com/sun/star/text/XTextRange.hpp> #include <com/sun/star/text/XTextTable.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/util/Color.hpp> #include <unotools/mediadescriptor.hxx> #include <unotools/tempfile.hxx> @@ -40,6 +43,7 @@ public: void tearDown() override; void registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) override; uno::Reference<lang::XComponent>& getComponent() { return mxComponent; } + void save(const OUString& rFilterName, utl::TempFile& rTempFile); }; void XmloffDrawTest::setUp() @@ -62,6 +66,16 @@ void XmloffDrawTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) XmlTestTools::registerODFNamespaces(pXmlXpathCtx); } +void XmloffDrawTest::save(const OUString& rFilterName, utl::TempFile& rTempFile) +{ + uno::Reference<frame::XStorable> 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. @@ -95,12 +109,8 @@ CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTdf141301_Extrusion_Angle) getComponent() = loadFromDesktop(aURL, "com.sun.star.comp.drawing.DrawingDocument"); // Prepare use of XPath - uno::Reference<frame::XStorable> 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<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(mxComponentContext, aTempFile.GetURL()); uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName("content.xml"), @@ -113,26 +123,36 @@ CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTdf141301_Extrusion_Angle) assertXPath(pXmlDoc, "//draw:enhanced-geometry", "extrusion-skew", "50 -135"); } -CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testTableInShape) +CPPUNIT_TEST_FIXTURE(XmloffDrawTest, testThemeExport) { - // Given a document with a shape with a "FrameX" parent style (starts with Frame, but is not - // Frame): - OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "table-in-shape.fodt"; - - // When loading that document: - getComponent() = loadFromDesktop(aURL); + // Create an Impress document which has a master page which has a theme associated with it. + getComponent() = loadFromDesktop("private:factory/simpress"); + uno::Reference<drawing::XDrawPagesSupplier> xDrawPagesSupplier(getComponent(), uno::UNO_QUERY); + uno::Reference<drawing::XMasterPageTarget> xDrawPage( + xDrawPagesSupplier->getDrawPages()->getByIndex(0), uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xMasterPage(xDrawPage->getMasterPage(), uno::UNO_QUERY); + comphelper::SequenceAsHashMap aMap; + aMap["Name"] <<= OUString("mytheme"); + aMap["ColorSchemeName"] <<= OUString("mycolorscheme"); + uno::Sequence<util::Color> 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); - // Then make sure the table inside the shape is not lost: - uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(getComponent(), uno::UNO_QUERY); - uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage(); - uno::Reference<text::XTextRange> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY); - uno::Reference<container::XEnumerationAccess> xText(xShape->getText(), uno::UNO_QUERY); - uno::Reference<container::XEnumeration> xEnum = xText->createEnumeration(); - uno::Reference<text::XTextTable> xTable(xEnum->nextElement(), uno::UNO_QUERY); - // Without the accompanying fix in place, this test would have crashed, as xTable was an empty - // reference, i.e. the table inside the shape was lost. - uno::Reference<text::XTextRange> xCell(xTable->getCellByName("A1"), uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL(OUString("A1"), xCell->getString()); + // Check if the 12 colors are written in the XML: + std::unique_ptr<SvStream> 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(); diff --git a/xmloff/source/core/xmltoken.cxx b/xmloff/source/core/xmltoken.cxx index 179bd5094245..cd8186c89617 100644 --- a/xmloff/source/core/xmltoken.cxx +++ b/xmloff/source/core/xmltoken.cxx @@ -3470,6 +3470,8 @@ namespace xmloff::token { TOKEN("linked-style-name", XML_LINKED_STYLE_NAME ), + TOKEN("theme", XML_THEME ), + TOKEN("content-control", XML_CONTENT_CONTROL ), TOKEN("showing-place-holder", XML_SHOWING_PLACE_HOLDER ), TOKEN("checked-state", XML_CHECKED_STATE), 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 <com/sun/star/document/XDocumentProperties.hpp> #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/util/Color.hpp> + +#include <comphelper/sequenceashashmap.hxx> 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<drawing::XDrawPage>& xDrawPage) +{ + uno::Reference<beans::XPropertySet> 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<util::Color> 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<beans::PropertyValue>& 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<css::drawing::XDrawPage>& 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 6d74b224296d..3af58c6aac26 100644 --- a/xmloff/source/token/tokens.txt +++ b/xmloff/source/token/tokens.txt @@ -3214,6 +3214,7 @@ rspace rtl symmetric linked-style-name +theme content-control showing-place-holder checked-state