tdf#166401: keep a placeholder's prompt in ODF

Commit 67ba6ccd5a (tdf#140912, tdf#159219:
fix import of graphic placeholder with custom prompt, 2024-01-17) left
the prompt out of ODF for want of a representation, so a reload replaced
an authored one with our own localized default. It now travels as a
coext:placeholder-prompt attribute beside presentation:placeholder, on
draw:frame and draw:page-thumbnail, written when saving with extensions
and added to the extended schema so the round trip validates. It is
also listed in the Collabora ODF extensions notes.

Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I12b51ff2bc103e5c26e94558802064919c7289dd
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/9070
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 6b1b6331ada0cc948e880eb83db5506f8bf1aa9c)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210297
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Mike Kaganski
2026-08-31 23:49:35 +02:00
committed by Xisco Fauli
parent 6a4ef93a54
commit 64fe896867
8 changed files with 67 additions and 0 deletions
+1
View File
@@ -1566,6 +1566,7 @@ namespace xmloff::token {
XML_PHYSICS_ANIMATION_BOUNCINESS,
XML_PIE_OFFSET,
XML_PLACEHOLDER,
XML_PLACEHOLDER_PROMPT,
XML_PLACEHOLDER_TYPE,
XML_PLACING,
XML_PLAIN_NUMBER,
@@ -2469,6 +2469,14 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.
</rng:attribute>
</rng:optional>
</rng:define>
<!-- The prompt an empty placeholder shows, when the document authors one of its own. -->
<rng:define name="presentation-shape-attlist" combine="interleave">
<rng:optional>
<rng:attribute name="loext:placeholder-prompt">
<rng:ref name="string"/>
</rng:attribute>
</rng:optional>
</rng:define>
<rng:define name="loext-columns-and-groups">
<rng:oneOrMore>
<rng:choice>
+33
View File
@@ -353,6 +353,39 @@ CPPUNIT_TEST_FIXTURE(SdExportTest2, testAnimationSoundExternalImportPPTX)
CPPUNIT_ASSERT(xmloff::getSoundAllowed(xAudio->getSource()));
}
CPPUNIT_TEST_FIXTURE(SdExportTest2, testPlaceholderPromptODFRoundTrip)
{
// Given one layout whose picture placeholder authored a prompt, and another whose authored
// none - a slide layout arrives as a master page, so that is where to look for them:
createSdImpressDoc("pptx/picture-placeholder-custom-prompt.pptx");
saveAndReload(TestFilter::ODP);
auto xMPs = mxComponent.queryThrow<drawing::XMasterPagesSupplier>()->getMasterPages();
OUString aPrompt;
sal_Int32 nPrompts = 0;
for (sal_Int32 nPage = 0; nPage < xMPs->getCount(); ++nPage)
{
auto xShapes = xMPs->getByIndex(nPage).queryThrow<drawing::XShapes>();
for (sal_Int32 nShape = 0; nShape < xShapes->getCount(); ++nShape)
{
auto xProps = xShapes->getByIndex(nShape).queryThrow<beans::XPropertySet>();
OUString aShapePrompt;
if (xProps->getPropertySetInfo()->hasPropertyByName(u"CustomPromptText"_ustr)
&& (xProps->getPropertyValue(u"CustomPromptText"_ustr) >>= aShapePrompt)
&& !aShapePrompt.isEmpty())
{
aPrompt = aShapePrompt;
++nPrompts;
}
}
}
// The authored prompt survives ODF, which has no representation of its own for one. Without
// the accompanying fix nothing carried it, and the reload showed a localized default instead.
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), nPrompts);
CPPUNIT_ASSERT_EQUAL(u"Custom prompt to insert an image"_ustr, aPrompt);
}
CPPUNIT_TEST_FIXTURE(SdExportTest2, testAnimationSoundEmbeddedImportPPTX)
{
// A PPTX animation sound embedded in the package becomes package content,
+1
View File
@@ -1578,6 +1578,7 @@ namespace xmloff::token {
TOKEN( "bounciness", XML_PHYSICS_ANIMATION_BOUNCINESS ),
TOKEN( "pie-offset", XML_PIE_OFFSET ),
TOKEN( "placeholder", XML_PLACEHOLDER ),
TOKEN( "placeholder-prompt", XML_PLACEHOLDER_PROMPT ),
TOKEN( "placeholder-type", XML_PLACEHOLDER_TYPE ),
TOKEN( "placing", XML_PLACING ),
TOKEN( "plain-number", XML_PLAIN_NUMBER ),
+11
View File
@@ -1735,6 +1735,17 @@ bool XMLShapeExport::ImpExportPresentationAttributes( const uno::Reference< bean
if(!bTemp)
mrExport.AddAttribute(XML_NAMESPACE_PRESENTATION, XML_USER_TRANSFORMED, XML_TRUE);
}
// ODF has no representation for the prompt an empty placeholder shows, so an authored one
// travels as an extension; otherwise a reload replaces it with our own localized default.
if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(u"CustomPromptText"_ustr)
&& (mrExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED))
{
OUString aPrompt;
xPropSet->getPropertyValue(u"CustomPromptText"_ustr) >>= aPrompt;
if (!aPrompt.isEmpty())
mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_PLACEHOLDER_PROMPT, aPrompt);
}
}
return bIsEmpty;
+11
View File
@@ -355,6 +355,14 @@ void SdXMLShapeContext::endFastElement(sal_Int32 )
GetImport().GetTextImport()->PopListContext();
}
// Applied once the text is in, because setting it is what makes an empty placeholder show it.
if (!maPlaceholderPrompt.isEmpty())
{
auto xProp = mxShape.query<beans::XPropertySet>();
if (xProp && xProp->getPropertySetInfo()->hasPropertyByName(u"CustomPromptText"_ustr))
xProp->setPropertyValue(u"CustomPromptText"_ustr, uno::Any(maPlaceholderPrompt));
}
if( !msHyperlink.isEmpty() ) try
{
uno::Reference< beans::XPropertySet > xProp( mxShape, uno::UNO_QUERY );
@@ -853,6 +861,9 @@ bool SdXMLShapeContext::processAttribute( const sax_fastparser::FastAttributeLis
case XML_ELEMENT(PRESENTATION, XML_CLASS):
maPresentationClass = aIter.toString();
break;
case XML_ELEMENT(LO_EXT, XML_PLACEHOLDER_PROMPT):
maPlaceholderPrompt = aIter.toString();
break;
case XML_ELEMENT(PRESENTATION, XML_STYLE_NAME):
maDrawStyleName = aIter.toString();
mnStyleFamily = XmlStyleFamily::SD_PRESENTATION_ID;
+1
View File
@@ -55,6 +55,7 @@ protected:
OUString maDrawStyleName;
OUString maTextStyleName;
OUString maPresentationClass;
OUString maPlaceholderPrompt;
OUString maShapeName;
OUString maThumbnailURL;
+1
View File
@@ -1477,6 +1477,7 @@ density
bounciness
pie-offset
placeholder
placeholder-prompt
placeholder-type
placing
plain-number