Skip to content

Commit e383b98

Browse files
vitali-priText-CI
authored andcommitted
Support structure destinations for intra-document links
DEVSIX-7956 Autoported commit. Original commit hash: [14f989f3a]
1 parent 4b18b38 commit e383b98

File tree

12 files changed

+214
-9
lines changed

12 files changed

+214
-9
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2023 Apryse Group NV
4+
Authors: Apryse Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
using System;
24+
using System.IO;
25+
using iText.Html2pdf.Resolver.Font;
26+
using iText.Kernel.Pdf;
27+
using iText.Kernel.Utils;
28+
using iText.Kernel.XMP;
29+
using iText.Layout.Font;
30+
using iText.Test;
31+
using iText.Test.Pdfa;
32+
33+
namespace iText.Html2pdf {
34+
[NUnit.Framework.Category("IntegrationTest")]
35+
public class HtmlConverterPdfUA2Test : ExtendedITextTest {
36+
private static readonly String SOURCE_FOLDER = iText.Test.TestUtil.GetParentProjectDirectory(NUnit.Framework.TestContext
37+
.CurrentContext.TestDirectory) + "/resources/itext/html2pdf/HtmlConverterPdfUA2Test/";
38+
39+
private static readonly String DESTINATION_FOLDER = NUnit.Framework.TestContext.CurrentContext.TestDirectory
40+
+ "/test/itext/html2pdf/HtmlConverterPdfUA2Test/";
41+
42+
[NUnit.Framework.OneTimeSetUp]
43+
public static void BeforeClass() {
44+
CreateOrClearDestinationFolder(DESTINATION_FOLDER);
45+
}
46+
47+
[NUnit.Framework.Test]
48+
public virtual void SimpleLinkTest() {
49+
String sourceHtml = SOURCE_FOLDER + "simpleLink.html";
50+
String cmpPdf = SOURCE_FOLDER + "cmp_simpleLink.pdf";
51+
String destinationPdf = DESTINATION_FOLDER + "simpleLink.pdf";
52+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destinationPdf, new WriterProperties().SetPdfVersion
53+
(PdfVersion.PDF_2_0)));
54+
CreateSimplePdfUA2Document(pdfDocument);
55+
ConverterProperties converterProperties = new ConverterProperties();
56+
FontProvider fontProvider = new DefaultFontProvider(false, true, false);
57+
converterProperties.SetFontProvider(fontProvider);
58+
HtmlConverter.ConvertToPdf(new FileStream(sourceHtml, FileMode.Open, FileAccess.Read), pdfDocument, converterProperties
59+
);
60+
/* TODO: DEVSIX-7996 - Links created from html2pdf are not ua-2 compliant
61+
* Two verapdf errors are generated here:
62+
* 1. clause="8.9.4.1", Link annotation neither has a Contents entry nor alternate description.
63+
* 2. clause="8.5.1", Real content that does not possess the semantics of text objects and does not have
64+
* an alternate textual representation is not enclosed within Figure or Formula structure elements.
65+
*/
66+
CompareAndCheckCompliance(destinationPdf, cmpPdf, false);
67+
}
68+
69+
[NUnit.Framework.Test]
70+
public virtual void BackwardLinkTest() {
71+
String sourceHtml = SOURCE_FOLDER + "backwardLink.html";
72+
String cmpPdf = SOURCE_FOLDER + "cmp_backwardLink.pdf";
73+
String destinationPdf = DESTINATION_FOLDER + "backwardLink.pdf";
74+
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destinationPdf, new WriterProperties().SetPdfVersion
75+
(PdfVersion.PDF_2_0)));
76+
CreateSimplePdfUA2Document(pdfDocument);
77+
ConverterProperties converterProperties = new ConverterProperties();
78+
FontProvider fontProvider = new DefaultFontProvider(false, true, false);
79+
converterProperties.SetFontProvider(fontProvider);
80+
HtmlConverter.ConvertToPdf(new FileStream(sourceHtml, FileMode.Open, FileAccess.Read), pdfDocument, converterProperties
81+
);
82+
/* TODO: DEVSIX-7996 - Links created from html2pdf are not ua-2 compliant
83+
* Two verapdf errors are generated here:
84+
* 1. clause="8.9.4.1", Link annotation neither has a Contents entry nor alternate description.
85+
* 2. clause="8.5.1", Real content that does not possess the semantics of text objects and does not have
86+
* an alternate textual representation is not enclosed within Figure or Formula structure elements.
87+
*/
88+
CompareAndCheckCompliance(destinationPdf, cmpPdf, false);
89+
}
90+
91+
private void CreateSimplePdfUA2Document(PdfDocument pdfDocument) {
92+
byte[] bytes = File.ReadAllBytes(System.IO.Path.Combine(SOURCE_FOLDER + "simplePdfUA2.xmp"));
93+
XMPMeta xmpMeta = XMPMetaFactory.Parse(new MemoryStream(bytes));
94+
pdfDocument.SetXmpMetadata(xmpMeta);
95+
pdfDocument.SetTagged();
96+
pdfDocument.GetCatalog().SetViewerPreferences(new PdfViewerPreferences().SetDisplayDocTitle(true));
97+
pdfDocument.GetCatalog().SetLang(new PdfString("en-US"));
98+
PdfDocumentInfo info = pdfDocument.GetDocumentInfo();
99+
info.SetTitle("PdfUA2 Title");
100+
}
101+
102+
private static void CompareAndCheckCompliance(String destinationPdf, String cmpPdf, bool isExpectedOk) {
103+
if (isExpectedOk) {
104+
NUnit.Framework.Assert.IsNull(new VeraPdfValidator().Validate(destinationPdf));
105+
}
106+
else {
107+
NUnit.Framework.Assert.IsNotNull(new VeraPdfValidator().Validate(destinationPdf));
108+
}
109+
NUnit.Framework.Assert.IsNull(new CompareTool().CompareByContent(destinationPdf, cmpPdf, DESTINATION_FOLDER
110+
, "diff_simple_"));
111+
}
112+
}
113+
}

