cool#16080: write the body the slide master holds

The master part was written from the page standing for the group of
equivalent masters, so it carried that layout's placeholders and
nothing else - for a deck whose first layout is a title slide, no body
at all, and PowerPoint offered no Master text styles to edit. Take a
type a master may hold that this page has none of from the first page
of the group that has it.

A layout placeholder was imported with what it inherits from the
master, so the first layout with a body carries the master's own
geometry, which is where PowerPoint puts the box. A deck whose first
body-bearing layout states a geometry of its own hands that over
instead.

testTdf106867 moved its expected spid from 65 to 66: the master part
writes one shape more, and shape ids are document-wide. It still names
the p:pic that holds the video.

Underneath is a document model difference: PowerPoint has masters that carry
layouts, Impress has masters alone, so the import maps each layout to a master
of its own and the export reconstructs both levels from them.

Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I1e6d311e5f20cd3377ce4c0c3f2dfa59ff4dfefb
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/9196
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
(cherry picked from commit 73dfd24f01a6fdf5fbd7c0d48f17c027442fb870)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210234
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Mike Kaganski
2026-08-29 13:48:05 +02:00
committed by Xisco Fauli
parent f829102cad
commit 9b3b1373b2
4 changed files with 50 additions and 4 deletions
+1 -1
View File
@@ -1325,7 +1325,7 @@ CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest2, testTdf106867)
"/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:cmd/"
"p:cBhvr/p:tgtEl/p:spTgt",
"spid", u"65");
"spid", u"66");
}
CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest2, testTdf112280)
+7
View File
@@ -2466,6 +2466,13 @@ CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testCool16080_masterKeepsItsPlaceholder
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, testCool16079_dateTimeField)
+3 -1
View File
@@ -113,7 +113,7 @@ private:
// shapes
void WriteShapeTree( const ::sax_fastparser::FSHelperPtr& pFS, PageType ePageType, bool bMaster,
bool bSlideMasterPart = false );
bool bSlideMasterPart = false, sal_uInt32 nMasterNum = 0 );
sal_uInt32 GetNewSlideId() { return mnSlideIdMax ++; }
sal_uInt32 GetNewSlideMasterId() { return mnSlideMasterIdMax ++; }
@@ -139,6 +139,8 @@ private:
void WritePlaceholderReferenceShapes(PowerPointShapeExport& rDML, PageType ePageType);
/** Writes the master page's content placeholders into the layout they belong on */
void WriteLayoutContentPlaceholders(PowerPointShapeExport& rDML);
/** Writes the placeholders a slide master holds that the page standing for it has none of */
void WriteMasterOwnPlaceholders(PowerPointShapeExport& rDML, sal_uInt32 nMasterNum);
void FindEquivalentMasterPages();
sal_uInt32 GetEquivalentMasterPage(sal_uInt32 nMasterPage);
+39 -2
View File
@@ -2208,7 +2208,7 @@ void PowerPointExport::ImplWriteSlideMaster(sal_uInt32 nPageNum, Reference< XPro
// every layout.
if (aXBackgroundPropSet)
ImplWriteBackground(pFS, aXBackgroundPropSet);
WriteShapeTree(pFS, MASTER, true, /*bSlideMasterPart=*/true);
WriteShapeTree(pFS, MASTER, true, /*bSlideMasterPart=*/true, nPageNum);
pFS->endElementNS(XML_p, XML_cSld);
@@ -2588,7 +2588,7 @@ void PowerPointExport::ImplWritePPTXLayoutWithContent(
}
void PowerPointExport::WriteShapeTree(const FSHelperPtr& pFS, PageType ePageType, bool bMaster,
bool bSlideMasterPart)
bool bSlideMasterPart, sal_uInt32 nMasterNum)
{
PowerPointShapeExport aDML(pFS, &maShapeMap, this);
aDML.SetMaster(bMaster);
@@ -2650,6 +2650,8 @@ void PowerPointExport::WriteShapeTree(const FSHelperPtr& pFS, PageType ePageType
WritePlaceholderReferenceShapes(aDML, ePageType);
if ( ePageType == LAYOUT )
WriteLayoutContentPlaceholders(aDML);
if (bSlideMasterPart)
WriteMasterOwnPlaceholders(aDML, nMasterNum);
pFS->endElementNS(XML_p, XML_spTree);
}
@@ -3282,6 +3284,41 @@ void PowerPointExport::WriteLayoutContentPlaceholders(PowerPointShapeExport& rDM
}
}
void PowerPointExport::WriteMasterOwnPlaceholders(PowerPointShapeExport& rDML,
sal_uInt32 nMasterNum)
{
SdPage* pPage = SdPage::getImplementation(mXDrawPage);
if (!pPage)
return;
for (const auto& [ePlaceholder, ePresObjKind] :
{ std::pair(Title, PresObjKind::Title), std::pair(Outliner, PresObjKind::Outline) })
{
assert(isPlaceholderAllowedOnSlideMaster(ePlaceholder));
if (pPage->GetPresObj(ePresObjKind))
continue; // the shape tree has written the page's own
// The pages standing for one slide master are its layouts, and a layout placeholder was
// imported with the geometry and the formatting it inherits from the master, so the first
// of them to carry this type carries the master's prototype of it. A layout that states
// its own geometry gives that instead - the closest thing left to ask.
for (sal_uInt32 i = 0; i < mnMasterPages; ++i)
{
if (i == mnCanvasMasterIndex
|| (i != nMasterNum && maEquivalentMasters[i] != nMasterNum))
continue;
SdPage* pOther = dynamic_cast<SdPage*>(maMastersLayouts[i].first);
SdrObject* pObj = pOther ? pOther->GetPresObj(ePresObjKind) : nullptr;
if (!pObj)
continue;
rDML.WritePlaceholderShape(GetXShapeForSdrObject(pObj), ePlaceholder);
break;
}
}
}
sal_Int32 PowerPointExport::GetOrCreatePlaceholderIndex(const css::uno::Reference<XShape> &rXShape)
{
// One index per shape, however many parts write it: a slide's placeholder pairs with the