pagyra-js 0.0.13 → 0.0.15
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/dist/browser/pagyra.min.js +32 -32
- package/dist/browser/pagyra.min.js.map +4 -4
- package/dist/src/pdf/font/binary-writer.d.ts +18 -0
- package/dist/src/pdf/font/binary-writer.js +67 -0
- package/dist/src/pdf/font/builtin-fonts.browser.js +1 -1
- package/dist/src/pdf/font/embedder.d.ts +1 -0
- package/dist/src/pdf/font/embedder.js +20 -23
- package/dist/src/pdf/font/font-registry.js +4 -25
- package/dist/src/pdf/font/font-subset.d.ts +7 -4
- package/dist/src/pdf/font/font-subset.js +329 -124
- package/dist/src/pdf/font/managers/subset-resource-manager.js +14 -2
- package/dist/src/pdf/font/ttf-lite.js +2 -1
- package/dist/src/pdf/font/ttf-table-parser.d.ts +1 -0
- package/dist/src/pdf/font/ttf-table-parser.js +7 -0
- package/dist/src/pdf/renderers/text-renderer.js +17 -16
- package/dist/src/pdf/renderers/text-shadow-renderer.d.ts +4 -0
- package/dist/src/pdf/renderers/text-shadow-renderer.js +34 -74
- package/dist/src/types/fonts.d.ts +9 -1
- package/dist/src/types/fonts.js +6 -1
- package/dist/tests/verify-subset-multi.spec.d.ts +1 -0
- package/dist/tests/verify-subset-multi.spec.js +36 -0
- package/dist/tests/verify-subset.spec.d.ts +1 -0
- package/dist/tests/verify-subset.spec.js +35 -0
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare class BinaryWriter {
|
|
2
|
+
private buffer;
|
|
3
|
+
private view;
|
|
4
|
+
private offset;
|
|
5
|
+
constructor(initialSize?: number);
|
|
6
|
+
ensureSize(size: number): void;
|
|
7
|
+
writeUint8(value: number): void;
|
|
8
|
+
writeInt8(value: number): void;
|
|
9
|
+
writeUint16(value: number): void;
|
|
10
|
+
writeInt16(value: number): void;
|
|
11
|
+
writeUint32(value: number): void;
|
|
12
|
+
writeInt32(value: number): void;
|
|
13
|
+
writeFixed(value: number): void;
|
|
14
|
+
writeString(value: string): void;
|
|
15
|
+
writeBytes(bytes: Uint8Array): void;
|
|
16
|
+
getData(): Uint8Array;
|
|
17
|
+
byteLength(): number;
|
|
18
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export class BinaryWriter {
|
|
2
|
+
constructor(initialSize = 1024) {
|
|
3
|
+
this.buffer = new Uint8Array(initialSize);
|
|
4
|
+
this.view = new DataView(this.buffer.buffer);
|
|
5
|
+
this.offset = 0;
|
|
6
|
+
}
|
|
7
|
+
ensureSize(size) {
|
|
8
|
+
if (this.offset + size > this.buffer.length) {
|
|
9
|
+
const newSize = Math.max(this.buffer.length * 2, this.offset + size);
|
|
10
|
+
const newBuffer = new Uint8Array(newSize);
|
|
11
|
+
newBuffer.set(this.buffer);
|
|
12
|
+
this.buffer = newBuffer;
|
|
13
|
+
this.view = new DataView(this.buffer.buffer);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
writeUint8(value) {
|
|
17
|
+
this.ensureSize(1);
|
|
18
|
+
this.view.setUint8(this.offset, value);
|
|
19
|
+
this.offset += 1;
|
|
20
|
+
}
|
|
21
|
+
writeInt8(value) {
|
|
22
|
+
this.ensureSize(1);
|
|
23
|
+
this.view.setInt8(this.offset, value);
|
|
24
|
+
this.offset += 1;
|
|
25
|
+
}
|
|
26
|
+
writeUint16(value) {
|
|
27
|
+
this.ensureSize(2);
|
|
28
|
+
this.view.setUint16(this.offset, value, false);
|
|
29
|
+
this.offset += 2;
|
|
30
|
+
}
|
|
31
|
+
writeInt16(value) {
|
|
32
|
+
this.ensureSize(2);
|
|
33
|
+
this.view.setInt16(this.offset, value, false);
|
|
34
|
+
this.offset += 2;
|
|
35
|
+
}
|
|
36
|
+
writeUint32(value) {
|
|
37
|
+
this.ensureSize(4);
|
|
38
|
+
this.view.setUint32(this.offset, value, false);
|
|
39
|
+
this.offset += 4;
|
|
40
|
+
}
|
|
41
|
+
writeInt32(value) {
|
|
42
|
+
this.ensureSize(4);
|
|
43
|
+
this.view.setInt32(this.offset, value, false);
|
|
44
|
+
this.offset += 4;
|
|
45
|
+
}
|
|
46
|
+
writeFixed(value) {
|
|
47
|
+
const integer = Math.floor(value);
|
|
48
|
+
const fraction = Math.floor((value - integer) * 65536);
|
|
49
|
+
this.writeInt16(integer);
|
|
50
|
+
this.writeUint16(fraction);
|
|
51
|
+
}
|
|
52
|
+
writeString(value) {
|
|
53
|
+
const encoded = new TextEncoder().encode(value);
|
|
54
|
+
this.writeBytes(encoded);
|
|
55
|
+
}
|
|
56
|
+
writeBytes(bytes) {
|
|
57
|
+
this.ensureSize(bytes.length);
|
|
58
|
+
this.buffer.set(bytes, this.offset);
|
|
59
|
+
this.offset += bytes.length;
|
|
60
|
+
}
|
|
61
|
+
getData() {
|
|
62
|
+
return this.buffer.slice(0, this.offset);
|
|
63
|
+
}
|
|
64
|
+
byteLength() {
|
|
65
|
+
return this.offset;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -98,7 +98,7 @@ function computeBaseUrl() {
|
|
|
98
98
|
return `${origin}assets/fonts/`;
|
|
99
99
|
}
|
|
100
100
|
try {
|
|
101
|
-
return new URL("../../assets/fonts/", import.meta.url).toString();
|
|
101
|
+
return new URL(/* @vite-ignore */ "../../assets/fonts/", import.meta.url).toString();
|
|
102
102
|
}
|
|
103
103
|
catch {
|
|
104
104
|
return "/assets/fonts/";
|
|
@@ -18,6 +18,7 @@ export declare class FontEmbedder {
|
|
|
18
18
|
initialize(): Promise<void>;
|
|
19
19
|
ensureFont(familyStack: string[], fontWeight?: number, fontStyle?: string): EmbeddedFont | null;
|
|
20
20
|
private embedFont;
|
|
21
|
+
private realizeFont;
|
|
21
22
|
private createToUnicodeCMap;
|
|
22
23
|
/**
|
|
23
24
|
* Return parsed TTF metrics for a loaded face by name or family, or null if not available.
|
|
@@ -87,6 +87,25 @@ export class FontEmbedder {
|
|
|
87
87
|
const metrics = this.faceMetrics.get(face.name);
|
|
88
88
|
if (!metrics)
|
|
89
89
|
return null;
|
|
90
|
+
const fullFontData = new Uint8Array(face.data);
|
|
91
|
+
const resourceName = `F${this.embeddedFonts.size + 1}`;
|
|
92
|
+
let realizedRef;
|
|
93
|
+
const self = this;
|
|
94
|
+
const embedded = {
|
|
95
|
+
resourceName,
|
|
96
|
+
baseFont: face.name,
|
|
97
|
+
metrics,
|
|
98
|
+
subset: fullFontData,
|
|
99
|
+
get ref() {
|
|
100
|
+
if (!realizedRef) {
|
|
101
|
+
realizedRef = self.realizeFont(face, metrics, resourceName);
|
|
102
|
+
}
|
|
103
|
+
return realizedRef;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
return embedded;
|
|
107
|
+
}
|
|
108
|
+
realizeFont(face, metrics, resourceName) {
|
|
90
109
|
const unitsPerEm = metrics.metrics.unitsPerEm;
|
|
91
110
|
const scaleTo1000 = (v) => Math.round((v / unitsPerEm) * 1000);
|
|
92
111
|
let fontBBox;
|
|
@@ -95,16 +114,9 @@ export class FontEmbedder {
|
|
|
95
114
|
fontBBox = [scaleTo1000(hb[0]), scaleTo1000(hb[1]), scaleTo1000(hb[2]), scaleTo1000(hb[3])];
|
|
96
115
|
}
|
|
97
116
|
else {
|
|
98
|
-
// Fallback if headBBox is missing
|
|
99
117
|
fontBBox = [-1000, -1000, 1000, 1000];
|
|
100
118
|
}
|
|
101
|
-
// Register the font file stream
|
|
102
|
-
// We need the full font data here. In the initialize method we updated face.data
|
|
103
|
-
// but here we need to access it.
|
|
104
|
-
// Since we can't easily change the interface, we'll assume face.data is the source.
|
|
105
|
-
// If it was converted to TTF in initialize, face.data (casted) holds the TTF buffer.
|
|
106
119
|
const fullFontData = new Uint8Array(face.data);
|
|
107
|
-
// FontFile2 streams require only /Length (PdfDocument adds it); avoid Type1-specific Length1 headers.
|
|
108
120
|
const fontFileRef = this.doc.registerStream(fullFontData, {});
|
|
109
121
|
const fontDescriptor = {
|
|
110
122
|
Type: "/FontDescriptor",
|
|
@@ -120,10 +132,7 @@ export class FontEmbedder {
|
|
|
120
132
|
FontFile2: fontFileRef
|
|
121
133
|
};
|
|
122
134
|
const fontDescriptorRef = this.doc.register(fontDescriptor);
|
|
123
|
-
// Compute DW and compressed W
|
|
124
135
|
const { DW, W } = computeWidths(metrics);
|
|
125
|
-
// Create CID font dictionary (include DW)
|
|
126
|
-
// CID fonts must declare string-valued CIDSystemInfo entries per PDF spec.
|
|
127
136
|
const cidFontDict = {
|
|
128
137
|
Type: "/Font",
|
|
129
138
|
Subtype: "/CIDFontType2",
|
|
@@ -136,15 +145,10 @@ export class FontEmbedder {
|
|
|
136
145
|
FontDescriptor: fontDescriptorRef,
|
|
137
146
|
DW,
|
|
138
147
|
W,
|
|
139
|
-
// Rely on built-in Identity CIDToGID mapping to keep font dictionaries simple/compatible.
|
|
140
148
|
CIDToGIDMap: "/Identity"
|
|
141
149
|
};
|
|
142
150
|
const cidFontRef = this.doc.register(cidFontDict);
|
|
143
|
-
// Create Unicode mapping (ToUnicode CMap)
|
|
144
151
|
const toUnicodeRef = this.createToUnicodeCMap(metrics);
|
|
145
|
-
// Create Type0 font dictionary
|
|
146
|
-
// Per PDF spec, Type0 BaseFont should include the CMap suffix (e.g. "-Identity-H")
|
|
147
|
-
// to make the composite font name unambiguous for parsers/renderers.
|
|
148
152
|
const type0Font = {
|
|
149
153
|
Type: "/Font",
|
|
150
154
|
Subtype: "/Type0",
|
|
@@ -153,14 +157,7 @@ export class FontEmbedder {
|
|
|
153
157
|
DescendantFonts: [cidFontRef],
|
|
154
158
|
ToUnicode: toUnicodeRef
|
|
155
159
|
};
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
resourceName: `F${this.embeddedFonts.size + 1}`,
|
|
159
|
-
ref: fontRef,
|
|
160
|
-
baseFont: face.name,
|
|
161
|
-
metrics,
|
|
162
|
-
subset: fullFontData
|
|
163
|
-
};
|
|
160
|
+
return this.doc.register(type0Font);
|
|
164
161
|
}
|
|
165
162
|
createToUnicodeCMap(metrics, _uniqueUnicodes = []) {
|
|
166
163
|
// Build inverse mapping gid -> unicode (pick first unicode when multiple map to same gid)
|
|
@@ -4,17 +4,6 @@ import { log } from "../../logging/debug.js";
|
|
|
4
4
|
import { needsUnicode } from "../../text/text.js";
|
|
5
5
|
import { fontWeightCacheKey, normalizeFontWeight } from "../../css/font-weight.js";
|
|
6
6
|
import { parseFontFaces, parseFamilyList, isItalicStyle, normalizeToken, } from "../../css/font-face-parser.js";
|
|
7
|
-
// import {
|
|
8
|
-
// BASE_FONT_ALIASES,
|
|
9
|
-
// GENERIC_FAMILIES,
|
|
10
|
-
// BASE14_FALLBACKS,
|
|
11
|
-
// BASE14_FAMILY_VARIANTS,
|
|
12
|
-
// BASE14_VARIANT_LOOKUP,
|
|
13
|
-
// detectBase14Family,
|
|
14
|
-
// classifyBase14Variant,
|
|
15
|
-
// type Base14Family,
|
|
16
|
-
// type Base14Variant,
|
|
17
|
-
// } from "./font-config.js";
|
|
18
7
|
import { applyWeightToBaseFont } from "./resolvers/weight-style-applicator.js";
|
|
19
8
|
import { buildAliasedFamilyStack } from "./resolvers/family-resolver.js";
|
|
20
9
|
import { resolveBaseFont } from "./resolvers/base-font-mapper.js";
|
|
@@ -23,20 +12,17 @@ import { SubsetResourceManager } from "./managers/subset-resource-manager.js";
|
|
|
23
12
|
export function getBase14(family) {
|
|
24
13
|
return { isBase14: true, baseName: family, name: family };
|
|
25
14
|
}
|
|
26
|
-
// Note: getFontForText needs access to doc and config, so we'll modify the signature
|
|
27
15
|
export function getFontForText(_requestedFamily, text, _doc, _config) {
|
|
28
16
|
if (needsUnicode(text)) {
|
|
29
|
-
// For now, use a simplified approach - we'll assume NotoSans-Regular is available
|
|
30
|
-
// In a full implementation, you'd initialize the embedder properly
|
|
31
17
|
const fontName = "NotoSans-Regular";
|
|
32
18
|
log("font", "info", "font-path", { base14: false, family: fontName, encoding: "Identity-H" });
|
|
33
19
|
return { isBase14: false, name: fontName };
|
|
34
20
|
}
|
|
35
|
-
const f = getBase14("Helvetica");
|
|
21
|
+
const f = getBase14("Helvetica");
|
|
36
22
|
log("font", "info", "font-path", { base14: true, family: f.baseName, encoding: "WinAnsi" });
|
|
37
23
|
return f;
|
|
38
24
|
}
|
|
39
|
-
const DEFAULT_FONT = "Times New Roman";
|
|
25
|
+
const DEFAULT_FONT = "Times New Roman";
|
|
40
26
|
export class FontRegistry {
|
|
41
27
|
constructor(doc, stylesheets) {
|
|
42
28
|
this.doc = doc;
|
|
@@ -62,7 +48,7 @@ export class FontRegistry {
|
|
|
62
48
|
const resource = {
|
|
63
49
|
baseFont: embedded.baseFont,
|
|
64
50
|
resourceName: embedded.resourceName,
|
|
65
|
-
ref
|
|
51
|
+
get ref() { return embedded.ref; },
|
|
66
52
|
isBase14: false,
|
|
67
53
|
metrics: embedded.metrics,
|
|
68
54
|
embedded,
|
|
@@ -75,7 +61,6 @@ export class FontRegistry {
|
|
|
75
61
|
this.fontResourceManager.setCached(familyKey, resolved);
|
|
76
62
|
return resolved;
|
|
77
63
|
}
|
|
78
|
-
// New method to get embedder reference
|
|
79
64
|
getEmbedder() {
|
|
80
65
|
return this.embedder;
|
|
81
66
|
}
|
|
@@ -94,14 +79,12 @@ export class FontRegistry {
|
|
|
94
79
|
}
|
|
95
80
|
if (this.embedder && this.fontConfig) {
|
|
96
81
|
const familyStack = buildAliasedFamilyStack(family, this.fontConfig?.defaultStack);
|
|
97
|
-
// Note: embedder.ensureFont is synchronous in its implementation (it uses pre-loaded data)
|
|
98
|
-
// even though the interface might not explicitly say so, we know it returns EmbeddedFont | null immediately.
|
|
99
82
|
const embedded = this.embedder.ensureFont(familyStack, normalizedWeight, style);
|
|
100
83
|
if (embedded) {
|
|
101
84
|
const resource = {
|
|
102
85
|
baseFont: embedded.baseFont,
|
|
103
86
|
resourceName: embedded.resourceName,
|
|
104
|
-
ref
|
|
87
|
+
get ref() { return embedded.ref; },
|
|
105
88
|
isBase14: false,
|
|
106
89
|
metrics: embedded.metrics,
|
|
107
90
|
embedded,
|
|
@@ -169,7 +152,6 @@ export function initFontSystem(doc, stylesheets) {
|
|
|
169
152
|
}
|
|
170
153
|
export async function ensureFontSubset(registry, run) {
|
|
171
154
|
const font = await registry.ensureFontResource(run.fontFamily, run.fontWeight, run.fontStyle);
|
|
172
|
-
// === diagnóstico cirúrgico: caminho de fonte ===
|
|
173
155
|
log("font", "info", "font-path", {
|
|
174
156
|
base14: font.isBase14 === true,
|
|
175
157
|
family: font.baseFont,
|
|
@@ -179,7 +161,6 @@ export async function ensureFontSubset(registry, run) {
|
|
|
179
161
|
}
|
|
180
162
|
export function ensureFontSubsetSync(registry, run) {
|
|
181
163
|
const font = registry.ensureFontResourceSync(run.fontFamily, run.fontWeight, run.fontStyle);
|
|
182
|
-
// === diagnóstico cirúrgico: caminho de fonte ===
|
|
183
164
|
log("font", "info", "font-path", {
|
|
184
165
|
base14: font.isBase14 === true,
|
|
185
166
|
family: font.baseFont,
|
|
@@ -188,8 +169,6 @@ export function ensureFontSubsetSync(registry, run) {
|
|
|
188
169
|
return font;
|
|
189
170
|
}
|
|
190
171
|
export function finalizeFontSubsets(_registry) {
|
|
191
|
-
// Actual font materialization happens during PdfDocument.finalize().
|
|
192
172
|
}
|
|
193
173
|
export function preflightFontsForPdfa(_registry) {
|
|
194
|
-
// Placeholder for PDF/A validations.
|
|
195
174
|
}
|
|
@@ -11,10 +11,13 @@ export interface PdfFontSubset {
|
|
|
11
11
|
fontFile: Uint8Array;
|
|
12
12
|
encodeGlyph(gid: number): number;
|
|
13
13
|
/**
|
|
14
|
-
* Ordered glyph IDs included in this subset.
|
|
15
|
-
* the width entries when using identity encoding.
|
|
14
|
+
* Ordered glyph IDs included in this subset.
|
|
16
15
|
*/
|
|
17
16
|
glyphIds: number[];
|
|
17
|
+
/**
|
|
18
|
+
* Map from original GID to new subset GID.
|
|
19
|
+
*/
|
|
20
|
+
gidMap: Map<number, number>;
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
20
23
|
* Options for creating a PDF font subset.
|
|
@@ -26,8 +29,8 @@ export interface PdfFontSubsetOptions {
|
|
|
26
29
|
usedGlyphIds: Set<number>;
|
|
27
30
|
/**
|
|
28
31
|
* Controls how glyph IDs are mapped to PDF character codes.
|
|
29
|
-
* - "identity" keeps CIDs =
|
|
30
|
-
* - "sequential" densely re-encodes glyphs starting at 0.
|
|
32
|
+
* - "identity" keeps CIDs = Original Glyph IDs (most stable for progressive rendering).
|
|
33
|
+
* - "sequential" densely re-encodes glyphs starting at 0 (most compact).
|
|
31
34
|
*/
|
|
32
35
|
encoding?: "identity" | "sequential";
|
|
33
36
|
}
|