itext.tests/itext.html2pdf.tests/itext/html2pdf/attach/util/LinkHelperTest.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ You should have received a copy of the GNU Affero General Public License
2121
along with this program. If not, see <https://www.gnu.org/licenses/>.
2222
*/
2323
using System;
24+
using iText.Commons.Datastructures;
2425
using iText.Html2pdf;
2526
using iText.Html2pdf.Attach;
2627
using iText.Html2pdf.Attach.Impl.Tags;
2728
using iText.Html2pdf.Css.Resolve.Func.Counter;
2829
using iText.Html2pdf.Html;
30+
using iText.Kernel.Pdf;
2931
using iText.Layout.Properties;
3032
using iText.StyledXmlParser.Jsoup.Nodes;
3133
using iText.StyledXmlParser.Node.Impl.Jsoup.Node;
@@ -45,8 +47,10 @@ public virtual void CreateDestinationDestinationTest() {
4547
ProcessorContext context = new ProcessorContext(new ConverterProperties());
4648
context.GetLinkContext().ScanForIds(elementNode);
4749
LinkHelper.CreateDestination(worker, elementNode, context);
48-
NUnit.Framework.Assert.AreEqual("some_id", worker.GetElementResult().GetProperty<String>(Property.DESTINATION
49-
));
50+
Object destination = worker.GetElementResult().GetProperty<Object>(Property.DESTINATION);
51+
Tuple2<String, PdfDictionary> destTuple = (Tuple2<String, PdfDictionary>)destination;
52+
NUnit.Framework.Assert.AreEqual("some_id", destTuple.GetFirst());
53+
NUnit.Framework.Assert.AreEqual(new PdfString("some_id"), destTuple.GetSecond().Get(PdfName.D));
5054
}
5155

