@chr33s/pdf-standard-fonts 5.0.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/LICENSE.md +21 -0
- package/README.md +41 -0
- package/dist/all-encodings.compressed.json +1 -0
- package/dist/courier-bold-oblique.compressed.json +1 -0
- package/dist/courier-bold.compressed.json +1 -0
- package/dist/courier-oblique.compressed.json +1 -0
- package/dist/courier.compressed.json +1 -0
- package/dist/encoding.d.ts +31 -0
- package/dist/encoding.js +58 -0
- package/dist/encoding.js.map +1 -0
- package/dist/font.d.ts +99 -0
- package/dist/font.js +105 -0
- package/dist/font.js.map +1 -0
- package/dist/helvetica-bold-oblique.compressed.json +1 -0
- package/dist/helvetica-bold.compressed.json +1 -0
- package/dist/helvetica-oblique.compressed.json +1 -0
- package/dist/helvetica.compressed.json +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/symbol.compressed.json +1 -0
- package/dist/times-bold-italic.compressed.json +1 -0
- package/dist/times-bold.compressed.json +1 -0
- package/dist/times-italic.compressed.json +1 -0
- package/dist/times-roman.compressed.json +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +17 -0
- package/dist/utils.js.map +1 -0
- package/dist/zapf-dingbats.compressed.json +1 -0
- package/encoding-metrics/all-encodings.compressed.json +1 -0
- package/encoding-metrics/all-encodings.json +1 -0
- package/encoding-metrics/symbol-encoding.json +1 -0
- package/encoding-metrics/symbol.txt +261 -0
- package/encoding-metrics/win1252-encoding.json +1 -0
- package/encoding-metrics/win1252.txt +279 -0
- package/encoding-metrics/zapfdingbats-encoding.json +1 -0
- package/encoding-metrics/zapfdingbats.txt +268 -0
- package/font-metrics/MustRead.html +19 -0
- package/font-metrics/courier-bold-oblique.afm +342 -0
- package/font-metrics/courier-bold-oblique.compressed.json +1 -0
- package/font-metrics/courier-bold-oblique.json +1 -0
- package/font-metrics/courier-bold.afm +342 -0
- package/font-metrics/courier-bold.compressed.json +1 -0
- package/font-metrics/courier-bold.json +1 -0
- package/font-metrics/courier-oblique.afm +342 -0
- package/font-metrics/courier-oblique.compressed.json +1 -0
- package/font-metrics/courier-oblique.json +1 -0
- package/font-metrics/courier.afm +342 -0
- package/font-metrics/courier.compressed.json +1 -0
- package/font-metrics/courier.json +1 -0
- package/font-metrics/helvetica-bold-oblique.afm +2827 -0
- package/font-metrics/helvetica-bold-oblique.compressed.json +1 -0
- package/font-metrics/helvetica-bold-oblique.json +1 -0
- package/font-metrics/helvetica-bold.afm +2827 -0
- package/font-metrics/helvetica-bold.compressed.json +1 -0
- package/font-metrics/helvetica-bold.json +1 -0
- package/font-metrics/helvetica-oblique.afm +3051 -0
- package/font-metrics/helvetica-oblique.compressed.json +1 -0
- package/font-metrics/helvetica-oblique.json +1 -0
- package/font-metrics/helvetica.afm +3051 -0
- package/font-metrics/helvetica.compressed.json +1 -0
- package/font-metrics/helvetica.json +1 -0
- package/font-metrics/symbol.afm +213 -0
- package/font-metrics/symbol.compressed.json +1 -0
- package/font-metrics/symbol.json +1 -0
- package/font-metrics/times-bold-italic.afm +2384 -0
- package/font-metrics/times-bold-italic.compressed.json +1 -0
- package/font-metrics/times-bold-italic.json +1 -0
- package/font-metrics/times-bold.afm +2588 -0
- package/font-metrics/times-bold.compressed.json +1 -0
- package/font-metrics/times-bold.json +1 -0
- package/font-metrics/times-italic.afm +2667 -0
- package/font-metrics/times-italic.compressed.json +1 -0
- package/font-metrics/times-italic.json +1 -0
- package/font-metrics/times-roman.afm +2419 -0
- package/font-metrics/times-roman.compressed.json +1 -0
- package/font-metrics/times-roman.json +1 -0
- package/font-metrics/zapf-dingbats.afm +225 -0
- package/font-metrics/zapf-dingbats.compressed.json +1 -0
- package/font-metrics/zapf-dingbats.json +1 -0
- package/package.json +51 -0
- package/scripts/encodings/parse-win1252.ts +120 -0
- package/scripts/encodings/parse-zapf-dingbats-or-symbol.ts +22 -0
- package/scripts/encodings/parse.ts +72 -0
- package/scripts/fonts/parse-character-metrics.ts +73 -0
- package/scripts/fonts/parse-font-metrics.ts +95 -0
- package/scripts/fonts/parse-kern-pairs.ts +30 -0
- package/scripts/fonts/parse.ts +91 -0
- package/scripts/fonts/utils.ts +43 -0
- package/src/all-encodings.compressed.json +1 -0
- package/src/courier-bold-oblique.compressed.json +1 -0
- package/src/courier-bold.compressed.json +1 -0
- package/src/courier-oblique.compressed.json +1 -0
- package/src/courier.compressed.json +1 -0
- package/src/encoding.ts +85 -0
- package/src/font.ts +164 -0
- package/src/helvetica-bold-oblique.compressed.json +1 -0
- package/src/helvetica-bold.compressed.json +1 -0
- package/src/helvetica-oblique.compressed.json +1 -0
- package/src/helvetica.compressed.json +1 -0
- package/src/index.ts +2 -0
- package/src/symbol.compressed.json +1 -0
- package/src/times-bold-italic.compressed.json +1 -0
- package/src/times-bold.compressed.json +1 -0
- package/src/times-italic.compressed.json +1 -0
- package/src/times-roman.compressed.json +1 -0
- package/src/utils.ts +20 -0
- package/src/zapf-dingbats.compressed.json +1 -0
- package/test/all-fonts.test.ts +133 -0
- package/test/encoding.test.ts +32 -0
- package/test/encodings-async.test.ts +78 -0
- package/test/font.test.ts +35 -0
- package/test/utils.test.ts +34 -0
- package/tsconfig.json +9 -0
- package/tsconfig.typecheck.json +14 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array of WinAnsi glyph names.
|
|
3
|
+
* Allows lookups of character names given a character code.
|
|
4
|
+
*
|
|
5
|
+
* From:
|
|
6
|
+
* https://github.com/foliojs/pdfkit/blob/83f5f7243172a017adcf6a7faa5547c55982c57b/lib/font/afm.js#L33-L105
|
|
7
|
+
*/
|
|
8
|
+
const WinAnsiCharNames = `
|
|
9
|
+
.notdef .notdef .notdef .notdef
|
|
10
|
+
.notdef .notdef .notdef .notdef
|
|
11
|
+
.notdef .notdef .notdef .notdef
|
|
12
|
+
.notdef .notdef .notdef .notdef
|
|
13
|
+
.notdef .notdef .notdef .notdef
|
|
14
|
+
.notdef .notdef .notdef .notdef
|
|
15
|
+
.notdef .notdef .notdef .notdef
|
|
16
|
+
.notdef .notdef .notdef .notdef
|
|
17
|
+
|
|
18
|
+
space exclam quotedbl numbersign
|
|
19
|
+
dollar percent ampersand quotesingle
|
|
20
|
+
parenleft parenright asterisk plus
|
|
21
|
+
comma hyphen period slash
|
|
22
|
+
zero one two three
|
|
23
|
+
four five six seven
|
|
24
|
+
eight nine colon semicolon
|
|
25
|
+
less equal greater question
|
|
26
|
+
|
|
27
|
+
at A B C
|
|
28
|
+
D E F G
|
|
29
|
+
H I J K
|
|
30
|
+
L M N O
|
|
31
|
+
P Q R S
|
|
32
|
+
T U V W
|
|
33
|
+
X Y Z bracketleft
|
|
34
|
+
backslash bracketright asciicircum underscore
|
|
35
|
+
|
|
36
|
+
grave a b c
|
|
37
|
+
d e f g
|
|
38
|
+
h i j k
|
|
39
|
+
l m n o
|
|
40
|
+
p q r s
|
|
41
|
+
t u v w
|
|
42
|
+
x y z braceleft
|
|
43
|
+
bar braceright asciitilde .notdef
|
|
44
|
+
|
|
45
|
+
Euro .notdef quotesinglbase florin
|
|
46
|
+
quotedblbase ellipsis dagger daggerdbl
|
|
47
|
+
circumflex perthousand Scaron guilsinglleft
|
|
48
|
+
OE .notdef Zcaron .notdef
|
|
49
|
+
.notdef quoteleft quoteright quotedblleft
|
|
50
|
+
quotedblright bullet endash emdash
|
|
51
|
+
tilde trademark scaron guilsinglright
|
|
52
|
+
oe .notdef zcaron ydieresis
|
|
53
|
+
|
|
54
|
+
space exclamdown cent sterling
|
|
55
|
+
currency yen brokenbar section
|
|
56
|
+
dieresis copyright ordfeminine guillemotleft
|
|
57
|
+
logicalnot hyphen registered macron
|
|
58
|
+
degree plusminus twosuperior threesuperior
|
|
59
|
+
acute mu paragraph periodcentered
|
|
60
|
+
cedilla onesuperior ordmasculine guillemotright
|
|
61
|
+
onequarter onehalf threequarters questiondown
|
|
62
|
+
|
|
63
|
+
Agrave Aacute Acircumflex Atilde
|
|
64
|
+
Adieresis Aring AE Ccedilla
|
|
65
|
+
Egrave Eacute Ecircumflex Edieresis
|
|
66
|
+
Igrave Iacute Icircumflex Idieresis
|
|
67
|
+
Eth Ntilde Ograve Oacute
|
|
68
|
+
Ocircumflex Otilde Odieresis multiply
|
|
69
|
+
Oslash Ugrave Uacute Ucircumflex
|
|
70
|
+
Udieresis Yacute Thorn germandbls
|
|
71
|
+
|
|
72
|
+
agrave aacute acircumflex atilde
|
|
73
|
+
adieresis aring ae ccedilla
|
|
74
|
+
egrave eacute ecircumflex edieresis
|
|
75
|
+
igrave iacute icircumflex idieresis
|
|
76
|
+
eth ntilde ograve oacute
|
|
77
|
+
ocircumflex otilde odieresis divide
|
|
78
|
+
oslash ugrave uacute ucircumflex
|
|
79
|
+
udieresis yacute thorn ydieresis
|
|
80
|
+
`
|
|
81
|
+
.trim()
|
|
82
|
+
.split(/\s+/);
|
|
83
|
+
|
|
84
|
+
export type EncodingMap = Record<number, [number, string]>;
|
|
85
|
+
|
|
86
|
+
type EncodingTuple = [number, number, string, string];
|
|
87
|
+
|
|
88
|
+
const isValidEncodingTuple = (
|
|
89
|
+
entry: [number, number, string, string | undefined],
|
|
90
|
+
): entry is EncodingTuple => {
|
|
91
|
+
const [, , , postscriptName] = entry;
|
|
92
|
+
return typeof postscriptName === "string" && postscriptName !== ".notdef";
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const parseWin1252 = (data: string): EncodingMap => {
|
|
96
|
+
const rows = data
|
|
97
|
+
.split("\n")
|
|
98
|
+
.filter((line) => line[0] !== "#")
|
|
99
|
+
.filter(Boolean)
|
|
100
|
+
.map((line) => line.split("\t"))
|
|
101
|
+
.map(([postscriptCode, unicodeCode, unicodeName]) => {
|
|
102
|
+
const unicode = Number(unicodeCode);
|
|
103
|
+
const postscript = Number(postscriptCode);
|
|
104
|
+
const postscriptName = WinAnsiCharNames[postscript];
|
|
105
|
+
return [unicode, postscript, unicodeName.substring(1), postscriptName] as [
|
|
106
|
+
number,
|
|
107
|
+
number,
|
|
108
|
+
string,
|
|
109
|
+
string | undefined,
|
|
110
|
+
];
|
|
111
|
+
})
|
|
112
|
+
.filter(isValidEncodingTuple);
|
|
113
|
+
|
|
114
|
+
const encodings: EncodingMap = {};
|
|
115
|
+
for (const [unicodeCode, postscriptCode, , postscriptName] of rows) {
|
|
116
|
+
encodings[unicodeCode] = [postscriptCode, postscriptName];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return encodings;
|
|
120
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { EncodingMap } from "./parse-win1252.ts";
|
|
2
|
+
|
|
3
|
+
export const parseZapfDingbatsOrSymbol = (data: string): EncodingMap => {
|
|
4
|
+
const rows = data
|
|
5
|
+
.split("\n")
|
|
6
|
+
.filter((line) => line[0] !== "#")
|
|
7
|
+
.filter(Boolean)
|
|
8
|
+
.map((line) => line.split("\t"))
|
|
9
|
+
.map(([unicodeCode, postscriptCode, unicodeName, postscriptName]) => [
|
|
10
|
+
Number(`0x${unicodeCode}`),
|
|
11
|
+
Number(`0x${postscriptCode}`),
|
|
12
|
+
unicodeName.substring(2),
|
|
13
|
+
postscriptName.substring(2).replace(" (CUS)", ""),
|
|
14
|
+
]) as Array<[number, number, string, string]>;
|
|
15
|
+
|
|
16
|
+
const encodings: EncodingMap = {};
|
|
17
|
+
for (const [unicodeCode, postscriptCode, , postscriptName] of rows) {
|
|
18
|
+
encodings[unicodeCode] = [postscriptCode, postscriptName];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return encodings;
|
|
22
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { base64, deflate } from "@chr33s/pdf-common";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { basename, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import type { EncodingMap } from "./parse-win1252.ts";
|
|
7
|
+
import { parseWin1252 } from "./parse-win1252.ts";
|
|
8
|
+
import { parseZapfDingbatsOrSymbol } from "./parse-zapf-dingbats-or-symbol.ts";
|
|
9
|
+
|
|
10
|
+
const textEncoder = new TextEncoder();
|
|
11
|
+
|
|
12
|
+
const compressJson = async (json: string) => {
|
|
13
|
+
const jsonBytes = textEncoder.encode(json);
|
|
14
|
+
const compressed = await deflate(jsonBytes);
|
|
15
|
+
const arrBuf = compressed.buffer.slice(
|
|
16
|
+
compressed.byteOffset,
|
|
17
|
+
compressed.byteOffset + compressed.byteLength,
|
|
18
|
+
) as ArrayBuffer;
|
|
19
|
+
const base64DeflatedJson = JSON.stringify(base64.encode(arrBuf));
|
|
20
|
+
return base64DeflatedJson;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = dirname(__filename);
|
|
25
|
+
|
|
26
|
+
const copyFileToSrc = async (src: string) => {
|
|
27
|
+
const fileName = basename(src);
|
|
28
|
+
const dest = dirname(dirname(__dirname)) + "/src/" + fileName;
|
|
29
|
+
await fs.copyFile(src, dest);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const main = async () => {
|
|
33
|
+
const parent = dirname(dirname(__dirname));
|
|
34
|
+
|
|
35
|
+
const fontNames = ["symbol", "zapfdingbats", "win1252"] as const;
|
|
36
|
+
const allEncodings: Record<(typeof fontNames)[number], EncodingMap> = {
|
|
37
|
+
symbol: {},
|
|
38
|
+
zapfdingbats: {},
|
|
39
|
+
win1252: {},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
for (const fontName of fontNames) {
|
|
43
|
+
const file = `${parent}/encoding-metrics/${fontName}.txt`;
|
|
44
|
+
console.log("Parsing:", file);
|
|
45
|
+
const data = await fs.readFile(file);
|
|
46
|
+
|
|
47
|
+
const parser: (input: string) => EncodingMap =
|
|
48
|
+
fontName === "win1252" ? parseWin1252 : parseZapfDingbatsOrSymbol;
|
|
49
|
+
const jsonMetrics = parser(String(data));
|
|
50
|
+
allEncodings[fontName] = jsonMetrics;
|
|
51
|
+
|
|
52
|
+
const json = JSON.stringify(jsonMetrics);
|
|
53
|
+
|
|
54
|
+
const jsonFile = `${parent}/encoding-metrics/${fontName}-encoding.json`;
|
|
55
|
+
await fs.writeFile(jsonFile, json);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const allJson = JSON.stringify(allEncodings);
|
|
59
|
+
const allCompressedJson = await compressJson(allJson);
|
|
60
|
+
|
|
61
|
+
const allJsonFile = `${parent}/encoding-metrics/all-encodings.json`;
|
|
62
|
+
const allCompressedJsonFile = `${parent}/encoding-metrics/all-encodings.compressed.json`;
|
|
63
|
+
|
|
64
|
+
await fs.writeFile(allJsonFile, allJson);
|
|
65
|
+
await fs.writeFile(allCompressedJsonFile, allCompressedJson);
|
|
66
|
+
await copyFileToSrc(allCompressedJsonFile);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
main().catch((err) => {
|
|
70
|
+
console.error(err);
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
error,
|
|
3
|
+
extractLinesFromSection,
|
|
4
|
+
takeAfterFirstSpace,
|
|
5
|
+
takeUntilFirstSpace,
|
|
6
|
+
} from "./utils.ts";
|
|
7
|
+
|
|
8
|
+
export interface ICharMetrics {
|
|
9
|
+
WX: number;
|
|
10
|
+
N: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* From https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5004.AFM_Spec.pdf :
|
|
15
|
+
*
|
|
16
|
+
* C integer:
|
|
17
|
+
* Decimal value of default character code (−1 if not encoded).
|
|
18
|
+
*
|
|
19
|
+
* WX number:
|
|
20
|
+
* Width of character.
|
|
21
|
+
*
|
|
22
|
+
* N name:
|
|
23
|
+
* (Optional.) PostScript language character name.
|
|
24
|
+
*
|
|
25
|
+
* B llx lly urx ury:
|
|
26
|
+
* (Optional.) Character bounding box where llx, lly, urx, and ury are all
|
|
27
|
+
* numbers. If a character makes no marks on the page (for example, the space
|
|
28
|
+
* character), this fi eld reads B 0 0 0 0 , and these values are not
|
|
29
|
+
* considered when computing the FontBBox.
|
|
30
|
+
*
|
|
31
|
+
* L successor ligature:
|
|
32
|
+
* (Optional.) Ligature sequence where successor and ligature are both names.
|
|
33
|
+
* The current character may join with the character named successor to form
|
|
34
|
+
* the character named ligature. Note that characters can have more than one
|
|
35
|
+
* such entry.
|
|
36
|
+
*
|
|
37
|
+
* Fallback link for AFM Spec:
|
|
38
|
+
* https://ia800603.us.archive.org/30/items/afm-format/afm-format.pdf
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
// prettier-ignore
|
|
42
|
+
const parseCharMetrics = (
|
|
43
|
+
// E.g. 'C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ;'
|
|
44
|
+
line: string,
|
|
45
|
+
): ICharMetrics => {
|
|
46
|
+
const SEMICOLON_WITH_SURROUDING_WHITESPACE = /\s*;\s*/;
|
|
47
|
+
const segments = line
|
|
48
|
+
.split(SEMICOLON_WITH_SURROUDING_WHITESPACE)
|
|
49
|
+
.map((segment) => segment.trim())
|
|
50
|
+
.filter((segment) => segment.length > 0);
|
|
51
|
+
|
|
52
|
+
const metrics = new Map<string, string>();
|
|
53
|
+
for (const metric of segments) {
|
|
54
|
+
const key = takeUntilFirstSpace(metric);
|
|
55
|
+
const value = takeAfterFirstSpace(metric);
|
|
56
|
+
metrics.set(key, value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const rawWx = metrics.get("WX") ?? error("Missing WX metric in character data");
|
|
60
|
+
const rawName = metrics.get("N") ?? error("Missing N metric in character data");
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
WX: Number(rawWx),
|
|
64
|
+
N: rawName,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const parseCharMetricsSection = (data: string): ICharMetrics[] => {
|
|
69
|
+
return extractLinesFromSection(data, {
|
|
70
|
+
startAt: "StartCharMetrics",
|
|
71
|
+
endAt: "EndCharMetrics",
|
|
72
|
+
}).map(parseCharMetrics);
|
|
73
|
+
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
error,
|
|
3
|
+
extractLinesFromSection,
|
|
4
|
+
takeAfterFirstSpace,
|
|
5
|
+
takeUntilFirstSpace,
|
|
6
|
+
} from "./utils.ts";
|
|
7
|
+
|
|
8
|
+
export interface IFontMetrics {
|
|
9
|
+
Comment: string;
|
|
10
|
+
FontName: string;
|
|
11
|
+
FullName: string;
|
|
12
|
+
FamilyName: string;
|
|
13
|
+
Weight: string;
|
|
14
|
+
CharacterSet: string;
|
|
15
|
+
Version: string;
|
|
16
|
+
Notice: string;
|
|
17
|
+
EncodingScheme: string;
|
|
18
|
+
ItalicAngle: number;
|
|
19
|
+
UnderlinePosition: number;
|
|
20
|
+
UnderlineThickness: number;
|
|
21
|
+
CapHeight: number | void;
|
|
22
|
+
XHeight: number | void;
|
|
23
|
+
Ascender: number | void;
|
|
24
|
+
Descender: number | void;
|
|
25
|
+
StdHW: number;
|
|
26
|
+
StdVW: number;
|
|
27
|
+
IsFixedPitch: boolean;
|
|
28
|
+
FontBBox: [number, number, number, number];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type IFontMetricKey = keyof IFontMetrics;
|
|
32
|
+
|
|
33
|
+
type FontMetricEntry = {
|
|
34
|
+
key: IFontMetricKey;
|
|
35
|
+
value: IFontMetrics[IFontMetricKey];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const parseString = (raw: string): string => raw;
|
|
39
|
+
const parseNumber = (raw: string): number => Number(raw);
|
|
40
|
+
const parseBoolean = (raw: string): boolean => Boolean(raw);
|
|
41
|
+
const parseFontBBox = (raw: string): [number, number, number, number] =>
|
|
42
|
+
raw.split(" ").map(Number) as [number, number, number, number];
|
|
43
|
+
|
|
44
|
+
const metricParsers: {
|
|
45
|
+
[K in IFontMetricKey]: (raw: string) => IFontMetrics[K];
|
|
46
|
+
} = {
|
|
47
|
+
Comment: parseString,
|
|
48
|
+
FontName: parseString,
|
|
49
|
+
FullName: parseString,
|
|
50
|
+
FamilyName: parseString,
|
|
51
|
+
Weight: parseString,
|
|
52
|
+
CharacterSet: parseString,
|
|
53
|
+
Version: parseString,
|
|
54
|
+
Notice: parseString,
|
|
55
|
+
EncodingScheme: parseString,
|
|
56
|
+
ItalicAngle: parseNumber,
|
|
57
|
+
UnderlinePosition: parseNumber,
|
|
58
|
+
UnderlineThickness: parseNumber,
|
|
59
|
+
CapHeight: parseNumber,
|
|
60
|
+
XHeight: parseNumber,
|
|
61
|
+
Ascender: parseNumber,
|
|
62
|
+
Descender: parseNumber,
|
|
63
|
+
StdHW: parseNumber,
|
|
64
|
+
StdVW: parseNumber,
|
|
65
|
+
IsFixedPitch: parseBoolean,
|
|
66
|
+
FontBBox: parseFontBBox,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const isFontMetricKey = (value: string): value is IFontMetricKey => value in metricParsers;
|
|
70
|
+
|
|
71
|
+
const parseFontMetric = (line: string): FontMetricEntry => {
|
|
72
|
+
const key = takeUntilFirstSpace(line);
|
|
73
|
+
const rawValue = takeAfterFirstSpace(line).trim();
|
|
74
|
+
|
|
75
|
+
if (!isFontMetricKey(key)) {
|
|
76
|
+
return error(`Unrecognized font metric key: "${key}"`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const parse = metricParsers[key];
|
|
80
|
+
return { key, value: parse(rawValue) };
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const parseFontMetricsSection = (data: string): IFontMetrics => {
|
|
84
|
+
const metrics = extractLinesFromSection(data, {
|
|
85
|
+
startAt: "StartFontMetrics",
|
|
86
|
+
endAt: "StartCharMetrics",
|
|
87
|
+
}).map(parseFontMetric);
|
|
88
|
+
|
|
89
|
+
const result: Partial<Record<IFontMetricKey, IFontMetrics[IFontMetricKey]>> = {};
|
|
90
|
+
for (const metric of metrics) {
|
|
91
|
+
result[metric.key] = metric.value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return result as IFontMetrics;
|
|
95
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { extractLinesFromSection } from "./utils.ts";
|
|
2
|
+
|
|
3
|
+
export type IKernPair = [string, string, number];
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* From https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5004.AFM_Spec.pdf :
|
|
7
|
+
*
|
|
8
|
+
* KPX name_1 name_2 number_x:
|
|
9
|
+
* Name of the first character in the kerning pair followed by the name of the
|
|
10
|
+
* second character followed by the kerning amount in the x direction
|
|
11
|
+
* (y is zero). The kerning amount is specified in the units of the character
|
|
12
|
+
* coordinate system.
|
|
13
|
+
*
|
|
14
|
+
* Fallback link for AFM Spec:
|
|
15
|
+
* https://ia800603.us.archive.org/30/items/afm-format/afm-format.pdf
|
|
16
|
+
*/
|
|
17
|
+
const parseKernPair = (
|
|
18
|
+
// E.g. 'KPX A G -50'
|
|
19
|
+
line: string,
|
|
20
|
+
): IKernPair => {
|
|
21
|
+
const [, firstCharName, secondCharName, kernXAmount] = line.split(" ");
|
|
22
|
+
return [String(firstCharName), String(secondCharName), Number(kernXAmount)];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const parseKernPairsSection = (data: string): IKernPair[] => {
|
|
26
|
+
return extractLinesFromSection(data, {
|
|
27
|
+
startAt: "StartKernPairs",
|
|
28
|
+
endAt: "EndKernPairs",
|
|
29
|
+
}).map(parseKernPair);
|
|
30
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { base64, deflate } from "@chr33s/pdf-common";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { basename, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { type ICharMetrics, parseCharMetricsSection } from "./parse-character-metrics.ts";
|
|
7
|
+
import { type IFontMetrics, parseFontMetricsSection } from "./parse-font-metrics.ts";
|
|
8
|
+
import { type IKernPair, parseKernPairsSection } from "./parse-kern-pairs.ts";
|
|
9
|
+
|
|
10
|
+
export interface IMetrics extends IFontMetrics {
|
|
11
|
+
CharMetrics: ICharMetrics[];
|
|
12
|
+
KernPairs: IKernPair[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const parseFontMetrics = (data: string): IMetrics => ({
|
|
16
|
+
...parseFontMetricsSection(data),
|
|
17
|
+
CharMetrics: parseCharMetricsSection(data),
|
|
18
|
+
KernPairs: parseKernPairsSection(data),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = dirname(__filename);
|
|
23
|
+
|
|
24
|
+
const getAfmFilePaths = async () => {
|
|
25
|
+
const parentDir = dirname(dirname(__dirname));
|
|
26
|
+
const metricsDir = `${parentDir}/font-metrics`;
|
|
27
|
+
const files = await fs.readdir(metricsDir);
|
|
28
|
+
const afmFiles = files.filter((name) => name.includes(".afm"));
|
|
29
|
+
return afmFiles.map((name) => `${metricsDir}/${name}`);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const textEncoder = new TextEncoder();
|
|
33
|
+
|
|
34
|
+
const compressJson = async (json: string) => {
|
|
35
|
+
const jsonBytes = textEncoder.encode(json);
|
|
36
|
+
const compressed = await deflate(jsonBytes);
|
|
37
|
+
const arrBuf = compressed.buffer.slice(
|
|
38
|
+
compressed.byteOffset,
|
|
39
|
+
compressed.byteOffset + compressed.byteLength,
|
|
40
|
+
) as ArrayBuffer;
|
|
41
|
+
const base64DeflatedJson = JSON.stringify(base64.encode(arrBuf));
|
|
42
|
+
return base64DeflatedJson;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const fontFileNameMap: Record<string, string> = {
|
|
46
|
+
"courier.compressed.json": "courier.compressed.json",
|
|
47
|
+
"courier-bold.compressed.json": "courier-bold.compressed.json",
|
|
48
|
+
"courier-oblique.compressed.json": "courier-oblique.compressed.json",
|
|
49
|
+
"courier-bold-oblique.compressed.json": "courier-bold-oblique.compressed.json",
|
|
50
|
+
"helvetica.compressed.json": "helvetica.compressed.json",
|
|
51
|
+
"helvetica-bold.compressed.json": "helvetica-bold.compressed.json",
|
|
52
|
+
"helvetica-oblique.compressed.json": "helvetica-oblique.compressed.json",
|
|
53
|
+
"helvetica-bold-oblique.compressed.json": "helvetica-bold-oblique.compressed.json",
|
|
54
|
+
"times-roman.compressed.json": "times-roman.compressed.json",
|
|
55
|
+
"times-bold.compressed.json": "times-bold.compressed.json",
|
|
56
|
+
"times-italic.compressed.json": "times-italic.compressed.json",
|
|
57
|
+
"times-bold-italic.compressed.json": "times-bold-italic.compressed.json",
|
|
58
|
+
"symbol.compressed.json": "symbol.compressed.json",
|
|
59
|
+
"zapf-dingbats.compressed.json": "zapf-dingbats.compressed.json",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const copyFileToSrc = async (src: string) => {
|
|
63
|
+
const fileName = basename(src);
|
|
64
|
+
const canonicalFileName = fontFileNameMap[fileName] ?? fileName;
|
|
65
|
+
const dest = `${dirname(dirname(__dirname))}/src/${canonicalFileName}`;
|
|
66
|
+
await fs.copyFile(src, dest);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const main = async () => {
|
|
70
|
+
const afmFiles = await getAfmFilePaths();
|
|
71
|
+
|
|
72
|
+
for (const afmFile of afmFiles) {
|
|
73
|
+
console.log("Parsing:", afmFile);
|
|
74
|
+
const data = await fs.readFile(afmFile);
|
|
75
|
+
|
|
76
|
+
const metrics = parseFontMetrics(String(data));
|
|
77
|
+
const jsonMetrics = JSON.stringify(metrics);
|
|
78
|
+
|
|
79
|
+
const jsonFile = afmFile.replace(".afm", ".json");
|
|
80
|
+
const compressedJsonFile = afmFile.replace(".afm", ".compressed.json");
|
|
81
|
+
|
|
82
|
+
await fs.writeFile(jsonFile, jsonMetrics);
|
|
83
|
+
await fs.writeFile(compressedJsonFile, await compressJson(jsonMetrics));
|
|
84
|
+
await copyFileToSrc(compressedJsonFile);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
main().catch((err) => {
|
|
89
|
+
console.error(err);
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const takeUntilFirstSpace = (str: string): string => str.substring(0, str.indexOf(" "));
|
|
2
|
+
|
|
3
|
+
export const takeAfterFirstSpace = (str: string): string => str.substring(str.indexOf(" ") + 1);
|
|
4
|
+
|
|
5
|
+
export const error = (msg: string) => {
|
|
6
|
+
throw new Error(msg);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type ISectionName =
|
|
10
|
+
| "StartFontMetrics"
|
|
11
|
+
| "StartCharMetrics"
|
|
12
|
+
| "StartKernPairs"
|
|
13
|
+
| "EndFontMetrics"
|
|
14
|
+
| "EndCharMetrics"
|
|
15
|
+
| "EndKernPairs";
|
|
16
|
+
|
|
17
|
+
export const extractLinesFromSection = (
|
|
18
|
+
data: string,
|
|
19
|
+
{ startAt, endAt }: { startAt: ISectionName; endAt: ISectionName },
|
|
20
|
+
) => {
|
|
21
|
+
// E.g. /^StartFontMetrics\s+\d+(\.\d+)?/m
|
|
22
|
+
const startRegex = new RegExp(`^${startAt}\\s+\\d+(\\.\\d+)?`, "m");
|
|
23
|
+
|
|
24
|
+
// E.g. /^StartCharMetrics/m
|
|
25
|
+
const endRegex = new RegExp(`^${endAt}`, "m");
|
|
26
|
+
|
|
27
|
+
const startMatch = data.match(startRegex);
|
|
28
|
+
if (!startMatch || startMatch.index === undefined) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const endMatch = data.match(endRegex);
|
|
33
|
+
if (!endMatch || endMatch.index === undefined) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const startIdx = startMatch.index + startMatch[0].length;
|
|
38
|
+
|
|
39
|
+
const sectionData = data.slice(startIdx, endMatch.index);
|
|
40
|
+
const sectionLines = sectionData.trim().split(/\r\n|\r|\n|\t/);
|
|
41
|
+
|
|
42
|
+
return sectionLines;
|
|
43
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"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"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"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"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"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"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"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"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"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"
|
package/src/encoding.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { decompressJson, padStart } from "./utils.js";
|
|
2
|
+
|
|
3
|
+
import AllEncodingsCompressed from "./all-encodings.compressed.json" with { type: "json" };
|
|
4
|
+
|
|
5
|
+
type EncodingCharCode = number;
|
|
6
|
+
type EncodingCharName = string;
|
|
7
|
+
interface UnicodeMappings {
|
|
8
|
+
[unicodeCodePoint: number]: [EncodingCharCode, EncodingCharName];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let allUnicodeMappings: {
|
|
12
|
+
symbol: UnicodeMappings;
|
|
13
|
+
zapfdingbats: UnicodeMappings;
|
|
14
|
+
win1252: UnicodeMappings;
|
|
15
|
+
} | null = null;
|
|
16
|
+
|
|
17
|
+
const getAllUnicodeMappings = async () => {
|
|
18
|
+
if (allUnicodeMappings) return allUnicodeMappings;
|
|
19
|
+
const decompressedEncodings = await decompressJson(AllEncodingsCompressed);
|
|
20
|
+
allUnicodeMappings = JSON.parse(decompressedEncodings);
|
|
21
|
+
return allUnicodeMappings;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type EncodingNames = "Symbol" | "ZapfDingbats" | "WinAnsi";
|
|
25
|
+
|
|
26
|
+
class Encoding {
|
|
27
|
+
name: EncodingNames;
|
|
28
|
+
supportedCodePoints: number[] = [];
|
|
29
|
+
#unicodeMappings: UnicodeMappings = {};
|
|
30
|
+
|
|
31
|
+
private constructor(name: EncodingNames) {
|
|
32
|
+
this.name = name;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static async create(name: EncodingNames, unicodeMappings: UnicodeMappings): Promise<Encoding> {
|
|
36
|
+
const encoding = new Encoding(name);
|
|
37
|
+
encoding.supportedCodePoints = Object.keys(unicodeMappings)
|
|
38
|
+
.map(Number)
|
|
39
|
+
.sort((a, b) => a - b);
|
|
40
|
+
encoding.#unicodeMappings = unicodeMappings;
|
|
41
|
+
return encoding;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
canEncodeUnicodeCodePoint = (codePoint: number) => codePoint in this.#unicodeMappings;
|
|
45
|
+
|
|
46
|
+
encodeUnicodeCodePoint = (codePoint: number) => {
|
|
47
|
+
const mapped = this.#unicodeMappings[codePoint];
|
|
48
|
+
if (!mapped) {
|
|
49
|
+
const str = String.fromCharCode(codePoint);
|
|
50
|
+
const hexCode = `0x${padStart(codePoint.toString(16), 4, "0")}`;
|
|
51
|
+
const msg = `${this.name} cannot encode "${str}" (${hexCode})`;
|
|
52
|
+
throw new Error(msg);
|
|
53
|
+
}
|
|
54
|
+
return { code: mapped[0], name: mapped[1] };
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type EncodingType = Encoding;
|
|
59
|
+
|
|
60
|
+
let encodingsCache: {
|
|
61
|
+
Symbol: Encoding;
|
|
62
|
+
ZapfDingbats: Encoding;
|
|
63
|
+
WinAnsi: Encoding;
|
|
64
|
+
} | null = null;
|
|
65
|
+
|
|
66
|
+
export const getEncodings = async () => {
|
|
67
|
+
if (encodingsCache) return encodingsCache;
|
|
68
|
+
const mappings = await getAllUnicodeMappings();
|
|
69
|
+
encodingsCache = {
|
|
70
|
+
Symbol: await Encoding.create("Symbol", mappings!.symbol),
|
|
71
|
+
ZapfDingbats: await Encoding.create("ZapfDingbats", mappings!.zapfdingbats),
|
|
72
|
+
WinAnsi: await Encoding.create("WinAnsi", mappings!.win1252),
|
|
73
|
+
};
|
|
74
|
+
return encodingsCache;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** @deprecated Use getEncodings() instead for async loading */
|
|
78
|
+
export const Encodings = {
|
|
79
|
+
Symbol: null as unknown as Encoding,
|
|
80
|
+
ZapfDingbats: null as unknown as Encoding,
|
|
81
|
+
WinAnsi: null as unknown as Encoding,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Initialize encodings asynchronously
|
|
85
|
+
void getEncodings().then((e) => Object.assign(Encodings, e));
|