@jasy/pdf 1.0.0-alpha.4 → 1.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/api/content.d.ts +3 -0
- package/dist/api/content.js +1 -0
- package/dist/api/structure.d.ts +19 -0
- package/dist/api/structure.js +25 -0
- package/dist/api/table.js +13 -5
- package/dist/api/text.d.ts +4 -1
- package/dist/api/text.js +1 -0
- package/dist/elements/image-element.d.ts +5 -1
- package/dist/elements/image-element.js +3 -1
- package/dist/elements/layout/struct-group.d.ts +28 -0
- package/dist/elements/layout/struct-group.js +42 -0
- package/dist/elements/pdf-element.d.ts +4 -0
- package/dist/elements/pdf-element.js +15 -0
- package/dist/elements/text-element.d.ts +7 -1
- package/dist/elements/text-element.js +5 -2
- package/dist/ir/display-list.d.ts +79 -1
- package/dist/layout/fragmentation.js +9 -2
- package/dist/renderer/emoji-image.d.ts +9 -0
- package/dist/renderer/emoji-image.js +68 -0
- package/dist/renderer/image-renderer.d.ts +1 -1
- package/dist/renderer/image-renderer.js +10 -2
- package/dist/renderer/page-renderer.js +19 -4
- package/dist/renderer/pdf-backend.d.ts +6 -2
- package/dist/renderer/pdf-backend.js +59 -3
- package/dist/renderer/pdf-renderer.js +11 -0
- package/dist/renderer/struct-group-renderer.d.ts +10 -0
- package/dist/renderer/struct-group-renderer.js +24 -0
- package/dist/renderer/text-renderer.d.ts +3 -0
- package/dist/renderer/text-renderer.js +182 -2
- package/dist/text/emoji-codepoints.d.ts +1 -0
- package/dist/text/emoji-codepoints.js +29 -0
- package/dist/text/line-breaker.js +6 -3
- package/dist/utils/pdf-object-manager.d.ts +20 -1
- package/dist/utils/pdf-object-manager.js +0 -0
- package/dist/utils/struct-tree.d.ts +44 -0
- package/dist/utils/struct-tree.js +130 -0
- package/dist/utils/ttf-parser.d.ts +73 -0
- package/dist/utils/ttf-parser.js +456 -16
- package/dist/utils/ua-xmp.d.ts +9 -0
- package/dist/utils/ua-xmp.js +32 -0
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Escapes a PDF literal string ( ... ) - the language tag is safe ASCII, but /Alt can carry arbitrary text.
|
|
2
|
+
const esc = (s) => s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
|
|
3
|
+
// A structure role reaches the PDF as a raw name (/H1, /S /Table). Roles come from a controlled set,
|
|
4
|
+
// but restrict them to a safe PDF-name token anyway (defence-in-depth) so a stray "/", whitespace, or
|
|
5
|
+
// operator can never corrupt the content stream / object dictionary. Empty after stripping -> /Span.
|
|
6
|
+
const roleName = (role) => role.replace(/[^A-Za-z0-9]/g, "") || "Span";
|
|
7
|
+
/** Per-page tagging context: hands out page-local MCIDs and links them to their StructElem. */
|
|
8
|
+
export class PageStructContext {
|
|
9
|
+
constructor(structParents, tree) {
|
|
10
|
+
this.structParents = structParents;
|
|
11
|
+
this.tree = tree;
|
|
12
|
+
this.mcid = 0;
|
|
13
|
+
}
|
|
14
|
+
/** Marked-content delimiters for one drawable node. Tagged content gets an MCID linked to its (already
|
|
15
|
+
* registered) StructElem; untagged content becomes an Artifact (decoration, skipped by assistive tech). */
|
|
16
|
+
mark(tag) {
|
|
17
|
+
// Artifact = decoration, no properties -> BMC (BDC would need a second operand and is a syntax error).
|
|
18
|
+
if (!tag)
|
|
19
|
+
return { open: "/Artifact BMC\n", close: "EMC\n" };
|
|
20
|
+
const mcid = this.mcid++;
|
|
21
|
+
this.tree.linkMarkedContent(tag.key, this.structParents, mcid);
|
|
22
|
+
return { open: `/${roleName(tag.role)} <</MCID ${mcid}>> BDC\n`, close: "EMC\n" };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Builds the PDF structure tree for accessible tagging. One instance per document (held by the object manager). */
|
|
26
|
+
export class StructTree {
|
|
27
|
+
constructor() {
|
|
28
|
+
this.enabled = false;
|
|
29
|
+
this.lang = "en-US";
|
|
30
|
+
this.counter = 0;
|
|
31
|
+
this.structParentsCounter = 0;
|
|
32
|
+
this.elems = new Map(); // insertion order = render order = reading order
|
|
33
|
+
this.byStructId = new Map(); // element structId -> element key (dedup across pages)
|
|
34
|
+
this.stack = []; // open containers; top is the current parent
|
|
35
|
+
this.pageObjByStructParents = new Map();
|
|
36
|
+
}
|
|
37
|
+
/** Register a structure element for a logical element (keyed by its stable `structId`, so a paragraph or
|
|
38
|
+
* table split across pages resolves to the SAME element - MCIDs then accrue from every page). Its parent
|
|
39
|
+
* is the currently-open container, else the Document. Containers also push()/pop() around their children. */
|
|
40
|
+
openElement(structId, role, opts) {
|
|
41
|
+
const existing = this.byStructId.get(structId);
|
|
42
|
+
if (existing !== undefined)
|
|
43
|
+
return existing; // already opened on an earlier page/fragment
|
|
44
|
+
const key = this.counter++;
|
|
45
|
+
this.byStructId.set(structId, key);
|
|
46
|
+
this.elems.set(key, {
|
|
47
|
+
key,
|
|
48
|
+
role,
|
|
49
|
+
alt: opts?.alt,
|
|
50
|
+
parent: this.stack[this.stack.length - 1],
|
|
51
|
+
mc: [],
|
|
52
|
+
});
|
|
53
|
+
return key;
|
|
54
|
+
}
|
|
55
|
+
push(key) {
|
|
56
|
+
this.stack.push(key);
|
|
57
|
+
}
|
|
58
|
+
pop() {
|
|
59
|
+
this.stack.pop();
|
|
60
|
+
}
|
|
61
|
+
/** Begin a page: allocates its StructParents index (its slot in the ParentTree). */
|
|
62
|
+
beginPage() {
|
|
63
|
+
return new PageStructContext(this.structParentsCounter++, this);
|
|
64
|
+
}
|
|
65
|
+
/** Tie a page's StructParents index to its now-known page object number (for /Pg references). */
|
|
66
|
+
setPageObject(structParents, pageObjNum) {
|
|
67
|
+
this.pageObjByStructParents.set(structParents, pageObjNum);
|
|
68
|
+
}
|
|
69
|
+
/** Link a page-local MCID to an element (called by the backend while serializing). */
|
|
70
|
+
linkMarkedContent(key, structParents, mcid) {
|
|
71
|
+
this.elems.get(key)?.mc.push({ structParents, mcid });
|
|
72
|
+
}
|
|
73
|
+
/** Emit the struct-tree objects and return the catalog additions (`/MarkInfo`, `/StructTreeRoot`, `/Lang`).
|
|
74
|
+
* A no-op returning "" when disabled or nothing was tagged, so a plain document stays byte-identical. */
|
|
75
|
+
finalize(om) {
|
|
76
|
+
if (!this.enabled || this.elems.size === 0)
|
|
77
|
+
return "";
|
|
78
|
+
// Reserve object numbers first - root <-> Document <-> elements <-> ParentTree reference each other.
|
|
79
|
+
const rootNum = om.addObject("");
|
|
80
|
+
const docNum = om.addObject("");
|
|
81
|
+
const parentTreeNum = om.addObject("");
|
|
82
|
+
const objNum = new Map();
|
|
83
|
+
for (const key of this.elems.keys())
|
|
84
|
+
objNum.set(key, om.addObject(""));
|
|
85
|
+
const pg = (structParents) => this.pageObjByStructParents.get(structParents);
|
|
86
|
+
const childrenOf = (parent) => [...this.elems.values()].filter((e) => e.parent === parent); // Map order = reading order
|
|
87
|
+
// One StructElem per registered element: /K = its child elements ++ its marked-content references.
|
|
88
|
+
for (const e of this.elems.values()) {
|
|
89
|
+
const kids = [
|
|
90
|
+
...childrenOf(e.key).map((c) => `${objNum.get(c.key)} 0 R`),
|
|
91
|
+
...e.mc.map((m) => `<< /Type /MCR /Pg ${pg(m.structParents)} 0 R /MCID ${m.mcid} >>`),
|
|
92
|
+
].join(" ");
|
|
93
|
+
const alt = e.alt ? ` /Alt (${esc(e.alt)})` : "";
|
|
94
|
+
// PDF/UA 7.5: a header cell needs a Scope so a reader can associate data cells with it. Our headers
|
|
95
|
+
// are a header ROW (the Table `header` option), so each TH is the head of its Column.
|
|
96
|
+
const attr = e.role === "TH" ? " /A << /O /Table /Scope /Column >>" : "";
|
|
97
|
+
const parentRef = e.parent !== undefined ? objNum.get(e.parent) : docNum;
|
|
98
|
+
om.replaceObject(objNum.get(e.key), `<< /Type /StructElem /S /${roleName(e.role)} /P ${parentRef} 0 R${alt}${attr} /K [ ${kids} ] >>`);
|
|
99
|
+
}
|
|
100
|
+
// The Document element wraps the top-level elements in reading order.
|
|
101
|
+
const docKids = childrenOf(undefined)
|
|
102
|
+
.map((e) => `${objNum.get(e.key)} 0 R`)
|
|
103
|
+
.join(" ");
|
|
104
|
+
om.replaceObject(docNum, `<< /Type /StructElem /S /Document /P ${rootNum} 0 R /K [ ${docKids} ] >>`);
|
|
105
|
+
// ParentTree: for each page (StructParents index), an array where [mcid] = the owning StructElem.
|
|
106
|
+
const byPage = new Map();
|
|
107
|
+
for (const e of this.elems.values()) {
|
|
108
|
+
for (const m of e.mc) {
|
|
109
|
+
const list = byPage.get(m.structParents) ?? [];
|
|
110
|
+
list.push({ mcid: m.mcid, key: e.key });
|
|
111
|
+
byPage.set(m.structParents, list);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const nums = [...byPage.keys()]
|
|
115
|
+
.sort((a, b) => a - b)
|
|
116
|
+
.map((sp) => {
|
|
117
|
+
const refs = byPage
|
|
118
|
+
.get(sp)
|
|
119
|
+
.slice()
|
|
120
|
+
.sort((a, b) => a.mcid - b.mcid)
|
|
121
|
+
.map((r) => `${objNum.get(r.key)} 0 R`)
|
|
122
|
+
.join(" ");
|
|
123
|
+
return `${sp} [ ${refs} ]`;
|
|
124
|
+
});
|
|
125
|
+
om.replaceObject(parentTreeNum, `<< /Nums [ ${nums.join(" ")} ] >>`);
|
|
126
|
+
om.replaceObject(rootNum, `<< /Type /StructTreeRoot /K ${docNum} 0 R /ParentTree ${parentTreeNum} 0 R ` +
|
|
127
|
+
`/ParentTreeNextKey ${this.structParentsCounter} >>`);
|
|
128
|
+
return `/MarkInfo << /Marked true >> /StructTreeRoot ${rootNum} 0 R /Lang (${esc(this.lang)})`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -1,3 +1,52 @@
|
|
|
1
|
+
export type GlyphPathCommand = {
|
|
2
|
+
type: "M";
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
} | {
|
|
6
|
+
type: "L";
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
} | {
|
|
10
|
+
type: "Q";
|
|
11
|
+
cx: number;
|
|
12
|
+
cy: number;
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
} | {
|
|
16
|
+
type: "Z";
|
|
17
|
+
};
|
|
18
|
+
export interface RGBA {
|
|
19
|
+
r: number;
|
|
20
|
+
g: number;
|
|
21
|
+
b: number;
|
|
22
|
+
a: number;
|
|
23
|
+
}
|
|
24
|
+
export interface ColorStop {
|
|
25
|
+
offset: number;
|
|
26
|
+
color: RGBA | null;
|
|
27
|
+
}
|
|
28
|
+
export type Paint = {
|
|
29
|
+
type: "solid";
|
|
30
|
+
color: RGBA | null;
|
|
31
|
+
} | {
|
|
32
|
+
type: "linearGradient";
|
|
33
|
+
p0: [number, number];
|
|
34
|
+
p1: [number, number];
|
|
35
|
+
stops: ColorStop[];
|
|
36
|
+
extend: "pad" | "repeat" | "reflect";
|
|
37
|
+
} | {
|
|
38
|
+
type: "radialGradient";
|
|
39
|
+
c0: [number, number, number];
|
|
40
|
+
c1: [number, number, number];
|
|
41
|
+
stops: ColorStop[];
|
|
42
|
+
extend: "pad" | "repeat" | "reflect";
|
|
43
|
+
};
|
|
44
|
+
export type Affine = [number, number, number, number, number, number];
|
|
45
|
+
export interface ColorGlyphLayer {
|
|
46
|
+
glyphId: number;
|
|
47
|
+
paint: Paint;
|
|
48
|
+
transform?: Affine;
|
|
49
|
+
}
|
|
1
50
|
export declare class TTFParser {
|
|
2
51
|
private data;
|
|
3
52
|
unitsPerEm: number;
|
|
@@ -6,10 +55,16 @@ export declare class TTFParser {
|
|
|
6
55
|
bbox: [number, number, number, number];
|
|
7
56
|
private numGlyphs;
|
|
8
57
|
private numHMetrics;
|
|
58
|
+
private indexToLocFormat;
|
|
9
59
|
private advanceWidths;
|
|
10
60
|
private cmap;
|
|
11
61
|
private cmapGroups;
|
|
12
62
|
private tables;
|
|
63
|
+
private colrBase;
|
|
64
|
+
private colrLayersOffset;
|
|
65
|
+
private palette;
|
|
66
|
+
private colrBaseV1;
|
|
67
|
+
private colrLayerListOffset;
|
|
13
68
|
constructor(data: Uint8Array);
|
|
14
69
|
getData(): Uint8Array;
|
|
15
70
|
glyphCount(): number;
|
|
@@ -26,4 +81,22 @@ export declare class TTFParser {
|
|
|
26
81
|
private readCmap;
|
|
27
82
|
private readCmapFormat4;
|
|
28
83
|
private readCmapFormat12;
|
|
84
|
+
hasGlyfOutlines(): boolean;
|
|
85
|
+
private glyfRange;
|
|
86
|
+
getGlyphPath(glyphId: number): GlyphPathCommand[];
|
|
87
|
+
private parseSimpleGlyph;
|
|
88
|
+
private readCoordinates;
|
|
89
|
+
private contourToCommands;
|
|
90
|
+
hasColorGlyphs(): boolean;
|
|
91
|
+
getColorGlyph(glyphId: number): ColorGlyphLayer[] | null;
|
|
92
|
+
private walkColrLayers;
|
|
93
|
+
private resolveFill;
|
|
94
|
+
private readColorLine;
|
|
95
|
+
private paletteColor;
|
|
96
|
+
private u24;
|
|
97
|
+
private f2dot14;
|
|
98
|
+
private i32;
|
|
99
|
+
private readAffine;
|
|
100
|
+
private readColr;
|
|
101
|
+
private readCpal;
|
|
29
102
|
}
|