@turntrout/subfont 1.6.0 → 1.7.1
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 +43 -43
- package/lib/FontTracerPool.d.ts +37 -0
- package/lib/FontTracerPool.d.ts.map +1 -0
- package/lib/FontTracerPool.js +212 -173
- package/lib/FontTracerPool.js.map +1 -0
- package/lib/HeadlessBrowser.js +11 -3
- package/lib/cli.d.ts +3 -0
- package/lib/cli.d.ts.map +1 -0
- package/lib/cli.js +15 -12
- package/lib/cli.js.map +1 -0
- package/lib/collectTextsByPage.js +425 -352
- package/lib/escapeJsStringLiteral.js +13 -0
- package/lib/extractVisibleText.js +6 -2
- package/lib/fontConverter.js +25 -0
- package/lib/fontConverterWorker.js +16 -0
- package/lib/fontFaceHelpers.js +16 -4
- package/lib/gatherStylesheetsWithPredicates.js +4 -5
- package/lib/normalizeFontPropertyValue.js +1 -1
- package/lib/sfntCache.js +10 -7
- package/lib/subfont.d.ts +33 -0
- package/lib/subfont.d.ts.map +1 -0
- package/lib/subfont.js +533 -591
- package/lib/subfont.js.map +1 -0
- package/lib/subsetFontWithGlyphs.d.ts +17 -0
- package/lib/subsetFontWithGlyphs.d.ts.map +1 -0
- package/lib/subsetFontWithGlyphs.js +231 -253
- package/lib/subsetFontWithGlyphs.js.map +1 -0
- package/lib/subsetFonts.d.ts +59 -0
- package/lib/subsetFonts.d.ts.map +1 -0
- package/lib/subsetFonts.js +921 -1180
- package/lib/subsetFonts.js.map +1 -0
- package/lib/subsetGeneration.d.ts +39 -0
- package/lib/subsetGeneration.d.ts.map +1 -0
- package/lib/subsetGeneration.js +294 -324
- package/lib/subsetGeneration.js.map +1 -0
- package/lib/unquote.js +9 -4
- package/lib/warnAboutMissingGlyphs.js +36 -25
- package/lib/wasmQueue.js +6 -2
- package/package.json +11 -3
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Escape a value for safe inclusion in any JS string context (single-quoted,
|
|
2
|
+
// double-quoted, or template literal). Uses JSON.stringify for robust escaping
|
|
3
|
+
// of backslashes, quotes, newlines, U+2028, U+2029, etc.
|
|
4
|
+
// The < escape prevents </script> from closing an inline script tag.
|
|
5
|
+
function escapeJsStringLiteral(str) {
|
|
6
|
+
return JSON.stringify(str)
|
|
7
|
+
.slice(1, -1)
|
|
8
|
+
.replace(/'/g, "\\'")
|
|
9
|
+
.replace(/`/g, '\\x60')
|
|
10
|
+
.replace(/</g, '\\x3c');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = escapeJsStringLiteral;
|
|
@@ -81,8 +81,12 @@ const namedEntities = {
|
|
|
81
81
|
const entityRe = /&(?:#x([0-9a-fA-F]+)|#(\d+)|([a-zA-Z]+));/g;
|
|
82
82
|
function decodeEntities(str) {
|
|
83
83
|
return str.replace(entityRe, (match, hex, dec, name) => {
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
try {
|
|
85
|
+
if (hex) return String.fromCodePoint(parseInt(hex, 16));
|
|
86
|
+
if (dec) return String.fromCodePoint(parseInt(dec, 10));
|
|
87
|
+
} catch {
|
|
88
|
+
return match;
|
|
89
|
+
}
|
|
86
90
|
if (name && namedEntities[name.toLowerCase()] !== undefined) {
|
|
87
91
|
return namedEntities[name.toLowerCase()];
|
|
88
92
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const pathModule = require('path');
|
|
2
|
+
const { Worker } = require('worker_threads');
|
|
3
|
+
|
|
4
|
+
const workerPath = pathModule.join(__dirname, 'fontConverterWorker.js');
|
|
5
|
+
|
|
6
|
+
function convert(buffer, targetFormat, sourceFormat) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const worker = new Worker(workerPath);
|
|
9
|
+
worker.on('message', (msg) => {
|
|
10
|
+
worker.terminate();
|
|
11
|
+
if (msg.type === 'result') {
|
|
12
|
+
resolve(Buffer.from(msg.buffer));
|
|
13
|
+
} else {
|
|
14
|
+
reject(new Error(msg.error));
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
worker.on('error', (err) => {
|
|
18
|
+
worker.terminate();
|
|
19
|
+
reject(err);
|
|
20
|
+
});
|
|
21
|
+
worker.postMessage({ buffer, targetFormat, sourceFormat });
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { convert };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const { parentPort } = require('worker_threads');
|
|
2
|
+
const fontverter = require('fontverter');
|
|
3
|
+
|
|
4
|
+
parentPort.on('message', async (msg) => {
|
|
5
|
+
try {
|
|
6
|
+
const buffer = Buffer.from(msg.buffer);
|
|
7
|
+
const result = await fontverter.convert(
|
|
8
|
+
buffer,
|
|
9
|
+
msg.targetFormat,
|
|
10
|
+
msg.sourceFormat
|
|
11
|
+
);
|
|
12
|
+
parentPort.postMessage({ type: 'result', buffer: result });
|
|
13
|
+
} catch (err) {
|
|
14
|
+
parentPort.postMessage({ type: 'error', error: err.message });
|
|
15
|
+
}
|
|
16
|
+
});
|
package/lib/fontFaceHelpers.js
CHANGED
|
@@ -75,15 +75,27 @@ function getFontFaceDeclarationText(node, relations) {
|
|
|
75
75
|
|
|
76
76
|
const fontOrder = ['woff2', 'woff', 'truetype'];
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
// Cache base64-encoded data URIs keyed by the underlying Buffer. Subset
|
|
79
|
+
// buffers are shared across pages (propagated from the canonical fontUsage),
|
|
80
|
+
// so without this every page re-encodes the same multi-hundred-KB buffer.
|
|
81
|
+
const subsetDataUrlCache = new WeakMap();
|
|
82
|
+
function getSubsetDataUrls(subsetsObj) {
|
|
83
|
+
let cached = subsetDataUrlCache.get(subsetsObj);
|
|
84
|
+
if (cached) return cached;
|
|
85
|
+
cached = fontOrder
|
|
86
|
+
.filter((format) => subsetsObj[format])
|
|
81
87
|
.map((format) => ({
|
|
82
88
|
format,
|
|
83
|
-
url: `data:${contentTypeByFontFormat[format]};base64,${
|
|
89
|
+
url: `data:${contentTypeByFontFormat[format]};base64,${subsetsObj[
|
|
84
90
|
format
|
|
85
91
|
].toString('base64')}`,
|
|
86
92
|
}));
|
|
93
|
+
subsetDataUrlCache.set(subsetsObj, cached);
|
|
94
|
+
return cached;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getFontFaceForFontUsage(fontUsage) {
|
|
98
|
+
const subsets = getSubsetDataUrls(fontUsage.subsets);
|
|
87
99
|
|
|
88
100
|
const resultString = ['@font-face {'];
|
|
89
101
|
|
|
@@ -3,18 +3,17 @@ module.exports = function gatherStylesheetsWithPredicates(
|
|
|
3
3
|
htmlAsset,
|
|
4
4
|
relationIndex
|
|
5
5
|
) {
|
|
6
|
-
const
|
|
6
|
+
const visiting = new Set();
|
|
7
7
|
const incomingMedia = [];
|
|
8
8
|
const conditionalCommentConditionStack = [];
|
|
9
9
|
const result = [];
|
|
10
10
|
(function traverse(asset, isWithinNotIeConditionalComment, isWithinNoscript) {
|
|
11
|
-
if (
|
|
12
|
-
// Cycle detected
|
|
11
|
+
if (visiting.has(asset)) {
|
|
13
12
|
return;
|
|
14
13
|
} else if (!asset.isLoaded) {
|
|
15
14
|
return;
|
|
16
15
|
}
|
|
17
|
-
|
|
16
|
+
visiting.add(asset);
|
|
18
17
|
// Use pre-built index if available, otherwise fall back to findRelations
|
|
19
18
|
const relations = relationIndex
|
|
20
19
|
? relationIndex.get(asset) || []
|
|
@@ -60,7 +59,7 @@ module.exports = function gatherStylesheetsWithPredicates(
|
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
|
-
|
|
62
|
+
visiting.delete(asset);
|
|
64
63
|
if (asset.type === 'Css') {
|
|
65
64
|
const predicates = {};
|
|
66
65
|
for (const incomingMedium of incomingMedia) {
|
|
@@ -34,7 +34,7 @@ function isValidWeight(weight) {
|
|
|
34
34
|
function normalizeFontPropertyValue(propName, value) {
|
|
35
35
|
const propNameLowerCase = propName.toLowerCase();
|
|
36
36
|
if (value === undefined) {
|
|
37
|
-
return initialValueByProp[
|
|
37
|
+
return initialValueByProp[propNameLowerCase];
|
|
38
38
|
}
|
|
39
39
|
if (propNameLowerCase === 'font-family') {
|
|
40
40
|
return unquote(value);
|
package/lib/sfntCache.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const fontverter = require('fontverter');
|
|
2
|
+
const { convert } = require('./fontConverter');
|
|
2
3
|
|
|
3
4
|
const sfntPromiseByBuffer = new WeakMap();
|
|
4
5
|
|
|
@@ -9,13 +10,15 @@ function toSfnt(buffer) {
|
|
|
9
10
|
let promise;
|
|
10
11
|
try {
|
|
11
12
|
const format = fontverter.detectFormat(buffer);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
if (format === 'sfnt') {
|
|
14
|
+
promise = Promise.resolve(buffer);
|
|
15
|
+
} else if (format === 'woff2') {
|
|
16
|
+
promise = convert(buffer, 'sfnt');
|
|
17
|
+
} else {
|
|
18
|
+
promise = fontverter.convert(buffer, 'sfnt');
|
|
19
|
+
}
|
|
20
|
+
} catch {
|
|
21
|
+
promise = convert(buffer, 'sfnt');
|
|
19
22
|
}
|
|
20
23
|
// Evict on rejection so retries with the same buffer aren't stuck
|
|
21
24
|
promise.catch(() => sfntPromiseByBuffer.delete(buffer));
|
package/lib/subfont.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import AssetGraph = require('assetgraph');
|
|
2
|
+
declare class UsageError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
interface SubfontOptions {
|
|
6
|
+
root?: string;
|
|
7
|
+
canonicalRoot?: string;
|
|
8
|
+
output?: string;
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
dryRun?: boolean;
|
|
11
|
+
silent?: boolean;
|
|
12
|
+
inlineCss?: boolean;
|
|
13
|
+
fontDisplay?: string;
|
|
14
|
+
inPlace?: boolean;
|
|
15
|
+
inputFiles?: Array<string | URL>;
|
|
16
|
+
recursive?: boolean;
|
|
17
|
+
relativeUrls?: boolean;
|
|
18
|
+
dynamic?: boolean;
|
|
19
|
+
fallbacks?: boolean;
|
|
20
|
+
text?: string;
|
|
21
|
+
sourceMaps?: boolean;
|
|
22
|
+
concurrency?: number;
|
|
23
|
+
chromeFlags?: string[];
|
|
24
|
+
cache?: boolean | string;
|
|
25
|
+
strict?: boolean;
|
|
26
|
+
}
|
|
27
|
+
interface SubfontFn {
|
|
28
|
+
(options: SubfontOptions, console?: Console): Promise<InstanceType<typeof AssetGraph>>;
|
|
29
|
+
UsageError: typeof UsageError;
|
|
30
|
+
}
|
|
31
|
+
declare const subfont: SubfontFn;
|
|
32
|
+
export = subfont;
|
|
33
|
+
//# sourceMappingURL=subfont.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subfont.d.ts","sourceRoot":"","sources":["../src/subfont.ts"],"names":[],"mappings":"AAKA,OAAO,UAAU,GAAG,QAAQ,YAAY,CAAC,CAAC;AAQ1C,cAAM,UAAW,SAAQ,KAAK;gBAChB,OAAO,EAAE,MAAM;CAI5B;AAED,UAAU,cAAc;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAqBD,UAAU,SAAS;IACjB,CACE,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,YAAY,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC;IAC5C,UAAU,EAAE,OAAO,UAAU,CAAC;CAC/B;AAED,QAAA,MAAM,OAAO,EA6pBR,SAAS,CAAC;AAIf,SAAS,OAAO,CAAC"}
|