From 942302efeae385a5932a704e69b4e9a44959add4 Mon Sep 17 00:00:00 2001 From: Xisco Fauli Date: Mon, 5 Oct 2026 15:37:11 +0200 Subject: [PATCH] CppunitTest_sd_export_tests-ooxml4: split in two it already has 111 tests Change-Id: I1d0596702d2fc69389436c8d02ae074fb0287f9a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211829 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- sd/CppunitTest_sd_export_tests-ooxml5.mk | 14 + sd/Module_sd.mk | 1 + sd/qa/unit/export-tests-ooxml4.cxx | 1347 --------------------- sd/qa/unit/export-tests-ooxml5.cxx | 1380 ++++++++++++++++++++++ 4 files changed, 1395 insertions(+), 1347 deletions(-) create mode 100644 sd/CppunitTest_sd_export_tests-ooxml5.mk create mode 100644 sd/qa/unit/export-tests-ooxml5.cxx diff --git a/sd/CppunitTest_sd_export_tests-ooxml5.mk b/sd/CppunitTest_sd_export_tests-ooxml5.mk new file mode 100644 index 000000000000..0e75dc1e9ef6 --- /dev/null +++ b/sd/CppunitTest_sd_export_tests-ooxml5.mk @@ -0,0 +1,14 @@ +# -*- 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 sd_export_test,-ooxml5)) + +# vim: set noet sw=4 ts=4: diff --git a/sd/Module_sd.mk b/sd/Module_sd.mk index 0890fcad4b19..3117aa752bc6 100644 --- a/sd/Module_sd.mk +++ b/sd/Module_sd.mk @@ -41,6 +41,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sd,\ CppunitTest_sd_export_tests-ooxml2 \ CppunitTest_sd_export_tests-ooxml3 \ CppunitTest_sd_export_tests-ooxml4 \ + CppunitTest_sd_export_tests-ooxml5 \ CppunitTest_sd_export_tests \ CppunitTest_sd_export_tests2 \ CppunitTest_sd_filters_test \ diff --git a/sd/qa/unit/export-tests-ooxml4.cxx b/sd/qa/unit/export-tests-ooxml4.cxx index ba12c3fd301d..33fd9cb8ae72 100644 --- a/sd/qa/unit/export-tests-ooxml4.cxx +++ b/sd/qa/unit/export-tests-ooxml4.cxx @@ -9,16 +9,11 @@ #include "sdmodeltestbase.hxx" #include -#include -#include -#include #include #include #include #include #include -#include -#include #include #include @@ -27,23 +22,15 @@ #include #include #include -#include -#include #include #include #include -#include -#include -#include #include #include -#include -#include #include #include -#include #include class SdOOXMLExportTest4 : public SdModelTestBase @@ -1353,1340 +1340,6 @@ CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testAutofittedTextboxIndent) u"1080000"); } -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf151622_oleIcon) -{ - createSdImpressDoc("odp/ole_icon.odp"); - - save(TestFilter::PPTX); - - xmlDocUniquePtr pXml = parseExport(u"ppt/slides/slide1.xml"_ustr); - - // Without the accompanying fix in place, this test would have failed with: - // - Expression: prop - // - In ..., XPath '//p:oleObj' no attribute 'showAsIcon' exist - // i.e. show as icon option wasn't exported. - assertXPath(pXml, "//p:oleObj", "showAsIcon", u"1"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf152436) -{ - createSdImpressDoc("pptx/ole-emf_min.pptx"); - saveAndReload(TestFilter::PPTX); - - // Check number of shapes after export. - CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getPage(0)->getCount()); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testLinkedOLE) -{ - // The linked OLE object needs its link completed during load so it can - // be exported. Use FULL_UPDATE since the default test mode defers links. - uno::Sequence aParams = { - comphelper::makePropertyValue(u"UpdateDocMode"_ustr, - sal_Int16(css::document::UpdateDocMode::FULL_UPDATE)), - }; - loadFromFile(u"odp/linked_ole.odp", aParams); - - save(TestFilter::PPTX); - - xmlDocUniquePtr pXml = parseExport(u"ppt/slides/slide1.xml"_ustr); - - // Without the accompanying fix in place, this test would have failed with: - // - Expected: 1 - // - Actual : 0 - // - In<>, XPath '//p:oleObj' number of nodes is incorrect - // i.e. the linked ole object wasn't exported. - assertXPath(pXml, "//p:oleObj", 1); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf102261_testParaTabStopDefaultDistance) -{ - createSdImpressDoc("pptx/tdf102261_testParaTabStopDefaultDistance.pptx"); - saveAndReload(TestFilter::PPTX); - - uno::Reference xShape(getShapeFromPage(0, 0)); - { - uno::Reference xPropSet(getParagraphFromShape(0, xShape), - uno::UNO_QUERY_THROW); - CPPUNIT_ASSERT_EQUAL( - sal_Int32{ 1270 }, - xPropSet->getPropertyValue(u"ParaTabStopDefaultDistance"_ustr).get()); - } - { - uno::Reference xPropSet(getParagraphFromShape(1, xShape), - uno::UNO_QUERY_THROW); - CPPUNIT_ASSERT_EQUAL( - sal_Int32{ 2540 }, - xPropSet->getPropertyValue(u"ParaTabStopDefaultDistance"_ustr).get()); - } -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testNotesAuthorDate) -{ - createSdImpressDoc("pptx/pres-with-notes.pptx"); - - using ScriptingCfg = officecfg::Office::Common::Security::Scripting; - ScopedConfigValue aCfg1(true); - { - // 1. Remove all personal info, but keep note info - ScopedConfigValue aCfg2(true); - - saveAndReload(TestFilter::PPTX); - - xmlDocUniquePtr pXml = parseExport(u"ppt/commentAuthors.xml"_ustr); - assertXPath(pXml, "/p:cmAuthorLst/p:cmAuthor[@id=0]", "name", u"Hans Wurst"); - assertXPath(pXml, "/p:cmAuthorLst/p:cmAuthor[@id=1]", "name", u"Max Muster"); - - pXml = parseExport(u"ppt/comments/comment1.xml"_ustr); - assertXPath(pXml, "/p:cmLst/p:cm", "dt", u"2024-06-13T12:03:08.000000000"); - } - - { - // 2. Remove all personal info - ScopedConfigValue aCfg2(false); - saveAndReload(TestFilter::PPTX); - - xmlDocUniquePtr pXml = parseExport(u"ppt/commentAuthors.xml"_ustr); - assertXPath(pXml, "/p:cmAuthorLst/p:cmAuthor[@id=0]", "name", u"Author1"); - assertXPath(pXml, "/p:cmAuthorLst/p:cmAuthor[@id=1]", "name", u"Author2"); - - pXml = parseExport(u"ppt/comments/comment1.xml"_ustr); - assertXPathNoAttribute(pXml, "/p:cmLst/p:cm", "dt"); - } -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTableCellVerticalPropertyRoundtrip) -{ - createSdImpressDoc("pptx/tcPr-vert-roundtrip.pptx"); - saveAndReload(TestFilter::PPTX); - - xmlDocUniquePtr pXml = parseExport(u"ppt/slides/slide1.xml"_ustr); - - assertXPath(pXml, "(//a:tcPr)[1]", "vert", u"vert"); - assertXPath(pXml, "(//a:tcPr)[2]", "vert", u"vert270"); - assertXPath(pXml, "(//a:tcPr)[3]", "vert", u"wordArtVert"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf157740_slideMasters) -{ - createSdImpressDoc("pptx/tdf157740.pptx"); - saveAndReload(TestFilter::PPTX); - - // The original file has 1 slide master and 7 slide layouts in that master - xmlDocUniquePtr pXmlDocContent = parseExport(u"ppt/presentation.xml"_ustr); - assertXPath(pXmlDocContent, "/p:presentation/p:sldMasterIdLst/p:sldMasterId", 1); - - pXmlDocContent = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); - assertXPath(pXmlDocContent, "/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId", 7); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testNotesMasterThemeNoAfterDeduplicatedMasters) -{ - createSdImpressDoc("n759180.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDocContent = parseExport(u"ppt/presentation.xml"_ustr); - assertXPath(pXmlDocContent, "/p:presentation/p:sldMasterIdLst/p:sldMasterId", 1); - - xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slideMasters/_rels/slideMaster1.xml.rels"_ustr); - assertXPath(pXmlDocRels, - "/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/" - "officeDocument/2006/relationships/theme']", - "Target", u"../theme/theme1.xml"); - - // The one slide master of this document has 12 layouts, which are imported as 12 master pages - // and written back as one slide master. The notes master theme takes the number after the slide - // master theme, as in the original file, without a gap for the master pages that were merged. - // Without the change in place, this test would have failed with: - // - Expected: ../theme/theme2.xml - // - Actual : ../theme/theme13.xml - pXmlDocRels = parseExport(u"ppt/notesMasters/_rels/notesMaster1.xml.rels"_ustr); - assertXPath(pXmlDocRels, - "/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/" - "officeDocument/2006/relationships/theme']", - "Target", u"../theme/theme2.xml"); - - uno::Reference xNameAccess - = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), - maTempFile.GetURL()); - CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName(u"ppt/theme/theme2.xml"_ustr))); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf159931_slideLayouts) -{ - createSdImpressDoc("odp/repeatBitmapMode.odp"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDocRels1 = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); - xmlDocUniquePtr pXmlDocRels2 = parseExport(u"ppt/slides/_rels/slide2.xml.rels"_ustr); - - assertXPath(pXmlDocRels1, "(/rels:Relationships/rels:Relationship[@Type='http://" - "schemas.openxmlformats.org/officeDocument/2006/relationships/" - "slideLayout'])"); - - // the relative target e.g. "../slideLayouts/slideLayout2.xml" - OUString sRelativeLayoutPath1 - = getXPathContent(pXmlDocRels1, "(/rels:Relationships/rels:Relationship[@Type='http://" - "schemas.openxmlformats.org/officeDocument/2006/" - "relationships/slideLayout'])/@Target"); - - assertXPath(pXmlDocRels2, "(/rels:Relationships/rels:Relationship[@Type='http://" - "schemas.openxmlformats.org/officeDocument/2006/relationships/" - "slideLayout'])"); - - // the relative target e.g. "../slideLayouts/slideLayout1.xml" - OUString sRelativeLayoutPath2 - = getXPathContent(pXmlDocRels2, "(/rels:Relationships/rels:Relationship[@Type='http://" - "schemas.openxmlformats.org/officeDocument/2006/" - "relationships/slideLayout'])/@Target"); - - uno::Reference xNameAccess - = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), - maTempFile.GetURL()); - - // Check that the referenced slideLayout files exist - // Without the accompanying fix in place, this test would have failed with: - // equality assertion failed - // - Expected: 1 - // - Actual : 0 - // i.e. the referenced slideLayout file was missing on export. - OUString sSlideLayoutName1 = sRelativeLayoutPath1.getToken(2, '/'); - OUString sSlideLayoutName2 = sRelativeLayoutPath2.getToken(2, '/'); - - CPPUNIT_ASSERT_EQUAL(true, - bool(xNameAccess->hasByName("ppt/slideLayouts/" + sSlideLayoutName1))); - CPPUNIT_ASSERT_EQUAL(true, - bool(xNameAccess->hasByName("ppt/slideLayouts/" + sSlideLayoutName2))); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf163483_export_math_fallback) -{ - createSdImpressDoc("odp/formula.fodp"); - - // Before export-and-reload, there is a formula on page. - { - auto xProps = getShapeFromPage(0, 0); - auto xInfo = xProps->getPropertyValue(u"Model"_ustr).queryThrow(); - CPPUNIT_ASSERT(xInfo->supportsService(u"com.sun.star.formula.FormulaProperties"_ustr)); - - // tdf#164101: check that the size is imported correctly - css::awt::Size formulaSize = xProps.queryThrow()->getSize(); - CPPUNIT_ASSERT_EQUAL(sal_Int32(1553), formulaSize.Width); - CPPUNIT_ASSERT_EQUAL(sal_Int32(528), formulaSize.Height); - - CPPUNIT_ASSERT_THROW(getShapeFromPage(0, 1), - css::lang::IndexOutOfBoundsException); // Only one shape on page - } - - saveAndReload(TestFilter::PPTX); - - // After save-and-reload, there must still be a single shape; now it's a fallback image. - // When we start to import formulas from PPTX, that will be formula. - { - // Without the fix, this threw IndexOutOfBoundsException, because there was no fallback, - // and no shape got imported. - auto xInfo = getShapeFromPage(0, 0).queryThrow(); - CPPUNIT_ASSERT(xInfo->supportsService(u"com.sun.star.drawing.CustomShape"_ustr)); - - css::awt::Size formulaSize = xInfo.queryThrow()->getSize(); - // The fallback image size after the roundtrip may be a bit different - allow some tolerance - CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1553), formulaSize.Width, 1); - CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(528), formulaSize.Height, 1); - - CPPUNIT_ASSERT_THROW(getShapeFromPage(0, 1), - css::lang::IndexOutOfBoundsException); // Only one shape on page - } - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); - - const OUString cNvPr_id = getXPath( - pXmlDoc, "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Choice/p:sp/p:nvSpPr/p:cNvPr", - "id"); - - // Check that the alternate content is exported - assertXPath( - pXmlDoc, - "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Fallback/p:sp/p:spPr/a:blipFill/a:blip", - "embed", u"rId1"); - - // Its cNvPr_id must be the same - assertXPath(pXmlDoc, - "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Fallback/p:sp/p:nvSpPr/p:cNvPr", - "id", cNvPr_id); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf169979_missing_ppic) -{ - createSdImpressDoc("odp/tdf169979.odp"); - - save(TestFilter::PPTX); - - xmlDocUniquePtr pXml2 = parseExport(u"ppt/slides/slide2.xml"_ustr); - - // Without the fix in place, this test would have failed with - // - Expected: 1 - // - Actual : 0 - // - In <>, XPath '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/p:oleObj/p:pic/p:nvPicPr/p:cNvPr' number of nodes is incorrect - assertXPath(pXml2, - "/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/" - "a:graphicData/p:oleObj/p:pic/p:nvPicPr/p:cNvPr", - "name", u""); - - assertXPath(pXml2, - "/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/" - "a:graphicData/p:oleObj/p:pic/p:nvPicPr/p:cNvPr", - "descr", u""); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testEmptyChildTnLstElement) -{ - createSdImpressDoc("odp/emptyChildTnLstElement.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 4 - // - validation error in OOXML export: Errors: 4 - saveAndReload(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); - - // tdf#170202: Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 1 - CPPUNIT_ASSERT_EQUAL(0, countXPathNodes(pXmlDoc, "//p:sld/p:timing")); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf169941_internal_link_to_shapes) -{ - createSdImpressDoc("odp/tdf169941.odp"); - - save(TestFilter::PPTX); - - xmlDocUniquePtr pRelsDoc2 = parseExport(u"ppt/slides/_rels/slide2.xml.rels"_ustr); - - // Without the fix in place, this test would have failed with - // - Expected: slide1.xml - // - Actual : - assertXPath(pRelsDoc2, "/rels:Relationships/rels:Relationship[@Id='rId1']", "Target", - u"slide1.xml"); - - xmlDocUniquePtr pRelsDoc3 = parseExport(u"ppt/slides/_rels/slide3.xml.rels"_ustr); - assertXPath(pRelsDoc3, "/rels:Relationships/rels:Relationship[@Id='rId1']", "Target", - u"slide1.xml"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testEmptyAttrNameElement) -{ - createSdImpressDoc("odp/emptyChildTnLstElement.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 2 - // - validation error in OOXML export: Errors: 2 - saveAndReload(TestFilter::PPTX); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testPlaceHolderFitHeightToText) -{ - createSdImpressDoc("pptx/tdf160487.pptx"); - saveAndReload(TestFilter::PPTX); - uno::Reference xShape(getShapeFromPage(1, 0)); - bool bTextAutoGrowHeight = false; - xShape->getPropertyValue(u"TextAutoGrowHeight"_ustr) >>= bTextAutoGrowHeight; - CPPUNIT_ASSERT_MESSAGE("PlaceHolder Fit height to text should be true.", bTextAutoGrowHeight); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testDeduplicateMasters) -{ - createSdImpressDoc("pptx/onemaster-twolayouts.pptx"); - saveAndReload(TestFilter::PPTX); - - // Check that the document still has one master and two layouts - xmlDocUniquePtr pXmlDocContent = parseExport("ppt/presentation.xml"); - assertXPath(pXmlDocContent, "/p:presentation/p:sldMasterIdLst/p:sldMasterId"_ostr, 1); - pXmlDocContent = parseExport("ppt/slideMasters/slideMaster1.xml"); - assertXPath(pXmlDocContent, "/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId"_ostr, 2); - - // Check that both background colors have been preserved - uno::Reference xDoc(mxComponent, uno::UNO_QUERY); - CPPUNIT_ASSERT(xDoc.is()); - CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xDoc->getMasterPages()->getCount()); - - uno::Reference xPage(xDoc->getMasterPages()->getByIndex(0), - uno::UNO_QUERY_THROW); - uno::Reference xPropSet(xPage, uno::UNO_QUERY); - uno::Any aAny = xPropSet->getPropertyValue("Background"); - CPPUNIT_ASSERT(aAny.hasValue()); - uno::Reference aXBackgroundPropSet; - aAny >>= aXBackgroundPropSet; - Color nColor; - CPPUNIT_ASSERT(aXBackgroundPropSet->getPropertyValue("FillColor") >>= nColor); - CPPUNIT_ASSERT_EQUAL(Color(0x0E2841), nColor); - - uno::Reference xPage1(xDoc->getMasterPages()->getByIndex(1), - uno::UNO_QUERY_THROW); - uno::Reference xPropSet1(xPage1, uno::UNO_QUERY); - aAny = xPropSet1->getPropertyValue("Background"); - CPPUNIT_ASSERT(aAny.hasValue()); - aAny >>= aXBackgroundPropSet; - CPPUNIT_ASSERT(aXBackgroundPropSet->getPropertyValue("FillColor") >>= nColor); - CPPUNIT_ASSERT_EQUAL(Color(0x000000), nColor); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testInvalidAttributeValuesInSrcRect) -{ - createSdImpressDoc("odp/invalidAttributeValuesInSrcRect.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 24 - // - validation error in OOXML export: Errors: 24 - saveAndReload(TestFilter::PPTX); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testInvalidBuAutoNumEnumValue) -{ - createSdImpressDoc("odp/invalidBuAutoNumEnumValue.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 4 - // - validation error in OOXML export: Errors: 4 - saveAndReload(TestFilter::PPTX); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testInvalidCxAndCyAttribute) -{ - createSdImpressDoc("odp/invalidCxAndCyAttribute.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 4 - // - validation error in OOXML export: Errors: 4 - saveAndReload(TestFilter::PPTX); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testBadPercentageValue) -{ - createSdImpressDoc("odp/badPercentageValue.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 4 - // - validation error in OOXML export: Errors: 4 - saveAndReload(TestFilter::PPTX); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testInvalidAudio) -{ - createSdImpressDoc("odp/invalidAudio.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 1 - // - validation error in OOXML export: Errors: 1 - saveAndReload(TestFilter::PPTX); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testInvalidPrstDashEnumValue) -{ - createSdImpressDoc("odp/invalidPrstDashEnumValue.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 113 - // - validation error in OOXML export: Errors: 113 - saveAndReload(TestFilter::PPTX); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testInvalidXmlBy) -{ - createSdImpressDoc("odp/invalidXmlBy.odp"); - - // Without the fix in place, this test would have failed with - // - Expected: 0 - // - Actual : 15 - // - validation error in OOXML export: Errors: 15 - saveAndReload(TestFilter::PPTX); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testConvertWithMasterDeduplication) -{ - createSdImpressDoc("odp/dupmastermultlayouts.odp"); - saveAndReload(TestFilter::PPTX); - - uno::Reference xNameAccess - = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), - maTempFile.GetURL()); - - // For each slide check that its layout exists - for (int i = 1; i <= 4; ++i) - { - xmlDocUniquePtr pXmlDocRels - = parseExport("ppt/slides/_rels/slide" + OUString::number(i) + ".xml.rels"); - - assertXPath( - pXmlDocRels, - "(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])"_ostr); - // the relative target e.g. "../slideLayouts/slideLayout2.xml" - OUString sRelativeLayoutPath = getXPathContent( - pXmlDocRels, - "(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])/@Target"_ostr); - - // Check that the referenced slideLayout files exist - // Without the accompanying fix in place, this test would have failed with: - // equality assertion failed - // - Expected: 1 - // - Actual : 0 - // i.e. the referenced slideLayout file was missing on export. - OUString sSlideLayoutName = sRelativeLayoutPath.getToken(2, '/'); - CPPUNIT_ASSERT_EQUAL(true, - bool(xNameAccess->hasByName("ppt/slideLayouts/" + sSlideLayoutName))); - } -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf162571HorzAnchor) -{ - createSdImpressDoc("pptx/tdf165261.pptx"); - saveAndReload(TestFilter::PPTX); - - uno::Reference xShape(getShapeFromPage(1, 0), uno::UNO_QUERY); - uno::Reference xProp(xShape, uno::UNO_QUERY); - - drawing::TextHorizontalAdjust eHori; - CPPUNIT_ASSERT(xProp->getPropertyValue(u"TextHorizontalAdjust"_ustr) >>= eHori); - CPPUNIT_ASSERT_EQUAL(drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT, eHori); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf165261HorzAnchor) -{ - createSdImpressDoc("pptx/tdf165261.pptx"); - saveAndReload(TestFilter::PPTX); - - uno::Reference xGroupShape(getShapeFromPage(0, 0), uno::UNO_QUERY); - uno::Reference xShape(xGroupShape->getByIndex(0), uno::UNO_QUERY); - - uno::Reference xProp(xShape, uno::UNO_QUERY); - drawing::TextHorizontalAdjust eHori; - CPPUNIT_ASSERT(xProp->getPropertyValue(u"TextHorizontalAdjust"_ustr) >>= eHori); - CPPUNIT_ASSERT_EQUAL(drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER, eHori); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testCustomPromptTexts) -{ - createSdImpressDoc("pptx/tdf163239.pptx"); - saveAndReload(TestFilter::PPTX); - - const SdrPage* pPage1 = GetPage(1); - { - // subtitle placeholder text - SdrTextObj* pTxtObj = DynCastSdrTextObj(pPage1->GetObj(0)); - CPPUNIT_ASSERT_MESSAGE("no text object", pTxtObj != nullptr); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong object type!", SdrObjKind::Text, - pTxtObj->GetObjIdentifier()); - const EditTextObject& aEdit = pTxtObj->GetOutlinerParaObject()->GetTextObject(); - OUString aText = aEdit.GetText(0); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text!", - u"Click to edit customized Master Subtitle style"_ustr, aText); - - auto xShapeProps(getShapeFromPage(0, 0)); - CPPUNIT_ASSERT(xShapeProps->getPropertyValue(u"CustomPromptText"_ustr) >>= aText); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text was set!", - u"Click to edit customized Master Subtitle style"_ustr, aText); - } - - { - SdrTextObj* pTxtObj = DynCastSdrTextObj(pPage1->GetObj(1)); - CPPUNIT_ASSERT_MESSAGE("no text object", pTxtObj != nullptr); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong object type!", SdrObjKind::TitleText, - pTxtObj->GetObjIdentifier()); - const EditTextObject& aEdit = pTxtObj->GetOutlinerParaObject()->GetTextObject(); - OUString aText = aEdit.GetText(0); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text!", u"Custom Title 1"_ustr, aText); - - auto xShapeProps(getShapeFromPage(1, 0)); - CPPUNIT_ASSERT(xShapeProps->getPropertyValue(u"CustomPromptText"_ustr) >>= aText); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text was set!", u"Custom Title 1"_ustr, - aText); - } - - const SdrPage* pPage2 = GetPage(3); - { - // body placeholder text - SdrTextObj* pTxtObj = DynCastSdrTextObj(pPage2->GetObj(0)); - CPPUNIT_ASSERT_MESSAGE("no text object", pTxtObj != nullptr); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong object type!", SdrObjKind::OutlineText, - pTxtObj->GetObjIdentifier()); - const EditTextObject& aEdit = pTxtObj->GetOutlinerParaObject()->GetTextObject(); - OUString aText = aEdit.GetText(0); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text!", u"Text placeholder"_ustr, aText); - - auto xShapeProps(getShapeFromPage(0, 1)); - CPPUNIT_ASSERT(xShapeProps->getPropertyValue(u"CustomPromptText"_ustr) >>= aText); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text was set!", u"Text placeholder"_ustr, - aText); - } -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testPresObjTextStyles) -{ - createSdImpressDoc("pptx/tdf163239_v2.pptx"); - saveAndReload(TestFilter::PPTX); - - uno::Reference xStyleFamiliesSupplier(mxComponent, - uno::UNO_QUERY); - uno::Reference xStyleFamilies - = xStyleFamiliesSupplier->getStyleFamilies(); - // 1st slide - uno::Reference xStyleFamily( - xStyleFamilies->getByName(u"Title Slide"_ustr), uno::UNO_QUERY); - { - // Title style - uno::Reference xStyle(xStyleFamily->getByName(u"title"_ustr), - uno::UNO_QUERY); - uno::Reference xPropSet(xStyle, uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL(60.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_CENTER), - xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, - xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); - } - // 2nd slide - xStyleFamily.set(xStyleFamilies->getByName(u"Title and Content"_ustr), uno::UNO_QUERY); - { - // Title style - uno::Reference xStyle(xStyleFamily->getByName(u"title"_ustr), - uno::UNO_QUERY); - uno::Reference xPropSet(xStyle, uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL(44.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_LEFT), - xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, - xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); - - // outline1 style - xStyle.set(xStyleFamily->getByName(u"outline1"_ustr), uno::UNO_QUERY); - xPropSet.set(xStyle, uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL(28.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_LEFT), - xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, - xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); - - // outline2 style - xStyle.set(xStyleFamily->getByName(u"outline2"_ustr), uno::UNO_QUERY); - xPropSet.set(xStyle, uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL(24.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_LEFT), - xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, - xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); - - // outline5 style - xStyle.set(xStyleFamily->getByName(u"outline5"_ustr), uno::UNO_QUERY); - xPropSet.set(xStyle, uno::UNO_QUERY); - CPPUNIT_ASSERT_EQUAL(18.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_LEFT), - xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); - CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, - xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); - } -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTextStylesXML) -{ - createSdImpressDoc("pptx/tdf163239_v2.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slideLayouts/slideLayout1.xml"_ustr); - - assertXPath(pXmlDocRels, "/p:sldLayout/p:cSld/p:spTree/p:sp[1]/p:txBody/a:lstStyle/a:lvl1pPr", - "algn", u"ctr"); - assertXPath(pXmlDocRels, - "/p:sldLayout/p:cSld/p:spTree/p:sp[1]/p:txBody/a:lstStyle/a:lvl1pPr/a:defRPr", "sz", - u"6000"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTextAlignLeft) -{ - createSdImpressDoc("pptx/presLeftAlign.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slides/slide1.xml"_ustr); - - assertXPath(pXmlDocRels, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p/a:pPr", "algn", u"l"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testSubtitleNoBullets) -{ - createSdImpressDoc("odp/tdf170166.odp"); - saveAndReload(TestFilter::PPTX); - - const SdrPage* pPage1 = GetPage(1); - { - // subtitle placeholder object - SdrTextObj* pTxtObj = DynCastSdrTextObj(pPage1->GetObj(0)); - CPPUNIT_ASSERT_MESSAGE("no text object", pTxtObj != nullptr); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong object type!", SdrObjKind::Text, - pTxtObj->GetObjIdentifier()); - const EditTextObject& aEdit = pTxtObj->GetOutlinerParaObject()->GetTextObject(); - const SvxNumBulletItem* pNumFmt = aEdit.GetParaAttribs(0).GetItem(EE_PARA_NUMBULLET); - // Without a fix, it will fail with numbering type: SVX_NUM_CHAR_SPECIAL - CPPUNIT_ASSERT(pNumFmt); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Bullet's numbering type is wrong!", SVX_NUM_NUMBER_NONE, - pNumFmt->GetNumRule().GetLevel(0).GetNumberingType()); - } -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testOLEObjectAnimationTarget) -{ - createSdImpressDoc("pptx/tdf169088.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); - - const OUString sOleId = getXPath( - pXmlDoc, "/p:sld/p:cSld/p:spTree/p:graphicFrame/p:nvGraphicFramePr/p:cNvPr", "id"); - const OUString sPicId = getXPath(pXmlDoc, - "/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/" - "a:graphicData/p:oleObj/p:pic/p:nvPicPr/p:cNvPr", - "id"); - - // Check OLE id is different from pic id - CPPUNIT_ASSERT_MESSAGE("OLE id is same as pic id", sOleId != sPicId); - - // Check animation target spid matches OLE object id - assertXPath(pXmlDoc, - "/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/" - "p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:set/" - "p:cBhvr/p:tgtEl/p:spTgt", - "spid", sOleId); - - assertXPath(pXmlDoc, - "/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/" - "p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:animEffect/" - "p:cBhvr/p:tgtEl/p:spTgt", - "spid", sOleId); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testtdf169496_hidden_graphic) -{ - createSdImpressDoc("pptx/tdf169496_hidden_graphic.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); - - // Graphic 5 is hidden and Graphic 4 is visible, but their order might change in the XML - // Without the fix the hidden attribute wasn't exported - OUString sName1 - = getXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[1]/p:nvPicPr/p:cNvPr", "name"); - OUString sName2 - = getXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[2]/p:nvPicPr/p:cNvPr", "name"); - if (sName1 == "Graphic 5" && sName2 == "Graphic 4") - { - OUString aHidden - = getXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[1]/p:nvPicPr/p:cNvPr", "hidden"); - bool bHidden = aHidden == u"true"_ustr || aHidden == u"1"; - CPPUNIT_ASSERT_EQUAL_MESSAGE("Graphic 3 is supposed to be hidden", true, bHidden); - - assertXPathNoAttribute(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[2]/p:nvPicPr/p:cNvPr", - "hidden"); - } - else if (sName1 == "Graphic 4" && sName2 == "Graphic 5") - { - assertXPathNoAttribute(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[1]/p:nvPicPr/p:cNvPr", - "hidden"); - - OUString aHidden - = getXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[2]/p:nvPicPr/p:cNvPr", "hidden"); - bool bHidden = aHidden == u"true"_ustr || aHidden == u"1"; - CPPUNIT_ASSERT_EQUAL_MESSAGE("Graphic 4 is supposed to be hidden", true, bHidden); - } - else - CPPUNIT_FAIL("Names of graphics is incorrect"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testHiddenGroupShape) -{ - createSdImpressDoc("pptx/hidden_group_shape.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); - assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:grpSp/p:nvGrpSpPr/p:cNvPr", "hidden", u"1"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testtdf169825_vertical_layouts) -{ - createSdImpressDoc("pptx/tdf169825_vertical_layouts.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); - CPPUNIT_ASSERT(pXmlDocRels); - // find layout XML for the slide from the relationship file - OUString sLayoutRelRelative - = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); - OUString sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); - xmlDocUniquePtr pXmlDocLayout = parseExport(sLayoutRelAbs); - CPPUNIT_ASSERT(pXmlDocLayout); - - // without the SlidePersist::getLayoutFromValueToken() part of the patch, - // this and the next layout types would both be exported as blank - assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"vertTitleAndTx"); - - pXmlDocRels = parseExport(u"ppt/slides/_rels/slide2.xml.rels"_ustr); - CPPUNIT_ASSERT(pXmlDocRels); - sLayoutRelRelative = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); - sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); - pXmlDocLayout = parseExport(sLayoutRelAbs); - CPPUNIT_ASSERT(pXmlDocLayout); - - assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"vertTx"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testtdf169825_vertical_layouts_from_scratch) -{ - createSdImpressDoc(); - - uno::Reference xDoc(mxComponent, uno::UNO_QUERY_THROW); - uno::Reference xPages = xDoc->getDrawPages(); - uno::Reference xPage(xPages->getByIndex(0), uno::UNO_QUERY); - uno::Reference xPageSet(xPage, uno::UNO_QUERY_THROW); - xPageSet->setPropertyValue( - "Layout", - uno::Any(static_cast(AutoLayout::AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT))); - - uno::Reference xPage2(xPages->insertNewByIndex(1), uno::UNO_SET_THROW); - uno::Reference xPageSet2(xPage2, uno::UNO_QUERY_THROW); - xPageSet2->setPropertyValue( - "Layout", uno::Any(static_cast(AutoLayout::AUTOLAYOUT_VTITLE_VCONTENT))); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); - CPPUNIT_ASSERT(pXmlDocRels); - // find layout XML for the slide from the relationship file - OUString sLayoutRelRelative - = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); - OUString sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); - xmlDocUniquePtr pXmlDocLayout = parseExport(sLayoutRelAbs); - CPPUNIT_ASSERT(pXmlDocLayout); - - // without the fix in place this would be exported as "objTx" - assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"vertTitleAndTxOverChart"); - - pXmlDocRels = parseExport(u"ppt/slides/_rels/slide2.xml.rels"_ustr); - CPPUNIT_ASSERT(pXmlDocRels); - sLayoutRelRelative = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); - sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); - pXmlDocLayout = parseExport(sLayoutRelAbs); - CPPUNIT_ASSERT(pXmlDocLayout); - - // without the fix in place this would be exported as "picTx" - assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"vertTitleAndTx"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testtdf169825_layout_type) -{ - createSdImpressDoc("odp/tdf169825_layout_type.odp"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); - CPPUNIT_ASSERT(pXmlDocRels); - // find layout XML for the slide from the relationship file - OUString sLayoutRelRelative - = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); - OUString sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); - xmlDocUniquePtr pXmlDocLayout = parseExport(sLayoutRelAbs); - CPPUNIT_ASSERT(pXmlDocLayout); - - // without the fix in place this would be exported as "vertTitleAndTxOverChart" - // while in the original ODP this was - still possibly bogus - notes layout - assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"blank"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testtdf170102_layout_type) -{ - createSdImpressDoc("odp/tdf170102_layout_type.odp"); - saveAndReload(TestFilter::PPTX); - - uno::Reference xDoc(mxComponent, uno::UNO_QUERY); - CPPUNIT_ASSERT(xDoc.is()); - sal_Int32 nMPCount = xDoc->getMasterPages()->getCount(); - // test roughly the same thing in document and in XML: no drawing object in master page/layout - for (sal_Int32 i = 0; i < nMPCount; i++) - { - uno::Reference xPage(xDoc->getMasterPages()->getByIndex(i), - uno::UNO_QUERY_THROW); - sal_Int32 nObjCount = xPage->getCount(); - for (sal_Int32 j = 0; j < nObjCount; j++) - { - uno::Reference xShapeInfo(xPage->getByIndex(j), - uno::UNO_QUERY_THROW); - // without the fix in place there would be a graphic shape placeholder - CPPUNIT_ASSERT_MESSAGE( - "Unexpected graphic object shape in exported master page", - !xShapeInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr)); - } - - xmlDocUniquePtr pXmlDocLayout = parseExport(u"ppt/slideLayouts/slideLayout"_ustr - + OUString::number(i + 1) + u".xml"_ustr); - CPPUNIT_ASSERT(pXmlDocLayout); - // without the fix in place there would be a pic layout element for the placeholder - CPPUNIT_ASSERT_EQUAL_MESSAGE( - "Unexpected \"pic\" element in exported PPTX slide layout", 0, - countXPathNodes(pXmlDocLayout, "/p:sldLayout/p:cSld/p:spTree/p:pic")); - } -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testFooterIdxConsistency) -{ - createSdImpressDoc("pptx/multiplelayoutfooter.pptx"); - save(TestFilter::PPTX); - - // slide1 points to slideLayout2 - xmlDocUniquePtr pRelsDoc = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); - assertXPath(pRelsDoc, - "/rels:Relationships/rels:Relationship[@Type='http://" - "schemas.openxmlformats.org/officeDocument/2006/" - "relationships/slideLayout']", - "Target", u"../slideLayouts/slideLayout2.xml"); - - // And they agree on the idx of the footer: - xmlDocUniquePtr pSlide = parseExport(u"ppt/slides/slide1.xml"_ustr); - OUString aFtrIdx = getXPath(pSlide, "//p:sp/p:nvSpPr/p:nvPr/p:ph", "idx"); - xmlDocUniquePtr pLayout = parseExport(u"ppt/slideLayouts/slideLayout2.xml"_ustr); - assertXPath(pLayout, "//p:sp/p:nvSpPr/p:nvPr/p:ph", "idx", aFtrIdx); - // Without the fix it fails with: - // - Expected: 1 - // - Actual : 2 - // - In <>, attribute 'idx' of '//p:sp/p:nvSpPr/p:nvPr/p:ph' incorrect value. -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testOmitCanvasSlideExport) -{ - createSdImpressDoc("odp/canvas-slide.odp"); - - SdXImpressDocument* pXImpressDocument = dynamic_cast(mxComponent.get()); - CPPUNIT_ASSERT(pXImpressDocument); - SdDrawDocument* pDoc = pXImpressDocument->GetDoc(); - CPPUNIT_ASSERT_MESSAGE("no document", pDoc != nullptr); - - // the document has 2 pages - one canvas page, and one normal page - CPPUNIT_ASSERT_EQUAL(sal_uInt16(2), pDoc->GetSdPageCount(PageKind::Standard)); - CPPUNIT_ASSERT(pDoc->HasCanvasPage()); - - save(TestFilter::PPTX); - - // Verify that the canvas slide was omitted from the export - // It should have one master slide, and one slide - xmlDocUniquePtr pXmlDocContent = parseExport(u"ppt/presentation.xml"_ustr); - assertXPath(pXmlDocContent, "/p:presentation/p:sldMasterIdLst/p:sldMasterId", 1); - assertXPath(pXmlDocContent, "/p:presentation/p:sldIdLst/p:sldId", 1); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testParaAlignStartEnd) -{ - createSdImpressDoc("odp/para-align-start-end.fodp"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); - - // PresentationML doesn't support start and end paragraph alignment. - // Paragraphs should be exported as left/right alignment, depending on para direction. - assertXPath(pDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p[1]/a:pPr[@algn='l']", 1); - assertXPath(pDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p[2]/a:pPr[@algn='r']", 1); - assertXPath(pDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p[3]/a:pPr[@algn='r']", 1); - assertXPath(pDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p[4]/a:pPr[@algn='l']", 1); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf169559) -{ - createSdImpressDoc("pptx/tdf169524.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); - assertXPath( - pXmlDoc, - "/p:sldMaster/p:cSld/p:spTree/p:sp[2]/p:txBody/a:lstStyle/a:lvl1pPr/a:spcAft/a:spcPts", - "val", u"1701"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf114443_PPSX) -{ - createSdImpressDoc("ppsx/tdf114443-6.ppsx"); - save(TestFilter::PPSX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"[Content_Types].xml"_ustr); - - // Without the fix, this would have been: - // application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml - // which means the file loses its slideshow/autoplay nature and PowerPoint rejects it. - assertXPath(pXmlDoc, - "/ContentType:Types/ContentType:Override[@PartName='/ppt/presentation.xml']", - "ContentType", - u"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testConnectorShapeAnimationTarget) -{ - createSdImpressDoc("pptx/connector-shape-animations.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); - - const OUString sConnectorId = getXPath( - pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp/p:nvCxnSpPr/p:cNvCxnSpPr/a:stCxn", "id"); - - const OUString sShapeId = getXPath( - pXmlDoc, "/p:sld/p:cSld/p:spTree/p:graphicFrame/p:nvGraphicFramePr/p:cNvPr", "id"); - - // Check connector and shape both have the same ID - CPPUNIT_ASSERT_EQUAL_MESSAGE("Connector and the shape doesn't have the same ID", sConnectorId, - sShapeId); - - // Check animation spid matches ShapeId - assertXPath(pXmlDoc, - "/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/" - "p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par[2]/p:cTn/p:childTnLst/p:set/" - "p:cBhvr/p:tgtEl/p:spTgt", - "spid", sShapeId); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testChartExExport) -{ - createSdImpressDoc("pptx/forum-mso-de-138303.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); - CPPUNIT_ASSERT(pDoc); - - const OString sPath1 = "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Fallback"_ostr; - assertXPath(pDoc, sPath1 + "/xdr:sp"_ostr, 0); - assertXPath(pDoc, sPath1 + "/p:sp"_ostr, 1); - assertXPath(pDoc, sPath1 + "/p:sp/p:nvSpPr/p:nvPr"_ostr, 1); - - pDoc = parseExport(u"ppt/charts/chartEx1.xml"_ustr); - CPPUNIT_ASSERT(pDoc); - - assertXPath(pDoc, "/cx:chartSpace/c:date1904", 0); -} - -// 1. Open PPTX with sections, re-export to PPTX, verify sections preserved -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testSlideSectionsPPTXImport) -{ - createSdImpressDoc("pptx/slide-section-test.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/presentation.xml"_ustr); - - static constexpr OString sPath = "/p:presentation/p:extLst/p:ext/p14:sectionLst"_ostr; - assertXPath(pXmlDoc, sPath + "/p14:section", 3); - assertXPath(pXmlDoc, sPath + "/p14:section[1]", "name", u"Section-1"); - assertXPath(pXmlDoc, sPath + "/p14:section[2]", "name", u"Section-2"); - assertXPath(pXmlDoc, sPath + "/p14:section[3]", "name", u"Section-3"); - assertXPath(pXmlDoc, sPath + "/p14:section[1]/p14:sldIdLst/p14:sldId", 4); - assertXPath(pXmlDoc, sPath + "/p14:section[2]/p14:sldIdLst/p14:sldId", 7); - assertXPath(pXmlDoc, sPath + "/p14:section[3]/p14:sldIdLst/p14:sldId", 2); - - // GUIDs preserved - for (sal_Int32 i = 1; i <= 3; ++i) - { - OUString sId = getXPath(pXmlDoc, sPath + "/p14:section[" + OString::number(i) + "]", "id"); - CPPUNIT_ASSERT(!sId.isEmpty()); - CPPUNIT_ASSERT(sId.startsWith("{")); - } - - // Slide IDs non-zero - OUString sSldId = getXPath(pXmlDoc, sPath + "/p14:section[1]/p14:sldIdLst/p14:sldId[1]", "id"); - CPPUNIT_ASSERT(sSldId.toInt32() > 0); -} - -// 2. Export PPTX with sections to ODP -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testSlideSectionsPPTXToODP) -{ - createSdImpressDoc("pptx/slide-section-test.pptx"); - skipValidation(); - save(TestFilter::ODP); - - xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr); - - static constexpr OString sPath - = "/office:document-content/office:body/office:presentation/loext:section-list"_ostr; - assertXPath(pXmlDoc, sPath + "/loext:section", 3); - assertXPath(pXmlDoc, sPath + "/loext:section[1]", "name", u"Section-1"); - assertXPath(pXmlDoc, sPath + "/loext:section[2]", "name", u"Section-2"); - assertXPath(pXmlDoc, sPath + "/loext:section[3]", "name", u"Section-3"); - assertXPath(pXmlDoc, sPath + "/loext:section[1]/loext:section-slide", 4); - assertXPath(pXmlDoc, sPath + "/loext:section[2]/loext:section-slide", 7); - assertXPath(pXmlDoc, sPath + "/loext:section[3]/loext:section-slide", 2); -} - -// 3. Open ODP with sections (PPTX -> ODP -> reload ODP) -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testSlideSectionsODPImport) -{ - createSdImpressDoc("pptx/slide-section-test.pptx"); - skipValidation(); - saveAndReload(TestFilter::ODP); - - // Verify the in-memory model loaded from ODP - auto* pXImpressDocument = dynamic_cast(mxComponent.get()); - CPPUNIT_ASSERT(pXImpressDocument); - SdDrawDocument* pDoc = pXImpressDocument->GetDoc(); - CPPUNIT_ASSERT(pDoc); - - sd::SlideSectionManager& rMgr = pDoc->GetSectionManager(); - CPPUNIT_ASSERT_EQUAL(sal_Int32(3), rMgr.GetSectionCount()); - CPPUNIT_ASSERT_EQUAL(u"Section-1"_ustr, rMgr.GetSection(0).maName); - CPPUNIT_ASSERT_EQUAL(u"Section-2"_ustr, rMgr.GetSection(1).maName); - CPPUNIT_ASSERT_EQUAL(u"Section-3"_ustr, rMgr.GetSection(2).maName); -} - -// 4. Export ODP with sections to PPTX -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testSlideSectionsODPToPPTX) -{ - createSdImpressDoc("pptx/slide-section-test.pptx"); - skipValidation(); - saveAndReload(TestFilter::ODP); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/presentation.xml"_ustr); - - static constexpr OString sPath = "/p:presentation/p:extLst/p:ext/p14:sectionLst"_ostr; - assertXPath(pXmlDoc, sPath + "/p14:section", 3); - assertXPath(pXmlDoc, sPath + "/p14:section[1]", "name", u"Section-1"); - assertXPath(pXmlDoc, sPath + "/p14:section[2]", "name", u"Section-2"); - assertXPath(pXmlDoc, sPath + "/p14:section[3]", "name", u"Section-3"); - assertXPath(pXmlDoc, sPath + "/p14:section[1]/p14:sldIdLst/p14:sldId", 4); - assertXPath(pXmlDoc, sPath + "/p14:section[2]/p14:sldIdLst/p14:sldId", 7); - assertXPath(pXmlDoc, sPath + "/p14:section[3]/p14:sldIdLst/p14:sldId", 2); - - // Slide IDs non-zero after ODP round-trip - OUString sSldId = getXPath(pXmlDoc, sPath + "/p14:section[1]/p14:sldIdLst/p14:sldId[1]", "id"); - CPPUNIT_ASSERT(sSldId.toInt32() > 0); - - // GUIDs survive ODP round-trip (generated if missing) - for (sal_Int32 i = 1; i <= 3; ++i) - { - OUString sId = getXPath(pXmlDoc, sPath + "/p14:section[" + OString::number(i) + "]", "id"); - CPPUNIT_ASSERT(!sId.isEmpty()); - } -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testCool16078_placeholderKeepsItsOutline) -{ - // Given a layout whose picture placeholder is clipped to an outline of two elements: - // a frame with two holes, one of them round, and a second path holding a triangle that crosses - // the round one: - createSdImpressDoc("pptx/custgeom-placeholder.pptx"); - save(TestFilter::PPTX); - - // The outline is written back as the placeholder's own geometry. Without it the placeholder - // saved as a plain rectangle, and its fill then hid both holes and what showed through. - xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Picture placeholder, clipped outline"); - OString aPath - = "//p:sp[p:nvSpPr/p:nvPr/p:ph/@type='pic']/p:spPr/a:custGeom/a:pathLst/a:path"_ostr; - assertXPath(pLayout, aPath, 1); - // Three contours in one path, which is what subtracts the holes from the frame - the same three - // the ODF side writes. The second path is not one of them: it is filled rather than cut out, so - // the triangle it holds took a piece of the round hole back instead of adding a contour. - assertXPath(pLayout, aPath + "/a:moveTo", 3); - assertXPath(pLayout, aPath + "/a:close", 3); - // Four sides each for the two rectangles, the closing one included. - assertXPath(pLayout, aPath + "/a:lnTo", 8); - // What is left of the round hole is still curves, not a polygon flattened out of them. - assertXPath(pLayout, aPath + "/a:cubicBezTo", 6); - assertXPath(pLayout, aPath + "/a:cubicBezTo[1]/a:pt", 3); - // The frame's own corner, in the path's coordinates. - assertXPath(pLayout, aPath + "/a:moveTo[1]/a:pt", "x", u"0"); - assertXPath(pLayout, aPath + "/a:moveTo[1]/a:pt", "y", u"0"); - - // The preset rectangle it used to write would have claimed the whole frame. - assertXPath(pLayout, "//p:sp[p:nvSpPr/p:nvPr/p:ph/@type='pic']/p:spPr/a:prstGeom", 0); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testCool16080_masterKeepsItsPlaceholders) -{ - // Given a deck of one master and eleven layouts, so one Impress master page stands for the - // whole group: - createSdImpressDoc("pptx/master-and-eleven-layouts.pptx"); - save(TestFilter::PPTX); - - // The master part carries its own placeholders. Without the fix it held a shape tree with - // nothing in it, so PowerPoint offered an empty slide master: nothing to inherit from, and - // an edit meant for every layout had to be repeated on each of them. - xmlDocUniquePtr pMaster = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); - static constexpr OString aTree("/p:sldMaster/p:cSld/p:spTree"_ostr); - assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='title']", 1); - assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='dt']", 1); - assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='ftr']", 1); - assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='sldNum']", 1); - - // The body comes from the first layout that has one, since the page standing for the group is - // the Title Slide layout and has none. That layout inherits the master's geometry, so the - // placeholder lands where the master put it rather than where a layout would. - assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='body']", 1); - assertXPath(pMaster, aTree + "/p:sp[p:nvSpPr/p:nvPr/p:ph/@type='body']/p:spPr/a:xfrm/a:off", - "y", u"1825560"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testCool16080_layoutShapesStayOnTheirLayout) -{ - // Given a deck whose first layout paints the whole slide blue, and a slide on another layout, - // which is therefore white: - createSdImpressDoc("pptx/layout-own-background.pptx"); - saveAndReload(TestFilter::PPTX); - - utl::TempFileNamed aPng; - aPng.EnableKillingFile(); - uno::Sequence aFilterData{ - comphelper::makePropertyValue(u"PixelWidth"_ustr, sal_Int32(64)), - comphelper::makePropertyValue(u"PixelHeight"_ustr, sal_Int32(36)) - }; - uno::Sequence aDescriptor{ - comphelper::makePropertyValue(u"URL"_ustr, aPng.GetURL()), - comphelper::makePropertyValue(u"FilterName"_ustr, u"PNG"_ustr), - comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) - }; - auto xExporter = drawing::GraphicExportFilter::create(getComponentContext()); - xExporter->setSourceDocument(getPage(0).queryThrow()); - xExporter->filter(aDescriptor); - - SvFileStream aStream(aPng.GetURL(), StreamMode::READ); - Bitmap aSlide = vcl::PngImageReader(aStream).read(); - CPPUNIT_ASSERT_EQUAL(Size(64, 36), aSlide.GetSizePixel()); - - // The slide is still white. An imported master and layout collapse onto one Impress master - // page, so the page standing for the group holds the first layout's rectangle - and writing - // that page's shapes into the master part painted it under every slide of the deck. Sample - // the side margins, which no placeholder of this layout reaches. - // Without the fix in place, this test would have failed with - // - Expected: rgba[ffffffff] - // - Actual : rgba[0000ffff] - CPPUNIT_ASSERT_EQUAL(COL_WHITE, aSlide.GetPixelColor(2, 18)); - CPPUNIT_ASSERT_EQUAL(COL_WHITE, aSlide.GetPixelColor(61, 18)); - - // The layout it belongs to still paints it, which is what a slide of that layout inherits. - xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Title Slide"); - assertXPath(pLayout, - "/p:sldLayout/p:cSld/p:spTree/p:sp[p:nvSpPr/p:cNvPr/@name='Blue background']" - "/p:spPr/a:solidFill/a:srgbClr", - "val", u"0000FF"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testCool16080_masterKeepsWhatItsLayoutsShare) -{ - // Given a deck of one master that draws a rule of its own across the top of the slide, in - // segments of colour, and seven layouts that draw nothing: - createSdImpressDoc("pptx/tdf157740.pptx"); - saveAndReload(TestFilter::PPTX); - - // The rule is on the slide master, so PowerPoint edits it once for every layout. The import - // gives each of the seven Impress master pages a copy, and the master used to get none. - // Without the fix in place, this test would have failed with - // - Expected: 1 - // - Actual : 0 - xmlDocUniquePtr pMaster = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); - assertXPath(pMaster, - "/p:sldMaster/p:cSld/p:spTree/p:grpSp[p:nvGrpSpPr/p:cNvPr/@name='Group 1']", 1); - - // It moved there rather than being written seven more times: a layout holds the three - // placeholders it inherits and nothing else. - xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Title, Content over Content"); - static constexpr OString aLayoutTree("/p:sldLayout/p:cSld/p:spTree"_ostr); - assertXPath(pLayout, aLayoutTree + "/*[not(self::p:nvGrpSpPr) and not(self::p:grpSpPr)]", 3); - assertXPath(pLayout, aLayoutTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph", 3); - - // And the slide still shows the rule, wherever it is written - utl::TempFileNamed aPng; - aPng.EnableKillingFile(); - uno::Sequence aFilterData{ - comphelper::makePropertyValue(u"PixelWidth"_ustr, sal_Int32(1920)), - comphelper::makePropertyValue(u"PixelHeight"_ustr, sal_Int32(1080)) - }; - uno::Sequence aDescriptor{ - comphelper::makePropertyValue(u"URL"_ustr, aPng.GetURL()), - comphelper::makePropertyValue(u"FilterName"_ustr, u"PNG"_ustr), - comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) - }; - auto xExporter = drawing::GraphicExportFilter::create(getComponentContext()); - xExporter->setSourceDocument(getPage(0).queryThrow()); - xExporter->filter(aDescriptor); - - SvFileStream aStream(aPng.GetURL(), StreamMode::READ); - Bitmap aSlide = vcl::PngImageReader(aStream).read(); - CPPUNIT_ASSERT_EQUAL(Size(1920, 1080), aSlide.GetSizePixel()); - // 90EBCD is accent5 of the deck's theme, the fill of the rule's second segment - CPPUNIT_ASSERT_EQUAL(Color(0x90EBCD), aSlide.GetPixelColor(200, 5)); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testCool16082_layoutKeepsSubtitle) -{ - // Given a deck whose Title Slide layout carries a subtitle placeholder: - createSdImpressDoc("pptx/master-and-eleven-layouts.pptx"); - save(TestFilter::PPTX); - - // The layout keeps it. Without the fix every layout of a master holding more than one lost its - // subtitle, so a slide made from the layout afterwards had nowhere to put one. - xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Title Slide"); - assertXPath(pLayout, "/p:sldLayout/p:cSld/p:spTree/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='subTitle']", - 1); - - // The slide master still gets none, which is what PowerPoint refuses the file over. - xmlDocUniquePtr pMaster = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); - assertXPath(pMaster, "/p:sldMaster/p:cSld/p:spTree/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='subTitle']", - 0); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testCool16079_dateTimeField) -{ - // Given a deck whose date placeholders hold an automatically updated date: - createSdImpressDoc("pptx/master-and-eleven-layouts.pptx"); - save(TestFilter::PPTX); - - // The placeholder keeps a field. Without the fix it held our own display string as literal - // text, so PowerPoint showed "" where the original showed a date. The type is the - // one that leaves the format to the reader, which is what a field holding none asks for - the - // alias PowerPoint writes here stands for a fixed MM/DD/YYYY. - xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Title Slide"); - assertXPath(pLayout, "//p:sp[p:nvSpPr/p:nvPr/p:ph/@type='dt']/p:txBody/a:p/a:fld", "type", - u"datetime"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testThemeKeepColorTransforms) -{ - createSdImpressDoc("pptx/empty-title-placeholder.pptx"); - save(TestFilter::PPTX); - - xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/theme/theme1.xml"_ustr); - const OString sBasePath = "/a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill[1]" - "/a:gsLst/a:gs[@pos='40000']/a:schemeClr"_ostr; - - // Without the fix satMod would end up negative - assertXPath(pXmlDoc, sBasePath, "val", u"phClr"); - assertXPathChildren(pXmlDoc, sBasePath, 3); - assertXPath(pXmlDoc, sBasePath + "/a:tint", "val", u"45000"); - assertXPath(pXmlDoc, sBasePath + "/a:shade", "val", u"99000"); - assertXPath(pXmlDoc, sBasePath + "/a:satMod", "val", u"350000"); -} - -CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf166401_textGivenToAPicturePlaceholder) -{ - // Given a slide whose picture placeholder is given text without any editing, which is what a - // script does and what leaves the placeholder standing for itself rather than for the text: - createSdImpressDoc("pptx/picture-placeholder-custom-prompt.pptx"); - getShapeFromPage(0, 0).queryThrow()->setString( - u"Given to a picture placeholder"_ustr); - save(TestFilter::PPTX); - - // The placeholder is written with its text. It holds no image, so writing it as a picture wrote - // nothing at all and the text went with the shape. - xmlDocUniquePtr pSlide = parseExport(u"ppt/slides/slide1.xml"_ustr); - static constexpr OString aPlaceholder( - "/p:sld/p:cSld/p:spTree/p:sp[p:nvSpPr/p:nvPr/p:ph/@type='pic']"_ostr); - assertXPath(pSlide, aPlaceholder, 1); - assertXPathContent(pSlide, aPlaceholder + "/p:txBody/a:p/a:r/a:t", - u"Given to a picture placeholder"); -} - CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/qa/unit/export-tests-ooxml5.cxx b/sd/qa/unit/export-tests-ooxml5.cxx new file mode 100644 index 000000000000..a3291d04a5bc --- /dev/null +++ b/sd/qa/unit/export-tests-ooxml5.cxx @@ -0,0 +1,1380 @@ +/* -*- 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 "sdmodeltestbase.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +class SdOOXMLExportTest5 : public SdModelTestBase +{ +public: + SdOOXMLExportTest5() + : SdModelTestBase(u"/sd/qa/unit/data/"_ustr) + { + } +}; + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf151622_oleIcon) +{ + createSdImpressDoc("odp/ole_icon.odp"); + + save(TestFilter::PPTX); + + xmlDocUniquePtr pXml = parseExport(u"ppt/slides/slide1.xml"_ustr); + + // Without the accompanying fix in place, this test would have failed with: + // - Expression: prop + // - In ..., XPath '//p:oleObj' no attribute 'showAsIcon' exist + // i.e. show as icon option wasn't exported. + assertXPath(pXml, "//p:oleObj", "showAsIcon", u"1"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf152436) +{ + createSdImpressDoc("pptx/ole-emf_min.pptx"); + saveAndReload(TestFilter::PPTX); + + // Check number of shapes after export. + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), getPage(0)->getCount()); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testLinkedOLE) +{ + // The linked OLE object needs its link completed during load so it can + // be exported. Use FULL_UPDATE since the default test mode defers links. + uno::Sequence aParams = { + comphelper::makePropertyValue(u"UpdateDocMode"_ustr, + sal_Int16(css::document::UpdateDocMode::FULL_UPDATE)), + }; + loadFromFile(u"odp/linked_ole.odp", aParams); + + save(TestFilter::PPTX); + + xmlDocUniquePtr pXml = parseExport(u"ppt/slides/slide1.xml"_ustr); + + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 1 + // - Actual : 0 + // - In<>, XPath '//p:oleObj' number of nodes is incorrect + // i.e. the linked ole object wasn't exported. + assertXPath(pXml, "//p:oleObj", 1); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf102261_testParaTabStopDefaultDistance) +{ + createSdImpressDoc("pptx/tdf102261_testParaTabStopDefaultDistance.pptx"); + saveAndReload(TestFilter::PPTX); + + uno::Reference xShape(getShapeFromPage(0, 0)); + { + uno::Reference xPropSet(getParagraphFromShape(0, xShape), + uno::UNO_QUERY_THROW); + CPPUNIT_ASSERT_EQUAL( + sal_Int32{ 1270 }, + xPropSet->getPropertyValue(u"ParaTabStopDefaultDistance"_ustr).get()); + } + { + uno::Reference xPropSet(getParagraphFromShape(1, xShape), + uno::UNO_QUERY_THROW); + CPPUNIT_ASSERT_EQUAL( + sal_Int32{ 2540 }, + xPropSet->getPropertyValue(u"ParaTabStopDefaultDistance"_ustr).get()); + } +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testNotesAuthorDate) +{ + createSdImpressDoc("pptx/pres-with-notes.pptx"); + + using ScriptingCfg = officecfg::Office::Common::Security::Scripting; + ScopedConfigValue aCfg1(true); + { + // 1. Remove all personal info, but keep note info + ScopedConfigValue aCfg2(true); + + saveAndReload(TestFilter::PPTX); + + xmlDocUniquePtr pXml = parseExport(u"ppt/commentAuthors.xml"_ustr); + assertXPath(pXml, "/p:cmAuthorLst/p:cmAuthor[@id=0]", "name", u"Hans Wurst"); + assertXPath(pXml, "/p:cmAuthorLst/p:cmAuthor[@id=1]", "name", u"Max Muster"); + + pXml = parseExport(u"ppt/comments/comment1.xml"_ustr); + assertXPath(pXml, "/p:cmLst/p:cm", "dt", u"2024-06-13T12:03:08.000000000"); + } + + { + // 2. Remove all personal info + ScopedConfigValue aCfg2(false); + saveAndReload(TestFilter::PPTX); + + xmlDocUniquePtr pXml = parseExport(u"ppt/commentAuthors.xml"_ustr); + assertXPath(pXml, "/p:cmAuthorLst/p:cmAuthor[@id=0]", "name", u"Author1"); + assertXPath(pXml, "/p:cmAuthorLst/p:cmAuthor[@id=1]", "name", u"Author2"); + + pXml = parseExport(u"ppt/comments/comment1.xml"_ustr); + assertXPathNoAttribute(pXml, "/p:cmLst/p:cm", "dt"); + } +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTableCellVerticalPropertyRoundtrip) +{ + createSdImpressDoc("pptx/tcPr-vert-roundtrip.pptx"); + saveAndReload(TestFilter::PPTX); + + xmlDocUniquePtr pXml = parseExport(u"ppt/slides/slide1.xml"_ustr); + + assertXPath(pXml, "(//a:tcPr)[1]", "vert", u"vert"); + assertXPath(pXml, "(//a:tcPr)[2]", "vert", u"vert270"); + assertXPath(pXml, "(//a:tcPr)[3]", "vert", u"wordArtVert"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf157740_slideMasters) +{ + createSdImpressDoc("pptx/tdf157740.pptx"); + saveAndReload(TestFilter::PPTX); + + // The original file has 1 slide master and 7 slide layouts in that master + xmlDocUniquePtr pXmlDocContent = parseExport(u"ppt/presentation.xml"_ustr); + assertXPath(pXmlDocContent, "/p:presentation/p:sldMasterIdLst/p:sldMasterId", 1); + + pXmlDocContent = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); + assertXPath(pXmlDocContent, "/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId", 7); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testNotesMasterThemeNoAfterDeduplicatedMasters) +{ + createSdImpressDoc("n759180.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDocContent = parseExport(u"ppt/presentation.xml"_ustr); + assertXPath(pXmlDocContent, "/p:presentation/p:sldMasterIdLst/p:sldMasterId", 1); + + xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slideMasters/_rels/slideMaster1.xml.rels"_ustr); + assertXPath(pXmlDocRels, + "/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/" + "officeDocument/2006/relationships/theme']", + "Target", u"../theme/theme1.xml"); + + // The one slide master of this document has 12 layouts, which are imported as 12 master pages + // and written back as one slide master. The notes master theme takes the number after the slide + // master theme, as in the original file, without a gap for the master pages that were merged. + // Without the change in place, this test would have failed with: + // - Expected: ../theme/theme2.xml + // - Actual : ../theme/theme13.xml + pXmlDocRels = parseExport(u"ppt/notesMasters/_rels/notesMaster1.xml.rels"_ustr); + assertXPath(pXmlDocRels, + "/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/" + "officeDocument/2006/relationships/theme']", + "Target", u"../theme/theme2.xml"); + + uno::Reference xNameAccess + = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), + maTempFile.GetURL()); + CPPUNIT_ASSERT_EQUAL(true, bool(xNameAccess->hasByName(u"ppt/theme/theme2.xml"_ustr))); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf159931_slideLayouts) +{ + createSdImpressDoc("odp/repeatBitmapMode.odp"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDocRels1 = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); + xmlDocUniquePtr pXmlDocRels2 = parseExport(u"ppt/slides/_rels/slide2.xml.rels"_ustr); + + assertXPath(pXmlDocRels1, "(/rels:Relationships/rels:Relationship[@Type='http://" + "schemas.openxmlformats.org/officeDocument/2006/relationships/" + "slideLayout'])"); + + // the relative target e.g. "../slideLayouts/slideLayout2.xml" + OUString sRelativeLayoutPath1 + = getXPathContent(pXmlDocRels1, "(/rels:Relationships/rels:Relationship[@Type='http://" + "schemas.openxmlformats.org/officeDocument/2006/" + "relationships/slideLayout'])/@Target"); + + assertXPath(pXmlDocRels2, "(/rels:Relationships/rels:Relationship[@Type='http://" + "schemas.openxmlformats.org/officeDocument/2006/relationships/" + "slideLayout'])"); + + // the relative target e.g. "../slideLayouts/slideLayout1.xml" + OUString sRelativeLayoutPath2 + = getXPathContent(pXmlDocRels2, "(/rels:Relationships/rels:Relationship[@Type='http://" + "schemas.openxmlformats.org/officeDocument/2006/" + "relationships/slideLayout'])/@Target"); + + uno::Reference xNameAccess + = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), + maTempFile.GetURL()); + + // Check that the referenced slideLayout files exist + // Without the accompanying fix in place, this test would have failed with: + // equality assertion failed + // - Expected: 1 + // - Actual : 0 + // i.e. the referenced slideLayout file was missing on export. + OUString sSlideLayoutName1 = sRelativeLayoutPath1.getToken(2, '/'); + OUString sSlideLayoutName2 = sRelativeLayoutPath2.getToken(2, '/'); + + CPPUNIT_ASSERT_EQUAL(true, + bool(xNameAccess->hasByName("ppt/slideLayouts/" + sSlideLayoutName1))); + CPPUNIT_ASSERT_EQUAL(true, + bool(xNameAccess->hasByName("ppt/slideLayouts/" + sSlideLayoutName2))); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf163483_export_math_fallback) +{ + createSdImpressDoc("odp/formula.fodp"); + + // Before export-and-reload, there is a formula on page. + { + auto xProps = getShapeFromPage(0, 0); + auto xInfo = xProps->getPropertyValue(u"Model"_ustr).queryThrow(); + CPPUNIT_ASSERT(xInfo->supportsService(u"com.sun.star.formula.FormulaProperties"_ustr)); + + // tdf#164101: check that the size is imported correctly + css::awt::Size formulaSize = xProps.queryThrow()->getSize(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1553), formulaSize.Width); + CPPUNIT_ASSERT_EQUAL(sal_Int32(528), formulaSize.Height); + + CPPUNIT_ASSERT_THROW(getShapeFromPage(0, 1), + css::lang::IndexOutOfBoundsException); // Only one shape on page + } + + saveAndReload(TestFilter::PPTX); + + // After save-and-reload, there must still be a single shape; now it's a fallback image. + // When we start to import formulas from PPTX, that will be formula. + { + // Without the fix, this threw IndexOutOfBoundsException, because there was no fallback, + // and no shape got imported. + auto xInfo = getShapeFromPage(0, 0).queryThrow(); + CPPUNIT_ASSERT(xInfo->supportsService(u"com.sun.star.drawing.CustomShape"_ustr)); + + css::awt::Size formulaSize = xInfo.queryThrow()->getSize(); + // The fallback image size after the roundtrip may be a bit different - allow some tolerance + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(1553), formulaSize.Width, 1); + CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(528), formulaSize.Height, 1); + + CPPUNIT_ASSERT_THROW(getShapeFromPage(0, 1), + css::lang::IndexOutOfBoundsException); // Only one shape on page + } + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); + + const OUString cNvPr_id = getXPath( + pXmlDoc, "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Choice/p:sp/p:nvSpPr/p:cNvPr", + "id"); + + // Check that the alternate content is exported + assertXPath( + pXmlDoc, + "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Fallback/p:sp/p:spPr/a:blipFill/a:blip", + "embed", u"rId1"); + + // Its cNvPr_id must be the same + assertXPath(pXmlDoc, + "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Fallback/p:sp/p:nvSpPr/p:cNvPr", + "id", cNvPr_id); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf169979_missing_ppic) +{ + createSdImpressDoc("odp/tdf169979.odp"); + + save(TestFilter::PPTX); + + xmlDocUniquePtr pXml2 = parseExport(u"ppt/slides/slide2.xml"_ustr); + + // Without the fix in place, this test would have failed with + // - Expected: 1 + // - Actual : 0 + // - In <>, XPath '/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/a:graphicData/p:oleObj/p:pic/p:nvPicPr/p:cNvPr' number of nodes is incorrect + assertXPath(pXml2, + "/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/" + "a:graphicData/p:oleObj/p:pic/p:nvPicPr/p:cNvPr", + "name", u""); + + assertXPath(pXml2, + "/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/" + "a:graphicData/p:oleObj/p:pic/p:nvPicPr/p:cNvPr", + "descr", u""); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testEmptyChildTnLstElement) +{ + createSdImpressDoc("odp/emptyChildTnLstElement.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 4 + // - validation error in OOXML export: Errors: 4 + saveAndReload(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); + + // tdf#170202: Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 1 + CPPUNIT_ASSERT_EQUAL(0, countXPathNodes(pXmlDoc, "//p:sld/p:timing")); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf169941_internal_link_to_shapes) +{ + createSdImpressDoc("odp/tdf169941.odp"); + + save(TestFilter::PPTX); + + xmlDocUniquePtr pRelsDoc2 = parseExport(u"ppt/slides/_rels/slide2.xml.rels"_ustr); + + // Without the fix in place, this test would have failed with + // - Expected: slide1.xml + // - Actual : + assertXPath(pRelsDoc2, "/rels:Relationships/rels:Relationship[@Id='rId1']", "Target", + u"slide1.xml"); + + xmlDocUniquePtr pRelsDoc3 = parseExport(u"ppt/slides/_rels/slide3.xml.rels"_ustr); + assertXPath(pRelsDoc3, "/rels:Relationships/rels:Relationship[@Id='rId1']", "Target", + u"slide1.xml"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testEmptyAttrNameElement) +{ + createSdImpressDoc("odp/emptyChildTnLstElement.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 2 + // - validation error in OOXML export: Errors: 2 + saveAndReload(TestFilter::PPTX); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testPlaceHolderFitHeightToText) +{ + createSdImpressDoc("pptx/tdf160487.pptx"); + saveAndReload(TestFilter::PPTX); + uno::Reference xShape(getShapeFromPage(1, 0)); + bool bTextAutoGrowHeight = false; + xShape->getPropertyValue(u"TextAutoGrowHeight"_ustr) >>= bTextAutoGrowHeight; + CPPUNIT_ASSERT_MESSAGE("PlaceHolder Fit height to text should be true.", bTextAutoGrowHeight); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testDeduplicateMasters) +{ + createSdImpressDoc("pptx/onemaster-twolayouts.pptx"); + saveAndReload(TestFilter::PPTX); + + // Check that the document still has one master and two layouts + xmlDocUniquePtr pXmlDocContent = parseExport("ppt/presentation.xml"); + assertXPath(pXmlDocContent, "/p:presentation/p:sldMasterIdLst/p:sldMasterId"_ostr, 1); + pXmlDocContent = parseExport("ppt/slideMasters/slideMaster1.xml"); + assertXPath(pXmlDocContent, "/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId"_ostr, 2); + + // Check that both background colors have been preserved + uno::Reference xDoc(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT(xDoc.is()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xDoc->getMasterPages()->getCount()); + + uno::Reference xPage(xDoc->getMasterPages()->getByIndex(0), + uno::UNO_QUERY_THROW); + uno::Reference xPropSet(xPage, uno::UNO_QUERY); + uno::Any aAny = xPropSet->getPropertyValue("Background"); + CPPUNIT_ASSERT(aAny.hasValue()); + uno::Reference aXBackgroundPropSet; + aAny >>= aXBackgroundPropSet; + Color nColor; + CPPUNIT_ASSERT(aXBackgroundPropSet->getPropertyValue("FillColor") >>= nColor); + CPPUNIT_ASSERT_EQUAL(Color(0x0E2841), nColor); + + uno::Reference xPage1(xDoc->getMasterPages()->getByIndex(1), + uno::UNO_QUERY_THROW); + uno::Reference xPropSet1(xPage1, uno::UNO_QUERY); + aAny = xPropSet1->getPropertyValue("Background"); + CPPUNIT_ASSERT(aAny.hasValue()); + aAny >>= aXBackgroundPropSet; + CPPUNIT_ASSERT(aXBackgroundPropSet->getPropertyValue("FillColor") >>= nColor); + CPPUNIT_ASSERT_EQUAL(Color(0x000000), nColor); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testInvalidAttributeValuesInSrcRect) +{ + createSdImpressDoc("odp/invalidAttributeValuesInSrcRect.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 24 + // - validation error in OOXML export: Errors: 24 + saveAndReload(TestFilter::PPTX); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testInvalidBuAutoNumEnumValue) +{ + createSdImpressDoc("odp/invalidBuAutoNumEnumValue.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 4 + // - validation error in OOXML export: Errors: 4 + saveAndReload(TestFilter::PPTX); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testInvalidCxAndCyAttribute) +{ + createSdImpressDoc("odp/invalidCxAndCyAttribute.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 4 + // - validation error in OOXML export: Errors: 4 + saveAndReload(TestFilter::PPTX); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testBadPercentageValue) +{ + createSdImpressDoc("odp/badPercentageValue.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 4 + // - validation error in OOXML export: Errors: 4 + saveAndReload(TestFilter::PPTX); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testInvalidAudio) +{ + createSdImpressDoc("odp/invalidAudio.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 1 + // - validation error in OOXML export: Errors: 1 + saveAndReload(TestFilter::PPTX); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testInvalidPrstDashEnumValue) +{ + createSdImpressDoc("odp/invalidPrstDashEnumValue.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 113 + // - validation error in OOXML export: Errors: 113 + saveAndReload(TestFilter::PPTX); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testInvalidXmlBy) +{ + createSdImpressDoc("odp/invalidXmlBy.odp"); + + // Without the fix in place, this test would have failed with + // - Expected: 0 + // - Actual : 15 + // - validation error in OOXML export: Errors: 15 + saveAndReload(TestFilter::PPTX); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testConvertWithMasterDeduplication) +{ + createSdImpressDoc("odp/dupmastermultlayouts.odp"); + saveAndReload(TestFilter::PPTX); + + uno::Reference xNameAccess + = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), + maTempFile.GetURL()); + + // For each slide check that its layout exists + for (int i = 1; i <= 4; ++i) + { + xmlDocUniquePtr pXmlDocRels + = parseExport("ppt/slides/_rels/slide" + OUString::number(i) + ".xml.rels"); + + assertXPath( + pXmlDocRels, + "(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])"_ostr); + // the relative target e.g. "../slideLayouts/slideLayout2.xml" + OUString sRelativeLayoutPath = getXPathContent( + pXmlDocRels, + "(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])/@Target"_ostr); + + // Check that the referenced slideLayout files exist + // Without the accompanying fix in place, this test would have failed with: + // equality assertion failed + // - Expected: 1 + // - Actual : 0 + // i.e. the referenced slideLayout file was missing on export. + OUString sSlideLayoutName = sRelativeLayoutPath.getToken(2, '/'); + CPPUNIT_ASSERT_EQUAL(true, + bool(xNameAccess->hasByName("ppt/slideLayouts/" + sSlideLayoutName))); + } +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf162571HorzAnchor) +{ + createSdImpressDoc("pptx/tdf165261.pptx"); + saveAndReload(TestFilter::PPTX); + + uno::Reference xShape(getShapeFromPage(1, 0), uno::UNO_QUERY); + uno::Reference xProp(xShape, uno::UNO_QUERY); + + drawing::TextHorizontalAdjust eHori; + CPPUNIT_ASSERT(xProp->getPropertyValue(u"TextHorizontalAdjust"_ustr) >>= eHori); + CPPUNIT_ASSERT_EQUAL(drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT, eHori); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf165261HorzAnchor) +{ + createSdImpressDoc("pptx/tdf165261.pptx"); + saveAndReload(TestFilter::PPTX); + + uno::Reference xGroupShape(getShapeFromPage(0, 0), uno::UNO_QUERY); + uno::Reference xShape(xGroupShape->getByIndex(0), uno::UNO_QUERY); + + uno::Reference xProp(xShape, uno::UNO_QUERY); + drawing::TextHorizontalAdjust eHori; + CPPUNIT_ASSERT(xProp->getPropertyValue(u"TextHorizontalAdjust"_ustr) >>= eHori); + CPPUNIT_ASSERT_EQUAL(drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER, eHori); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testCustomPromptTexts) +{ + createSdImpressDoc("pptx/tdf163239.pptx"); + saveAndReload(TestFilter::PPTX); + + const SdrPage* pPage1 = GetPage(1); + { + // subtitle placeholder text + SdrTextObj* pTxtObj = DynCastSdrTextObj(pPage1->GetObj(0)); + CPPUNIT_ASSERT_MESSAGE("no text object", pTxtObj != nullptr); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong object type!", SdrObjKind::Text, + pTxtObj->GetObjIdentifier()); + const EditTextObject& aEdit = pTxtObj->GetOutlinerParaObject()->GetTextObject(); + OUString aText = aEdit.GetText(0); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text!", + u"Click to edit customized Master Subtitle style"_ustr, aText); + + auto xShapeProps(getShapeFromPage(0, 0)); + CPPUNIT_ASSERT(xShapeProps->getPropertyValue(u"CustomPromptText"_ustr) >>= aText); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text was set!", + u"Click to edit customized Master Subtitle style"_ustr, aText); + } + + { + SdrTextObj* pTxtObj = DynCastSdrTextObj(pPage1->GetObj(1)); + CPPUNIT_ASSERT_MESSAGE("no text object", pTxtObj != nullptr); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong object type!", SdrObjKind::TitleText, + pTxtObj->GetObjIdentifier()); + const EditTextObject& aEdit = pTxtObj->GetOutlinerParaObject()->GetTextObject(); + OUString aText = aEdit.GetText(0); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text!", u"Custom Title 1"_ustr, aText); + + auto xShapeProps(getShapeFromPage(1, 0)); + CPPUNIT_ASSERT(xShapeProps->getPropertyValue(u"CustomPromptText"_ustr) >>= aText); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text was set!", u"Custom Title 1"_ustr, + aText); + } + + const SdrPage* pPage2 = GetPage(3); + { + // body placeholder text + SdrTextObj* pTxtObj = DynCastSdrTextObj(pPage2->GetObj(0)); + CPPUNIT_ASSERT_MESSAGE("no text object", pTxtObj != nullptr); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong object type!", SdrObjKind::OutlineText, + pTxtObj->GetObjIdentifier()); + const EditTextObject& aEdit = pTxtObj->GetOutlinerParaObject()->GetTextObject(); + OUString aText = aEdit.GetText(0); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text!", u"Text placeholder"_ustr, aText); + + auto xShapeProps(getShapeFromPage(0, 1)); + CPPUNIT_ASSERT(xShapeProps->getPropertyValue(u"CustomPromptText"_ustr) >>= aText); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong placeholder text was set!", u"Text placeholder"_ustr, + aText); + } +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testPresObjTextStyles) +{ + createSdImpressDoc("pptx/tdf163239_v2.pptx"); + saveAndReload(TestFilter::PPTX); + + uno::Reference xStyleFamiliesSupplier(mxComponent, + uno::UNO_QUERY); + uno::Reference xStyleFamilies + = xStyleFamiliesSupplier->getStyleFamilies(); + // 1st slide + uno::Reference xStyleFamily( + xStyleFamilies->getByName(u"Title Slide"_ustr), uno::UNO_QUERY); + { + // Title style + uno::Reference xStyle(xStyleFamily->getByName(u"title"_ustr), + uno::UNO_QUERY); + uno::Reference xPropSet(xStyle, uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(60.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_CENTER), + xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, + xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); + } + // 2nd slide + xStyleFamily.set(xStyleFamilies->getByName(u"Title and Content"_ustr), uno::UNO_QUERY); + { + // Title style + uno::Reference xStyle(xStyleFamily->getByName(u"title"_ustr), + uno::UNO_QUERY); + uno::Reference xPropSet(xStyle, uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(44.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_LEFT), + xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, + xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); + + // outline1 style + xStyle.set(xStyleFamily->getByName(u"outline1"_ustr), uno::UNO_QUERY); + xPropSet.set(xStyle, uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(28.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_LEFT), + xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, + xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); + + // outline2 style + xStyle.set(xStyleFamily->getByName(u"outline2"_ustr), uno::UNO_QUERY); + xPropSet.set(xStyle, uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(24.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_LEFT), + xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, + xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); + + // outline5 style + xStyle.set(xStyleFamily->getByName(u"outline5"_ustr), uno::UNO_QUERY); + xPropSet.set(xStyle, uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(18.0f, xPropSet->getPropertyValue(u"CharHeight"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(sal_Int16(style::ParagraphAdjust_LEFT), + xPropSet->getPropertyValue(u"ParaAdjust"_ustr).get()); + CPPUNIT_ASSERT_EQUAL(u"Marianne"_ustr, + xPropSet->getPropertyValue(u"CharFontName"_ustr).get()); + } +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTextStylesXML) +{ + createSdImpressDoc("pptx/tdf163239_v2.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slideLayouts/slideLayout1.xml"_ustr); + + assertXPath(pXmlDocRels, "/p:sldLayout/p:cSld/p:spTree/p:sp[1]/p:txBody/a:lstStyle/a:lvl1pPr", + "algn", u"ctr"); + assertXPath(pXmlDocRels, + "/p:sldLayout/p:cSld/p:spTree/p:sp[1]/p:txBody/a:lstStyle/a:lvl1pPr/a:defRPr", "sz", + u"6000"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTextAlignLeft) +{ + createSdImpressDoc("pptx/presLeftAlign.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slides/slide1.xml"_ustr); + + assertXPath(pXmlDocRels, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p/a:pPr", "algn", u"l"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testSubtitleNoBullets) +{ + createSdImpressDoc("odp/tdf170166.odp"); + saveAndReload(TestFilter::PPTX); + + const SdrPage* pPage1 = GetPage(1); + { + // subtitle placeholder object + SdrTextObj* pTxtObj = DynCastSdrTextObj(pPage1->GetObj(0)); + CPPUNIT_ASSERT_MESSAGE("no text object", pTxtObj != nullptr); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong object type!", SdrObjKind::Text, + pTxtObj->GetObjIdentifier()); + const EditTextObject& aEdit = pTxtObj->GetOutlinerParaObject()->GetTextObject(); + const SvxNumBulletItem* pNumFmt = aEdit.GetParaAttribs(0).GetItem(EE_PARA_NUMBULLET); + // Without a fix, it will fail with numbering type: SVX_NUM_CHAR_SPECIAL + CPPUNIT_ASSERT(pNumFmt); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Bullet's numbering type is wrong!", SVX_NUM_NUMBER_NONE, + pNumFmt->GetNumRule().GetLevel(0).GetNumberingType()); + } +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testOLEObjectAnimationTarget) +{ + createSdImpressDoc("pptx/tdf169088.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); + + const OUString sOleId = getXPath( + pXmlDoc, "/p:sld/p:cSld/p:spTree/p:graphicFrame/p:nvGraphicFramePr/p:cNvPr", "id"); + const OUString sPicId = getXPath(pXmlDoc, + "/p:sld/p:cSld/p:spTree/p:graphicFrame/a:graphic/" + "a:graphicData/p:oleObj/p:pic/p:nvPicPr/p:cNvPr", + "id"); + + // Check OLE id is different from pic id + CPPUNIT_ASSERT_MESSAGE("OLE id is same as pic id", sOleId != sPicId); + + // Check animation target spid matches OLE object id + assertXPath(pXmlDoc, + "/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/" + "p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:set/" + "p:cBhvr/p:tgtEl/p:spTgt", + "spid", sOleId); + + assertXPath(pXmlDoc, + "/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/" + "p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:animEffect/" + "p:cBhvr/p:tgtEl/p:spTgt", + "spid", sOleId); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testtdf169496_hidden_graphic) +{ + createSdImpressDoc("pptx/tdf169496_hidden_graphic.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); + + // Graphic 5 is hidden and Graphic 4 is visible, but their order might change in the XML + // Without the fix the hidden attribute wasn't exported + OUString sName1 + = getXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[1]/p:nvPicPr/p:cNvPr", "name"); + OUString sName2 + = getXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[2]/p:nvPicPr/p:cNvPr", "name"); + if (sName1 == "Graphic 5" && sName2 == "Graphic 4") + { + OUString aHidden + = getXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[1]/p:nvPicPr/p:cNvPr", "hidden"); + bool bHidden = aHidden == u"true"_ustr || aHidden == u"1"; + CPPUNIT_ASSERT_EQUAL_MESSAGE("Graphic 3 is supposed to be hidden", true, bHidden); + + assertXPathNoAttribute(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[2]/p:nvPicPr/p:cNvPr", + "hidden"); + } + else if (sName1 == "Graphic 4" && sName2 == "Graphic 5") + { + assertXPathNoAttribute(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[1]/p:nvPicPr/p:cNvPr", + "hidden"); + + OUString aHidden + = getXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:pic[2]/p:nvPicPr/p:cNvPr", "hidden"); + bool bHidden = aHidden == u"true"_ustr || aHidden == u"1"; + CPPUNIT_ASSERT_EQUAL_MESSAGE("Graphic 4 is supposed to be hidden", true, bHidden); + } + else + CPPUNIT_FAIL("Names of graphics is incorrect"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testHiddenGroupShape) +{ + createSdImpressDoc("pptx/hidden_group_shape.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); + assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:grpSp/p:nvGrpSpPr/p:cNvPr", "hidden", u"1"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testtdf169825_vertical_layouts) +{ + createSdImpressDoc("pptx/tdf169825_vertical_layouts.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); + CPPUNIT_ASSERT(pXmlDocRels); + // find layout XML for the slide from the relationship file + OUString sLayoutRelRelative + = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); + OUString sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); + xmlDocUniquePtr pXmlDocLayout = parseExport(sLayoutRelAbs); + CPPUNIT_ASSERT(pXmlDocLayout); + + // without the SlidePersist::getLayoutFromValueToken() part of the patch, + // this and the next layout types would both be exported as blank + assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"vertTitleAndTx"); + + pXmlDocRels = parseExport(u"ppt/slides/_rels/slide2.xml.rels"_ustr); + CPPUNIT_ASSERT(pXmlDocRels); + sLayoutRelRelative = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); + sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); + pXmlDocLayout = parseExport(sLayoutRelAbs); + CPPUNIT_ASSERT(pXmlDocLayout); + + assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"vertTx"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testtdf169825_vertical_layouts_from_scratch) +{ + createSdImpressDoc(); + + uno::Reference xDoc(mxComponent, uno::UNO_QUERY_THROW); + uno::Reference xPages = xDoc->getDrawPages(); + uno::Reference xPage(xPages->getByIndex(0), uno::UNO_QUERY); + uno::Reference xPageSet(xPage, uno::UNO_QUERY_THROW); + xPageSet->setPropertyValue( + "Layout", + uno::Any(static_cast(AutoLayout::AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT))); + + uno::Reference xPage2(xPages->insertNewByIndex(1), uno::UNO_SET_THROW); + uno::Reference xPageSet2(xPage2, uno::UNO_QUERY_THROW); + xPageSet2->setPropertyValue( + "Layout", uno::Any(static_cast(AutoLayout::AUTOLAYOUT_VTITLE_VCONTENT))); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); + CPPUNIT_ASSERT(pXmlDocRels); + // find layout XML for the slide from the relationship file + OUString sLayoutRelRelative + = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); + OUString sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); + xmlDocUniquePtr pXmlDocLayout = parseExport(sLayoutRelAbs); + CPPUNIT_ASSERT(pXmlDocLayout); + + // without the fix in place this would be exported as "objTx" + assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"vertTitleAndTxOverChart"); + + pXmlDocRels = parseExport(u"ppt/slides/_rels/slide2.xml.rels"_ustr); + CPPUNIT_ASSERT(pXmlDocRels); + sLayoutRelRelative = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); + sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); + pXmlDocLayout = parseExport(sLayoutRelAbs); + CPPUNIT_ASSERT(pXmlDocLayout); + + // without the fix in place this would be exported as "picTx" + assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"vertTitleAndTx"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testtdf169825_layout_type) +{ + createSdImpressDoc("odp/tdf169825_layout_type.odp"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDocRels = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); + CPPUNIT_ASSERT(pXmlDocRels); + // find layout XML for the slide from the relationship file + OUString sLayoutRelRelative + = getXPath(pXmlDocRels, "/rels:Relationships/rels:Relationship", "Target"); + OUString sLayoutRelAbs = sLayoutRelRelative.replaceFirst("..", "ppt"); + xmlDocUniquePtr pXmlDocLayout = parseExport(sLayoutRelAbs); + CPPUNIT_ASSERT(pXmlDocLayout); + + // without the fix in place this would be exported as "vertTitleAndTxOverChart" + // while in the original ODP this was - still possibly bogus - notes layout + assertXPath(pXmlDocLayout, "/p:sldLayout", "type", u"blank"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testtdf170102_layout_type) +{ + createSdImpressDoc("odp/tdf170102_layout_type.odp"); + saveAndReload(TestFilter::PPTX); + + uno::Reference xDoc(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT(xDoc.is()); + sal_Int32 nMPCount = xDoc->getMasterPages()->getCount(); + // test roughly the same thing in document and in XML: no drawing object in master page/layout + for (sal_Int32 i = 0; i < nMPCount; i++) + { + uno::Reference xPage(xDoc->getMasterPages()->getByIndex(i), + uno::UNO_QUERY_THROW); + sal_Int32 nObjCount = xPage->getCount(); + for (sal_Int32 j = 0; j < nObjCount; j++) + { + uno::Reference xShapeInfo(xPage->getByIndex(j), + uno::UNO_QUERY_THROW); + // without the fix in place there would be a graphic shape placeholder + CPPUNIT_ASSERT_MESSAGE( + "Unexpected graphic object shape in exported master page", + !xShapeInfo->supportsService(u"com.sun.star.drawing.GraphicObjectShape"_ustr)); + } + + xmlDocUniquePtr pXmlDocLayout = parseExport(u"ppt/slideLayouts/slideLayout"_ustr + + OUString::number(i + 1) + u".xml"_ustr); + CPPUNIT_ASSERT(pXmlDocLayout); + // without the fix in place there would be a pic layout element for the placeholder + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Unexpected \"pic\" element in exported PPTX slide layout", 0, + countXPathNodes(pXmlDocLayout, "/p:sldLayout/p:cSld/p:spTree/p:pic")); + } +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testFooterIdxConsistency) +{ + createSdImpressDoc("pptx/multiplelayoutfooter.pptx"); + save(TestFilter::PPTX); + + // slide1 points to slideLayout2 + xmlDocUniquePtr pRelsDoc = parseExport(u"ppt/slides/_rels/slide1.xml.rels"_ustr); + assertXPath(pRelsDoc, + "/rels:Relationships/rels:Relationship[@Type='http://" + "schemas.openxmlformats.org/officeDocument/2006/" + "relationships/slideLayout']", + "Target", u"../slideLayouts/slideLayout2.xml"); + + // And they agree on the idx of the footer: + xmlDocUniquePtr pSlide = parseExport(u"ppt/slides/slide1.xml"_ustr); + OUString aFtrIdx = getXPath(pSlide, "//p:sp/p:nvSpPr/p:nvPr/p:ph", "idx"); + xmlDocUniquePtr pLayout = parseExport(u"ppt/slideLayouts/slideLayout2.xml"_ustr); + assertXPath(pLayout, "//p:sp/p:nvSpPr/p:nvPr/p:ph", "idx", aFtrIdx); + // Without the fix it fails with: + // - Expected: 1 + // - Actual : 2 + // - In <>, attribute 'idx' of '//p:sp/p:nvSpPr/p:nvPr/p:ph' incorrect value. +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testOmitCanvasSlideExport) +{ + createSdImpressDoc("odp/canvas-slide.odp"); + + SdXImpressDocument* pXImpressDocument = dynamic_cast(mxComponent.get()); + CPPUNIT_ASSERT(pXImpressDocument); + SdDrawDocument* pDoc = pXImpressDocument->GetDoc(); + CPPUNIT_ASSERT_MESSAGE("no document", pDoc != nullptr); + + // the document has 2 pages - one canvas page, and one normal page + CPPUNIT_ASSERT_EQUAL(sal_uInt16(2), pDoc->GetSdPageCount(PageKind::Standard)); + CPPUNIT_ASSERT(pDoc->HasCanvasPage()); + + save(TestFilter::PPTX); + + // Verify that the canvas slide was omitted from the export + // It should have one master slide, and one slide + xmlDocUniquePtr pXmlDocContent = parseExport(u"ppt/presentation.xml"_ustr); + assertXPath(pXmlDocContent, "/p:presentation/p:sldMasterIdLst/p:sldMasterId", 1); + assertXPath(pXmlDocContent, "/p:presentation/p:sldIdLst/p:sldId", 1); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testParaAlignStartEnd) +{ + createSdImpressDoc("odp/para-align-start-end.fodp"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); + + // PresentationML doesn't support start and end paragraph alignment. + // Paragraphs should be exported as left/right alignment, depending on para direction. + assertXPath(pDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p[1]/a:pPr[@algn='l']", 1); + assertXPath(pDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p[2]/a:pPr[@algn='r']", 1); + assertXPath(pDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p[3]/a:pPr[@algn='r']", 1); + assertXPath(pDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:p[4]/a:pPr[@algn='l']", 1); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf169559) +{ + createSdImpressDoc("pptx/tdf169524.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); + assertXPath( + pXmlDoc, + "/p:sldMaster/p:cSld/p:spTree/p:sp[2]/p:txBody/a:lstStyle/a:lvl1pPr/a:spcAft/a:spcPts", + "val", u"1701"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf114443_PPSX) +{ + createSdImpressDoc("ppsx/tdf114443-6.ppsx"); + save(TestFilter::PPSX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"[Content_Types].xml"_ustr); + + // Without the fix, this would have been: + // application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml + // which means the file loses its slideshow/autoplay nature and PowerPoint rejects it. + assertXPath(pXmlDoc, + "/ContentType:Types/ContentType:Override[@PartName='/ppt/presentation.xml']", + "ContentType", + u"application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testConnectorShapeAnimationTarget) +{ + createSdImpressDoc("pptx/connector-shape-animations.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); + + const OUString sConnectorId = getXPath( + pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp/p:nvCxnSpPr/p:cNvCxnSpPr/a:stCxn", "id"); + + const OUString sShapeId = getXPath( + pXmlDoc, "/p:sld/p:cSld/p:spTree/p:graphicFrame/p:nvGraphicFramePr/p:cNvPr", "id"); + + // Check connector and shape both have the same ID + CPPUNIT_ASSERT_EQUAL_MESSAGE("Connector and the shape doesn't have the same ID", sConnectorId, + sShapeId); + + // Check animation spid matches ShapeId + assertXPath(pXmlDoc, + "/p:sld/p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/" + "p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par[2]/p:cTn/p:childTnLst/p:set/" + "p:cBhvr/p:tgtEl/p:spTgt", + "spid", sShapeId); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testChartExExport) +{ + createSdImpressDoc("pptx/forum-mso-de-138303.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pDoc = parseExport(u"ppt/slides/slide1.xml"_ustr); + CPPUNIT_ASSERT(pDoc); + + const OString sPath1 = "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Fallback"_ostr; + assertXPath(pDoc, sPath1 + "/xdr:sp"_ostr, 0); + assertXPath(pDoc, sPath1 + "/p:sp"_ostr, 1); + assertXPath(pDoc, sPath1 + "/p:sp/p:nvSpPr/p:nvPr"_ostr, 1); + + pDoc = parseExport(u"ppt/charts/chartEx1.xml"_ustr); + CPPUNIT_ASSERT(pDoc); + + assertXPath(pDoc, "/cx:chartSpace/c:date1904", 0); +} + +// 1. Open PPTX with sections, re-export to PPTX, verify sections preserved +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testSlideSectionsPPTXImport) +{ + createSdImpressDoc("pptx/slide-section-test.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/presentation.xml"_ustr); + + static constexpr OString sPath = "/p:presentation/p:extLst/p:ext/p14:sectionLst"_ostr; + assertXPath(pXmlDoc, sPath + "/p14:section", 3); + assertXPath(pXmlDoc, sPath + "/p14:section[1]", "name", u"Section-1"); + assertXPath(pXmlDoc, sPath + "/p14:section[2]", "name", u"Section-2"); + assertXPath(pXmlDoc, sPath + "/p14:section[3]", "name", u"Section-3"); + assertXPath(pXmlDoc, sPath + "/p14:section[1]/p14:sldIdLst/p14:sldId", 4); + assertXPath(pXmlDoc, sPath + "/p14:section[2]/p14:sldIdLst/p14:sldId", 7); + assertXPath(pXmlDoc, sPath + "/p14:section[3]/p14:sldIdLst/p14:sldId", 2); + + // GUIDs preserved + for (sal_Int32 i = 1; i <= 3; ++i) + { + OUString sId = getXPath(pXmlDoc, sPath + "/p14:section[" + OString::number(i) + "]", "id"); + CPPUNIT_ASSERT(!sId.isEmpty()); + CPPUNIT_ASSERT(sId.startsWith("{")); + } + + // Slide IDs non-zero + OUString sSldId = getXPath(pXmlDoc, sPath + "/p14:section[1]/p14:sldIdLst/p14:sldId[1]", "id"); + CPPUNIT_ASSERT(sSldId.toInt32() > 0); +} + +// 2. Export PPTX with sections to ODP +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testSlideSectionsPPTXToODP) +{ + createSdImpressDoc("pptx/slide-section-test.pptx"); + skipValidation(); + save(TestFilter::ODP); + + xmlDocUniquePtr pXmlDoc = parseExport(u"content.xml"_ustr); + + static constexpr OString sPath + = "/office:document-content/office:body/office:presentation/loext:section-list"_ostr; + assertXPath(pXmlDoc, sPath + "/loext:section", 3); + assertXPath(pXmlDoc, sPath + "/loext:section[1]", "name", u"Section-1"); + assertXPath(pXmlDoc, sPath + "/loext:section[2]", "name", u"Section-2"); + assertXPath(pXmlDoc, sPath + "/loext:section[3]", "name", u"Section-3"); + assertXPath(pXmlDoc, sPath + "/loext:section[1]/loext:section-slide", 4); + assertXPath(pXmlDoc, sPath + "/loext:section[2]/loext:section-slide", 7); + assertXPath(pXmlDoc, sPath + "/loext:section[3]/loext:section-slide", 2); +} + +// 3. Open ODP with sections (PPTX -> ODP -> reload ODP) +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testSlideSectionsODPImport) +{ + createSdImpressDoc("pptx/slide-section-test.pptx"); + skipValidation(); + saveAndReload(TestFilter::ODP); + + // Verify the in-memory model loaded from ODP + auto* pXImpressDocument = dynamic_cast(mxComponent.get()); + CPPUNIT_ASSERT(pXImpressDocument); + SdDrawDocument* pDoc = pXImpressDocument->GetDoc(); + CPPUNIT_ASSERT(pDoc); + + sd::SlideSectionManager& rMgr = pDoc->GetSectionManager(); + CPPUNIT_ASSERT_EQUAL(sal_Int32(3), rMgr.GetSectionCount()); + CPPUNIT_ASSERT_EQUAL(u"Section-1"_ustr, rMgr.GetSection(0).maName); + CPPUNIT_ASSERT_EQUAL(u"Section-2"_ustr, rMgr.GetSection(1).maName); + CPPUNIT_ASSERT_EQUAL(u"Section-3"_ustr, rMgr.GetSection(2).maName); +} + +// 4. Export ODP with sections to PPTX +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testSlideSectionsODPToPPTX) +{ + createSdImpressDoc("pptx/slide-section-test.pptx"); + skipValidation(); + saveAndReload(TestFilter::ODP); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/presentation.xml"_ustr); + + static constexpr OString sPath = "/p:presentation/p:extLst/p:ext/p14:sectionLst"_ostr; + assertXPath(pXmlDoc, sPath + "/p14:section", 3); + assertXPath(pXmlDoc, sPath + "/p14:section[1]", "name", u"Section-1"); + assertXPath(pXmlDoc, sPath + "/p14:section[2]", "name", u"Section-2"); + assertXPath(pXmlDoc, sPath + "/p14:section[3]", "name", u"Section-3"); + assertXPath(pXmlDoc, sPath + "/p14:section[1]/p14:sldIdLst/p14:sldId", 4); + assertXPath(pXmlDoc, sPath + "/p14:section[2]/p14:sldIdLst/p14:sldId", 7); + assertXPath(pXmlDoc, sPath + "/p14:section[3]/p14:sldIdLst/p14:sldId", 2); + + // Slide IDs non-zero after ODP round-trip + OUString sSldId = getXPath(pXmlDoc, sPath + "/p14:section[1]/p14:sldIdLst/p14:sldId[1]", "id"); + CPPUNIT_ASSERT(sSldId.toInt32() > 0); + + // GUIDs survive ODP round-trip (generated if missing) + for (sal_Int32 i = 1; i <= 3; ++i) + { + OUString sId = getXPath(pXmlDoc, sPath + "/p14:section[" + OString::number(i) + "]", "id"); + CPPUNIT_ASSERT(!sId.isEmpty()); + } +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testCool16078_placeholderKeepsItsOutline) +{ + // Given a layout whose picture placeholder is clipped to an outline of two elements: + // a frame with two holes, one of them round, and a second path holding a triangle that crosses + // the round one: + createSdImpressDoc("pptx/custgeom-placeholder.pptx"); + save(TestFilter::PPTX); + + // The outline is written back as the placeholder's own geometry. Without it the placeholder + // saved as a plain rectangle, and its fill then hid both holes and what showed through. + xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Picture placeholder, clipped outline"); + OString aPath + = "//p:sp[p:nvSpPr/p:nvPr/p:ph/@type='pic']/p:spPr/a:custGeom/a:pathLst/a:path"_ostr; + assertXPath(pLayout, aPath, 1); + // Three contours in one path, which is what subtracts the holes from the frame - the same three + // the ODF side writes. The second path is not one of them: it is filled rather than cut out, so + // the triangle it holds took a piece of the round hole back instead of adding a contour. + assertXPath(pLayout, aPath + "/a:moveTo", 3); + assertXPath(pLayout, aPath + "/a:close", 3); + // Four sides each for the two rectangles, the closing one included. + assertXPath(pLayout, aPath + "/a:lnTo", 8); + // What is left of the round hole is still curves, not a polygon flattened out of them. + assertXPath(pLayout, aPath + "/a:cubicBezTo", 6); + assertXPath(pLayout, aPath + "/a:cubicBezTo[1]/a:pt", 3); + // The frame's own corner, in the path's coordinates. + assertXPath(pLayout, aPath + "/a:moveTo[1]/a:pt", "x", u"0"); + assertXPath(pLayout, aPath + "/a:moveTo[1]/a:pt", "y", u"0"); + + // The preset rectangle it used to write would have claimed the whole frame. + assertXPath(pLayout, "//p:sp[p:nvSpPr/p:nvPr/p:ph/@type='pic']/p:spPr/a:prstGeom", 0); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testCool16080_masterKeepsItsPlaceholders) +{ + // Given a deck of one master and eleven layouts, so one Impress master page stands for the + // whole group: + createSdImpressDoc("pptx/master-and-eleven-layouts.pptx"); + save(TestFilter::PPTX); + + // The master part carries its own placeholders. Without the fix it held a shape tree with + // nothing in it, so PowerPoint offered an empty slide master: nothing to inherit from, and + // an edit meant for every layout had to be repeated on each of them. + xmlDocUniquePtr pMaster = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); + static constexpr OString aTree("/p:sldMaster/p:cSld/p:spTree"_ostr); + assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='title']", 1); + assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='dt']", 1); + assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='ftr']", 1); + assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='sldNum']", 1); + + // The body comes from the first layout that has one, since the page standing for the group is + // the Title Slide layout and has none. That layout inherits the master's geometry, so the + // placeholder lands where the master put it rather than where a layout would. + assertXPath(pMaster, aTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='body']", 1); + assertXPath(pMaster, aTree + "/p:sp[p:nvSpPr/p:nvPr/p:ph/@type='body']/p:spPr/a:xfrm/a:off", + "y", u"1825560"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testCool16080_layoutShapesStayOnTheirLayout) +{ + // Given a deck whose first layout paints the whole slide blue, and a slide on another layout, + // which is therefore white: + createSdImpressDoc("pptx/layout-own-background.pptx"); + saveAndReload(TestFilter::PPTX); + + utl::TempFileNamed aPng; + aPng.EnableKillingFile(); + uno::Sequence aFilterData{ + comphelper::makePropertyValue(u"PixelWidth"_ustr, sal_Int32(64)), + comphelper::makePropertyValue(u"PixelHeight"_ustr, sal_Int32(36)) + }; + uno::Sequence aDescriptor{ + comphelper::makePropertyValue(u"URL"_ustr, aPng.GetURL()), + comphelper::makePropertyValue(u"FilterName"_ustr, u"PNG"_ustr), + comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) + }; + auto xExporter = drawing::GraphicExportFilter::create(getComponentContext()); + xExporter->setSourceDocument(getPage(0).queryThrow()); + xExporter->filter(aDescriptor); + + SvFileStream aStream(aPng.GetURL(), StreamMode::READ); + Bitmap aSlide = vcl::PngImageReader(aStream).read(); + CPPUNIT_ASSERT_EQUAL(Size(64, 36), aSlide.GetSizePixel()); + + // The slide is still white. An imported master and layout collapse onto one Impress master + // page, so the page standing for the group holds the first layout's rectangle - and writing + // that page's shapes into the master part painted it under every slide of the deck. Sample + // the side margins, which no placeholder of this layout reaches. + // Without the fix in place, this test would have failed with + // - Expected: rgba[ffffffff] + // - Actual : rgba[0000ffff] + CPPUNIT_ASSERT_EQUAL(COL_WHITE, aSlide.GetPixelColor(2, 18)); + CPPUNIT_ASSERT_EQUAL(COL_WHITE, aSlide.GetPixelColor(61, 18)); + + // The layout it belongs to still paints it, which is what a slide of that layout inherits. + xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Title Slide"); + assertXPath(pLayout, + "/p:sldLayout/p:cSld/p:spTree/p:sp[p:nvSpPr/p:cNvPr/@name='Blue background']" + "/p:spPr/a:solidFill/a:srgbClr", + "val", u"0000FF"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testCool16080_masterKeepsWhatItsLayoutsShare) +{ + // Given a deck of one master that draws a rule of its own across the top of the slide, in + // segments of colour, and seven layouts that draw nothing: + createSdImpressDoc("pptx/tdf157740.pptx"); + saveAndReload(TestFilter::PPTX); + + // The rule is on the slide master, so PowerPoint edits it once for every layout. The import + // gives each of the seven Impress master pages a copy, and the master used to get none. + // Without the fix in place, this test would have failed with + // - Expected: 1 + // - Actual : 0 + xmlDocUniquePtr pMaster = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); + assertXPath(pMaster, + "/p:sldMaster/p:cSld/p:spTree/p:grpSp[p:nvGrpSpPr/p:cNvPr/@name='Group 1']", 1); + + // It moved there rather than being written seven more times: a layout holds the three + // placeholders it inherits and nothing else. + xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Title, Content over Content"); + static constexpr OString aLayoutTree("/p:sldLayout/p:cSld/p:spTree"_ostr); + assertXPath(pLayout, aLayoutTree + "/*[not(self::p:nvGrpSpPr) and not(self::p:grpSpPr)]", 3); + assertXPath(pLayout, aLayoutTree + "/p:sp/p:nvSpPr/p:nvPr/p:ph", 3); + + // And the slide still shows the rule, wherever it is written + utl::TempFileNamed aPng; + aPng.EnableKillingFile(); + uno::Sequence aFilterData{ + comphelper::makePropertyValue(u"PixelWidth"_ustr, sal_Int32(1920)), + comphelper::makePropertyValue(u"PixelHeight"_ustr, sal_Int32(1080)) + }; + uno::Sequence aDescriptor{ + comphelper::makePropertyValue(u"URL"_ustr, aPng.GetURL()), + comphelper::makePropertyValue(u"FilterName"_ustr, u"PNG"_ustr), + comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) + }; + auto xExporter = drawing::GraphicExportFilter::create(getComponentContext()); + xExporter->setSourceDocument(getPage(0).queryThrow()); + xExporter->filter(aDescriptor); + + SvFileStream aStream(aPng.GetURL(), StreamMode::READ); + Bitmap aSlide = vcl::PngImageReader(aStream).read(); + CPPUNIT_ASSERT_EQUAL(Size(1920, 1080), aSlide.GetSizePixel()); + // 90EBCD is accent5 of the deck's theme, the fill of the rule's second segment + CPPUNIT_ASSERT_EQUAL(Color(0x90EBCD), aSlide.GetPixelColor(200, 5)); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testCool16082_layoutKeepsSubtitle) +{ + // Given a deck whose Title Slide layout carries a subtitle placeholder: + createSdImpressDoc("pptx/master-and-eleven-layouts.pptx"); + save(TestFilter::PPTX); + + // The layout keeps it. Without the fix every layout of a master holding more than one lost its + // subtitle, so a slide made from the layout afterwards had nowhere to put one. + xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Title Slide"); + assertXPath(pLayout, "/p:sldLayout/p:cSld/p:spTree/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='subTitle']", + 1); + + // The slide master still gets none, which is what PowerPoint refuses the file over. + xmlDocUniquePtr pMaster = parseExport(u"ppt/slideMasters/slideMaster1.xml"_ustr); + assertXPath(pMaster, "/p:sldMaster/p:cSld/p:spTree/p:sp/p:nvSpPr/p:nvPr/p:ph[@type='subTitle']", + 0); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testCool16079_dateTimeField) +{ + // Given a deck whose date placeholders hold an automatically updated date: + createSdImpressDoc("pptx/master-and-eleven-layouts.pptx"); + save(TestFilter::PPTX); + + // The placeholder keeps a field. Without the fix it held our own display string as literal + // text, so PowerPoint showed "" where the original showed a date. The type is the + // one that leaves the format to the reader, which is what a field holding none asks for - the + // alias PowerPoint writes here stands for a fixed MM/DD/YYYY. + xmlDocUniquePtr pLayout = parseExportedLayoutNamed(u"Title Slide"); + assertXPath(pLayout, "//p:sp[p:nvSpPr/p:nvPr/p:ph/@type='dt']/p:txBody/a:p/a:fld", "type", + u"datetime"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testThemeKeepColorTransforms) +{ + createSdImpressDoc("pptx/empty-title-placeholder.pptx"); + save(TestFilter::PPTX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"ppt/theme/theme1.xml"_ustr); + const OString sBasePath = "/a:theme/a:themeElements/a:fmtScheme/a:bgFillStyleLst/a:gradFill[1]" + "/a:gsLst/a:gs[@pos='40000']/a:schemeClr"_ostr; + + // Without the fix satMod would end up negative + assertXPath(pXmlDoc, sBasePath, "val", u"phClr"); + assertXPathChildren(pXmlDoc, sBasePath, 3); + assertXPath(pXmlDoc, sBasePath + "/a:tint", "val", u"45000"); + assertXPath(pXmlDoc, sBasePath + "/a:shade", "val", u"99000"); + assertXPath(pXmlDoc, sBasePath + "/a:satMod", "val", u"350000"); +} + +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest5, testTdf166401_textGivenToAPicturePlaceholder) +{ + // Given a slide whose picture placeholder is given text without any editing, which is what a + // script does and what leaves the placeholder standing for itself rather than for the text: + createSdImpressDoc("pptx/picture-placeholder-custom-prompt.pptx"); + getShapeFromPage(0, 0).queryThrow()->setString( + u"Given to a picture placeholder"_ustr); + save(TestFilter::PPTX); + + // The placeholder is written with its text. It holds no image, so writing it as a picture wrote + // nothing at all and the text went with the shape. + xmlDocUniquePtr pSlide = parseExport(u"ppt/slides/slide1.xml"_ustr); + static constexpr OString aPlaceholder( + "/p:sld/p:cSld/p:spTree/p:sp[p:nvSpPr/p:nvPr/p:ph/@type='pic']"_ostr); + assertXPath(pSlide, aPlaceholder, 1); + assertXPathContent(pSlide, aPlaceholder + "/p:txBody/a:p/a:r/a:t", + u"Given to a picture placeholder"); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */