@jasy/pdf 1.0.0-alpha.1 → 1.0.0-alpha.3
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 +3 -3
- 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 +27 -12
- package/dist/api/layout.js +46 -45
- package/dist/api/structure.d.ts +60 -13
- package/dist/api/structure.js +132 -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 +6 -0
- package/dist/assets/font-data.ts +7 -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/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 -10
- package/dist/elements/index.js +12 -28
- 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 +44 -0
- package/dist/elements/layout/positioned-element.js +61 -0
- 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 +31 -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 +28 -3
- package/dist/elements/pdf-element.js +10 -19
- package/dist/elements/rectangle-element.d.ts +14 -6
- package/dist/elements/rectangle-element.js +44 -21
- 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 +22 -3
- 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 +34 -17
- 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 +83 -90
- package/dist/renderer/positioned-renderer.d.ts +6 -0
- package/dist/renderer/positioned-renderer.js +10 -0
- package/dist/renderer/rectangle-renderer.d.ts +3 -3
- package/dist/renderer/rectangle-renderer.js +45 -44
- 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 +80 -0
- package/dist/utils/pdf-object-manager.d.ts +10 -6
- package/dist/utils/pdf-object-manager.js +89 -94
- 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 +17 -23
- package/package.json +14 -2
|
@@ -1,57 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.PDFObjectManager = exports.FontStyle = void 0;
|
|
37
|
-
const page_sizes_1 = require("../constants/page-sizes");
|
|
38
|
-
const fs = __importStar(require("fs"));
|
|
39
|
-
const path = __importStar(require("path"));
|
|
40
|
-
const crypto_1 = require("crypto");
|
|
41
|
-
const zlib_1 = require("zlib");
|
|
42
|
-
const afm_parser_1 = require("./afm-parser");
|
|
43
|
-
const ttf_parser_1 = require("./ttf-parser");
|
|
44
|
-
const ttf_subsetter_1 = require("./ttf-subsetter");
|
|
1
|
+
import { pageFormats, PageSize } from "../constants/page-sizes.js";
|
|
2
|
+
import { STANDARD_AFM } from "../assets/font-data.js";
|
|
3
|
+
import { md5, md5Hex } from "./md5.js";
|
|
4
|
+
import { zlibSync } from "fflate";
|
|
5
|
+
import { bytesFromLatin1, latin1FromBytes } from "./bytes.js";
|
|
6
|
+
import { AFMParser } from "./afm-parser.js";
|
|
7
|
+
import { TTFParser } from "./ttf-parser.js";
|
|
8
|
+
import { subsetTTF } from "./ttf-subsetter.js";
|
|
9
|
+
import { getArrayBuffer } from "./utf8-to-windows1252-encoder.js";
|
|
45
10
|
// Enums come from the leaf config module (never in a cycle); the config type is
|
|
46
11
|
// erased at runtime so it can come from the cyclic module safely.
|
|
47
|
-
|
|
48
|
-
var FontStyle;
|
|
12
|
+
import { ColorMode, Orientation } from "../renderer/pdf-config.js";
|
|
13
|
+
export var FontStyle;
|
|
49
14
|
(function (FontStyle) {
|
|
50
15
|
FontStyle["Normal"] = "normal";
|
|
51
16
|
FontStyle["Bold"] = "bold";
|
|
52
17
|
FontStyle["Italic"] = "italic";
|
|
53
18
|
FontStyle["BoldItalic"] = "boldItalic";
|
|
54
|
-
})(FontStyle || (
|
|
19
|
+
})(FontStyle || (FontStyle = {}));
|
|
20
|
+
// Escapes a string into a valid PDF /Name token. A raw space or delimiter in a /Name (e.g. a font
|
|
21
|
+
// family like "Great Vibes" or "Times New Roman") breaks the dictionary, so such chars become #XX.
|
|
22
|
+
const pdfName = (s) => s.replace(/[^\x21-\x7e]|[#()<>[\]{}/%]/g, (c) => "#" + c.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0"));
|
|
55
23
|
// Suffix appended to an embedded font's PDF /BaseFont so each style variant of a family keeps
|
|
56
24
|
// a distinct name (the variants are otherwise selected by the font resource, not the name).
|
|
57
25
|
const STYLE_SUFFIX = {
|
|
@@ -152,7 +120,7 @@ class FontManager {
|
|
|
152
120
|
return `${fontName}-${fontStyle}`;
|
|
153
121
|
}
|
|
154
122
|
}
|
|
155
|
-
class PDFObjectManager {
|
|
123
|
+
export class PDFObjectManager {
|
|
156
124
|
constructor(pageSize) {
|
|
157
125
|
this.objects = [];
|
|
158
126
|
this.objectPositions = [];
|
|
@@ -160,7 +128,7 @@ class PDFObjectManager {
|
|
|
160
128
|
this.fonts = new FontManager(); // Stores the fonts
|
|
161
129
|
this.images = new ImageManager(); // Stores the images (object numbers and names)
|
|
162
130
|
this.extGStates = new ExtGStateManager(); // Transparency states
|
|
163
|
-
this.pageFormat =
|
|
131
|
+
this.pageFormat = pageFormats[PageSize.A4];
|
|
164
132
|
this.afmParsers = [];
|
|
165
133
|
// Embedded TrueType fonts, keyed by the name the user registers them under. When a font
|
|
166
134
|
// name is in here the metric/emission paths take the TTF branch instead of the AFM one.
|
|
@@ -172,12 +140,13 @@ class PDFObjectManager {
|
|
|
172
140
|
// the catalog's /Names/EmbeddedFiles + /AF. Drives ZUGFeRD's embedded factur-x.xml.
|
|
173
141
|
this.attachments = [];
|
|
174
142
|
// PDF header version (PDF/A-3 needs 1.7) and whether to write a trailer /ID (PDF/A needs one).
|
|
175
|
-
// Both default to the pre-PDF/A
|
|
143
|
+
// Both default to the pre-PDF/A behavior, so a normal document stays byte-identical.
|
|
176
144
|
this.pdfVersion = "1.4";
|
|
177
145
|
this.documentId = false;
|
|
178
146
|
this.compress = false;
|
|
147
|
+
this.overflowPolicy = "error";
|
|
179
148
|
if (pageSize)
|
|
180
|
-
this.pageFormat =
|
|
149
|
+
this.pageFormat = pageFormats[pageSize];
|
|
181
150
|
this.fillConfigWithStandardValues();
|
|
182
151
|
}
|
|
183
152
|
// Adds an object and returns its number
|
|
@@ -198,40 +167,51 @@ class PDFObjectManager {
|
|
|
198
167
|
setCompress(on) {
|
|
199
168
|
this.compress = on;
|
|
200
169
|
}
|
|
170
|
+
// Overflow policy for unbreakable content taller than a page region (default "error"); the renderer
|
|
171
|
+
// seeds it into the layout-context root so packChildren can act on it.
|
|
172
|
+
setOverflowPolicy(policy) {
|
|
173
|
+
this.overflowPolicy = policy;
|
|
174
|
+
}
|
|
175
|
+
getOverflowPolicy() {
|
|
176
|
+
return this.overflowPolicy;
|
|
177
|
+
}
|
|
201
178
|
// Builds a stream object body: `<< extraDict /Length n >> stream … endstream`, FlateDecode-compressed
|
|
202
179
|
// when enabled AND it actually helps (tiny streams aren't inflated). Binary bytes ride as a latin1
|
|
203
180
|
// string - the final encoder passes 0x00-0xFF through unchanged.
|
|
204
181
|
stream(extraDict, data) {
|
|
205
182
|
const head = extraDict ? extraDict + " " : "";
|
|
206
183
|
if (this.compress) {
|
|
207
|
-
const z = (
|
|
184
|
+
const z = zlibSync(data);
|
|
208
185
|
if (z.length < data.length) {
|
|
209
|
-
return `<< ${head}/Filter /FlateDecode /Length ${z.length} >>\nstream\n${z
|
|
186
|
+
return `<< ${head}/Filter /FlateDecode /Length ${z.length} >>\nstream\n${latin1FromBytes(z)}\nendstream`;
|
|
210
187
|
}
|
|
211
188
|
}
|
|
212
|
-
return `<< ${head}/Length ${data.length} >>\nstream\n${data
|
|
189
|
+
return `<< ${head}/Length ${data.length} >>\nstream\n${latin1FromBytes(data)}\nendstream`;
|
|
213
190
|
}
|
|
214
191
|
// Adds a page content stream (compressed when enabled). The caller passes the raw operator string.
|
|
192
|
+
// Bytes go through the Windows-1252 encoder (NOT a latin1 low-byte cast): a Tj literal may carry a
|
|
193
|
+
// CP1252 char like "…"/"—"/"€" whose codepoint is > 0xFF, and latin1 would mangle it (… -> "&").
|
|
194
|
+
// For every char <= 0xFF the encoder passes the byte through, so existing streams stay byte-identical.
|
|
215
195
|
addContentStream(content) {
|
|
216
|
-
return this.addObject(this.stream("",
|
|
196
|
+
return this.addObject(this.stream("", new Uint8Array(getArrayBuffer(content))));
|
|
217
197
|
}
|
|
218
198
|
changePDFConfig(config) {
|
|
219
|
-
this.pdfConfig =
|
|
199
|
+
this.pdfConfig = { ...this.pdfConfig, ...config };
|
|
220
200
|
}
|
|
221
201
|
getPDFConfig() {
|
|
222
202
|
return this.pdfConfig;
|
|
223
203
|
}
|
|
224
204
|
fillConfigWithStandardValues() {
|
|
225
205
|
this.pdfConfig = {
|
|
226
|
-
orientation:
|
|
206
|
+
orientation: Orientation.portrait,
|
|
227
207
|
defaultFont: {
|
|
228
208
|
fontFamily: "Helvetica",
|
|
229
209
|
fontSize: 12,
|
|
230
210
|
fontStyle: FontStyle.Normal,
|
|
231
211
|
},
|
|
232
|
-
pageSize:
|
|
212
|
+
pageSize: PageSize.A4,
|
|
233
213
|
margin: { left: 72, top: 72, bottom: 72, right: 72 },
|
|
234
|
-
colorMode:
|
|
214
|
+
colorMode: ColorMode.color,
|
|
235
215
|
};
|
|
236
216
|
}
|
|
237
217
|
// Calculates the total length of the document in bytes (for XRef)
|
|
@@ -240,10 +220,8 @@ class PDFObjectManager {
|
|
|
240
220
|
for (let i = 0; i < this.objects.length; i++) {
|
|
241
221
|
const obj = this.objects[i];
|
|
242
222
|
const objectContent = `${i + 1} 0 obj\n${obj}\nendobj\n`;
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
// Add the actual byte length to the total length
|
|
246
|
-
length += encodedContent.length;
|
|
223
|
+
// The body is a latin1/binary string (every char <= 0xFF), so its length IS its byte length.
|
|
224
|
+
length += objectContent.length;
|
|
247
225
|
}
|
|
248
226
|
return length;
|
|
249
227
|
}
|
|
@@ -256,7 +234,23 @@ class PDFObjectManager {
|
|
|
256
234
|
return this.parentObjectNumber;
|
|
257
235
|
}
|
|
258
236
|
// Registers an image
|
|
259
|
-
registerImage(width, height, imageType, imageData) {
|
|
237
|
+
registerImage(width, height, imageType, imageData, smaskData) {
|
|
238
|
+
// A transparent PNG carries its alpha as a separate DeviceGray /SMask image the viewer composites with.
|
|
239
|
+
let smaskEntry = "";
|
|
240
|
+
if (smaskData) {
|
|
241
|
+
const smaskObject = `<< /Type /XObject
|
|
242
|
+
/Subtype /Image
|
|
243
|
+
/Width ${width}
|
|
244
|
+
/Height ${height}
|
|
245
|
+
/ColorSpace /DeviceGray
|
|
246
|
+
/BitsPerComponent 8
|
|
247
|
+
/Filter /FlateDecode
|
|
248
|
+
/Length ${smaskData.length} >>
|
|
249
|
+
stream
|
|
250
|
+
${smaskData}
|
|
251
|
+
endstream`;
|
|
252
|
+
smaskEntry = ` /SMask ${this.addObject(smaskObject)} 0 R\n`;
|
|
253
|
+
}
|
|
260
254
|
const imageObject = `<< /Type /XObject
|
|
261
255
|
/Subtype /Image
|
|
262
256
|
/Width ${width}
|
|
@@ -264,9 +258,9 @@ class PDFObjectManager {
|
|
|
264
258
|
/ColorSpace /DeviceRGB
|
|
265
259
|
/BitsPerComponent 8
|
|
266
260
|
/Filter /${imageType}
|
|
267
|
-
/Length ${imageData.length} >>
|
|
261
|
+
${smaskEntry} /Length ${imageData.length} >>
|
|
268
262
|
stream
|
|
269
|
-
${imageData}
|
|
263
|
+
${imageData}
|
|
270
264
|
endstream`;
|
|
271
265
|
// Add the image and its object number to the image manager - return the object number
|
|
272
266
|
const imageObjectNumber = this.addObject(imageObject);
|
|
@@ -278,13 +272,12 @@ endstream`;
|
|
|
278
272
|
// `relationship` is the /AFRelationship (e.g. "Data" for the ZUGFeRD factur-x.xml). Binary bytes
|
|
279
273
|
// ride as a latin1 string, like images/fonts (the final encoder passes 0x00-0xFF through).
|
|
280
274
|
attachFile(name, data, opts = {}) {
|
|
281
|
-
|
|
282
|
-
const subtype = ((_a = opts.mimeType) !== null && _a !== void 0 ? _a : "application/octet-stream").replace(/\//g, "#2F");
|
|
275
|
+
const subtype = (opts.mimeType ?? "application/octet-stream").replace(/\//g, "#2F");
|
|
283
276
|
const embedded = this.addObject(this.stream(`/Type /EmbeddedFile /Subtype /${subtype} /Params << /Size ${data.length} >>`, data));
|
|
284
277
|
const escaped = (s) => s.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)");
|
|
285
278
|
const desc = opts.description ? ` /Desc (${escaped(opts.description)})` : "";
|
|
286
279
|
const filespec = this.addObject(`<< /Type /Filespec /F (${escaped(name)}) /UF (${escaped(name)}) ` +
|
|
287
|
-
`/AFRelationship /${
|
|
280
|
+
`/AFRelationship /${opts.relationship ?? "Unspecified"}${desc} ` +
|
|
288
281
|
`/EF << /F ${embedded} 0 R /UF ${embedded} 0 R >> >>`);
|
|
289
282
|
this.attachments.push({ name, filespec });
|
|
290
283
|
}
|
|
@@ -302,11 +295,10 @@ endstream`;
|
|
|
302
295
|
// Embeds an ICC profile and a PDF/A /OutputIntent that points at it (catalog /OutputIntents).
|
|
303
296
|
// `icc` are the raw profile bytes (an RGB profile, /N 3 - e.g. sRGB).
|
|
304
297
|
setOutputIntent(icc, opts = {}) {
|
|
305
|
-
var _a, _b;
|
|
306
298
|
const profile = this.addObject(this.stream("/N 3", icc));
|
|
307
299
|
this.outputIntent = this.addObject(`<< /Type /OutputIntent /S /GTS_PDFA1 ` +
|
|
308
|
-
`/OutputConditionIdentifier (${
|
|
309
|
-
`/Info (${
|
|
300
|
+
`/OutputConditionIdentifier (${opts.identifier ?? "sRGB"}) ` +
|
|
301
|
+
`/Info (${opts.info ?? "sRGB IEC61966-2.1"}) /DestOutputProfile ${profile} 0 R >>`);
|
|
310
302
|
}
|
|
311
303
|
getOutputIntent() {
|
|
312
304
|
return this.outputIntent;
|
|
@@ -354,20 +346,19 @@ endstream`;
|
|
|
354
346
|
if (this.fonts.hasFont(fontName, fontStyle)) {
|
|
355
347
|
return this.fonts.getFont(fontName, fontStyle); // Already exists? Return it!
|
|
356
348
|
}
|
|
357
|
-
const
|
|
358
|
-
if (
|
|
359
|
-
const data = fs.readFileSync(afmFilePath, "utf-8");
|
|
349
|
+
const data = STANDARD_AFM[fullName];
|
|
350
|
+
if (data !== undefined) {
|
|
360
351
|
this.afmParsers.push({
|
|
361
352
|
fontName,
|
|
362
353
|
fontStyle,
|
|
363
354
|
fullFontName: fullName,
|
|
364
|
-
parser: new
|
|
355
|
+
parser: new AFMParser(data),
|
|
365
356
|
});
|
|
366
357
|
}
|
|
367
358
|
const resourceNumber = this.objects.length + 1; // New resource number
|
|
368
359
|
const fontNumber = this.fonts.getLastFontIndex() + 1; // New font index number
|
|
369
360
|
this.fonts.addFont(fontName, fontNumber, resourceNumber, fontStyle); // Store it
|
|
370
|
-
const fontObject = `<</BaseFont/${fullName}/Type/Font\n/Encoding/WinAnsiEncoding\n/Subtype/Type1>>`;
|
|
361
|
+
const fontObject = `<</BaseFont/${pdfName(fullName)}/Type/Font\n/Encoding/WinAnsiEncoding\n/Subtype/Type1>>`;
|
|
371
362
|
this.addObject(fontObject);
|
|
372
363
|
return {
|
|
373
364
|
fontIndex: fontNumber,
|
|
@@ -383,7 +374,7 @@ endstream`;
|
|
|
383
374
|
const key = this.customKey(name, style);
|
|
384
375
|
if (this.customFonts.has(key))
|
|
385
376
|
return;
|
|
386
|
-
this.customFonts.set(key, new
|
|
377
|
+
this.customFonts.set(key, new TTFParser(data));
|
|
387
378
|
// Emission (the PDF font objects + page resource) is DEFERRED to first use via ensureEmitted(),
|
|
388
379
|
// so a registered-but-never-rendered face - e.g. a bundled family the document doesn't touch -
|
|
389
380
|
// costs nothing in the output. The metrics above are still available for layout immediately.
|
|
@@ -429,9 +420,7 @@ endstream`;
|
|
|
429
420
|
}
|
|
430
421
|
// A deterministic 6-uppercase-letter subset tag (PDF/A wants the "TAG+FontName" form).
|
|
431
422
|
subsetTag(pdfName, used) {
|
|
432
|
-
const h = (
|
|
433
|
-
.update(pdfName + [...used].sort((a, b) => a - b).join(","))
|
|
434
|
-
.digest();
|
|
423
|
+
const h = md5(new TextEncoder().encode(pdfName + [...used].sort((a, b) => a - b).join(",")));
|
|
435
424
|
let tag = "";
|
|
436
425
|
for (let i = 0; i < 6; i++)
|
|
437
426
|
tag += String.fromCharCode(65 + (h[i] % 26));
|
|
@@ -489,13 +478,13 @@ endstream`;
|
|
|
489
478
|
// The SUBSET font program (only the used glyphs' outlines). Binary bytes survive as a latin1
|
|
490
479
|
// string - the final Windows-1252 encoder passes 0x00-0xFF through unchanged (see getArrayBuffer).
|
|
491
480
|
buildFontFile2(ttf, used) {
|
|
492
|
-
const bytes =
|
|
481
|
+
const bytes = subsetTTF(ttf.getData(), used);
|
|
493
482
|
// /Length1 is the UNCOMPRESSED font-program length (required even when FlateDecode'd).
|
|
494
483
|
return this.stream(`/Length1 ${bytes.length}`, bytes);
|
|
495
484
|
}
|
|
496
485
|
buildFontDescriptor(name, ttf, fontFile) {
|
|
497
486
|
const [x0, y0, x1, y1] = ttf.bbox;
|
|
498
|
-
return (`<< /Type /FontDescriptor /FontName /${name} /Flags 4 ` +
|
|
487
|
+
return (`<< /Type /FontDescriptor /FontName /${pdfName(name)} /Flags 4 ` +
|
|
499
488
|
`/FontBBox [${x0} ${y0} ${x1} ${y1}] /ItalicAngle 0 ` +
|
|
500
489
|
`/Ascent ${ttf.ascent} /Descent ${ttf.descent} /CapHeight ${ttf.ascent} /StemV 80 ` +
|
|
501
490
|
`/FontFile2 ${fontFile} 0 R >>`);
|
|
@@ -506,9 +495,9 @@ endstream`;
|
|
|
506
495
|
const all = ttf.glyphWidths();
|
|
507
496
|
const w = [...used]
|
|
508
497
|
.sort((a, b) => a - b)
|
|
509
|
-
.map((g) =>
|
|
498
|
+
.map((g) => `${g} [${all[g] ?? 1000}]`)
|
|
510
499
|
.join(" ");
|
|
511
|
-
return (`<< /Type /Font /Subtype /CIDFontType2 /BaseFont /${name} ` +
|
|
500
|
+
return (`<< /Type /Font /Subtype /CIDFontType2 /BaseFont /${pdfName(name)} ` +
|
|
512
501
|
`/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> ` +
|
|
513
502
|
`/FontDescriptor ${descriptor} 0 R /CIDToGIDMap /Identity ` +
|
|
514
503
|
`/DW 1000 /W [${w}] >>`);
|
|
@@ -533,10 +522,10 @@ endstream`;
|
|
|
533
522
|
`/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n` +
|
|
534
523
|
`1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n${blocks.join("\n")}\n` +
|
|
535
524
|
`endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend`;
|
|
536
|
-
return this.stream("",
|
|
525
|
+
return this.stream("", bytesFromLatin1(body));
|
|
537
526
|
}
|
|
538
527
|
buildType0(name, cidFont, toUnicode) {
|
|
539
|
-
return (`<< /Type /Font /Subtype /Type0 /BaseFont /${name} /Encoding /Identity-H ` +
|
|
528
|
+
return (`<< /Type /Font /Subtype /Type0 /BaseFont /${pdfName(name)} /Encoding /Identity-H ` +
|
|
540
529
|
`/DescendantFonts [${cidFont} 0 R] /ToUnicode ${toUnicode} 0 R >>`);
|
|
541
530
|
}
|
|
542
531
|
// Returns the current width of a text, included kernings
|
|
@@ -561,6 +550,11 @@ endstream`;
|
|
|
561
550
|
return char.charCodeAt(0).toString();
|
|
562
551
|
}
|
|
563
552
|
getAVMParserByFont(fullFontName, fontName, fontStyle) {
|
|
553
|
+
// A non-string family means font bytes were passed where a name belongs - hint at the fix instead of
|
|
554
|
+
// stringifying the byte blob into an unreadable "parser not found".
|
|
555
|
+
for (const name of [fullFontName, fontName])
|
|
556
|
+
if (name != null && typeof name !== "string")
|
|
557
|
+
throw new Error(`Font family must be a string name, got ${typeof name}. Register font bytes via the document \`fonts\` map (or addFont) and reference them by name.`);
|
|
564
558
|
if (!fullFontName && (!fontName || !fontStyle)) {
|
|
565
559
|
throw new Error("No font family is given. Please set a full font name or a font with font style");
|
|
566
560
|
}
|
|
@@ -577,14 +571,16 @@ endstream`;
|
|
|
577
571
|
}
|
|
578
572
|
// Methode zur Berechnung der Zeichenbreite anhand der Schriftgröße
|
|
579
573
|
getCharWidth(char, fontSize, fullFontName, fontName, fontStyle) {
|
|
580
|
-
const ttf = this.getCustomFont(fullFontName
|
|
574
|
+
const ttf = this.getCustomFont(fullFontName ?? fontName, fontStyle);
|
|
581
575
|
if (ttf)
|
|
582
576
|
return ttf.getCharWidth(char, fontSize);
|
|
583
577
|
const currentParser = this.getAVMParserByFont(fullFontName, fontName, fontStyle);
|
|
584
|
-
|
|
585
|
-
//
|
|
578
|
+
let advanceWidth = currentParser.parser.getAdvanceWidth(char);
|
|
579
|
+
// A character a standard font cannot represent (not in Windows-1252) is drawn as "?" by the
|
|
580
|
+
// encoder; measure it as "?" too so the width matches the glyph - graceful instead of a crash.
|
|
586
581
|
if (!advanceWidth) {
|
|
587
|
-
|
|
582
|
+
advanceWidth =
|
|
583
|
+
currentParser.parser.getAdvanceWidth("?") || currentParser.parser.getAdvanceWidth(" ");
|
|
588
584
|
}
|
|
589
585
|
// Width of the character multiplied by the font size (scaled proportionally)
|
|
590
586
|
return (advanceWidth / 1000) * fontSize;
|
|
@@ -592,7 +588,7 @@ endstream`;
|
|
|
592
588
|
// Method to get the kerning, if available, between two signs
|
|
593
589
|
getKerning(char, nextChar, fullFontName, fontName, fontStyle) {
|
|
594
590
|
// TrueType kerning (the kern/GPOS tables) is not wired up yet - no kerning for custom fonts.
|
|
595
|
-
if (this.getCustomFont(fullFontName
|
|
591
|
+
if (this.getCustomFont(fullFontName ?? fontName, fontStyle))
|
|
596
592
|
return 0;
|
|
597
593
|
const currentParser = this.getAVMParserByFont(fullFontName, fontName, fontStyle);
|
|
598
594
|
return currentParser.parser.getKerning(char, nextChar) / 1000;
|
|
@@ -635,11 +631,10 @@ endstream`;
|
|
|
635
631
|
return `trailer\n<< /Size ${objectCount + 1} /Root ${root} 0 R${id} >>\nstartxref\n${startxref}\n%%EOF`;
|
|
636
632
|
}
|
|
637
633
|
contentId() {
|
|
638
|
-
return `<${(
|
|
634
|
+
return `<${md5Hex(new TextEncoder().encode(this.objects.join(""))).toUpperCase()}>`;
|
|
639
635
|
}
|
|
640
636
|
// Returns the number of objects
|
|
641
637
|
getObjectCount() {
|
|
642
638
|
return this.objects.length;
|
|
643
639
|
}
|
|
644
640
|
}
|
|
645
|
-
exports.PDFObjectManager = PDFObjectManager;
|
|
@@ -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;
|