tdf#163272 sw: give a paragraph its own /Lang

Write a paragraph in a language other than the document's, then File -
Export As - Export as PDF with Tagged PDF on. The paragraph's element
said nothing about the language, and the Span holding its text said it
instead:

 /StructElem /S/Standard /K[6 0 R]
 /StructElem /S/Span /Lang (en-GB) /K[1 2 3 4 5 6 7 8]

A run of text was compared against the language of the document, so
every run of a paragraph that declares one of its own differed from it
and opened a Span. The comparison is against the paragraph now, and a
paragraph unlike the document names its language itself. A run unlike
its paragraph still opens a Span and names its own, which it did not
where it was the document's language the paragraph had departed from.
A Span that carries anything else - a strikeout, a script, a character
style - still stands, without the language it was repeating, and so
does a Link. A paragraph with something anchored in it says nothing,
as before: what hangs there is tagged underneath it and would take the
paragraph's language for its own.

testSpans reads four list items, three of them en-GB in a de-DE
document. It asserted the shape the old rule made - for one of them it
looked for a single Span and took the language off that, and for the
rest it counted the Spans that rule opened. It reads the language off
the paragraph now, asserts that the item at the document's own
language names none, and where an item holds a hyperlink and a
footnote it finds the two Links alone.

Signed-off-by: Mike Kaganski <mike.kaganski@collabora.com>
Change-Id: I4082891666598793f59d1bf62c33e883ebdebb8f
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13117
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 0d2d9e656d87647f904f1a463ed9b879c2bfb10c)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211375
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Mike Kaganski
2026-09-23 19:56:06 +02:00
committed by Xisco Fauli
parent e5a0c5b3da
commit f0600a8d88
5 changed files with 186 additions and 104 deletions
+4 -1
View File
@@ -20,6 +20,7 @@
#pragma once
#include <i18nlangtag/lang.h>
#include <svl/typedwhich.hxx>
#include <vcl/pdfwriter.hxx>
#include "swrect.hxx"
#include "swtypes.hxx"
@@ -32,6 +33,7 @@ namespace vcl
class PDFExtOutDevData;
}
class OutputDevice;
class SvxLanguageItem;
class SwFrame;
class SwLinePortion;
class SwPageFrame;
@@ -228,7 +230,8 @@ class SwEnhancedPDFExportHelper
const SwPrintData& mrPrintData;
void EnhancedPDFExport(LanguageType const eLanguageDefault);
void EnhancedPDFExport(LanguageType const eLanguageDefault,
const TypedWhichId<SvxLanguageItem> nLanguageWhich);
/// Exports bibliography entry links.
void ExportAuthorityEntryLinks();
@@ -355,6 +355,49 @@ CPPUNIT_TEST_FIXTURE(Test, testLayoutAttributeUnits)
// the 1.5in column the cell sits in
CPPUNIT_ASSERT_DOUBLES_EQUAL(108.0, getLength(pCell, "Width"_ostr), 0.01);
}
CPPUNIT_TEST_FIXTURE(Test, testParagraphLanguage)
{
createSwDoc("paragraph-language.fodt");
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)));
// every structure element that names a language, sorted: they are emitted by object and
// not by document order
std::vector<OString> aLanguages;
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));
auto pLang
= dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pObject->Lookup("Lang"_ostr));
if (!pType || !pLang)
continue;
aLanguages.push_back(pType->GetValue() + "=" + pLang->GetValue());
}
std::sort(aLanguages.begin(), aLanguages.end());
CPPUNIT_ASSERT_EQUAL(size_t(5), aLanguages.size());
// the German words in an English paragraph, and the text of the paragraph holding a frame,
// which says nothing itself because what is anchored there hangs under it
CPPUNIT_ASSERT_EQUAL("Span=de-DE"_ostr, aLanguages[0]);
CPPUNIT_ASSERT_EQUAL("Span=de-DE"_ostr, aLanguages[1]);
// Without the fix this was de-DE: the run differs from its paragraph, not from the document
CPPUNIT_ASSERT_EQUAL("Span=en-US"_ostr, aLanguages[2]);
// and without it neither German paragraph named a language at all
CPPUNIT_ASSERT_EQUAL("Standard=de-DE"_ostr, aLanguages[3]);
CPPUNIT_ASSERT_EQUAL("Standard=de-DE"_ostr, aLanguages[4]);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
@@ -0,0 +1,36 @@
<?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:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 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="P1" style:family="paragraph" style:parent-style-name="Standard">
<style:paragraph-properties/>
<style:text-properties fo:language="de" fo:country="DE"/>
</style:style>
<style:style style:name="fr1" style:family="graphic">
<style:graphic-properties style:wrap="none" style:vertical-pos="top" style:vertical-rel="paragraph" style:horizontal-pos="left" style:horizontal-rel="paragraph" fo:padding="0in" fo:border="none"/>
</style:style>
<style:style style:name="T2" style:family="text">
<style:text-properties fo:language="en" fo:country="US"/>
</style:style>
<style:style style:name="T1" style:family="text">
<style:text-properties fo:language="de" fo:country="DE"/>
</style:style>
</office:automatic-styles>
<office:body>
<office:text>
<text:p text:style-name="Standard">A paragraph in the language of the document.</text:p>
<text:p text:style-name="P1">Ein Absatz, der ganz und gar deutsch ist.</text:p>
<text:p text:style-name="Standard">English with <text:span text:style-name="T1">ein paar deutsche Worte</text:span> in it.</text:p>
<text:p text:style-name="P1">Ein deutscher Absatz mit <text:span text:style-name="T2">a few English words</text:span> darin.</text:p>
<text:p text:style-name="P1">Ein deutscher Absatz mit einem Rahmen.<draw:frame draw:style-name="fr1" draw:name="Frame1" text:anchor-type="char" svg:x="3in" svg:y="0in" svg:width="2in" svg:height="0.4in"><draw:text-box><text:p text:style-name="Standard">Text inside the frame.</text:p></draw:text-box></draw:frame></text:p>
</office:text>
</office:body>
</office:document>
@@ -159,6 +159,8 @@ struct SwEnhancedPDFState
std::unordered_map<const SwTextNode*, sal_Int32> m_NodeTagIdMap;
LanguageType m_eLanguageDefault;
// the language item the application's script uses, which the default was read with
TypedWhichId<SvxLanguageItem> m_nLanguageWhich;
struct Span
{
@@ -192,8 +194,10 @@ struct SwEnhancedPDFState
// left open for a following portion, innermost last
std::vector<sal_Int32> m_DeferredTags;
SwEnhancedPDFState(LanguageType const eLanguageDefault)
SwEnhancedPDFState(LanguageType const eLanguageDefault,
const TypedWhichId<SvxLanguageItem> nLanguageWhich)
: m_eLanguageDefault(eLanguageDefault)
, m_nLanguageWhich(nLanguageWhich)
{
}
};
@@ -456,6 +460,25 @@ const SwTextNode* lcl_JumpedToNode(const SwEditShell& rSh, const SwPosition& rBe
return rPoint == rBeforeJump ? nullptr : rPoint.GetNode().GetTextNode();
}
// the language a paragraph declares for itself, as against the one a run of text sets
LanguageType lcl_GetParagraphLanguage(const SwTextFrame& rFrame,
const TypedWhichId<SvxLanguageItem> nWhich)
{
// what is anchored here is tagged inside the paragraph's element and would inherit a
// language put there; the node, not the frame, because a split paragraph is one element
if (!rFrame.GetTextNodeFirst()->GetAnchoredFlys().empty())
return LANGUAGE_DONTKNOW;
return rFrame.GetTextNodeForParaProps()->GetSwAttrSet().Get(nWhich).GetLanguage();
}
// the language a run of text is measured against
LanguageType lcl_GetEnclosingLanguage(const SwTextPaintInfo& rInf, const SwEnhancedPDFState& rState)
{
const LanguageType nParagraph(
lcl_GetParagraphLanguage(*rInf.GetTextFrame(), rState.m_nLanguageWhich));
return LANGUAGE_DONTKNOW == nParagraph ? rState.m_eLanguageDefault : nParagraph;
}
// the StarMath source of the formula a frame holds, empty for anything else
OUString lcl_GetFormulaSource(const SwFlyFrame& rFly)
{
@@ -955,6 +978,7 @@ void SwTaggedPDFHelper::SetAttributes(vcl::pdf::StructElement eType)
bool bBox = false;
bool bRowSpan = false;
bool bAltText = false;
bool bLanguage = false;
// Check which attributes to set:
@@ -1019,7 +1043,8 @@ void SwTaggedPDFHelper::SetAttributes(vcl::pdf::StructElement eType)
bStartIndent =
bEndIndent =
bTextIndent =
bTextAlign = true;
bTextAlign =
bLanguage = true;
break;
case vcl::pdf::StructElement::Formula:
@@ -1134,6 +1159,18 @@ void SwTaggedPDFHelper::SetAttributes(vcl::pdf::StructElement eType)
mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::pdf::PDFWriter::TextIndent, nVal );
}
if (bLanguage && pFrame->IsTextFrame())
{
const SwEnhancedPDFState& rState(*mpPDFExtOutDevData->GetSwPDFState());
const LanguageType nLanguage(lcl_GetParagraphLanguage(
static_cast<const SwTextFrame&>(*pFrame), rState.m_nLanguageWhich));
if (LANGUAGE_DONTKNOW != nLanguage && rState.m_eLanguageDefault != nLanguage)
{
mpPDFExtOutDevData->SetStructureAttributeNumerical(
vcl::pdf::PDFWriter::Language, static_cast<sal_uInt16>(nLanguage));
}
}
if ( bTextAlign )
{
OSL_ENSURE( pFrame->IsTextFrame(), "Frame type <-> tag attribute mismatch" );
@@ -1377,9 +1414,10 @@ void SwTaggedPDFHelper::SetAttributes(vcl::pdf::StructElement eType)
{
const LanguageType nCurrentLanguage = rInf.GetFont()->GetLanguage();
const LanguageType nDefaultLang(mpPDFExtOutDevData->GetSwPDFState()->m_eLanguageDefault);
const LanguageType nEnclosingLanguage(
lcl_GetEnclosingLanguage(rInf, *mpPDFExtOutDevData->GetSwPDFState()));
if ( nDefaultLang != nCurrentLanguage )
if (nEnclosingLanguage != nCurrentLanguage)
mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::pdf::PDFWriter::Language, static_cast<sal_uInt16>(nCurrentLanguage) );
}
@@ -2331,7 +2369,8 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
{
const LanguageType nCurrentLanguage = rInf.GetFont()->GetLanguage();
const SwFontScript nFont = rInf.GetFont()->GetActual();
const LanguageType nDefaultLang(mpPDFExtOutDevData->GetSwPDFState()->m_eLanguageDefault);
const LanguageType nEnclosingLanguage(
lcl_GetEnclosingLanguage(rInf, *mpPDFExtOutDevData->GetSwPDFState()));
if ( LINESTYLE_NONE != rInf.GetFont()->GetUnderline() ||
LINESTYLE_NONE != rInf.GetFont()->GetOverline() ||
@@ -2339,7 +2378,7 @@ void SwTaggedPDFHelper::BeginInlineStructureElements()
FontEmphasisMark::NONE != rInf.GetFont()->GetEmphasisMark() ||
0 != rInf.GetFont()->GetEscapement() ||
SwFontScript::Latin != nFont ||
nCurrentLanguage != nDefaultLang ||
nCurrentLanguage != nEnclosingLanguage ||
!sStyleName.isEmpty())
{
nPDFType = sal_uInt16(vcl::pdf::StructElement::Span);
@@ -2519,7 +2558,7 @@ SwEnhancedPDFExportHelper::SwEnhancedPDFExportHelper( SwEditShell& rSh,
const SvxLanguageItem& rLangItem = mrSh.GetDoc()->GetDefault( nLangRes );
auto const eLanguageDefault = rLangItem.GetLanguage();
EnhancedPDFExport(eLanguageDefault);
EnhancedPDFExport(eLanguageDefault, nLangRes);
}
SwEnhancedPDFExportHelper::~SwEnhancedPDFExportHelper()
@@ -2582,7 +2621,8 @@ sal_Int32 SwEnhancedPDFExportHelper::CreateDestination(const SwPageFrame* pCurrP
return nDestId;
}
void SwEnhancedPDFExportHelper::EnhancedPDFExport(LanguageType const eLanguageDefault)
void SwEnhancedPDFExportHelper::EnhancedPDFExport(
LanguageType const eLanguageDefault, const TypedWhichId<SvxLanguageItem> nLanguageWhich)
{
vcl::PDFExtOutDevData* pPDFExtOutDevData =
dynamic_cast< vcl::PDFExtOutDevData*>( mrOut.GetExtOutDevData() );
@@ -2613,7 +2653,7 @@ void SwEnhancedPDFExportHelper::EnhancedPDFExport(LanguageType const eLanguageDe
if ( !mbEditEngineOnly )
{
assert(pPDFExtOutDevData->GetSwPDFState() == nullptr);
pPDFExtOutDevData->SetSwPDFState(new SwEnhancedPDFState(eLanguageDefault));
pPDFExtOutDevData->SetSwPDFState(new SwEnhancedPDFState(eLanguageDefault, nLanguageWhich));
// POSTITS
+54 -94
View File
@@ -3178,25 +3178,18 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testSpans)
auto pKids10010
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10010->Lookup("K"_ostr));
CPPUNIT_ASSERT(pKids10010);
// assume there are no MCID ref at this level
// the paragraph is wholly in one language, so it names it itself
auto pLang10010 = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
pObject10010->Lookup("Lang"_ostr));
CPPUNIT_ASSERT(pLang10010);
CPPUNIT_ASSERT_EQUAL("en-GB"_ostr, pLang10010->GetValue());
// and its content needs no span between
auto vKids10010 = pKids10010->GetElements();
// only one span
CPPUNIT_ASSERT_EQUAL(size_t(1), vKids10010.size());
auto pRefKid100100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10010[0]);
CPPUNIT_ASSERT(pRefKid100100);
auto pObject100100 = pRefKid100100->LookupObject();
CPPUNIT_ASSERT(pObject100100);
auto pType100100 = dynamic_cast<vcl::filter::PDFNameElement*>(
pObject100100->Lookup("Type"_ostr));
CPPUNIT_ASSERT_EQUAL("StructElem"_ostr, pType100100->GetValue());
auto pS100100
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject100100->Lookup("S"_ostr));
CPPUNIT_ASSERT_EQUAL("Span"_ostr, pS100100->GetValue());
// this span exists because of lang
auto pLang100100 = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
pObject100100->Lookup("Lang"_ostr));
CPPUNIT_ASSERT_EQUAL("en-GB"_ostr, pLang100100->GetValue());
CPPUNIT_ASSERT_EQUAL(size_t(8), vKids10010.size());
for (const auto pKid : vKids10010)
{
CPPUNIT_ASSERT(!dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid));
}
auto pRefKid101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[1]);
CPPUNIT_ASSERT(pRefKid101);
@@ -3258,6 +3251,8 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testSpans)
auto pKids10110
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10110->Lookup("K"_ostr));
CPPUNIT_ASSERT(pKids10110);
// this paragraph is in the language of the document, so it names none
CPPUNIT_ASSERT(!pObject10110->Lookup("Lang"_ostr));
auto vKids10110 = pKids10110->GetElements();
// only MCIDs, no span
for (size_t i = 0; i < vKids10110.size(); ++i)
@@ -3326,89 +3321,48 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testSpans)
auto pKids10210
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10210->Lookup("K"_ostr));
CPPUNIT_ASSERT(pKids10210);
// assume there are no MCID ref at this level
auto vKids10210 = pKids10210->GetElements();
// 2 span and a hyperlink
CPPUNIT_ASSERT_EQUAL(size_t(3), vKids10210.size());
CPPUNIT_ASSERT_EQUAL(size_t(8), vKids10210.size());
// this paragraph too is wholly in one language and names it itself
auto pLang10210 = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
pObject10210->Lookup("Lang"_ostr));
CPPUNIT_ASSERT(pLang10210);
CPPUNIT_ASSERT_EQUAL("en-GB"_ostr, pLang10210->GetValue());
auto pRefKid102100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[0]);
CPPUNIT_ASSERT(pRefKid102100);
auto pObject102100 = pRefKid102100->LookupObject();
CPPUNIT_ASSERT(pObject102100);
auto pType102100 = dynamic_cast<vcl::filter::PDFNameElement*>(
pObject102100->Lookup("Type"_ostr));
CPPUNIT_ASSERT_EQUAL("StructElem"_ostr, pType102100->GetValue());
auto pS102100
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102100->Lookup("S"_ostr));
CPPUNIT_ASSERT_EQUAL("Span"_ostr, pS102100->GetValue());
auto pKids102100
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102100->Lookup("K"_ostr));
CPPUNIT_ASSERT(pKids102100);
auto vKids102100 = pKids102100->GetElements();
for (size_t i = 0; i < vKids102100.size(); ++i)
// its own text needs no span, so the only elements left are the two links, and
// neither repeats the language
std::vector<vcl::filter::PDFObjectElement*> aLinks;
for (const auto pKid : vKids10210)
{
auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102100[i]);
CPPUNIT_ASSERT(!pKid);
auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid);
if (!pRef)
continue;
auto pLinked = pRef->LookupObject();
CPPUNIT_ASSERT(pLinked);
CPPUNIT_ASSERT_EQUAL(
"StructElem"_ostr,
dynamic_cast<vcl::filter::PDFNameElement*>(pLinked->Lookup("Type"_ostr))
->GetValue());
CPPUNIT_ASSERT_EQUAL("Link"_ostr, dynamic_cast<vcl::filter::PDFNameElement*>(
pLinked->Lookup("S"_ostr))
->GetValue());
CPPUNIT_ASSERT(!pLinked->Lookup("Lang"_ostr));
aLinks.push_back(pLinked);
}
CPPUNIT_ASSERT_EQUAL(size_t(2), aLinks.size());
auto pRefKid102101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[1]);
CPPUNIT_ASSERT(pRefKid102101);
auto pObject102101 = pRefKid102101->LookupObject();
CPPUNIT_ASSERT(pObject102101);
auto pType102101 = dynamic_cast<vcl::filter::PDFNameElement*>(
pObject102101->Lookup("Type"_ostr));
CPPUNIT_ASSERT_EQUAL("StructElem"_ostr, pType102101->GetValue());
auto pS102101
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102101->Lookup("S"_ostr));
CPPUNIT_ASSERT_EQUAL("Link"_ostr, pS102101->GetValue());
auto pKids102101
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102101->Lookup("K"_ostr));
CPPUNIT_ASSERT(pKids102101);
auto vKids102101 = pKids102101->GetElements();
int nRef(0);
for (size_t i = 0; i < vKids102101.size(); ++i)
// the hyperlink comes before the footnote in the text, so it is the first
// of the two
auto pKidsHyperlink
= dynamic_cast<vcl::filter::PDFArrayElement*>(aLinks[0]->Lookup("K"_ostr));
CPPUNIT_ASSERT(pKidsHyperlink);
int nAnnots(0);
for (const auto pKid : pKidsHyperlink->GetElements())
{
auto pKid = dynamic_cast<vcl::filter::PDFDictionaryElement*>(vKids102101[i]);
if (pKid)
{
++nRef; // annotation
}
if (dynamic_cast<vcl::filter::PDFDictionaryElement*>(pKid))
++nAnnots;
}
CPPUNIT_ASSERT_EQUAL(1, nRef);
auto pRefKid102102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[2]);
CPPUNIT_ASSERT(pRefKid102102);
auto pObject102102 = pRefKid102102->LookupObject();
CPPUNIT_ASSERT(pObject102102);
auto pType102102 = dynamic_cast<vcl::filter::PDFNameElement*>(
pObject102102->Lookup("Type"_ostr));
CPPUNIT_ASSERT_EQUAL("StructElem"_ostr, pType102102->GetValue());
auto pS102102
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject102102->Lookup("S"_ostr));
CPPUNIT_ASSERT_EQUAL("Span"_ostr, pS102102->GetValue());
auto pKids102102
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102102->Lookup("K"_ostr));
CPPUNIT_ASSERT(pKids102102);
auto vKids102102 = pKids102102->GetElements();
// there is a footnote
int nFtn(0);
for (size_t i = 0; i < vKids102102.size(); ++i)
{
auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102102[i]);
if (pKid)
{
auto pObject = pKid->LookupObject();
CPPUNIT_ASSERT(pObject);
auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(
pObject->Lookup("Type"_ostr));
CPPUNIT_ASSERT_EQUAL("StructElem"_ostr, pType->GetValue());
auto pS
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"_ostr));
CPPUNIT_ASSERT_EQUAL("Link"_ostr, pS->GetValue());
++nFtn;
}
}
CPPUNIT_ASSERT_EQUAL(1, nFtn);
CPPUNIT_ASSERT_EQUAL(1, nAnnots);
auto pRefKid103 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[3]);
CPPUNIT_ASSERT(pRefKid103);
@@ -3467,6 +3421,10 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testSpans)
auto pS10310
= dynamic_cast<vcl::filter::PDFNameElement*>(pObject10310->Lookup("S"_ostr));
CPPUNIT_ASSERT_EQUAL("Standard"_ostr, pS10310->GetValue());
auto pLang10310 = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
pObject10310->Lookup("Lang"_ostr));
CPPUNIT_ASSERT(pLang10310);
CPPUNIT_ASSERT_EQUAL("en-GB"_ostr, pLang10310->GetValue());
auto pKids10310
= dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10310->Lookup("K"_ostr));
CPPUNIT_ASSERT(pKids10310);
@@ -3498,6 +3456,8 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testSpans)
dynamic_cast<vcl::filter::PDFNameElement*>(
pDictA103101->LookupElement("TextDecorationType"_ostr))
->GetValue());
// the span stands for the strikeout, and no longer repeats the language
CPPUNIT_ASSERT(!pObject103101->Lookup("Lang"_ostr));
// now the footnote container - following the list
auto pRefKid11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1[1]);