reamkit 1.30.0 → 1.31.0
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 +60 -24
- package/dist/esm/core/converter/project.js +3 -1
- package/dist/esm/core/crypto/offcrypto.js +1 -1
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +63 -0
- package/dist/esm/core/font/index.d.ts +2 -0
- package/dist/esm/core/font/ttf-build.d.ts +113 -0
- package/dist/esm/core/font/ttf-build.js +1224 -0
- package/dist/esm/core/font/ttf-subset.d.ts +19 -0
- package/dist/esm/core/font/ttf-subset.js +14 -1
- package/dist/esm/core/fonts/provider.d.ts +17 -0
- package/dist/esm/core/fonts/provider.js +27 -2
- package/dist/esm/core/fonts/remote-fonts.js +8 -1
- package/dist/esm/core/ir/flow.d.ts +71 -1
- package/dist/esm/core/numbering/index.d.ts +1 -1
- package/dist/esm/core/numbering/state.d.ts +11 -1
- package/dist/esm/core/numbering/state.js +10 -1
- package/dist/esm/core/style-cascade/resolver.js +35 -4
- package/dist/esm/core/style-cascade/types.d.ts +19 -1
- package/dist/esm/core/style-cascade/types.js +3 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/layout/page-doc.d.ts +10 -3
- package/dist/esm/layout/styled-layout.d.ts +48 -7
- package/dist/esm/layout/styled-layout.js +881 -119
- package/dist/esm/layout/turned-section.d.ts +38 -0
- package/dist/esm/layout/turned-section.js +193 -0
- package/dist/esm/pdf/styled-page-emitter.js +78 -2
- package/dist/esm/pdf-reader/cff-outline.d.ts +27 -0
- package/dist/esm/pdf-reader/cff-outline.js +169 -21
- package/dist/esm/pdf-reader/content.d.ts +53 -0
- package/dist/esm/pdf-reader/content.js +9 -1
- package/dist/esm/pdf-reader/display.d.ts +36 -0
- package/dist/esm/pdf-reader/display.js +66 -1
- package/dist/esm/pdf-reader/document.js +5 -1
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +2 -1
- package/dist/esm/pdf-reader/embedded-fonts.js +32 -2
- package/dist/esm/pdf-reader/encodings.d.ts +8 -0
- package/dist/esm/pdf-reader/encodings.js +25 -3
- package/dist/esm/pdf-reader/face-outlines.d.ts +78 -0
- package/dist/esm/pdf-reader/face-outlines.js +362 -0
- package/dist/esm/pdf-reader/figures.d.ts +52 -0
- package/dist/esm/pdf-reader/figures.js +433 -0
- package/dist/esm/pdf-reader/flow-build.d.ts +131 -5
- package/dist/esm/pdf-reader/flow-build.js +345 -26
- package/dist/esm/pdf-reader/font.js +321 -36
- package/dist/esm/pdf-reader/glyf-outline.d.ts +33 -0
- package/dist/esm/pdf-reader/glyf-outline.js +135 -1
- package/dist/esm/pdf-reader/glyph-names.js +154 -1
- package/dist/esm/pdf-reader/layout.d.ts +93 -0
- package/dist/esm/pdf-reader/layout.js +1703 -214
- package/dist/esm/pdf-reader/page-numbers.d.ts +53 -0
- package/dist/esm/pdf-reader/page-numbers.js +167 -0
- package/dist/esm/pdf-reader/tagged.js +182 -23
- package/dist/esm/pdf-reader/text.d.ts +6 -3
- package/dist/esm/pdf-reader/text.js +98 -9
- package/dist/esm/pdf-reader/type1-outline.d.ts +11 -0
- package/dist/esm/pdf-reader/type1-outline.js +63 -8
- package/dist/esm/pdf-reader/vector.js +71 -1
- package/dist/esm/word/doc/doc-reader.js +6 -2
- package/dist/esm/word/doc/doc-text.d.ts +6 -0
- package/dist/esm/word/doc/doc-text.js +19 -1
- package/dist/esm/word/document-parser.d.ts +2 -2
- package/dist/esm/word/document-parser.js +11 -1
- package/dist/esm/word/docx-reader.js +5 -3
- package/dist/esm/word/docx-writer.js +207 -21
- package/dist/esm/word/drawing-parser.d.ts +5 -3
- package/dist/esm/word/drawing-parser.js +49 -8
- package/dist/esm/word/font-embed.d.ts +30 -0
- package/dist/esm/word/font-embed.js +173 -0
- package/dist/esm/word/font-table.d.ts +10 -0
- package/dist/esm/word/font-table.js +13 -1
- package/dist/esm/word/index.js +1 -1
- package/dist/esm/word/numbering-parser.d.ts +3 -1
- package/dist/esm/word/numbering-parser.js +2 -1
- package/dist/esm/word/paragraph-properties.d.ts +7 -6
- package/dist/esm/word/paragraph-properties.js +14 -2
- package/dist/esm/word/run-properties.js +26 -0
- package/package.json +8 -3
|
@@ -5,10 +5,10 @@ import { parseToUnicodeCMap } from "./cmap.js";
|
|
|
5
5
|
import { decodePredefined, predefinedCMap, splitPredefined } from "./predefined-cmap.js";
|
|
6
6
|
import { isZapfDingbats, zapfDingbatsChar } from "./dingbats.js";
|
|
7
7
|
import { textForGlyphName } from "./glyph-names.js";
|
|
8
|
-
import {
|
|
8
|
+
import { baseEncodingTable, isStandardLatinFace, macGlyphName, standardEncodingTable, winAnsiLatinName } from "./encodings.js";
|
|
9
|
+
import { cffCidToGid, cffFsType, cffNameToGid, cffOutlineSource, cffSpaceAdvance, openTypeCff } from "./cff-outline.js";
|
|
9
10
|
import { type1Font } from "./type1-outline.js";
|
|
10
|
-
import {
|
|
11
|
-
import { outlineSource, postGlyphNames } from "./glyf-outline.js";
|
|
11
|
+
import { cmapSubtables, outlineSource, postGlyphNames, sfntFsType, sfntSpaceAdvance } from "./glyf-outline.js";
|
|
12
12
|
import { standardFace, standardWidth } from "./standard-widths.js";
|
|
13
13
|
import { eachPageFont, embeddedFontName, hasLiftableProgram, programStyle } from "./embedded-fonts.js";
|
|
14
14
|
//#region src/pdf-reader/font.ts
|
|
@@ -69,6 +69,7 @@ function buildContentFont(file, fontDict) {
|
|
|
69
69
|
const dingbats = !isType0 && isZapfDingbats(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
|
|
70
70
|
const stated = file.resolve(fontDict.get("Encoding") ?? PDF_NULL);
|
|
71
71
|
const statesEncoding = stated instanceof Map || stated instanceof PdfName && baseEncodingTable(stated.value) !== void 0;
|
|
72
|
+
const numbered = !isType0 && toUnicode.size === 0 && glyphNames.size === 0 && stated === PDF_NULL && glyphs?.numbersGlyphs?.() === true;
|
|
72
73
|
const baseNames = isType0 ? void 0 : statesEncoding ? baseEncoding(file, fontDict) : glyphs?.builtIn ?? baseEncoding(file, fontDict);
|
|
73
74
|
const fromBase = /* @__PURE__ */ new Map();
|
|
74
75
|
for (const [code, glyph] of baseNames ?? []) {
|
|
@@ -79,18 +80,24 @@ function buildContentFont(file, fontDict) {
|
|
|
79
80
|
const style = faceStyle(file, fontDict, isType0);
|
|
80
81
|
const name = runFontName(file, fontDict, isType0);
|
|
81
82
|
const type3 = asName(file.resolve(fontDict.get("Subtype") ?? PDF_NULL)) === "Type3" ? type3Face(file, fontDict) : void 0;
|
|
82
|
-
const decodeOne = (code) => (indices ? UNANSWERABLE : void 0) ?? unicode.get(code) ?? fromProgramFor(code) ?? (named ? decodePredefined(named, code) : void 0) ?? fromNames?.get(code) ?? (glyphNames.get(code) === NOTDEF ? "" : void 0) ?? (dingbats ? zapfDingbatsChar(code) : void 0) ?? fromBase.get(code) ?? (bytesPerCode === 1 ? latin1(code) : UNANSWERABLE);
|
|
83
|
+
const decodeOne = (code) => (indices || numbered ? UNANSWERABLE : void 0) ?? unicode.get(code) ?? fromProgramFor(code) ?? (named ? decodePredefined(named, code) : void 0) ?? fromNames?.get(code) ?? (glyphNames.get(code) === NOTDEF ? "" : void 0) ?? (dingbats ? zapfDingbatsChar(code) : void 0) ?? fromBase.get(code) ?? (bytesPerCode === 1 ? latin1(code) : UNANSWERABLE);
|
|
84
|
+
const composite = isType0 ? compositeGlyphs(file, fontDict) : void 0;
|
|
83
85
|
const simple = simpleWidths(file, fontDict, decodeOne);
|
|
84
|
-
const width = isType0 ? cidWidths(file, fontDict) : type3 ? (code) => simple(code) * type3.matrix[0] * 1e3 : simple;
|
|
86
|
+
const width = isType0 ? cidWidths(file, fontDict) : type3 ? (code) => simple.width(code) * type3.matrix[0] * 1e3 : simple.width;
|
|
87
|
+
const spaceWidth = isType0 || type3 || namesOf.get(32) !== "space" && unicode.get(32) !== " " ? void 0 : simple.stated(32);
|
|
88
|
+
const fixedPitch = !isType0 && !type3 && fixedPitchOf(file, fontDict);
|
|
85
89
|
return {
|
|
86
90
|
bytesPerCode,
|
|
87
91
|
...named ? { splitCodes: (b) => splitPredefined(named, b) } : {},
|
|
88
|
-
...isType0 ? outlineOf(
|
|
92
|
+
...isType0 ? outlineOf(composite, decodeOne) : simpleOutlineOf(decodeOne, namesOf, glyphs, numbered),
|
|
93
|
+
...name !== void 0 && !type3 ? programOf(file, fontDict, isType0, isType0 ? compositeGlyph(composite, fontDict, file) : simpleGlyph(glyphs, namesOf, baseNames === baseEncodingTable("WinAnsiEncoding")), isType0 ? composite : glyphs) : {},
|
|
89
94
|
...named?.vertical ? { verticalAdvance: cidVerticalAdvance(file, fontDict) } : {},
|
|
90
95
|
...type3 ? { type3 } : {},
|
|
91
96
|
...name !== void 0 ? { name } : {},
|
|
92
97
|
decode: (codes) => codes.map((c) => lettersOf(readable(decodeOne(c)))).join(""),
|
|
93
98
|
width,
|
|
99
|
+
...spaceWidth !== void 0 ? { spaceWidth } : {},
|
|
100
|
+
...fixedPitch ? { fixedPitch: true } : {},
|
|
94
101
|
...style
|
|
95
102
|
};
|
|
96
103
|
}
|
|
@@ -316,46 +323,77 @@ function programCharacters(file, owner) {
|
|
|
316
323
|
return byGlyph.size > 0 ? byGlyph : void 0;
|
|
317
324
|
}
|
|
318
325
|
/**
|
|
319
|
-
* §9.
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
* makes the code a CID, `/CIDToGIDMap` turns that into a glyph index, and the
|
|
323
|
-
* embedded program holds the contours. complex_ttf_font.pdf is eight lines of
|
|
324
|
-
* Arabic in a subset with no `cmap` and no `/ToUnicode`, and every one of them
|
|
325
|
-
* was dropped.
|
|
326
|
-
*
|
|
327
|
-
* Deliberately NOT a fallback for text: a code the font CAN answer for is set
|
|
328
|
-
* as type, and only the unanswerable ones are traced.
|
|
326
|
+
* §9.7.4 — a composite font's embedded program, read by glyph: the code is a
|
|
327
|
+
* CID, `/CIDToGIDMap` (or, for a CID-keyed CFF, its charset) turns that into a
|
|
328
|
+
* glyph index, and the program holds the contours.
|
|
329
329
|
*
|
|
330
|
-
* @param file
|
|
331
|
-
* @param fontDict
|
|
332
|
-
* @
|
|
333
|
-
*
|
|
334
|
-
* program carries no outlines this reads.
|
|
330
|
+
* @param file The owning file.
|
|
331
|
+
* @param fontDict The Type 0 font dictionary.
|
|
332
|
+
* @returns The program's outlines and how a CID reaches them, or `undefined`
|
|
333
|
+
* where the font embeds no program this reads.
|
|
335
334
|
*/
|
|
336
|
-
function
|
|
335
|
+
function compositeGlyphs(file, fontDict) {
|
|
337
336
|
const cidFont = descendantFont(file, fontDict);
|
|
338
337
|
const descriptor = file.resolve(cidFont.get("FontDescriptor") ?? PDF_NULL);
|
|
339
|
-
if (!(descriptor instanceof Map)) return
|
|
338
|
+
if (!(descriptor instanceof Map)) return void 0;
|
|
340
339
|
const truetype = file.resolve(descriptor.get("FontFile2") ?? PDF_NULL);
|
|
341
340
|
const compact = file.resolve(descriptor.get("FontFile3") ?? PDF_NULL);
|
|
342
341
|
const program = truetype instanceof PdfStream ? truetype : compact;
|
|
343
|
-
if (!(program instanceof PdfStream)) return
|
|
342
|
+
if (!(program instanceof PdfStream)) return void 0;
|
|
344
343
|
let source;
|
|
345
344
|
let charsetCids;
|
|
345
|
+
let fsType;
|
|
346
|
+
let spaceAdvance;
|
|
346
347
|
try {
|
|
347
348
|
const bytes = file.streamData(program);
|
|
348
349
|
source = outlineSource(bytes);
|
|
350
|
+
fsType = sfntFsType(bytes);
|
|
351
|
+
spaceAdvance = sfntSpaceAdvance(bytes);
|
|
349
352
|
if (!source) {
|
|
350
353
|
const cff = openTypeCff(bytes) ?? bytes;
|
|
351
354
|
source = cffOutlineSource(cff);
|
|
352
355
|
charsetCids = source ? cffCidToGid(cff) : void 0;
|
|
356
|
+
fsType ??= cffFsType(cff);
|
|
353
357
|
}
|
|
354
358
|
} catch {
|
|
355
|
-
return
|
|
359
|
+
return;
|
|
356
360
|
}
|
|
357
|
-
if (!source) return
|
|
361
|
+
if (!source) return void 0;
|
|
358
362
|
const cidToGid = readCidToGid(file, cidFont);
|
|
363
|
+
return {
|
|
364
|
+
source,
|
|
365
|
+
glyphOf: (cid) => {
|
|
366
|
+
const mapped = cidToGid ? cidToGid[cid] : cid;
|
|
367
|
+
if (mapped === void 0) return void 0;
|
|
368
|
+
return charsetCids ? charsetCids.get(mapped) : mapped;
|
|
369
|
+
},
|
|
370
|
+
looseGlyphOf: (cid) => {
|
|
371
|
+
const mapped = cidToGid ? cidToGid[cid] ?? 0 : cid;
|
|
372
|
+
return charsetCids ? charsetCids.get(mapped) ?? mapped : mapped;
|
|
373
|
+
},
|
|
374
|
+
...fsType !== void 0 ? { fsType } : {},
|
|
375
|
+
...spaceAdvance !== void 0 ? { spaceAdvance } : {}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* §9.6.6 — the outline a code draws, for a code that stands for no character.
|
|
380
|
+
*
|
|
381
|
+
* The glyph is there even when the character is not: `/Encoding /Identity-H`
|
|
382
|
+
* makes the code a CID, `/CIDToGIDMap` turns that into a glyph index, and the
|
|
383
|
+
* embedded program holds the contours. complex_ttf_font.pdf is eight lines of
|
|
384
|
+
* Arabic in a subset with no `cmap` and no `/ToUnicode`, and every one of them
|
|
385
|
+
* was dropped.
|
|
386
|
+
*
|
|
387
|
+
* Deliberately NOT a fallback for text: a code the font CAN answer for is set
|
|
388
|
+
* as type, and only the unanswerable ones are traced.
|
|
389
|
+
*
|
|
390
|
+
* @param glyphs The font's program, read by glyph (see `compositeGlyphs`).
|
|
391
|
+
* @param decodeOne What one code comes to, to tell the two cases apart.
|
|
392
|
+
* @returns The `outline` field of a {@link ContentFont}, or nothing where the
|
|
393
|
+
* program carries no outlines this reads.
|
|
394
|
+
*/
|
|
395
|
+
function outlineOf(glyphs, decodeOne) {
|
|
396
|
+
if (!glyphs) return {};
|
|
359
397
|
return { outline: {
|
|
360
398
|
matrix: [
|
|
361
399
|
1,
|
|
@@ -367,13 +405,28 @@ function outlineOf(file, fontDict, decodeOne) {
|
|
|
367
405
|
],
|
|
368
406
|
path: (code) => {
|
|
369
407
|
if (readable(decodeOne(code)) !== UNANSWERABLE) return void 0;
|
|
370
|
-
|
|
371
|
-
const gid = charsetCids ? charsetCids.get(mapped) ?? mapped : mapped;
|
|
372
|
-
return source.path(gid);
|
|
408
|
+
return glyphs.source.path(glyphs.looseGlyphOf(code));
|
|
373
409
|
}
|
|
374
410
|
} };
|
|
375
411
|
}
|
|
376
412
|
/**
|
|
413
|
+
* §9.7.5.2 — the glyph ANY code of a composite font draws, for a writer that
|
|
414
|
+
* embeds the face.
|
|
415
|
+
*
|
|
416
|
+
* Only under `Identity-H` or `Identity-V`, where the code is the CID: any other
|
|
417
|
+
* CMap turns codes into CIDs by a table of its own, and a glyph looked up by
|
|
418
|
+
* the code there would be some other character's.
|
|
419
|
+
*/
|
|
420
|
+
function compositeGlyph(glyphs, fontDict, file) {
|
|
421
|
+
const encoding = asName(file.resolve(fontDict.get("Encoding") ?? PDF_NULL));
|
|
422
|
+
if (!glyphs || encoding !== "Identity-H" && encoding !== "Identity-V") return void 0;
|
|
423
|
+
return (code) => {
|
|
424
|
+
const gid = glyphs.glyphOf(code);
|
|
425
|
+
if (gid === void 0 || gid <= 0 || gid >= glyphs.source.count) return void 0;
|
|
426
|
+
return glyphs.source.path(gid) ?? [];
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
377
430
|
* §9.6.6 — the outline a SIMPLE font's code draws, for a code that stands for
|
|
378
431
|
* no character.
|
|
379
432
|
*
|
|
@@ -388,9 +441,12 @@ function outlineOf(file, fontDict, decodeOne) {
|
|
|
388
441
|
* @param fontDict The simple font's dictionary.
|
|
389
442
|
* @param decodeOne What one code comes to, to tell the two cases apart.
|
|
390
443
|
* @param nameOf The glyph name a code selects, where the font states one.
|
|
444
|
+
* @param numbered Whether the program numbers its glyphs (see
|
|
445
|
+
* {@link numbersGlyphs}), which its `cmap` then reaches by
|
|
446
|
+
* the code.
|
|
391
447
|
* @returns The `outline` field of a {@link ContentFont}, or nothing.
|
|
392
448
|
*/
|
|
393
|
-
function simpleOutlineOf(decodeOne, nameOf, source) {
|
|
449
|
+
function simpleOutlineOf(decodeOne, nameOf, source, numbered) {
|
|
394
450
|
if (!source) return {};
|
|
395
451
|
return { outline: {
|
|
396
452
|
matrix: [
|
|
@@ -403,6 +459,10 @@ function simpleOutlineOf(decodeOne, nameOf, source) {
|
|
|
403
459
|
],
|
|
404
460
|
path: (code) => {
|
|
405
461
|
if (readable(decodeOne(code)) !== UNANSWERABLE) return void 0;
|
|
462
|
+
if (numbered) {
|
|
463
|
+
const segs = source.glyph(code, void 0);
|
|
464
|
+
return segs !== void 0 && segs.length > 0 ? [...segs] : void 0;
|
|
465
|
+
}
|
|
406
466
|
const name = nameOf.get(code) ?? source.builtIn?.get(code);
|
|
407
467
|
if (name === void 0) return source.byIndex?.(code);
|
|
408
468
|
return source.byName(name);
|
|
@@ -437,20 +497,34 @@ function simpleGlyphs(file, fontDict) {
|
|
|
437
497
|
return {
|
|
438
498
|
byName: face.path,
|
|
439
499
|
blank: (name) => face.has(name) && face.path(name) === void 0,
|
|
440
|
-
...face.encoding ? { builtIn: face.encoding } : {}
|
|
500
|
+
...face.encoding ? { builtIn: face.encoding } : {},
|
|
501
|
+
glyph: (_code, name) => name === void 0 || name === NOTDEF || !face.has(name) ? void 0 : face.path(name) ?? [],
|
|
502
|
+
...face.fsType !== void 0 ? { fsType: face.fsType } : {},
|
|
503
|
+
...spaceOf(face.advance("space"))
|
|
441
504
|
};
|
|
442
505
|
}
|
|
443
506
|
const stream = compact instanceof PdfStream ? compact : truetype;
|
|
444
507
|
if (!(stream instanceof PdfStream)) return void 0;
|
|
445
508
|
const bytes = file.streamData(stream);
|
|
446
509
|
const glyf = outlineSource(bytes);
|
|
510
|
+
const fsType = sfntFsType(bytes);
|
|
447
511
|
if (glyf) {
|
|
448
512
|
const named = postGlyphNames(bytes);
|
|
449
513
|
const gidOf = (name) => {
|
|
450
514
|
const gid = named?.get(name) ?? numberedGlyph(name, numbering);
|
|
451
515
|
return gid !== void 0 && gid < glyf.count ? gid : void 0;
|
|
452
516
|
};
|
|
517
|
+
const flags = asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0);
|
|
518
|
+
const encoding = file.resolve(fontDict.get("Encoding") ?? PDF_NULL);
|
|
519
|
+
const byCode = trueTypeGlyphs(bytes, (flags & FLAG_SYMBOLIC) !== 0 && (flags & FLAG_NONSYMBOLIC) === 0, encoding instanceof Map || encoding instanceof PdfName, gidOf);
|
|
453
520
|
return {
|
|
521
|
+
glyph: (code, name) => {
|
|
522
|
+
const gid = byCode(code, name);
|
|
523
|
+
if (gid === void 0 || gid <= 0 || gid >= glyf.count) return void 0;
|
|
524
|
+
return glyf.path(gid) ?? [];
|
|
525
|
+
},
|
|
526
|
+
...fsType !== void 0 ? { fsType } : {},
|
|
527
|
+
...spaceOf(sfntSpaceAdvance(bytes)),
|
|
454
528
|
byName: (name) => {
|
|
455
529
|
const gid = gidOf(name);
|
|
456
530
|
return gid === void 0 ? void 0 : glyf.path(gid);
|
|
@@ -460,7 +534,8 @@ function simpleGlyphs(file, fontDict) {
|
|
|
460
534
|
return gid !== void 0 && glyf.path(gid) === void 0;
|
|
461
535
|
},
|
|
462
536
|
byIndex: (gid) => gid < glyf.count ? glyf.path(gid) : void 0,
|
|
463
|
-
indexed: !glyf.cmap
|
|
537
|
+
indexed: !glyf.cmap,
|
|
538
|
+
numbersGlyphs: () => numbersGlyphs(bytes, glyf, named, (code) => byCode(code, void 0))
|
|
464
539
|
};
|
|
465
540
|
}
|
|
466
541
|
const cff = openTypeCff(bytes) ?? bytes;
|
|
@@ -471,7 +546,14 @@ function simpleGlyphs(file, fontDict) {
|
|
|
471
546
|
const gid = names?.get(name) ?? numberedGlyph(name, numbering);
|
|
472
547
|
return gid !== void 0 && gid < outlines.count ? gid : void 0;
|
|
473
548
|
};
|
|
549
|
+
const stated = fsType ?? cffFsType(cff);
|
|
474
550
|
return {
|
|
551
|
+
glyph: (_code, name) => {
|
|
552
|
+
const gid = name === void 0 || name === NOTDEF ? void 0 : gidOf(name);
|
|
553
|
+
return gid === void 0 || gid === 0 ? void 0 : outlines.path(gid) ?? [];
|
|
554
|
+
},
|
|
555
|
+
...stated !== void 0 ? { fsType: stated } : {},
|
|
556
|
+
...spaceOf(sfntSpaceAdvance(bytes) ?? cffSpaceAdvance(cff)),
|
|
475
557
|
byName: (name) => {
|
|
476
558
|
const gid = gidOf(name);
|
|
477
559
|
return gid === void 0 ? void 0 : outlines.path(gid);
|
|
@@ -485,6 +567,190 @@ function simpleGlyphs(file, fontDict) {
|
|
|
485
567
|
return;
|
|
486
568
|
}
|
|
487
569
|
}
|
|
570
|
+
/** The `spaceAdvance` field, where a program states a sensible one. */
|
|
571
|
+
function spaceOf(advance) {
|
|
572
|
+
return advance !== void 0 && advance > 0 && advance < 1e3 ? { spaceAdvance: advance } : {};
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* §9.6.6.4 — how a SIMPLE TrueType font's code reaches a glyph: through the
|
|
576
|
+
* program's `cmap`, and which subtable decides what the code is looked up as.
|
|
577
|
+
*
|
|
578
|
+
* A face that states an encoding turns the code into its glyph NAME, and the
|
|
579
|
+
* name into a character for the Unicode subtable (3,1), a Mac Roman code for
|
|
580
|
+
* (1,0), or an entry of the program's own `post` names — and past ASCII, only
|
|
581
|
+
* that: the same byte is another letter in Mac Roman, and a glyph found by the
|
|
582
|
+
* code there is the wrong one. A symbolic face with a Windows symbol subtable
|
|
583
|
+
* (3,0), and a face that states no encoding, is looked up by the CODE: in
|
|
584
|
+
* (3,0), where it stands in one of four ranges (itself, or behind 0xF000,
|
|
585
|
+
* 0xF100 or 0xF200), or byte for byte in (1,0) — its names the fallback, as a
|
|
586
|
+
* viewer tries them. A program with no `cmap` at all is read by index: the
|
|
587
|
+
* code IS the glyph.
|
|
588
|
+
*
|
|
589
|
+
* @param program The raw `/FontFile2` bytes.
|
|
590
|
+
* @param symbolic Whether `/Flags` call the face symbolic (bit 3, not bit 6).
|
|
591
|
+
* @param hasEncoding Whether the font dictionary states an `/Encoding`.
|
|
592
|
+
* @param byName The glyph a name selects through `post` or the
|
|
593
|
+
* numbered-name convention (see {@link numberedGlyph}).
|
|
594
|
+
* @returns Code (and the name the encoding gives it) → glyph index, or
|
|
595
|
+
* `undefined` where nothing reaches one.
|
|
596
|
+
*/
|
|
597
|
+
function trueTypeGlyphs(program, symbolic, hasEncoding, byName) {
|
|
598
|
+
let tables;
|
|
599
|
+
return (code, name) => {
|
|
600
|
+
tables ??= cmapSubtables(program);
|
|
601
|
+
const unicode = tables.get("3,1") ?? tables.get("0,3") ?? tables.get("0,1") ?? tables.get("0,0");
|
|
602
|
+
const symbol = tables.get("3,0");
|
|
603
|
+
const roman = tables.get("1,0");
|
|
604
|
+
const bySymbol = () => {
|
|
605
|
+
if (symbol) {
|
|
606
|
+
for (const high of SYMBOL_RANGES) {
|
|
607
|
+
const gid = symbol(high | code);
|
|
608
|
+
if (gid > 0) return gid;
|
|
609
|
+
}
|
|
610
|
+
return 0;
|
|
611
|
+
}
|
|
612
|
+
return roman ? roman(code) : 0;
|
|
613
|
+
};
|
|
614
|
+
const byGlyphName = () => {
|
|
615
|
+
if (name === void 0 || name === NOTDEF) return 0;
|
|
616
|
+
const text = textForGlyphName(name);
|
|
617
|
+
const cp = text !== void 0 && [...text].length === 1 ? text.codePointAt(0) : void 0;
|
|
618
|
+
const viaUnicode = unicode && cp !== void 0 ? unicode(cp) : 0;
|
|
619
|
+
if (viaUnicode > 0) return viaUnicode;
|
|
620
|
+
const mac = macRomanCode(name);
|
|
621
|
+
const viaRoman = roman && mac !== void 0 ? roman(mac) : 0;
|
|
622
|
+
if (viaRoman > 0) return viaRoman;
|
|
623
|
+
return byName(name) ?? 0;
|
|
624
|
+
};
|
|
625
|
+
const gid = symbolic && symbol !== void 0 ? bySymbol() || byGlyphName() : hasEncoding ? byGlyphName() || (code < ASCII_END ? bySymbol() : 0) : bySymbol() || byGlyphName() || (unicode ? unicode(code) : 0);
|
|
626
|
+
if (gid > 0) return gid;
|
|
627
|
+
return tables.size === 0 ? code : void 0;
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
/** Below this, a byte is the same character in every encoding a simple font uses. */
|
|
631
|
+
var ASCII_END = 128;
|
|
632
|
+
/**
|
|
633
|
+
* §9.6.6.4 — whether a TrueType program NUMBERS its glyphs, where a font would
|
|
634
|
+
* code characters with them: its codes are the order some producer came to
|
|
635
|
+
* each glyph in, and read as Latin-1 they spell nothing.
|
|
636
|
+
*
|
|
637
|
+
* Nothing in such a program says what its codes are — no Unicode subtable, no
|
|
638
|
+
* glyph names — and its SPACE says what they are not. A text face's blank
|
|
639
|
+
* glyph is its space, and Latin-1 puts the space at 32. A program that reaches
|
|
640
|
+
* no glyph at 32, and a blank one where Latin-1 reads a mark, is not coded in
|
|
641
|
+
* Latin-1: comments.pdf's chart faces put their spaces at 35 and 34, "#" and
|
|
642
|
+
* `"`.
|
|
643
|
+
*
|
|
644
|
+
* @param program The raw `/FontFile2` bytes.
|
|
645
|
+
* @param glyf Its outlines.
|
|
646
|
+
* @param named The glyphs its `post` table names.
|
|
647
|
+
* @param gidOf The glyph a code reaches, looked up as the text is.
|
|
648
|
+
*/
|
|
649
|
+
function numbersGlyphs(program, glyf, named, gidOf) {
|
|
650
|
+
if (named !== void 0 && named.size > 0) return false;
|
|
651
|
+
const tables = cmapSubtables(program);
|
|
652
|
+
if (UNICODE_SUBTABLES.some((key) => tables.has(key))) return false;
|
|
653
|
+
if (!tables.has("1,0") && !tables.has("3,0")) return false;
|
|
654
|
+
const reached = (code) => {
|
|
655
|
+
const gid = gidOf(code);
|
|
656
|
+
return gid !== void 0 && gid > 0 && gid < glyf.count ? gid : void 0;
|
|
657
|
+
};
|
|
658
|
+
if (reached(SPACE) !== void 0) return false;
|
|
659
|
+
const blanks = [];
|
|
660
|
+
for (let code = 0; code <= 255; code++) {
|
|
661
|
+
const gid = reached(code);
|
|
662
|
+
if (gid !== void 0 && glyf.path(gid) === void 0) blanks.push(code);
|
|
663
|
+
}
|
|
664
|
+
return blanks.length > 0 && blanks.length <= MOST_BLANKS && blanks.every(inkedInLatin1);
|
|
665
|
+
}
|
|
666
|
+
/** The `cmap` subtables that key a program's glyphs by character. */
|
|
667
|
+
var UNICODE_SUBTABLES = [
|
|
668
|
+
"3,1",
|
|
669
|
+
"0,3",
|
|
670
|
+
"0,1",
|
|
671
|
+
"0,0"
|
|
672
|
+
];
|
|
673
|
+
/**
|
|
674
|
+
* How many blank glyphs a text face has: its space and, beside it, a no-break
|
|
675
|
+
* one. A program with more has emptied the glyphs it does not use, and its
|
|
676
|
+
* codes may be Latin-1 for all its blanks say.
|
|
677
|
+
*/
|
|
678
|
+
var MOST_BLANKS = 2;
|
|
679
|
+
/** Whether Latin-1 reads a code as a mark: not a control, a space or a soft hyphen. */
|
|
680
|
+
function inkedInLatin1(code) {
|
|
681
|
+
return code > 32 && code < 127 || code > 160 && code <= 255 && code !== 173;
|
|
682
|
+
}
|
|
683
|
+
/** §9.6.6.4 — where a symbolic face's (3,0) subtable may put a one-byte code. */
|
|
684
|
+
var SYMBOL_RANGES = [
|
|
685
|
+
0,
|
|
686
|
+
61440,
|
|
687
|
+
61696,
|
|
688
|
+
61952
|
|
689
|
+
];
|
|
690
|
+
/** §9.8.2 `/Flags` — bit 3 is Symbolic, bit 6 Nonsymbolic (bits numbered from 1). */
|
|
691
|
+
var FLAG_SYMBOLIC = 4;
|
|
692
|
+
var FLAG_NONSYMBOLIC = 32;
|
|
693
|
+
/** The Mac Roman code of a glyph name, which is what a (1,0) `cmap` is keyed by. */
|
|
694
|
+
function macRomanCode(name) {
|
|
695
|
+
if (!macRomanCodes) {
|
|
696
|
+
macRomanCodes = /* @__PURE__ */ new Map();
|
|
697
|
+
for (const [code, glyph] of baseEncodingTable("MacRomanEncoding") ?? []) if (!macRomanCodes.has(glyph)) macRomanCodes.set(glyph, code);
|
|
698
|
+
}
|
|
699
|
+
return macRomanCodes.get(name);
|
|
700
|
+
}
|
|
701
|
+
var macRomanCodes;
|
|
702
|
+
/**
|
|
703
|
+
* §9.6.6 — the glyph ANY code of a simple font draws, for a writer that embeds
|
|
704
|
+
* the face: the name the encoding gives the code, as the outline path reads it.
|
|
705
|
+
*
|
|
706
|
+
* WinAnsiEncoding's Latin-1 half is named too (Annex D.2), though the text
|
|
707
|
+
* reads it as Latin-1 without a name: unnamed, an é in a program addressed by
|
|
708
|
+
* name selected nothing — and in a TrueType one, the Mac Roman subtable's È.
|
|
709
|
+
*
|
|
710
|
+
* @param glyphs The program, read by name and code.
|
|
711
|
+
* @param nameOf The name each code has: `/Differences` over the base encoding.
|
|
712
|
+
* @param winAnsi Whether that base encoding is WinAnsiEncoding.
|
|
713
|
+
*/
|
|
714
|
+
function simpleGlyph(glyphs, nameOf, winAnsi) {
|
|
715
|
+
if (!glyphs) return void 0;
|
|
716
|
+
return (code) => glyphs.glyph(code, nameOf.get(code) ?? (winAnsi ? winAnsiLatinName(code) : void 0) ?? glyphs.builtIn?.get(code));
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* §9.9 — the {@link FaceProgram} of a font whose program this reads: the glyph
|
|
720
|
+
* lookup, the licence, and what the descriptor states (§9.8.1).
|
|
721
|
+
*
|
|
722
|
+
* @param file The owning file.
|
|
723
|
+
* @param fontDict The font dictionary.
|
|
724
|
+
* @param isType0 Whether it is a composite font, whose descendant owns the
|
|
725
|
+
* descriptor.
|
|
726
|
+
* @param glyph The glyph each code draws, where the program can be read.
|
|
727
|
+
* @param facts What the program states of itself: its licence and its space.
|
|
728
|
+
* @returns The `program` field of a {@link ContentFont}, or nothing.
|
|
729
|
+
*/
|
|
730
|
+
function programOf(file, fontDict, isType0, glyph, facts) {
|
|
731
|
+
if (!glyph) return {};
|
|
732
|
+
const owner = isType0 ? descendantFont(file, fontDict) : fontDict;
|
|
733
|
+
const descriptor = file.resolve(owner.get("FontDescriptor") ?? PDF_NULL);
|
|
734
|
+
if (!(descriptor instanceof Map)) return {};
|
|
735
|
+
const stated = (key) => {
|
|
736
|
+
const v = file.resolve(descriptor.get(key) ?? PDF_NULL);
|
|
737
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
738
|
+
};
|
|
739
|
+
const flags = stated("Flags") ?? 0;
|
|
740
|
+
const capHeight = stated("CapHeight");
|
|
741
|
+
const xHeight = stated("XHeight");
|
|
742
|
+
const base = asName(file.resolve(owner.get("BaseFont") ?? fontDict.get("BaseFont") ?? PDF_NULL));
|
|
743
|
+
return { program: {
|
|
744
|
+
glyph,
|
|
745
|
+
...facts?.fsType !== void 0 ? { fsType: facts.fsType } : {},
|
|
746
|
+
...facts?.spaceAdvance !== void 0 ? { spaceAdvance: facts.spaceAdvance } : {},
|
|
747
|
+
postScriptName: base.replace(/^[A-Z]{6}\+/u, ""),
|
|
748
|
+
...capHeight !== void 0 && capHeight > 0 ? { capHeight } : {},
|
|
749
|
+
...xHeight !== void 0 && xHeight > 0 ? { xHeight } : {},
|
|
750
|
+
italicAngle: stated("ItalicAngle") ?? 0,
|
|
751
|
+
fixedPitch: (flags & FLAG_FIXED_PITCH) !== 0
|
|
752
|
+
} };
|
|
753
|
+
}
|
|
488
754
|
/**
|
|
489
755
|
* The glyph index a name carries, for the names that carry one.
|
|
490
756
|
*
|
|
@@ -894,6 +1160,23 @@ function descendantFont(file, fontDict) {
|
|
|
894
1160
|
const first = Array.isArray(descFonts) ? file.resolve(descFonts[0] ?? PDF_NULL) : PDF_NULL;
|
|
895
1161
|
return first instanceof Map ? first : /* @__PURE__ */ new Map();
|
|
896
1162
|
}
|
|
1163
|
+
/**
|
|
1164
|
+
* §9.8.2 — whether every glyph of a simple font is as wide as the next: the
|
|
1165
|
+
* descriptor's FixedPitch flag, the standard Courier, or a /Widths array that
|
|
1166
|
+
* says so itself. pdfTeX sets no flag on Computer Modern's typewriter face,
|
|
1167
|
+
* and its widths do: all of them 525. Ten widths at the least — a face that
|
|
1168
|
+
* shows four digits shows four equal widths and is no typewriter's.
|
|
1169
|
+
*/
|
|
1170
|
+
function fixedPitchOf(file, fontDict) {
|
|
1171
|
+
const descriptor = file.resolve(fontDict.get("FontDescriptor") ?? PDF_NULL);
|
|
1172
|
+
if (((descriptor instanceof Map ? asNumber(file.resolve(descriptor.get("Flags") ?? PDF_NULL), 0) : 0) & FLAG_FIXED_PITCH) !== 0) return true;
|
|
1173
|
+
if (/^courier/iu.test(plainFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL))))) return true;
|
|
1174
|
+
const stated = file.resolve(fontDict.get("Widths") ?? PDF_NULL);
|
|
1175
|
+
const widths = (Array.isArray(stated) ? stated : []).map((w) => file.resolve(w)).filter((w) => typeof w === "number" && w > 0);
|
|
1176
|
+
return widths.length >= FIXED_PITCH_WIDTHS && widths.every((w) => Math.abs(w - widths[0]) < .5);
|
|
1177
|
+
}
|
|
1178
|
+
/** How many widths it takes to say every glyph is as wide as the next. */
|
|
1179
|
+
var FIXED_PITCH_WIDTHS = 10;
|
|
897
1180
|
function simpleWidths(file, fontDict, decodeOne) {
|
|
898
1181
|
const first = asNumber(file.resolve(fontDict.get("FirstChar") ?? PDF_NULL), 0);
|
|
899
1182
|
const widthsVal = file.resolve(fontDict.get("Widths") ?? PDF_NULL);
|
|
@@ -901,12 +1184,14 @@ function simpleWidths(file, fontDict, decodeOne) {
|
|
|
901
1184
|
const descriptor = file.resolve(fontDict.get("FontDescriptor") ?? PDF_NULL);
|
|
902
1185
|
const missing = descriptor instanceof Map ? asNumber(file.resolve(descriptor.get("MissingWidth") ?? PDF_NULL), 0) : 0;
|
|
903
1186
|
const face = standardFace(asName(file.resolve(fontDict.get("BaseFont") ?? PDF_NULL)));
|
|
904
|
-
|
|
1187
|
+
const stated = (code) => {
|
|
905
1188
|
const w = widths[code - first];
|
|
906
1189
|
if (typeof w === "number") return w;
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1190
|
+
return face === void 0 ? void 0 : standardWidth(face, code, decodeOne(code));
|
|
1191
|
+
};
|
|
1192
|
+
return {
|
|
1193
|
+
width: (code) => stated(code) ?? (missing > 0 ? missing : 500),
|
|
1194
|
+
stated
|
|
910
1195
|
};
|
|
911
1196
|
}
|
|
912
1197
|
function cidWidths(file, fontDict) {
|
|
@@ -41,3 +41,36 @@ export declare function outlineSource(program: Uint8Array): OutlineSource | unde
|
|
|
41
41
|
* states no names of its own (formats 1.0 and 3.0).
|
|
42
42
|
*/
|
|
43
43
|
export declare function postGlyphNames(program: Uint8Array): Map<string, number> | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* OS/2 `fsType` — the embedding a TrueType or OpenType program's licence allows
|
|
46
|
+
* (ISO/IEC 14496-22, OS/2 table).
|
|
47
|
+
*
|
|
48
|
+
* @param program The raw sfnt bytes.
|
|
49
|
+
* @returns The field, or `undefined` where the program carries no OS/2 table —
|
|
50
|
+
* a subsetter may drop it, and then the program states nothing.
|
|
51
|
+
*/
|
|
52
|
+
export declare function sfntFsType(program: Uint8Array): number | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* How far the pen moves after an sfnt program's space, in thousandths of an em:
|
|
55
|
+
* the glyph its `cmap` (or its `post` names) gives U+0020, measured in `hmtx`.
|
|
56
|
+
* A subset keeps the metrics of glyphs it dropped the outlines of, and the
|
|
57
|
+
* width a page's words were set apart by is the one a writer should give the
|
|
58
|
+
* space the page never showed.
|
|
59
|
+
*
|
|
60
|
+
* @param program The raw sfnt bytes.
|
|
61
|
+
* @returns The advance, or `undefined` where the program says nothing of a space.
|
|
62
|
+
*/
|
|
63
|
+
export declare function sfntSpaceAdvance(program: Uint8Array): number | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Every `cmap` subtable an sfnt program carries, keyed `platform,encoding` —
|
|
66
|
+
* `3,1` for Windows Unicode, `3,0` for Windows symbol, `1,0` for Mac Roman.
|
|
67
|
+
*
|
|
68
|
+
* A simple TrueType font reaches its glyphs through whichever of these it has,
|
|
69
|
+
* and which one decides what a code is looked up AS (§9.6.6.4): a character, a
|
|
70
|
+
* symbol code, a Mac Roman byte.
|
|
71
|
+
*
|
|
72
|
+
* @param program The raw sfnt bytes.
|
|
73
|
+
* @returns Subtable → code-to-glyph lookup (0 where it maps nothing). Formats
|
|
74
|
+
* 0, 4, 6 and 12 are read; any other is left out.
|
|
75
|
+
*/
|
|
76
|
+
export declare function cmapSubtables(program: Uint8Array): Map<string, (code: number) => number>;
|
|
@@ -120,6 +120,140 @@ function postGlyphNames(program) {
|
|
|
120
120
|
}
|
|
121
121
|
return out.size > 0 ? out : void 0;
|
|
122
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* OS/2 `fsType` — the embedding a TrueType or OpenType program's licence allows
|
|
125
|
+
* (ISO/IEC 14496-22, OS/2 table).
|
|
126
|
+
*
|
|
127
|
+
* @param program The raw sfnt bytes.
|
|
128
|
+
* @returns The field, or `undefined` where the program carries no OS/2 table —
|
|
129
|
+
* a subsetter may drop it, and then the program states nothing.
|
|
130
|
+
*/
|
|
131
|
+
function sfntFsType(program) {
|
|
132
|
+
let tables;
|
|
133
|
+
try {
|
|
134
|
+
tables = sfntTables(program);
|
|
135
|
+
} catch {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const os2 = tables.get("OS/2");
|
|
139
|
+
if (!os2 || os2.length < 10 || os2.offset + 10 > program.length) return void 0;
|
|
140
|
+
return new DataView(program.buffer, program.byteOffset, program.byteLength).getUint16(os2.offset + 8);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* How far the pen moves after an sfnt program's space, in thousandths of an em:
|
|
144
|
+
* the glyph its `cmap` (or its `post` names) gives U+0020, measured in `hmtx`.
|
|
145
|
+
* A subset keeps the metrics of glyphs it dropped the outlines of, and the
|
|
146
|
+
* width a page's words were set apart by is the one a writer should give the
|
|
147
|
+
* space the page never showed.
|
|
148
|
+
*
|
|
149
|
+
* @param program The raw sfnt bytes.
|
|
150
|
+
* @returns The advance, or `undefined` where the program says nothing of a space.
|
|
151
|
+
*/
|
|
152
|
+
function sfntSpaceAdvance(program) {
|
|
153
|
+
let tables;
|
|
154
|
+
try {
|
|
155
|
+
tables = sfntTables(program);
|
|
156
|
+
} catch {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const head = tables.get("head");
|
|
160
|
+
const hhea = tables.get("hhea");
|
|
161
|
+
const hmtx = tables.get("hmtx");
|
|
162
|
+
if (!head || !hhea || !hmtx) return void 0;
|
|
163
|
+
const view = new DataView(program.buffer, program.byteOffset, program.byteLength);
|
|
164
|
+
const cmaps = cmapSubtables(program);
|
|
165
|
+
const gid = [
|
|
166
|
+
cmaps.get("3,1")?.(32),
|
|
167
|
+
cmaps.get("0,3")?.(32),
|
|
168
|
+
cmaps.get("3,0")?.(61472),
|
|
169
|
+
cmaps.get("3,0")?.(32),
|
|
170
|
+
cmaps.get("1,0")?.(32)
|
|
171
|
+
].find((gid) => gid !== void 0 && gid > 0) ?? postGlyphNames(program)?.get("space");
|
|
172
|
+
if (gid === void 0 || gid <= 0) return void 0;
|
|
173
|
+
const unitsPerEm = safeUint16(view, head.offset + 18) || 1e3;
|
|
174
|
+
const metrics = safeUint16(view, hhea.offset + 34);
|
|
175
|
+
if (metrics === 0) return void 0;
|
|
176
|
+
const at = hmtx.offset + Math.min(gid, metrics - 1) * 4;
|
|
177
|
+
if (at + 2 > hmtx.offset + hmtx.length) return void 0;
|
|
178
|
+
const advance = safeUint16(view, at);
|
|
179
|
+
return advance > 0 ? advance / unitsPerEm * 1e3 : void 0;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Every `cmap` subtable an sfnt program carries, keyed `platform,encoding` —
|
|
183
|
+
* `3,1` for Windows Unicode, `3,0` for Windows symbol, `1,0` for Mac Roman.
|
|
184
|
+
*
|
|
185
|
+
* A simple TrueType font reaches its glyphs through whichever of these it has,
|
|
186
|
+
* and which one decides what a code is looked up AS (§9.6.6.4): a character, a
|
|
187
|
+
* symbol code, a Mac Roman byte.
|
|
188
|
+
*
|
|
189
|
+
* @param program The raw sfnt bytes.
|
|
190
|
+
* @returns Subtable → code-to-glyph lookup (0 where it maps nothing). Formats
|
|
191
|
+
* 0, 4, 6 and 12 are read; any other is left out.
|
|
192
|
+
*/
|
|
193
|
+
function cmapSubtables(program) {
|
|
194
|
+
const out = /* @__PURE__ */ new Map();
|
|
195
|
+
let tables;
|
|
196
|
+
try {
|
|
197
|
+
tables = sfntTables(program);
|
|
198
|
+
} catch {
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
const cmap = tables.get("cmap");
|
|
202
|
+
if (!cmap) return out;
|
|
203
|
+
const view = new DataView(program.buffer, program.byteOffset, program.byteLength);
|
|
204
|
+
const count = safeUint16(view, cmap.offset + 2);
|
|
205
|
+
for (let i = 0; i < count; i++) {
|
|
206
|
+
const at = cmap.offset + 4 + i * 8;
|
|
207
|
+
const key = `${safeUint16(view, at)},${safeUint16(view, at + 2)}`;
|
|
208
|
+
if (out.has(key)) continue;
|
|
209
|
+
const lookup = cmapLookup(view, cmap.offset + safeUint32(view, at + 4));
|
|
210
|
+
if (lookup) out.set(key, lookup);
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
/** One `cmap` subtable as a lookup, for the formats a simple font's program uses. */
|
|
215
|
+
function cmapLookup(view, at) {
|
|
216
|
+
const format = safeUint16(view, at);
|
|
217
|
+
if (format === 0) return (code) => code >= 0 && code < 256 ? safeUint8(view, at + 6 + code) : 0;
|
|
218
|
+
if (format === 6) {
|
|
219
|
+
const first = safeUint16(view, at + 6);
|
|
220
|
+
const entries = safeUint16(view, at + 8);
|
|
221
|
+
return (code) => code >= first && code < first + entries ? safeUint16(view, at + 10 + (code - first) * 2) : 0;
|
|
222
|
+
}
|
|
223
|
+
if (format === 4) {
|
|
224
|
+
const segments = safeUint16(view, at + 6) >> 1;
|
|
225
|
+
const ends = at + 14;
|
|
226
|
+
const starts = ends + segments * 2 + 2;
|
|
227
|
+
const deltas = starts + segments * 2;
|
|
228
|
+
const ranges = deltas + segments * 2;
|
|
229
|
+
return (code) => {
|
|
230
|
+
if (code < 0 || code > 65535) return 0;
|
|
231
|
+
for (let s = 0; s < segments; s++) {
|
|
232
|
+
if (safeUint16(view, ends + s * 2) < code) continue;
|
|
233
|
+
const start = safeUint16(view, starts + s * 2);
|
|
234
|
+
if (start > code) return 0;
|
|
235
|
+
const delta = safeUint16(view, deltas + s * 2);
|
|
236
|
+
const range = safeUint16(view, ranges + s * 2);
|
|
237
|
+
if (range === 0) return code + delta & 65535;
|
|
238
|
+
const gid = safeUint16(view, ranges + s * 2 + range + (code - start) * 2);
|
|
239
|
+
return gid === 0 ? 0 : gid + delta & 65535;
|
|
240
|
+
}
|
|
241
|
+
return 0;
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
if (format === 12) {
|
|
245
|
+
const groups = safeUint32(view, at + 12);
|
|
246
|
+
return (code) => {
|
|
247
|
+
for (let g = 0; g < groups; g++) {
|
|
248
|
+
const entry = at + 16 + g * 12;
|
|
249
|
+
const start = safeUint32(view, entry);
|
|
250
|
+
const end = safeUint32(view, entry + 4);
|
|
251
|
+
if (code >= start && code <= end) return safeUint32(view, entry + 8) + (code - start);
|
|
252
|
+
}
|
|
253
|
+
return 0;
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
123
257
|
/** `post` version 2.0 — the only one that states names of its own. */
|
|
124
258
|
var POST_NAMED = 131072;
|
|
125
259
|
/** How many names the Macintosh standard order holds (§post). */
|
|
@@ -359,4 +493,4 @@ function safeUint32(view, at) {
|
|
|
359
493
|
return at >= 0 && at + 4 <= view.byteLength ? view.getUint32(at) : 0;
|
|
360
494
|
}
|
|
361
495
|
//#endregion
|
|
362
|
-
export { outlineSource, postGlyphNames };
|
|
496
|
+
export { cmapSubtables, outlineSource, postGlyphNames, sfntFsType, sfntSpaceAdvance };
|