@jasy/pdf 1.0.0-alpha.2 → 1.0.0-alpha.4
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/LICENSE +21 -0
- package/README.md +24 -14
- package/dist/api/args.d.ts +1 -1
- package/dist/api/args.js +2 -5
- package/dist/api/color.d.ts +4 -4
- package/dist/api/color.js +11 -17
- package/dist/api/content.d.ts +8 -8
- package/dist/api/content.js +23 -24
- package/dist/api/descriptor.d.ts +2 -2
- package/dist/api/descriptor.js +75 -31
- package/dist/api/index.d.ts +8 -8
- package/dist/api/index.js +8 -24
- package/dist/api/insets.js +4 -8
- package/dist/api/layout.d.ts +18 -16
- package/dist/api/layout.js +41 -52
- package/dist/api/structure.d.ts +65 -13
- package/dist/api/structure.js +140 -88
- package/dist/api/table.d.ts +5 -5
- package/dist/api/table.js +28 -24
- package/dist/api/text.d.ts +27 -2
- package/dist/api/text.js +45 -27
- package/dist/assets/font-data.d.ts +2 -0
- package/dist/assets/font-data.js +21 -0
- package/dist/assets/font-data.ts +37 -0
- package/dist/common/color.js +1 -5
- package/dist/constants/page-sizes.js +3 -6
- package/dist/constants/pdf-parts.js +1 -4
- package/dist/crypto/security-handler.d.ts +46 -0
- package/dist/crypto/security-handler.js +129 -0
- package/dist/crypto/webcrypto.d.ts +11 -0
- package/dist/crypto/webcrypto.js +62 -0
- package/dist/elements/container-element.d.ts +4 -4
- package/dist/elements/container-element.js +9 -13
- package/dist/elements/image-element.d.ts +18 -2
- package/dist/elements/image-element.js +81 -105
- package/dist/elements/index.d.ts +12 -11
- package/dist/elements/index.js +12 -29
- package/dist/elements/layout/default-text-style-element.d.ts +30 -0
- package/dist/elements/layout/default-text-style-element.js +47 -0
- package/dist/elements/layout/deferred-element.d.ts +3 -3
- package/dist/elements/layout/deferred-element.js +4 -8
- package/dist/elements/layout/expanded-element.d.ts +3 -3
- package/dist/elements/layout/expanded-element.js +10 -14
- package/dist/elements/layout/padding-element.d.ts +3 -3
- package/dist/elements/layout/padding-element.js +9 -14
- package/dist/elements/layout/positioned-element.d.ts +17 -4
- package/dist/elements/layout/positioned-element.js +29 -25
- package/dist/elements/layout/repeating-header-element.d.ts +3 -3
- package/dist/elements/layout/repeating-header-element.js +8 -12
- package/dist/elements/layout/sized-container-element.d.ts +2 -2
- package/dist/elements/layout/sized-container-element.js +6 -11
- package/dist/elements/line-element.d.ts +3 -3
- package/dist/elements/line-element.js +5 -10
- package/dist/elements/page-element.d.ts +8 -6
- package/dist/elements/page-element.js +20 -23
- package/dist/elements/pdf-document-element.d.ts +10 -4
- package/dist/elements/pdf-document-element.js +11 -10
- package/dist/elements/pdf-element.d.ts +12 -3
- package/dist/elements/pdf-element.js +10 -19
- package/dist/elements/rectangle-element.d.ts +5 -5
- package/dist/elements/rectangle-element.js +19 -25
- package/dist/elements/row-element.d.ts +3 -3
- package/dist/elements/row-element.js +7 -11
- package/dist/elements/text-element.d.ts +37 -11
- package/dist/elements/text-element.js +64 -39
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -19
- package/dist/ir/display-list.d.ts +4 -2
- package/dist/ir/display-list.js +1 -2
- package/dist/layout/box-constraints.js +2 -6
- package/dist/layout/fragmentation.d.ts +8 -1
- package/dist/layout/fragmentation.js +22 -10
- package/dist/platform/browser-fs.d.ts +2 -0
- package/dist/platform/browser-fs.js +9 -0
- package/dist/platform/browser-image.d.ts +5 -0
- package/dist/platform/browser-image.js +13 -0
- package/dist/platform/node-fs.d.ts +2 -0
- package/dist/platform/node-fs.js +10 -0
- package/dist/platform/node-image.d.ts +5 -0
- package/dist/platform/node-image.js +9 -0
- package/dist/renderer/container-renderer.d.ts +3 -3
- package/dist/renderer/container-renderer.js +12 -27
- package/dist/renderer/default-text-style-renderer.d.ts +6 -0
- package/dist/renderer/default-text-style-renderer.js +10 -0
- package/dist/renderer/deferred-renderer.d.ts +3 -3
- package/dist/renderer/deferred-renderer.js +8 -23
- package/dist/renderer/expanded-renderer.d.ts +3 -3
- package/dist/renderer/expanded-renderer.js +6 -21
- package/dist/renderer/image-renderer.d.ts +3 -3
- package/dist/renderer/image-renderer.js +77 -75
- package/dist/renderer/index.d.ts +10 -10
- package/dist/renderer/index.js +10 -26
- package/dist/renderer/line-renderer.d.ts +3 -3
- package/dist/renderer/line-renderer.js +13 -28
- package/dist/renderer/padding-renderer.d.ts +3 -3
- package/dist/renderer/padding-renderer.js +6 -21
- package/dist/renderer/page-renderer.d.ts +2 -2
- package/dist/renderer/page-renderer.js +61 -77
- package/dist/renderer/pdf-backend.d.ts +2 -2
- package/dist/renderer/pdf-backend.js +21 -19
- package/dist/renderer/pdf-config.js +4 -7
- package/dist/renderer/pdf-document-class.d.ts +5 -5
- package/dist/renderer/pdf-document-class.js +24 -41
- package/dist/renderer/pdf-document-renderer.d.ts +3 -3
- package/dist/renderer/pdf-document-renderer.js +71 -85
- package/dist/renderer/pdf-renderer.d.ts +2 -2
- package/dist/renderer/pdf-renderer.js +85 -93
- package/dist/renderer/positioned-renderer.d.ts +3 -3
- package/dist/renderer/positioned-renderer.js +8 -23
- package/dist/renderer/rectangle-renderer.d.ts +3 -3
- package/dist/renderer/rectangle-renderer.js +45 -52
- package/dist/renderer/repeating-header-renderer.d.ts +3 -3
- package/dist/renderer/repeating-header-renderer.js +11 -26
- package/dist/renderer/row-renderer.d.ts +3 -3
- package/dist/renderer/row-renderer.js +12 -27
- package/dist/renderer/text-renderer.d.ts +6 -5
- package/dist/renderer/text-renderer.js +33 -42
- package/dist/text/line-breaker.d.ts +8 -5
- package/dist/text/line-breaker.js +67 -16
- package/dist/text/text-style.d.ts +25 -0
- package/dist/text/text-style.js +29 -0
- package/dist/utils/afm-parser.js +3 -13
- package/dist/utils/bytes.d.ts +24 -0
- package/dist/utils/bytes.js +76 -0
- package/dist/utils/flex-layout.d.ts +2 -2
- package/dist/utils/flex-layout.js +15 -20
- package/dist/utils/font-metrics.d.ts +1 -1
- package/dist/utils/font-metrics.js +1 -2
- package/dist/utils/font-path.js +3 -6
- package/dist/utils/image-helper.d.ts +6 -5
- package/dist/utils/image-helper.js +101 -111
- package/dist/utils/md5.d.ts +4 -0
- package/dist/utils/md5.js +79 -0
- package/dist/utils/pdf-object-manager.d.ts +18 -6
- package/dist/utils/pdf-object-manager.js +0 -0
- package/dist/utils/renderer-registry.js +1 -5
- package/dist/utils/ttf-parser.d.ts +2 -2
- package/dist/utils/ttf-parser.js +32 -36
- package/dist/utils/ttf-subsetter.d.ts +1 -1
- package/dist/utils/ttf-subsetter.js +40 -42
- package/dist/utils/utf8-to-windows1252-encoder.js +1 -4
- package/dist/validators/element-validator.d.ts +2 -2
- package/dist/validators/element-validator.js +9 -13
- package/package.json +14 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { OverflowPolicy } from "../layout/fragmentation.js";
|
|
2
|
+
import type { SecurityHandler } from "../crypto/security-handler.js";
|
|
3
|
+
import type { PDFConfig } from "../renderer/pdf-document-class.js";
|
|
4
|
+
import type { FontMetrics } from "./font-metrics.js";
|
|
3
5
|
interface FontIndexes {
|
|
4
6
|
fontIndex: number;
|
|
5
7
|
resourceIndex: number;
|
|
@@ -30,10 +32,18 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
30
32
|
private pdfVersion;
|
|
31
33
|
private documentId;
|
|
32
34
|
private compress;
|
|
35
|
+
private overflowPolicy;
|
|
36
|
+
private security?;
|
|
37
|
+
private encJobs;
|
|
38
|
+
private encryptObjNum?;
|
|
33
39
|
constructor();
|
|
34
40
|
addObject(content: string): number;
|
|
35
41
|
replaceObject(objectNumber: number, content: string): void;
|
|
36
42
|
setCompress(on: boolean): void;
|
|
43
|
+
setOverflowPolicy(policy: OverflowPolicy): void;
|
|
44
|
+
getOverflowPolicy(): OverflowPolicy;
|
|
45
|
+
private encToken;
|
|
46
|
+
private streamPayload;
|
|
37
47
|
private stream;
|
|
38
48
|
addContentStream(content: string): number;
|
|
39
49
|
changePDFConfig(config: PDFConfig): void;
|
|
@@ -42,8 +52,8 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
42
52
|
private getCurrentByteLength;
|
|
43
53
|
setParentObjectNumber(number: number): void;
|
|
44
54
|
getParentObjectNumber(): number;
|
|
45
|
-
registerImage(width: number, height: number, imageType: string, imageData: string): number;
|
|
46
|
-
attachFile(name: string, data:
|
|
55
|
+
registerImage(width: number, height: number, imageType: string, imageData: string, smaskData?: string): number;
|
|
56
|
+
attachFile(name: string, data: Uint8Array, opts?: {
|
|
47
57
|
relationship?: string;
|
|
48
58
|
mimeType?: string;
|
|
49
59
|
description?: string;
|
|
@@ -54,7 +64,7 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
54
64
|
}[];
|
|
55
65
|
setXmpMetadata(xml: string): void;
|
|
56
66
|
getXmpMetadata(): string | undefined;
|
|
57
|
-
setOutputIntent(icc:
|
|
67
|
+
setOutputIntent(icc: Uint8Array, opts?: {
|
|
58
68
|
identifier?: string;
|
|
59
69
|
info?: string;
|
|
60
70
|
}): void;
|
|
@@ -63,10 +73,12 @@ export declare class PDFObjectManager implements FontMetrics {
|
|
|
63
73
|
getPdfVersion(): string;
|
|
64
74
|
getHeader(): string;
|
|
65
75
|
enableDocumentId(): void;
|
|
76
|
+
setSecurityHandler(handler: SecurityHandler): void;
|
|
77
|
+
finalizeEncryption(): Promise<void>;
|
|
66
78
|
registerExtGState(fillAlpha: number, strokeAlpha: number): string;
|
|
67
79
|
getAllExtGStatesRaw(): Map<string, number>;
|
|
68
80
|
registerFont(fontName: string, fontStyle?: FontStyle, fullName?: string): FontIndexes;
|
|
69
|
-
registerCustomFont(name: string, data:
|
|
81
|
+
registerCustomFont(name: string, data: Uint8Array, style?: FontStyle): void;
|
|
70
82
|
private ensureEmitted;
|
|
71
83
|
finalizeCustomFonts(): void;
|
|
72
84
|
private subsetTag;
|
|
Binary file
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RendererRegistry = void 0;
|
|
4
|
-
class RendererRegistry {
|
|
1
|
+
export class RendererRegistry {
|
|
5
2
|
static register(elementClass, renderer) {
|
|
6
3
|
if (!RendererRegistry.renderers.has(elementClass)) {
|
|
7
4
|
RendererRegistry.renderers.set(elementClass, renderer);
|
|
@@ -15,5 +12,4 @@ class RendererRegistry {
|
|
|
15
12
|
return renderer.constructor.name === "AsyncFunction";
|
|
16
13
|
}
|
|
17
14
|
}
|
|
18
|
-
exports.RendererRegistry = RendererRegistry;
|
|
19
15
|
RendererRegistry.renderers = new Map();
|
|
@@ -10,8 +10,8 @@ export declare class TTFParser {
|
|
|
10
10
|
private cmap;
|
|
11
11
|
private cmapGroups;
|
|
12
12
|
private tables;
|
|
13
|
-
constructor(data:
|
|
14
|
-
getData():
|
|
13
|
+
constructor(data: Uint8Array);
|
|
14
|
+
getData(): Uint8Array;
|
|
15
15
|
glyphCount(): number;
|
|
16
16
|
glyphWidths(): number[];
|
|
17
17
|
reverseCmap(): Map<number, number>;
|
package/dist/utils/ttf-parser.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Parses the metric tables of a TrueType (.ttf) font - the binary counterpart to AFMParser.
|
|
3
2
|
// Slice 1: glyph advance widths (hmtx) + the Unicode→glyph map (cmap), enough to compute
|
|
4
3
|
// text width the same way AFMParser does for the standard-14. Embedding/subsetting come later.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class TTFParser {
|
|
4
|
+
import { i16, latin1FromBytes, u16, u32 } from "./bytes.js";
|
|
5
|
+
export class TTFParser {
|
|
8
6
|
constructor(data) {
|
|
9
7
|
this.data = data;
|
|
10
8
|
this.unitsPerEm = 1000;
|
|
@@ -20,18 +18,18 @@ class TTFParser {
|
|
|
20
18
|
this.tables = {};
|
|
21
19
|
this.readTableDirectory();
|
|
22
20
|
const head = this.table("head").offset;
|
|
23
|
-
this.unitsPerEm = this.data
|
|
24
|
-
this.numGlyphs = this.data
|
|
25
|
-
this.numHMetrics = this.data
|
|
21
|
+
this.unitsPerEm = u16(this.data, head + 18);
|
|
22
|
+
this.numGlyphs = u16(this.data, this.table("maxp").offset + 4);
|
|
23
|
+
this.numHMetrics = u16(this.data, this.table("hhea").offset + 34);
|
|
26
24
|
this.bbox = [
|
|
27
|
-
this.toGlyphSpace(this.data
|
|
28
|
-
this.toGlyphSpace(this.data
|
|
29
|
-
this.toGlyphSpace(this.data
|
|
30
|
-
this.toGlyphSpace(this.data
|
|
25
|
+
this.toGlyphSpace(i16(this.data, head + 36)), // xMin
|
|
26
|
+
this.toGlyphSpace(i16(this.data, head + 38)), // yMin
|
|
27
|
+
this.toGlyphSpace(i16(this.data, head + 40)), // xMax
|
|
28
|
+
this.toGlyphSpace(i16(this.data, head + 42)), // yMax
|
|
31
29
|
];
|
|
32
30
|
const hhea = this.table("hhea").offset;
|
|
33
|
-
this.ascent = this.toGlyphSpace(this.data
|
|
34
|
-
this.descent = this.toGlyphSpace(this.data
|
|
31
|
+
this.ascent = this.toGlyphSpace(i16(this.data, hhea + 4));
|
|
32
|
+
this.descent = this.toGlyphSpace(i16(this.data, hhea + 6));
|
|
35
33
|
this.readHmtx();
|
|
36
34
|
this.readCmap();
|
|
37
35
|
}
|
|
@@ -76,9 +74,8 @@ class TTFParser {
|
|
|
76
74
|
}
|
|
77
75
|
// Advance width of a glyph in font units. Glyphs past numHMetrics reuse the last advance.
|
|
78
76
|
getAdvanceWidth(glyphIndex) {
|
|
79
|
-
var _a;
|
|
80
77
|
const i = Math.min(glyphIndex, this.numHMetrics - 1);
|
|
81
|
-
return
|
|
78
|
+
return this.advanceWidths[i] ?? 0;
|
|
82
79
|
}
|
|
83
80
|
// Char width at fontSize, scaled from font units (em = unitsPerEm) to points.
|
|
84
81
|
getCharWidth(char, fontSize) {
|
|
@@ -99,13 +96,13 @@ class TTFParser {
|
|
|
99
96
|
return t;
|
|
100
97
|
}
|
|
101
98
|
readTableDirectory() {
|
|
102
|
-
const numTables = this.data
|
|
99
|
+
const numTables = u16(this.data, 4);
|
|
103
100
|
let p = 12; // after the offset table (sfntVersion + numTables + 3 fields)
|
|
104
101
|
for (let i = 0; i < numTables; i++) {
|
|
105
|
-
const tag = this.data.
|
|
102
|
+
const tag = latin1FromBytes(this.data.subarray(p, p + 4));
|
|
106
103
|
this.tables[tag] = {
|
|
107
|
-
offset: this.data
|
|
108
|
-
length: this.data
|
|
104
|
+
offset: u32(this.data, p + 8),
|
|
105
|
+
length: u32(this.data, p + 12),
|
|
109
106
|
};
|
|
110
107
|
p += 16;
|
|
111
108
|
}
|
|
@@ -116,20 +113,20 @@ class TTFParser {
|
|
|
116
113
|
let last = 0;
|
|
117
114
|
for (let i = 0; i < this.numGlyphs; i++) {
|
|
118
115
|
if (i < this.numHMetrics)
|
|
119
|
-
last = this.data
|
|
116
|
+
last = u16(this.data, o + i * 4);
|
|
120
117
|
this.advanceWidths.push(last);
|
|
121
118
|
}
|
|
122
119
|
}
|
|
123
120
|
readCmap() {
|
|
124
121
|
const base = this.table("cmap").offset;
|
|
125
|
-
const numTables = this.data
|
|
122
|
+
const numTables = u16(this.data, base + 2);
|
|
126
123
|
// Prefer the Windows Unicode BMP subtable (3,1); accept full-Unicode (3,10) or any (0,x).
|
|
127
124
|
let subtable = -1;
|
|
128
125
|
let p = base + 4;
|
|
129
126
|
for (let i = 0; i < numTables; i++) {
|
|
130
|
-
const platform = this.data
|
|
131
|
-
const encoding = this.data
|
|
132
|
-
const offset = this.data
|
|
127
|
+
const platform = u16(this.data, p);
|
|
128
|
+
const encoding = u16(this.data, p + 2);
|
|
129
|
+
const offset = u32(this.data, p + 4);
|
|
133
130
|
const unicode = (platform === 3 && (encoding === 1 || encoding === 10)) || platform === 0;
|
|
134
131
|
if (unicode) {
|
|
135
132
|
subtable = base + offset;
|
|
@@ -140,7 +137,7 @@ class TTFParser {
|
|
|
140
137
|
}
|
|
141
138
|
if (subtable < 0)
|
|
142
139
|
return;
|
|
143
|
-
const format = this.data
|
|
140
|
+
const format = u16(this.data, subtable);
|
|
144
141
|
if (format === 4)
|
|
145
142
|
this.readCmapFormat4(subtable);
|
|
146
143
|
else if (format === 12)
|
|
@@ -149,23 +146,23 @@ class TTFParser {
|
|
|
149
146
|
// Format 4: segment mapping. Four parallel arrays keyed by segment; the glyph comes either
|
|
150
147
|
// from idDelta or, when idRangeOffset != 0, from the glyphIdArray it points into.
|
|
151
148
|
readCmapFormat4(o) {
|
|
152
|
-
const segCountX2 = this.data
|
|
149
|
+
const segCountX2 = u16(this.data, o + 6);
|
|
153
150
|
const endCodes = o + 14;
|
|
154
151
|
const startCodes = endCodes + segCountX2 + 2; // +2 reservedPad
|
|
155
152
|
const idDeltas = startCodes + segCountX2;
|
|
156
153
|
const idRangeOffsets = idDeltas + segCountX2;
|
|
157
154
|
for (let i = 0; i < segCountX2 / 2; i++) {
|
|
158
|
-
const end = this.data
|
|
159
|
-
const start = this.data
|
|
160
|
-
const delta = this.data
|
|
161
|
-
const rangeOffset = this.data
|
|
155
|
+
const end = u16(this.data, endCodes + i * 2);
|
|
156
|
+
const start = u16(this.data, startCodes + i * 2);
|
|
157
|
+
const delta = u16(this.data, idDeltas + i * 2);
|
|
158
|
+
const rangeOffset = u16(this.data, idRangeOffsets + i * 2);
|
|
162
159
|
for (let c = start; c <= end && c !== 0xffff; c++) {
|
|
163
160
|
let glyph;
|
|
164
161
|
if (rangeOffset === 0) {
|
|
165
162
|
glyph = (c + delta) & 0xffff;
|
|
166
163
|
}
|
|
167
164
|
else {
|
|
168
|
-
glyph = this.data
|
|
165
|
+
glyph = u16(this.data, idRangeOffsets + i * 2 + rangeOffset + (c - start) * 2);
|
|
169
166
|
if (glyph !== 0)
|
|
170
167
|
glyph = (glyph + delta) & 0xffff;
|
|
171
168
|
}
|
|
@@ -176,16 +173,15 @@ class TTFParser {
|
|
|
176
173
|
}
|
|
177
174
|
// Format 12: sequential groups for full Unicode (incl. astral). Kept un-expanded.
|
|
178
175
|
readCmapFormat12(o) {
|
|
179
|
-
const nGroups = this.data
|
|
176
|
+
const nGroups = u32(this.data, o + 12);
|
|
180
177
|
let p = o + 16;
|
|
181
178
|
for (let i = 0; i < nGroups; i++) {
|
|
182
179
|
this.cmapGroups.push({
|
|
183
|
-
start: this.data
|
|
184
|
-
end: this.data
|
|
185
|
-
startGlyph: this.data
|
|
180
|
+
start: u32(this.data, p),
|
|
181
|
+
end: u32(this.data, p + 4),
|
|
182
|
+
startGlyph: u32(this.data, p + 8),
|
|
186
183
|
});
|
|
187
184
|
p += 12;
|
|
188
185
|
}
|
|
189
186
|
}
|
|
190
187
|
}
|
|
191
|
-
exports.TTFParser = TTFParser;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function subsetTTF(data:
|
|
1
|
+
export declare function subsetTTF(data: Uint8Array, used: Set<number>): Uint8Array;
|
|
@@ -1,32 +1,30 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Builds a subset TrueType font that KEEPS the original glyph ids: the PDF content stream emits
|
|
3
2
|
// original gids and /W + CIDToGIDMap=Identity stay valid, so only the font program shrinks. We drop
|
|
4
3
|
// the `glyf` outlines of unused glyphs (the bulk of the file) and rebuild `loca`; every other table
|
|
5
4
|
// is copied verbatim. The used-glyph set is closed over composite-glyph components first, so a glyph
|
|
6
5
|
// built from others (e.g. "ä") keeps its parts. Fonts without `glyf`/`loca` (CFF/OTF) pass through.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const u32 = (n) => n >>> 0;
|
|
6
|
+
import { concatBytes, i16, latin1FromBytes, u16, u32, wi16, wu16, wu32, writeLatin1, } from "./bytes.js";
|
|
7
|
+
const wrap32 = (n) => n >>> 0;
|
|
10
8
|
/** TrueType table checksum: sum of big-endian uint32 over the 4-byte-padded table data. */
|
|
11
9
|
function checksum(buf) {
|
|
12
10
|
let sum = 0;
|
|
13
11
|
for (let i = 0; i < buf.length; i += 4)
|
|
14
|
-
sum =
|
|
12
|
+
sum = wrap32(sum + u32(buf, i));
|
|
15
13
|
return sum;
|
|
16
14
|
}
|
|
17
15
|
/** Right-pad to a 4-byte boundary (table data is aligned in the sfnt). */
|
|
18
16
|
function pad4(buf) {
|
|
19
17
|
const rem = buf.length % 4;
|
|
20
|
-
return rem === 0 ? buf :
|
|
18
|
+
return rem === 0 ? buf : concatBytes([buf, new Uint8Array(4 - rem)]);
|
|
21
19
|
}
|
|
22
|
-
function subsetTTF(data, used) {
|
|
23
|
-
const numTables = data
|
|
20
|
+
export function subsetTTF(data, used) {
|
|
21
|
+
const numTables = u16(data, 4);
|
|
24
22
|
const dir = new Map();
|
|
25
23
|
let p = 12;
|
|
26
24
|
for (let i = 0; i < numTables; i++) {
|
|
27
|
-
dir.set(data.
|
|
28
|
-
offset: data
|
|
29
|
-
length: data
|
|
25
|
+
dir.set(latin1FromBytes(data.subarray(p, p + 4)), {
|
|
26
|
+
offset: u32(data, p + 8),
|
|
27
|
+
length: u32(data, p + 12),
|
|
30
28
|
});
|
|
31
29
|
p += 16;
|
|
32
30
|
}
|
|
@@ -34,14 +32,14 @@ function subsetTTF(data, used) {
|
|
|
34
32
|
if (!dir.has("glyf") || !dir.has("loca") || !dir.has("head") || !dir.has("maxp"))
|
|
35
33
|
return data;
|
|
36
34
|
const headOff = dir.get("head").offset;
|
|
37
|
-
const longLoca = data
|
|
38
|
-
const numGlyphs = data
|
|
35
|
+
const longLoca = i16(data, headOff + 50) === 1;
|
|
36
|
+
const numGlyphs = u16(data, dir.get("maxp").offset + 4);
|
|
39
37
|
const locaOff = dir.get("loca").offset;
|
|
40
38
|
const glyfOff = dir.get("glyf").offset;
|
|
41
39
|
// Original glyph offsets (numGlyphs + 1 entries; short loca stores half-offsets).
|
|
42
40
|
const loca = [];
|
|
43
41
|
for (let g = 0; g <= numGlyphs; g++) {
|
|
44
|
-
loca.push(longLoca ? data
|
|
42
|
+
loca.push(longLoca ? u32(data, locaOff + g * 4) : u16(data, locaOff + g * 2) * 2);
|
|
45
43
|
}
|
|
46
44
|
const glyphBytes = (g) => data.subarray(glyfOff + loca[g], glyfOff + loca[g + 1]);
|
|
47
45
|
// Close the used set over composite components (and always keep .notdef = gid 0).
|
|
@@ -53,12 +51,12 @@ function subsetTTF(data, used) {
|
|
|
53
51
|
continue;
|
|
54
52
|
keep.add(g);
|
|
55
53
|
const gd = glyphBytes(g);
|
|
56
|
-
if (gd.length >= 10 && gd
|
|
54
|
+
if (gd.length >= 10 && i16(gd, 0) < 0) {
|
|
57
55
|
// Composite glyph: walk the component records to collect the referenced gids.
|
|
58
56
|
let q = 10; // numberOfContours(2) + xMin/yMin/xMax/yMax(8)
|
|
59
57
|
for (;;) {
|
|
60
|
-
const flags = gd
|
|
61
|
-
stack.push(gd
|
|
58
|
+
const flags = u16(gd, q);
|
|
59
|
+
stack.push(u16(gd, q + 2));
|
|
62
60
|
q += 4 + (flags & 0x0001 ? 4 : 2); // ARG_1_AND_2_ARE_WORDS
|
|
63
61
|
if (flags & 0x0008)
|
|
64
62
|
q += 2; // WE_HAVE_A_SCALE
|
|
@@ -73,24 +71,24 @@ function subsetTTF(data, used) {
|
|
|
73
71
|
}
|
|
74
72
|
// Rebuild glyf (kept glyphs only, original numbering) + a long-format loca.
|
|
75
73
|
const parts = [];
|
|
76
|
-
const newLoca =
|
|
74
|
+
const newLoca = new Uint8Array((numGlyphs + 1) * 4);
|
|
77
75
|
let off = 0;
|
|
78
76
|
for (let g = 0; g < numGlyphs; g++) {
|
|
79
|
-
newLoca
|
|
77
|
+
wu32(newLoca, off, g * 4);
|
|
80
78
|
if (keep.has(g)) {
|
|
81
|
-
let gd =
|
|
79
|
+
let gd = glyphBytes(g).slice(); // copy: padded + concatenated, must not view `data`
|
|
82
80
|
if (gd.length % 2 === 1)
|
|
83
|
-
gd =
|
|
81
|
+
gd = concatBytes([gd, new Uint8Array(1)]); // keep offsets even
|
|
84
82
|
parts.push(gd);
|
|
85
83
|
off += gd.length;
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
|
-
newLoca
|
|
89
|
-
const newGlyf =
|
|
86
|
+
wu32(newLoca, off, numGlyphs * 4);
|
|
87
|
+
const newGlyf = concatBytes(parts);
|
|
90
88
|
// head copy with indexToLocFormat = 1 (long); checkSumAdjustment zeroed, fixed up at the end.
|
|
91
|
-
const newHead =
|
|
92
|
-
newHead
|
|
93
|
-
newHead
|
|
89
|
+
const newHead = data.slice(headOff, headOff + dir.get("head").length);
|
|
90
|
+
wi16(newHead, 1, 50);
|
|
91
|
+
wu32(newHead, 0, 8);
|
|
94
92
|
// Tables a PDF-embedded CIDFontType2 doesn't need - layout (GSUB/GPOS…), hinting hints and the
|
|
95
93
|
// signature are dead weight; dropping them is most of the size win. `post` is reduced to format 3
|
|
96
94
|
// (header only, no glyph names). cmap/name/OS-2 are kept for a still-valid standalone font.
|
|
@@ -119,8 +117,8 @@ function subsetTTF(data, used) {
|
|
|
119
117
|
body = newHead;
|
|
120
118
|
else if (tag === "post") {
|
|
121
119
|
// Keep the 32-byte header, force version 3.0 (drops the per-glyph name list).
|
|
122
|
-
body =
|
|
123
|
-
body
|
|
120
|
+
body = data.slice(dir.get("post").offset, dir.get("post").offset + 32);
|
|
121
|
+
wu32(body, 0x00030000, 0);
|
|
124
122
|
}
|
|
125
123
|
else {
|
|
126
124
|
const d = dir.get(tag);
|
|
@@ -132,30 +130,30 @@ function subsetTTF(data, used) {
|
|
|
132
130
|
const n = tables.length;
|
|
133
131
|
const maxPow2 = 1 << Math.floor(Math.log2(n));
|
|
134
132
|
const out = [];
|
|
135
|
-
const offsetTable =
|
|
136
|
-
offsetTable
|
|
137
|
-
offsetTable
|
|
138
|
-
offsetTable
|
|
139
|
-
offsetTable
|
|
140
|
-
offsetTable
|
|
133
|
+
const offsetTable = new Uint8Array(12);
|
|
134
|
+
wu32(offsetTable, u32(data, 0), 0); // sfntVersion
|
|
135
|
+
wu16(offsetTable, n, 4);
|
|
136
|
+
wu16(offsetTable, maxPow2 * 16, 6); // searchRange
|
|
137
|
+
wu16(offsetTable, Math.floor(Math.log2(n)), 8); // entrySelector
|
|
138
|
+
wu16(offsetTable, n * 16 - maxPow2 * 16, 10); // rangeShift
|
|
141
139
|
out.push(offsetTable);
|
|
142
|
-
const dirBuf =
|
|
140
|
+
const dirBuf = new Uint8Array(n * 16);
|
|
143
141
|
let tableOffset = 12 + n * 16;
|
|
144
142
|
const dataBufs = [];
|
|
145
143
|
tables.forEach((t, i) => {
|
|
146
144
|
const padded = pad4(t.body);
|
|
147
|
-
dirBuf
|
|
148
|
-
dirBuf
|
|
149
|
-
dirBuf
|
|
150
|
-
dirBuf
|
|
145
|
+
writeLatin1(dirBuf, t.tag, i * 16);
|
|
146
|
+
wu32(dirBuf, checksum(padded), i * 16 + 4);
|
|
147
|
+
wu32(dirBuf, tableOffset, i * 16 + 8);
|
|
148
|
+
wu32(dirBuf, t.length, i * 16 + 12); // real (unpadded) length
|
|
151
149
|
tableOffset += padded.length;
|
|
152
150
|
dataBufs.push(padded);
|
|
153
151
|
});
|
|
154
152
|
out.push(dirBuf, ...dataBufs);
|
|
155
|
-
const font =
|
|
153
|
+
const font = concatBytes(out);
|
|
156
154
|
// head.checkSumAdjustment = 0xB1B0AFBA - checksum(whole font). Locate head in the assembled font.
|
|
157
155
|
const headIdx = tables.findIndex((t) => t.tag === "head");
|
|
158
|
-
const headPos = dirBuf
|
|
159
|
-
font
|
|
156
|
+
const headPos = u32(dirBuf, headIdx * 16 + 8);
|
|
157
|
+
wu32(font, wrap32(0xb1b0afba - checksum(font)), headPos + 8);
|
|
160
158
|
return font;
|
|
161
159
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getArrayBuffer = getArrayBuffer;
|
|
4
1
|
// Windows-1252 equals Latin-1 (codepoint == byte) EXCEPT in 0x80-0x9F, where it carries
|
|
5
2
|
// printable punctuation - the Euro sign, smart quotes, dashes, ellipsis, ... - instead of
|
|
6
3
|
// C1 controls. Those glyphs sit at Unicode codepoints far outside 0x00-0xFF, so a naive
|
|
@@ -37,7 +34,7 @@ const CP1252_FROM_UNICODE = {
|
|
|
37
34
|
0x0178: 0x9f, // Ÿ Y diaeresis
|
|
38
35
|
};
|
|
39
36
|
/** Encodes a JavaScript string to a Windows-1252 byte buffer (the PDF text encoding). */
|
|
40
|
-
function getArrayBuffer(data) {
|
|
37
|
+
export function getArrayBuffer(data) {
|
|
41
38
|
const u8 = new Uint8Array(data.length);
|
|
42
39
|
for (let i = 0; i < data.length; i++) {
|
|
43
40
|
const code = data.charCodeAt(i);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PDFDocumentElement } from "../elements/pdf-document-element";
|
|
2
|
-
import { PDFElement, FlexiblePDFElement, SizedPDFElement } from "../elements/pdf-element";
|
|
1
|
+
import { PDFDocumentElement } from "../elements/pdf-document-element.js";
|
|
2
|
+
import { PDFElement, FlexiblePDFElement, SizedPDFElement } from "../elements/pdf-element.js";
|
|
3
3
|
export declare class Validator {
|
|
4
4
|
static validateDocument(document: PDFDocumentElement): void;
|
|
5
5
|
static validateElement(element: PDFElement): void;
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const pdf_document_element_1 = require("../elements/pdf-document-element");
|
|
5
|
-
const pdf_element_1 = require("../elements/pdf-element");
|
|
6
|
-
class Validator {
|
|
1
|
+
import { PDFDocumentElement } from "../elements/pdf-document-element.js";
|
|
2
|
+
import { FlexiblePDFElement, hasChildProp, SizedPDFElement, } from "../elements/pdf-element.js";
|
|
3
|
+
export class Validator {
|
|
7
4
|
static validateDocument(document) {
|
|
8
5
|
// More validation will be added later...
|
|
9
6
|
document.getProps().children.forEach((page) => {
|
|
10
7
|
page.getProps().children.forEach((element) => {
|
|
11
|
-
if (element instanceof
|
|
8
|
+
if (element instanceof PDFDocumentElement) {
|
|
12
9
|
throw new Error("PDFDocument cannot be nested inside another element.");
|
|
13
10
|
}
|
|
14
11
|
});
|
|
@@ -16,11 +13,11 @@ class Validator {
|
|
|
16
13
|
}
|
|
17
14
|
static validateElement(element) {
|
|
18
15
|
// Structural validation
|
|
19
|
-
if (element instanceof
|
|
16
|
+
if (element instanceof PDFDocumentElement) {
|
|
20
17
|
throw new Error("PDFDocument cannot be nested inside another element.");
|
|
21
18
|
}
|
|
22
19
|
// Layout validation: geometry comes from the typed getSize(), not the props bag.
|
|
23
|
-
if (element instanceof
|
|
20
|
+
if (element instanceof SizedPDFElement) {
|
|
24
21
|
const { width, height } = element.getSize();
|
|
25
22
|
// Negative coordinates are allowed: a `Positioned` child overflows its frame on purpose,
|
|
26
23
|
// and the page clips anything past its edge. 0 size is legitimate (a hairline divider, an
|
|
@@ -30,7 +27,7 @@ class Validator {
|
|
|
30
27
|
}
|
|
31
28
|
}
|
|
32
29
|
// Logical validation: Flexible and fixed elements
|
|
33
|
-
if (element instanceof
|
|
30
|
+
if (element instanceof FlexiblePDFElement) {
|
|
34
31
|
this.validateFlexElement(element);
|
|
35
32
|
}
|
|
36
33
|
}
|
|
@@ -49,11 +46,10 @@ class Validator {
|
|
|
49
46
|
throw new Error(`Flexible element ${element.constructor.name} has invalid flex value`);
|
|
50
47
|
}
|
|
51
48
|
// Ensure a flexible element does not contain another flexible element
|
|
52
|
-
if (
|
|
53
|
-
if (element.child instanceof
|
|
49
|
+
if (hasChildProp(element)) {
|
|
50
|
+
if (element.child instanceof FlexiblePDFElement) {
|
|
54
51
|
throw new Error(`Flexible element ${element.constructor.name} cannot hold another flexible element`);
|
|
55
52
|
}
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
|
-
exports.Validator = Validator;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jasy/pdf",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.4",
|
|
4
4
|
"description": "Declarative, component-based PDF generation in pure TypeScript - Flutter-style components, real AFM font metrics, a hand-rolled PDF writer. No headless browser, no Java.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"declarative",
|
|
@@ -19,9 +19,21 @@
|
|
|
19
19
|
"files": [
|
|
20
20
|
"dist"
|
|
21
21
|
],
|
|
22
|
+
"type": "module",
|
|
22
23
|
"main": "dist/index.js",
|
|
24
|
+
"browser": {
|
|
25
|
+
"./dist/platform/node-fs.js": "./dist/platform/browser-fs.js",
|
|
26
|
+
"./dist/platform/node-image.js": "./dist/platform/browser-image.js"
|
|
27
|
+
},
|
|
23
28
|
"types": "dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
24
35
|
"dependencies": {
|
|
36
|
+
"fflate": "^0.8.3",
|
|
25
37
|
"jimp": "^1.6.0"
|
|
26
38
|
},
|
|
27
39
|
"devDependencies": {
|
|
@@ -41,7 +53,7 @@
|
|
|
41
53
|
"play": "pnpm --filter @jasy/playground play",
|
|
42
54
|
"test": "vitest",
|
|
43
55
|
"test:coverage": "vitest --coverage",
|
|
44
|
-
"build": "tsc && pnpm run copy-assets",
|
|
56
|
+
"build": "tsc && node scripts/fix-dts-ext.mjs && pnpm run copy-assets",
|
|
45
57
|
"lint": "oxlint",
|
|
46
58
|
"lint:fix": "oxlint --fix",
|
|
47
59
|
"fmt": "oxfmt",
|