diff --git a/sw/qa/core/text/EnhancedPDFExportHelper.cxx b/sw/qa/core/text/EnhancedPDFExportHelper.cxx index c5effb0056df..e57d18436dd4 100644 --- a/sw/qa/core/text/EnhancedPDFExportHelper.cxx +++ b/sw/qa/core/text/EnhancedPDFExportHelper.cxx @@ -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(rDocElement.get()); + if (!pObject) + continue; + + auto pType = dynamic_cast(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(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: */ diff --git a/sw/qa/core/text/data/formula-alt.fodt b/sw/qa/core/text/data/formula-alt.fodt new file mode 100644 index 000000000000..d040e95b3c55 --- /dev/null +++ b/sw/qa/core/text/data/formula-alt.fodt @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + The mass energy equivalence is E=mc2E=mc^2 in a paragraph. + A described one is a2a over 2Half of a here. + + + diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx index 288476cf1b10..492dc4242bb1 100644 --- a/sw/source/core/text/EnhancedPDFExportHelper.cxx +++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx @@ -22,13 +22,16 @@ #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -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(rFly.Lower())->GetNode(); + SwOLENode* pOLENd = pNode ? const_cast(pNode->GetOLENode()) : nullptr; + if (!pOLENd) + return OUString(); + + const uno::Reference 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()); + 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(pFrame)->GetFormat()); + SwFlyFrame const& rFlyFrame(*static_cast(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);