Skip to content

Commit 0b01ef4

Browse files
adyryakgn268
andauthored
Accessibility: Fixed Link annotation is not nested inside a Link structure element (#1664)
* Accessibility: Fixed Link annotation is not nested inside a Link structure element * Accessibility: Fixed Link annotation is not nested inside a Link structure element * Changelog * Fix links leakage into subsequent structures * Prettier --------- Co-authored-by: akowalczewski <akowalczewski@gn.com>
1 parent d010815 commit 0b01ef4

15 files changed

+378
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ playground/
66
build/
77
js/
88
.vscode
9+
.idea
910
coverage
1011
package-lock.json
1112
/examples/browserify/bundle.js

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
### Unreleased
44

55
- Fix garbled text copying in Chrome/Edge for PDFs with >256 unique characters (#1659)
6+
- Fix Link accessibility issues
67

78
### [v0.17.2] - 2025-08-30
89

examples/accessible-links.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
var PDFDocument = require('../');
2+
var fs = require('fs');
3+
4+
// Create a new PDFDocument
5+
var doc = new PDFDocument({
6+
autoFirstPage: true,
7+
bufferPages: true,
8+
pdfVersion: '1.5',
9+
// @ts-ignore PDF/UA needs to be enforced for PAC accessibility checker
10+
subset: 'PDF/UA',
11+
tagged: true,
12+
displayTitle: true,
13+
lang: 'en-US',
14+
fontSize: 12,
15+
});
16+
17+
doc.pipe(fs.createWriteStream('accessible-links.pdf'));
18+
19+
// Set some meta data
20+
doc.info['Title'] = 'Test Document';
21+
doc.info['Author'] = 'Devon Govett';
22+
23+
// Initialise document logical structure
24+
var struct = doc.struct('Document');
25+
doc.addStructure(struct);
26+
27+
// Register a font name for use later
28+
doc.registerFont('Palatino', 'fonts/PalatinoBold.ttf');
29+
30+
// Set the font and draw some text
31+
struct.add(
32+
doc.struct('P', () => {
33+
doc
34+
.font('Palatino')
35+
.fontSize(25)
36+
.text('Some text with an embedded font! ', 100, 100);
37+
}),
38+
);
39+
40+
// Add another page
41+
doc.addPage();
42+
43+
// Add some text with annotations
44+
var linkSection = doc.struct('Sect');
45+
struct.add(linkSection);
46+
47+
var paragraph = doc.struct('P');
48+
linkSection.add(paragraph);
49+
50+
paragraph.add(
51+
doc.struct('Span', () => {
52+
doc
53+
.font('Palatino')
54+
.fillColor('black')
55+
.text('This is some text before ', 100, 100, {
56+
continued: true,
57+
});
58+
}),
59+
);
60+
61+
paragraph.add(
62+
doc.struct(
63+
'Link',
64+
{
65+
alt: 'Here is a link! ',
66+
},
67+
() => {
68+
doc.fillColor('blue').text('Here is a link!', {
69+
link: 'http://google.com/',
70+
underline: true,
71+
continued: true,
72+
});
73+
},
74+
),
75+
);
76+
77+
paragraph.add(
78+
doc.struct('Span', () => {
79+
doc.fillColor('black').text(' and this is text after the link.');
80+
}),
81+
);
82+
83+
paragraph.end();
84+
linkSection.end();
85+
86+
// End and flush the document
87+
doc.end();

examples/accessible-links.pdf

15.5 KB
Binary file not shown.

examples/kitchen-sink-accessible.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ var doc = new PDFDocument({
77
pdfVersion: '1.5',
88
lang: 'en-US',
99
tagged: true,
10-
displayTitle: true
10+
displayTitle: true,
11+
// @ts-ignore PDF/UA needs to be enforced for PAC accessibility checker
12+
subset: 'PDF/UA',
1113
});
1214

1315
doc.pipe(fs.createWriteStream('kitchen-sink-accessible.pdf'));
491 Bytes
Binary file not shown.

lib/mixins/annotations.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import PDFAnnotationReference from '../structure_annotation';
2+
13
export default {
24
annotate(x, y, w, h, options) {
35
options.Type = 'Annot';
@@ -19,6 +21,9 @@ export default {
1921
options.Dest = new String(options.Dest);
2022
}
2123

24+
const structParent = options.structParent;
25+
delete options.structParent;
26+
2227
// Capitalize keys
2328
for (let key in options) {
2429
const val = options[key];
@@ -27,6 +32,12 @@ export default {
2732

2833
const ref = this.ref(options);
2934
this.page.annotations.push(ref);
35+
36+
if (structParent && typeof structParent.add === 'function') {
37+
const annotRef = new PDFAnnotationReference(ref);
38+
structParent.add(annotRef);
39+
}
40+
3041
ref.end();
3142
return this;
3243
},
@@ -77,6 +88,10 @@ export default {
7788
options.A.end();
7889
}
7990

91+
if (options.structParent && !options.Contents) {
92+
options.Contents = new String('');
93+
}
94+
8095
return this.annotate(x, y, w, h, options);
8196
},
8297

lib/mixins/markings.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ export default {
9999
endMarkedContent() {
100100
this.page.markings.pop();
101101
this.addContent('EMC');
102+
if (this._textOptions) {
103+
delete this._textOptions.link;
104+
delete this._textOptions.goTo;
105+
delete this._textOptions.destination;
106+
delete this._textOptions.underline;
107+
delete this._textOptions.strike;
108+
}
102109
return this;
103110
},
104111

lib/mixins/text.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,21 @@ export default {
531531

532532
// create link annotations if the link option is given
533533
if (options.link != null) {
534-
this.link(x, y, renderedWidth, this.currentLineHeight(), options.link);
534+
const linkOptions = {};
535+
if (
536+
this._currentStructureElement &&
537+
this._currentStructureElement.dictionary.data.S === 'Link'
538+
) {
539+
linkOptions.structParent = this._currentStructureElement;
540+
}
541+
this.link(
542+
x,
543+
y,
544+
renderedWidth,
545+
this.currentLineHeight(),
546+
options.link,
547+
linkOptions,
548+
);
535549
}
536550
if (options.goTo != null) {
537551
this.goTo(x, y, renderedWidth, this.currentLineHeight(), options.goTo);

lib/structure_annotation.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class PDFAnnotationReference {
2+
constructor(annotationRef) {
3+
this.annotationRef = annotationRef;
4+
}
5+
}
6+
7+
export default PDFAnnotationReference;

0 commit comments

Comments
 (0)