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:
committed by
Xisco Fauli
parent
e5a0c5b3da
commit
f0600a8d88
@@ -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
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user