tdf#167121 sw: tag the link an index mark covers
A hyperlink whose text an alphabetical index entry covers exported a link annotation with no tag around it, so PDF/UA checkers reject the file: Link annotation is not nested inside a Link structure element that is ISO 14289-1 7.18.5. The same happens under a reference mark or an RDF metadata field. Each of the three lays its run out as its own portion type, a SwTextPortion whose only addition is a shading that never reaches an export. BeginInlineStructureElements listed Text, Lay and Para, so the run matched no case and got no element at all - not even the Span carrying its language. Handle the three with the other text portions. Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com> Change-Id: Ie38193f64923cd6a0a3afa509691fb29a007f272 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/12104 Tested-by: Jenkins CPCI <releng@collaboraoffice.com> Reviewed-by: Miklos Vajna <vmiklos@collabora.com> (cherry picked from commit 409e0cc615e191a0392ee080717bb84eb61e4724) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211027 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Tested-by: Jenkins
This commit is contained in:
committed by
Xisco Fauli
parent
0916bb2e7a
commit
7c2c72d189
@@ -2061,6 +2061,10 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
|
||||
case PortionType::Lay :
|
||||
case PortionType::Text :
|
||||
case PortionType::Para :
|
||||
// index, reference and metadata marks
|
||||
case PortionType::Tox:
|
||||
case PortionType::Ref:
|
||||
case PortionType::Meta:
|
||||
{
|
||||
// Check for Link:
|
||||
if( pInetFormatAttr )
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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: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:dc="http://purl.org/dc/elements/1.1/" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta><dc:title>Index mark over a language change</dc:title></office:meta>
|
||||
<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:font-name-asian="Liberation Serif" style:font-size-asian="12pt" style:font-name-complex="Liberation Serif" style:font-size-complex="12pt"/>
|
||||
</style:default-style>
|
||||
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
<style:text-properties fo:language="de" fo:country="DE"/>
|
||||
</style:style>
|
||||
<style:page-layout style:name="pm1">
|
||||
<style:page-layout-properties fo:page-width="21cm" fo:page-height="29.7cm" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb"/>
|
||||
</style:page-layout>
|
||||
</office:automatic-styles>
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="pm1"/>
|
||||
</office:master-styles>
|
||||
<office:body>
|
||||
<office:text>
|
||||
<text:p text:style-name="Standard">alpha<text:alphabetical-index-mark-start text:id="IMark1" text:key1="Keyword"/><text:span text:style-name="T1">deutsch</text:span><text:alphabetical-index-mark-end text:id="IMark1"/>gamma</text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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: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:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta><dc:title>Link with an alphabetical index mark</dc:title></office:meta>
|
||||
<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:font-name-asian="Liberation Serif" style:font-size-asian="12pt" style:font-name-complex="Liberation Serif" style:font-size-complex="12pt"/>
|
||||
</style:default-style>
|
||||
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
|
||||
<style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text">
|
||||
<style:text-properties fo:color="#000080" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:page-layout style:name="pm1">
|
||||
<style:page-layout-properties fo:page-width="21cm" fo:page-height="29.7cm" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb"/>
|
||||
</style:page-layout>
|
||||
</office:automatic-styles>
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="pm1"/>
|
||||
</office:master-styles>
|
||||
<office:body>
|
||||
<office:text>
|
||||
<text:p text:style-name="Standard"><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="before the mark" text:style-name="Internet_20_link">alpha</text:a><text:alphabetical-index-mark-start text:id="IMark1" text:key1="Keyword"/><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="inside the mark" text:style-name="Internet_20_link">beta</text:a><text:alphabetical-index-mark-end text:id="IMark1"/><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="after the mark" text:style-name="Internet_20_link">gamma</text:a></text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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: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:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta><dc:title>Link with an RDF metadata field</dc:title></office:meta>
|
||||
<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:font-name-asian="Liberation Serif" style:font-size-asian="12pt" style:font-name-complex="Liberation Serif" style:font-size-complex="12pt"/>
|
||||
</style:default-style>
|
||||
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
|
||||
<style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text">
|
||||
<style:text-properties fo:color="#000080" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:page-layout style:name="pm1">
|
||||
<style:page-layout-properties fo:page-width="21cm" fo:page-height="29.7cm" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb"/>
|
||||
</style:page-layout>
|
||||
</office:automatic-styles>
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="pm1"/>
|
||||
</office:master-styles>
|
||||
<office:body>
|
||||
<office:text>
|
||||
<text:p text:style-name="Standard"><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="before the mark" text:style-name="Internet_20_link">alpha</text:a><text:meta xml:id="meta1"><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="inside the mark" text:style-name="Internet_20_link">beta</text:a></text:meta><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="after the mark" text:style-name="Internet_20_link">gamma</text:a></text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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: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:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta><dc:title>Link with a reference mark</dc:title></office:meta>
|
||||
<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:font-name-asian="Liberation Serif" style:font-size-asian="12pt" style:font-name-complex="Liberation Serif" style:font-size-complex="12pt"/>
|
||||
</style:default-style>
|
||||
<style:style style:name="Standard" style:family="paragraph" style:class="text"/>
|
||||
<style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text">
|
||||
<style:text-properties fo:color="#000080" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color"/>
|
||||
</style:style>
|
||||
</office:styles>
|
||||
<office:automatic-styles>
|
||||
<style:page-layout style:name="pm1">
|
||||
<style:page-layout-properties fo:page-width="21cm" fo:page-height="29.7cm" style:print-orientation="portrait" fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb"/>
|
||||
</style:page-layout>
|
||||
</office:automatic-styles>
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="pm1"/>
|
||||
</office:master-styles>
|
||||
<office:body>
|
||||
<office:text>
|
||||
<text:p text:style-name="Standard"><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="before the mark" text:style-name="Internet_20_link">alpha</text:a><text:reference-mark-start text:name="RefMark1"/><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="inside the mark" text:style-name="Internet_20_link">beta</text:a><text:reference-mark-end text:name="RefMark1"/><text:a xlink:type="simple" xlink:href="https://www.example.com/" office:name="after the mark" text:style-name="Internet_20_link">gamma</text:a></text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <com/sun/star/view/XPrintable.hpp>
|
||||
|
||||
#include <comphelper/propertysequence.hxx>
|
||||
#include <comphelper/propertyvalue.hxx>
|
||||
#include <comphelper/sequenceashashmap.hxx>
|
||||
#include <unotools/tempfile.hxx>
|
||||
#include <vcl/filter/pdfdocument.hxx>
|
||||
@@ -2928,6 +2929,151 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157816)
|
||||
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), pAnnots->GetElements().size());
|
||||
}
|
||||
|
||||
// ISO 14289-1 7.18.5: every link annotation is an OBJR kid of a Link structure element
|
||||
void AssertLinkAnnotsAreTagged(vcl::filter::PDFDocument& rDocument, const size_t nLinks,
|
||||
const size_t nAnnots)
|
||||
{
|
||||
std::vector<vcl::filter::PDFObjectElement*> aPages = rDocument.GetPages();
|
||||
CPPUNIT_ASSERT_EQUAL(size_t(1), aPages.size());
|
||||
auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"_ostr));
|
||||
CPPUNIT_ASSERT(pAnnots);
|
||||
CPPUNIT_ASSERT_EQUAL(nAnnots, pAnnots->GetElements().size());
|
||||
|
||||
// what the Link structure elements claim as their annotations
|
||||
size_t nLinkSE(0);
|
||||
std::set<vcl::filter::PDFObjectElement*> aKidAnnots;
|
||||
for (const auto& rDocElement : rDocument.GetElements())
|
||||
{
|
||||
auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
|
||||
if (!pObject)
|
||||
continue;
|
||||
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"_ostr));
|
||||
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"_ostr));
|
||||
if (!pType || pType->GetValue() != "StructElem" || !pS || pS->GetValue() != "Link")
|
||||
continue;
|
||||
++nLinkSE;
|
||||
auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject->Lookup("K"_ostr));
|
||||
CPPUNIT_ASSERT(pKids);
|
||||
for (const auto pElement : pKids->GetElements())
|
||||
{
|
||||
auto pObjR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pElement);
|
||||
if (!pObjR)
|
||||
continue;
|
||||
auto pOType
|
||||
= dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->LookupElement("Type"_ostr));
|
||||
CPPUNIT_ASSERT(pOType);
|
||||
// a kid on another page is an MCR dictionary, not an OBJR
|
||||
if (pOType->GetValue() == "MCR")
|
||||
continue;
|
||||
CPPUNIT_ASSERT_EQUAL("OBJR"_ostr, pOType->GetValue());
|
||||
auto pRef
|
||||
= dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->LookupElement("Obj"_ostr));
|
||||
CPPUNIT_ASSERT(pRef);
|
||||
auto pKidAnnot = pRef->LookupObject();
|
||||
CPPUNIT_ASSERT_MESSAGE("an OBJR must resolve to an annotation", pKidAnnot);
|
||||
CPPUNIT_ASSERT_MESSAGE("an annotation is claimed by more than one OBJR",
|
||||
aKidAnnots.insert(pKidAnnot).second);
|
||||
}
|
||||
}
|
||||
CPPUNIT_ASSERT_EQUAL(nLinks, nLinkSE);
|
||||
CPPUNIT_ASSERT_EQUAL(nAnnots, aKidAnnots.size());
|
||||
|
||||
for (const auto pElement : pAnnots->GetElements())
|
||||
{
|
||||
auto pAnnotRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pElement);
|
||||
CPPUNIT_ASSERT(pAnnotRef);
|
||||
auto pAnnot = pAnnotRef->LookupObject();
|
||||
CPPUNIT_ASSERT(pAnnot);
|
||||
CPPUNIT_ASSERT_MESSAGE("every annotation needs a StructParent",
|
||||
pAnnot->Lookup("StructParent"_ostr));
|
||||
CPPUNIT_ASSERT_MESSAGE("every annotation needs an OBJR in a Link",
|
||||
aKidAnnots.contains(pAnnot));
|
||||
}
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf167121)
|
||||
{
|
||||
loadFromFile(u"LinkWithIndexMark.fodt");
|
||||
|
||||
uno::Sequence aFilterData{ comphelper::makePropertyValue(u"PDFUACompliance"_ustr, true) };
|
||||
save(TestFilter::PDF_WRITER,
|
||||
{ comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) });
|
||||
|
||||
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
||||
vcl::filter::PDFDocument aDocument;
|
||||
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
||||
|
||||
// an index mark over one of three links must not orphan that link's annotation
|
||||
AssertLinkAnnotsAreTagged(aDocument, /*nLinks=*/3, /*nAnnots=*/3);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest, testLinkWithRefMark)
|
||||
{
|
||||
loadFromFile(u"LinkWithRefMark.fodt");
|
||||
|
||||
uno::Sequence aFilterData{ comphelper::makePropertyValue(u"PDFUACompliance"_ustr, true) };
|
||||
save(TestFilter::PDF_WRITER,
|
||||
{ comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) });
|
||||
|
||||
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
||||
vcl::filter::PDFDocument aDocument;
|
||||
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
||||
|
||||
// a reference mark shades a run the way an index mark does, and lost the tag with it
|
||||
AssertLinkAnnotsAreTagged(aDocument, /*nLinks=*/3, /*nAnnots=*/3);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest, testLinkWithMeta)
|
||||
{
|
||||
loadFromFile(u"LinkWithMeta.fodt");
|
||||
|
||||
uno::Sequence aFilterData{ comphelper::makePropertyValue(u"PDFUACompliance"_ustr, true) };
|
||||
save(TestFilter::PDF_WRITER,
|
||||
{ comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) });
|
||||
|
||||
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
||||
vcl::filter::PDFDocument aDocument;
|
||||
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
||||
|
||||
// an RDF metadata field shades a run the same way, and lost the tag with it
|
||||
AssertLinkAnnotsAreTagged(aDocument, /*nLinks=*/3, /*nAnnots=*/3);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest, testIndexMarkWithSpan)
|
||||
{
|
||||
loadFromFile(u"IndexMarkWithSpan.fodt");
|
||||
|
||||
uno::Sequence aFilterData{ comphelper::makePropertyValue(u"PDFUACompliance"_ustr, true) };
|
||||
save(TestFilter::PDF_WRITER,
|
||||
{ comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData) });
|
||||
|
||||
SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
|
||||
vcl::filter::PDFDocument aDocument;
|
||||
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
||||
|
||||
// the tag a marked run needs is not always a Link: this one carries the language
|
||||
vcl::filter::PDFObjectElement* pSpanSE(nullptr);
|
||||
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("Type"_ostr));
|
||||
auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"_ostr));
|
||||
if (pType && pType->GetValue() == "StructElem" && pS && pS->GetValue() == "Span")
|
||||
{
|
||||
CPPUNIT_ASSERT(!pSpanSE);
|
||||
pSpanSE = pObject;
|
||||
}
|
||||
}
|
||||
CPPUNIT_ASSERT(pSpanSE);
|
||||
|
||||
auto pSpanLang
|
||||
= dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pSpanSE->Lookup("Lang"_ostr));
|
||||
CPPUNIT_ASSERT(pSpanLang);
|
||||
CPPUNIT_ASSERT_EQUAL("de-DE"_ostr, pSpanLang->GetValue());
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157816Link)
|
||||
{
|
||||
// Enable PDF/UA
|
||||
|
||||
Reference in New Issue
Block a user