tdf#168720 sw: an undescribed formula's /Alt is its source
Insert - OLE Object - Formula Object, write E=mc^2, save and reopen, then export with File - Export As - Export as PDF and Universal Accessibility (PDF/UA) on. PAC 2026 fails it under Alternative Descriptions, the element saying nothing about what it holds: <</Type/StructElem/S/Formula /P 5 0 R /Pg 1 0 R ... /K[1 ]>> Writer takes a frame's Alt from the title and description a user typed; a formula has neither unless asked. A tagged export falls back to the formula's own source now, so the element says E=mc^2 and PAC passes that checkpoint: /Alt <FEFF0045003D006D0063005E0032> A title or description still wins. The report asks for an option; a formula with neither said nothing at all, so the source goes in with none to switch it off. Reading the source needs the object running, so the export runs each undescribed formula and puts it back. PAC's separate Structure tree warning still stands. Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com> Change-Id: I09a68eff3d15981a7e112f79409b3448bceb4bc0 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12987 Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com> (cherry picked from commit 5b84c1873533bcdd98d917e8b6fb6d149a238047) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211348 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
committed by
Xisco Fauli
parent
722410514b
commit
c3bcf7d5f8
@@ -204,6 +204,42 @@ CPPUNIT_TEST_FIXTURE(Test, testTOCItemRef)
|
||||
// one item per heading, each naming its own
|
||||
CPPUNIT_ASSERT_EQUAL("H1 H2 "_ostr, aTargets.makeStringAndClear());
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(Test, testFormulaAltFromSource)
|
||||
{
|
||||
createSwDoc("formula-alt.fodt");
|
||||
// a formula reaches the export loaded, not running, in every document but a freshly typed one
|
||||
saveAndReload(TestFilter::ODT);
|
||||
|
||||
uno::Sequence aFilterData{ comphelper::makePropertyValue(u"UseTaggedPDF"_ustr, true) };
|
||||
save(TestFilter::PDF_WRITER,
|
||||
{ comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) });
|
||||
|
||||
vcl::filter::PDFDocument aDocument;
|
||||
maTempFile.CloseStream();
|
||||
CPPUNIT_ASSERT(aDocument.Read(*maTempFile.GetStream(StreamMode::READ)));
|
||||
|
||||
OUStringBuffer aAlts;
|
||||
for (const auto& rDocElement : aDocument.GetElements())
|
||||
{
|
||||
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
||||
if (!pObject)
|
||||
continue;
|
||||
|
||||
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"_ostr));
|
||||
if (!pType || pType->GetValue() != "Formula")
|
||||
continue;
|
||||
|
||||
// Without the fix a formula nobody had described carried no Alt, which the PDF/UA
|
||||
// checkers count against the file
|
||||
auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pObject->Lookup("Alt"_ostr));
|
||||
CPPUNIT_ASSERT(pAlt);
|
||||
aAlts.append(vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt) + "|");
|
||||
}
|
||||
|
||||
// the source where nobody described the formula, and the title where somebody did
|
||||
CPPUNIT_ASSERT_EQUAL(u"E=mc^2|Half of a|"_ustr, aAlts.makeStringAndClear());
|
||||
}
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:font-face-decls>
|
||||
<style:font-face style:name="Liberation Serif" svg:font-family="'Liberation Serif'" style:font-family-generic="roman" style:font-pitch="variable"/>
|
||||
</office:font-face-decls>
|
||||
<office:styles>
|
||||
<style:default-style style:family="paragraph">
|
||||
<style:text-properties style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="en" fo:country="US"/>
|
||||
</style:default-style>
|
||||
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:style style:name="fr1" style:family="graphic">
|
||||
<style:graphic-properties style:vertical-pos="middle" style:vertical-rel="text" fo:padding="0in" fo:border="none"/>
|
||||
</style:style>
|
||||
</office:automatic-styles>
|
||||
<office:body>
|
||||
<office:text>
|
||||
<text:p text:style-name="Standard">The mass energy equivalence is <draw:frame draw:style-name="fr1" draw:name="Object1" text:anchor-type="as-char" svg:width="0.9in" svg:height="0.3in" draw:z-index="0"><draw:object><math:math><math:semantics><math:mrow><math:mi math:mathvariant="normal">E</math:mi><math:mo math:stretchy="false">=</math:mo><math:msup><math:mi math:mathvariant="italic">mc</math:mi><math:mn>2</math:mn></math:msup></math:mrow><math:annotation math:encoding="StarMath 5.0">E=mc^2</math:annotation></math:semantics></math:math></draw:object></draw:frame> in a paragraph.</text:p>
|
||||
<text:p text:style-name="Standard">A described one is <draw:frame draw:style-name="fr1" draw:name="Object2" text:anchor-type="as-char" svg:width="0.6in" svg:height="0.4in" draw:z-index="1"><draw:object><math:math><math:semantics><math:mrow><math:mfrac><math:mi math:mathvariant="italic">a</math:mi><math:mn>2</math:mn></math:mfrac></math:mrow><math:annotation math:encoding="StarMath 5.0">a over 2</math:annotation></math:semantics></math:math></draw:object><svg:title>Half of a</svg:title></draw:frame> here.</text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -22,13 +22,16 @@
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <com/sun/star/embed/EmbedStates.hpp>
|
||||
#include <com/sun/star/embed/XEmbeddedObject.hpp>
|
||||
#include <com/sun/star/i18n/ScriptType.hpp>
|
||||
#include <com/sun/star/drawing/XShape.hpp>
|
||||
#include <com/sun/star/beans/XPropertySet.hpp>
|
||||
#include <hintids.hxx>
|
||||
|
||||
#include <comphelper/scopeguard.hxx>
|
||||
#include <sot/exchange.hxx>
|
||||
#include <svtools/embedhlp.hxx>
|
||||
#include <vcl/outdev.hxx>
|
||||
#include <vcl/pdfextoutdevdata.hxx>
|
||||
#include <vcl/pdf/PDFNote.hxx>
|
||||
@@ -453,6 +456,45 @@ const SwTextNode* lcl_JumpedToNode(const SwEditShell& rSh, const SwPosition& rBe
|
||||
return rPoint == rBeforeJump ? nullptr : rPoint.GetNode().GetTextNode();
|
||||
}
|
||||
|
||||
// the StarMath source of the formula a frame holds, empty for anything else
|
||||
OUString lcl_GetFormulaSource(const SwFlyFrame& rFly)
|
||||
{
|
||||
if (!rFly.Lower() || !rFly.Lower()->IsNoTextFrame())
|
||||
return OUString();
|
||||
|
||||
const SwContentNode* pNode = static_cast<const SwNoTextFrame*>(rFly.Lower())->GetNode();
|
||||
SwOLENode* pOLENd = pNode ? const_cast<SwOLENode*>(pNode->GetOLENode()) : nullptr;
|
||||
if (!pOLENd)
|
||||
return OUString();
|
||||
|
||||
const uno::Reference<embed::XEmbeddedObject> xObj(pOLENd->GetOLEObj().GetOleRef());
|
||||
if (!xObj.is() || !SotExchange::IsMath(SvGlobalName(xObj->getClassID())))
|
||||
return OUString();
|
||||
|
||||
OUString aSource;
|
||||
try
|
||||
{
|
||||
// a loaded object has no component to read the source from
|
||||
const bool bWasLoaded(xObj->getCurrentState() == embed::EmbedStates::LOADED);
|
||||
if (!svt::EmbeddedObjectRef::TryRunningState(xObj))
|
||||
return OUString();
|
||||
|
||||
// reading it ran the object, and an export leaves the document as it found it
|
||||
const comphelper::ScopeGuard aRestore([&xObj, bWasLoaded] {
|
||||
if (bWasLoaded)
|
||||
xObj->changeState(embed::EmbedStates::LOADED);
|
||||
});
|
||||
|
||||
const auto xProps(xObj->getComponent().query<beans::XPropertySet>());
|
||||
if (xProps.is())
|
||||
xProps->getPropertyValue(u"Formula"_ustr) >>= aSource;
|
||||
}
|
||||
catch (const uno::Exception&)
|
||||
{
|
||||
}
|
||||
return aSource;
|
||||
}
|
||||
|
||||
// the node a table of contents entry links to, which the mark in its URL names
|
||||
const SwTextNode* lcl_GetLinkedNode(const SwDoc& rDoc, const SwTextNode& rEntry)
|
||||
{
|
||||
@@ -1113,11 +1155,17 @@ void SwTaggedPDFHelper::SetAttributes(vcl::pdf::StructElement eType)
|
||||
// text here again.
|
||||
if (bAltText)
|
||||
{
|
||||
SwFlyFrameFormat const& rFly(*static_cast<SwFlyFrame const*>(pFrame)->GetFormat());
|
||||
SwFlyFrame const& rFlyFrame(*static_cast<SwFlyFrame const*>(pFrame));
|
||||
SwFlyFrameFormat const& rFly(*rFlyFrame.GetFormat());
|
||||
OUString const sep(
|
||||
(rFly.GetObjTitle().isEmpty() || rFly.GetObjDescription().isEmpty())
|
||||
? OUString() : u" - "_ustr);
|
||||
OUString const altText(rFly.GetObjTitle() + sep + rFly.GetObjDescription());
|
||||
OUString altText(rFly.GetObjTitle() + sep + rFly.GetObjDescription());
|
||||
// a formula nobody described names its own source
|
||||
if (altText.isEmpty() && eType == vcl::pdf::StructElement::Formula)
|
||||
{
|
||||
altText = lcl_GetFormulaSource(rFlyFrame);
|
||||
}
|
||||
if (!altText.isEmpty())
|
||||
{
|
||||
mpPDFExtOutDevData->SetAlternateText(altText);
|
||||
|
||||
Reference in New Issue
Block a user