5256
[NUnit.Framework.Test]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
5+
</head>
6+
<body>
7+
8+
<div id="15=15">Target div</div>
9+
<div style="height:1000px">Some content</div>
10+
<a href="#15=15">Long link text to bottom div. Long link text to bottom div. Long link text to bottom div.
11+
Long link text to bottom div. Long link text to bottom div. Long link text to bottom div.</a>
12+
13+
</body>
14+
</html>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
5+
</head>
6+
<body>
7+
8+
<a href="#15=15">Long link text to bottom div. Long link text to bottom div. Long link text to bottom div.
9+
Long link text to bottom div. Long link text to bottom div. Long link text to bottom div.</a>
10+
<div style="height:1000px">Some content</div>
11+
<div id="15=15">Target div</div>
12+
13+
</body>
14+
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.0-jc003">
2+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
3+
<rdf:Description rdf:about=""
4+
xmlns:dc="http://purl.org/dc/elements/1.1/"
5+
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
6+
xmlns:pdf="http://ns.adobe.com/pdf/1.3/"
7+
xmlns:pdfuaid="http://www.aiim.org/pdfua/ns/id/"
8+
dc:format="application/pdf">
9+
<pdfuaid:part>2</pdfuaid:part>
10+
<pdfuaid:rev>2024</pdfuaid:rev>
11+
</rdf:Description>
12+
</rdf:RDF>
13+
</x:xmpmeta>

itext/itext.html2pdf/itext/html2pdf/attach/impl/LinkContext.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ You should have received a copy of the GNU Affero General Public License
2323
using System;
2424
using System.Collections.Generic;
2525
using iText.Html2pdf.Html;
26+
using iText.Kernel.Pdf.Annot;
2627
using iText.StyledXmlParser.Node;
2728

2829
namespace iText.Html2pdf.Attach.Impl {
@@ -39,6 +40,14 @@ public class LinkContext {
3940
/// <summary>the ids currently in use as valid link destinations</summary>
4041
private ICollection<String> linkDestinations = new HashSet<String>();
4142

43+
/// <summary>Link annotations per destination id.</summary>
44+
/// <remarks>
45+
/// Link annotations per destination id. Used to cache link annotations to be able to pass the same annotation
46+
/// to different model elements.
47+
/// </remarks>
48+
private IDictionary<String, PdfLinkAnnotation> linkAnnotations = new Dictionary<String, PdfLinkAnnotation>
49+
();
50+
4251
/// <summary>Construct an (empty) LinkContext</summary>
4352
public LinkContext() {
4453
}
@@ -80,5 +89,19 @@ public virtual iText.Html2pdf.Attach.Impl.LinkContext ScanForIds(INode root) {
8089
public virtual bool IsUsedLinkDestination(String linkDestination) {
8190
return linkDestinations.Contains(linkDestination);
8291
}
92+
93+
/// <summary>Add link annotation to the context.</summary>
94+
/// <param name="id">link destination.</param>
95+
/// <param name="annot">link annotation to store.</param>
96+
public virtual void AddLinkAnnotation(String id, PdfLinkAnnotation annot) {
97+
linkAnnotations.Put(id, annot);
98+
}
99+
100+
/// <summary>Get link annotation.</summary>
101+
/// <param name="id">link destination.</param>
102+
/// <returns>link annotation for the given link destination.</returns>
103+
public virtual PdfLinkAnnotation GetLinkAnnotation(String id) {
104+
return linkAnnotations.Get(id);
105+
}
83106
}
84107
}

itext/itext.html2pdf/itext/html2pdf/attach/impl/tags/ABlockTagWorker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public override void ProcessEnd(IElementNode element, ProcessorContext context)
6969
}
7070
}
7171
((Div)GetElementResult()).GetAccessibilityProperties().SetRole(StandardRoles.LINK);
72-
LinkHelper.ApplyLinkAnnotation(GetElementResult(), url);
72+
LinkHelper.ApplyLinkAnnotation(GetElementResult(), url, context);
7373
}
7474
if (GetElementResult() != null) {
7575
String name = element.GetAttribute(AttributeConstants.NAME);

itext/itext.html2pdf/itext/html2pdf/attach/impl/tags/ATagWorker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public override void ProcessEnd(IElementNode element, ProcessorContext context)
9898
}
9999
GetAllElements()[i] = simulatedDiv;
100100
}
101-
LinkHelper.ApplyLinkAnnotation(GetAllElements()[i], url);
101+
LinkHelper.ApplyLinkAnnotation(GetAllElements()[i], url, context);
102102
}
103103
}
104104
if (!GetAllElements().IsEmpty()) {

0 commit comments

Comments
 (0)