@overpunch/vf-clamp 2.1.4 → 2.2.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 +21 -0
- package/README.md +538 -372
- package/dist/index.cjs +8 -8
- package/dist/index.d.ts +34 -2
- package/dist/index.js +168 -103
- package/package.json +70 -70
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const $=require("node:module");var T=typeof document<"u"?document.currentScript:null;const k=$.createRequire(typeof document>"u"?require("url").pathToFileURL(__filename).href:T&&T.tagName.toUpperCase()==="SCRIPT"&&T.src||new URL("index.cjs",document.baseURI).href),{preparePyodide:d,PyodideFile:x}=k("@web-alchemy/fonttools/src/pyodide.js");let b=null;async function C(){return b||(b=d().then(t=>t.runPythonAsync(`
|
|
2
2
|
from fontTools.ttLib import TTFont
|
|
3
3
|
|
|
4
4
|
def vf_convert_flavor(file_options):
|
|
@@ -7,7 +7,7 @@ def vf_convert_flavor(file_options):
|
|
|
7
7
|
font.save(file_options['output-file'])
|
|
8
8
|
|
|
9
9
|
vf_convert_flavor
|
|
10
|
-
`))),
|
|
10
|
+
`))),b}async function P(t,o){const a=await d(),e=new x({pyodide:a}),n=new x({pyodide:a});try{await e.upload(t);const i=new Map([["input-file",e.filename],["output-file",n.filename],["flavor",o]]);return(await C())(i),n.download()}finally{try{e.delete()}catch{}try{n.delete()}catch{}}}async function j(t){return P(t,"woff")}async function M(t){return P(t,"woff2")}let F=null;async function E(){return F||(F=d().then(t=>t.runPythonAsync(`
|
|
11
11
|
import json
|
|
12
12
|
from fontTools.ttLib import TTFont
|
|
13
13
|
|
|
@@ -42,7 +42,7 @@ def get_instances_fn(input_file):
|
|
|
42
42
|
return json.dumps({'axes': axes, 'instances': instances})
|
|
43
43
|
|
|
44
44
|
get_instances_fn
|
|
45
|
-
`))),
|
|
45
|
+
`))),F}async function L(t){const o=await d(),a=t instanceof ArrayBuffer?new Uint8Array(t):t,e=new x({pyodide:o});try{await e.upload(a);const i=(await E())(e.filename);return JSON.parse(i)}finally{try{e.delete()}catch{}}}let S=null,I=null,V=null,D=null,O=null;async function z(){return S||(S=d().then(t=>t.runPythonAsync(`
|
|
46
46
|
from fontTools import ttLib
|
|
47
47
|
|
|
48
48
|
def patch_font_names_fn(file_options):
|
|
@@ -95,7 +95,7 @@ def patch_font_names_fn(file_options):
|
|
|
95
95
|
font.save(file_options['output-file'])
|
|
96
96
|
|
|
97
97
|
patch_font_names_fn
|
|
98
|
-
`))),
|
|
98
|
+
`))),S}async function W(){return I||(I=d().then(t=>t.runPythonAsync(`
|
|
99
99
|
from fontTools.ttLib import TTFont
|
|
100
100
|
from fontTools.varLib import instancer
|
|
101
101
|
import json
|
|
@@ -124,7 +124,7 @@ def vf_clamp_instantiate(file_options, axes_json):
|
|
|
124
124
|
partial.save(file_options['output-file'])
|
|
125
125
|
|
|
126
126
|
vf_clamp_instantiate
|
|
127
|
-
`))),
|
|
127
|
+
`))),I}async function q(){return V||(V=d().then(t=>t.runPythonAsync(`
|
|
128
128
|
from fontTools.ttLib import TTFont
|
|
129
129
|
|
|
130
130
|
def vf_clamp_normalize_wght(file_options, new_min_str):
|
|
@@ -181,7 +181,7 @@ def vf_clamp_normalize_wght(file_options, new_min_str):
|
|
|
181
181
|
font.save(file_options['output-file'])
|
|
182
182
|
|
|
183
183
|
vf_clamp_normalize_wght
|
|
184
|
-
`))),
|
|
184
|
+
`))),V}async function B(){return D||(D=d().then(t=>t.runPythonAsync(`
|
|
185
185
|
from fontTools.ttLib import TTFont
|
|
186
186
|
|
|
187
187
|
def vf_clamp_prune_stat(file_options):
|
|
@@ -239,7 +239,7 @@ def vf_clamp_prune_stat(file_options):
|
|
|
239
239
|
font.save(file_options['output-file'])
|
|
240
240
|
|
|
241
241
|
vf_clamp_prune_stat
|
|
242
|
-
`))),
|
|
242
|
+
`))),D}async function G(){return O||(O=d().then(t=>t.runPythonAsync(`
|
|
243
243
|
from fontTools.ttLib import TTFont
|
|
244
244
|
|
|
245
245
|
def vf_clamp_update_os2(file_options):
|
|
@@ -284,4 +284,4 @@ def vf_clamp_update_os2(file_options):
|
|
|
284
284
|
font.save(file_options['output-file'])
|
|
285
285
|
|
|
286
286
|
vf_clamp_update_os2
|
|
287
|
-
`))),
|
|
287
|
+
`))),O}function H(t){return t.replace(/[^A-Za-z0-9 -]/g,"").trim().split(/\s+/).join("-").replace(/^-+|-+$/g,"").slice(0,63)}async function J(t,o){if(!o)return t;const a=await d(),e=new x({pyodide:a}),n=new x({pyodide:a});try{const i=H(o);await e.upload(t);const s=new Map([["input-file",e.filename],["output-file",n.filename],["family-name",o],["postscript-name",i]]);return(await z())(s),n.download()}catch(i){throw Object.assign(new Error(`vf-clamp: name table patching failed for "${o}"`),{cause:i})}finally{try{e.delete()}catch{}try{n.delete()}catch{}}}function Z(t){return typeof t=="number"?t:t===null?null:[t.min,t.max]}async function K(t,o){const a=await d(),e=new x({pyodide:a}),n=new x({pyodide:a});try{await e.upload(t);const i=new Map([["input-file",e.filename],["output-file",n.filename]]);return(await W())(i,JSON.stringify(o)),n.download()}finally{try{e.delete()}catch{}try{n.delete()}catch{}}}async function Q(t){const o=await d(),a=new x({pyodide:o}),e=new x({pyodide:o});try{await a.upload(t);const n=new Map([["input-file",a.filename],["output-file",e.filename]]);return(await B())(n),e.download()}catch(n){throw Object.assign(new Error("vf-clamp: STAT pruning failed"),{cause:n})}finally{try{a.delete()}catch{}try{e.delete()}catch{}}}async function X(t){const o=await d(),a=new x({pyodide:o}),e=new x({pyodide:o});try{await a.upload(t);const n=new Map([["input-file",a.filename],["output-file",e.filename]]);return(await G())(n),e.download()}catch(n){throw Object.assign(new Error("vf-clamp: OS/2 and macStyle update failed"),{cause:n})}finally{try{a.delete()}catch{}try{e.delete()}catch{}}}async function Y(t,o){const a=await d(),e=new x({pyodide:a}),n=new x({pyodide:a});try{await e.upload(t);const i=new Map([["input-file",e.filename],["output-file",n.filename]]);return(await q())(i,String(o)),n.download()}catch(i){throw Object.assign(new Error("vf-clamp: wght normalisation failed"),{cause:i})}finally{try{e.delete()}catch{}try{n.delete()}catch{}}}function tt(t,o){const a=new Map(o.map(i=>[i.name,i])),e={};for(const i of t){const s=a.get(i);if(!s)throw new Error(`Named instance "${i}" not found in font`);for(const[f,c]of Object.entries(s.coordinates))e[f]?(e[f].min=Math.min(e[f].min,c),e[f].max=Math.max(e[f].max,c)):e[f]={min:c,max:c}}const n={};for(const[i,{min:s,max:f}]of Object.entries(e))n[i]=s===f?s:{min:s,max:f};return n}function et(t){return t.length>=4&&t[0]===79&&t[1]===84&&t[2]===84&&t[3]===79}async function nt(t,o){var f,c,w;if(o.outputs.length===0)return[];const a=t instanceof ArrayBuffer?new Uint8Array(t):t,e=o.format??"ttf",{instances:n,axes:i}=await L(a),s=[];for(const m of o.outputs){let _={};if((f=m.instances)!=null&&f.length&&(_=tt(m.instances,n)),m.axes&&(_={..._,...m.axes}),o.strict&&((c=m.instances)!=null&&c.length)){const p=new Set(m.instances),g=n.filter(h=>!p.has(h.name)&&i.every(y=>{const A=h.coordinates[y.tag]??y.default,v=_[y.tag];return v==null?!0:typeof v=="number"?A===v:A>=v.min&&A<=v.max})).map(h=>h.name);if(g.length)throw new Error(`vf-clamp: output "${m.name||m.instances.join(", ")}" would include unselected instances (${g.join(", ")}); use planOutputs() to split the selection`)}for(const p of i){const g=_[p.tag];if(g!=null&&typeof g=="object"){const h=g;if(p.default<h.min||p.default>h.max){const y=Math.max(h.min,Math.min(h.max,p.default));console.warn(`vf-clamp: axis "${p.tag}" default (${p.default}) is outside restricted range [${h.min}, ${h.max}] — will be clamped to ${y}`)}}}const r={};for(const[p,g]of Object.entries(_))g!==null&&(r[p]=Z(g));const u=m.name||((w=m.instances)!=null&&w.length?m.instances.length===1?m.instances[0]:`${m.instances[0]}-${m.instances[m.instances.length-1]}`:"output");let l=await K(a,r);if(e==="otf"&&!et(l))throw new Error("vf-clamp: format 'otf' needs a CFF/CFF2 source font; this font has TrueType outlines — use 'ttf'");l=await Q(l),o.normalizeWeightAxis&&(l=await Y(l,100)),l=await X(l),l=await J(l,u),e==="woff2"?l=await M(l):e==="woff"&&(l=await j(l)),s.push({name:u,buffer:l,format:e})}return s}function U(t,o){if(t===o)return t;const a=t.split(" "),e=o.split(" ");let n=0;for(;n<a.length&&n<e.length&&a[n]===e[n];)n++;let i=0;for(;i<a.length-n&&i<e.length-n&&a[a.length-1-i]===e[e.length-1-i];)i++;const s=a.slice(0,n).join(" "),f=a.slice(n,a.length-(i||0)).join(" "),c=e.slice(n,e.length-(i||0)).join(" "),w=i>0?a.slice(a.length-i).join(" "):"",m=f&&c?`${f}-${c}`:f||c;return[s,m,w].filter(Boolean).join(" ")}function R(t,o){const a={};for(const e of o){const n=t.map(i=>i.coordinates[e.tag]??e.default);a[e.tag]={min:Math.min(...n),max:Math.max(...n)}}return a}function N(t,o,a,e){return o.filter(n=>a.has(n.name)?!1:e.every(i=>{const s=n.coordinates[i.tag]??i.default,f=t[i.tag];return!f||s>=f.min&&s<=f.max}))}function at(t){return t.reduce((o,a)=>!o||a.maximum-a.minimum>o.maximum-o.minimum?a:o,void 0)}function it(t,o,a){const{axes:e,instances:n}=t,i=new Map(n.map(r=>[r.name,r])),s=[];for(const r of new Set(o)){const u=i.get(r);if(!u)throw new Error(`Named instance "${r}" not found in font`);s.push(u)}if(!s.length)return[];const f=new Set(s.map(r=>r.name));let c;if(!N(R(s,e),n,f,e).length)c=[s];else{c=s.map(u=>[u]);let r=!0;for(;r;){r=!1;t:for(let u=0;u<c.length;u++)for(let l=u+1;l<c.length;l++){const p=[...c[u],...c[l]];if(!N(R(p,e),n,f,e).length){c[u]=p,c.splice(l,1),r=!0;break t}}}}const w=at(e),m=w?[w,...e.filter(r=>r.tag!==w.tag)]:e,_=(r,u)=>{for(const l of m){const p=(r.coordinates[l.tag]??l.default)-(u.coordinates[l.tag]??l.default);if(p)return p}return 0};return c=c.map(r=>[...r].sort(_)).sort((r,u)=>_(r[0],u[0])),c.map(r=>{const u=r[0],l=r[r.length-1],p=r.length===1?u.name:U(u.name,l.name);return{name:a?`${a} ${p}`:p,instances:r.map(g=>g.name)}})}function ot(t,o){const a=new Map(t.instances.map(n=>[n.name,n])),e=o.map(n=>a.get(n)).filter(n=>!!n);return e.length?N(R(e,t.axes),t.instances,new Set(o),t.axes).map(n=>n.name):[]}exports.clampFont=nt;exports.compactName=U;exports.convertToWoff=j;exports.convertToWoff2=M;exports.getInstances=L;exports.planOutputs=it;exports.unboughtInstances=ot;
|
package/dist/index.d.ts
CHANGED
|
@@ -52,9 +52,15 @@ export declare interface ClampOptions {
|
|
|
52
52
|
/**
|
|
53
53
|
* Output format — defaults to 'ttf'.
|
|
54
54
|
* 'woff' and 'woff2' transcode the result to a web-compressed flavour.
|
|
55
|
-
* 'otf' is a
|
|
55
|
+
* 'otf' is a label, not a conversion — it requires a CFF/CFF2 source and throws for TrueType outlines.
|
|
56
56
|
*/
|
|
57
57
|
format?: OutputFormat;
|
|
58
|
+
/**
|
|
59
|
+
* Refuse any instances-based output whose range would include a named instance that was not listed
|
|
60
|
+
* (e.g. Regular + Bold would also hand over Medium and SemiBold). Throws instead of clamping.
|
|
61
|
+
* Use planOutputs() to split a customer's selection into safe outputs. Defaults to false.
|
|
62
|
+
*/
|
|
63
|
+
strict?: boolean;
|
|
58
64
|
/**
|
|
59
65
|
* Remap the wght axis so its minimum becomes 100, making CSS font-weight values
|
|
60
66
|
* (100–900) work as expected. Instance and STAT axis value coordinates are
|
|
@@ -147,14 +153,40 @@ export declare interface OutputConfig {
|
|
|
147
153
|
/**
|
|
148
154
|
* Output format for clampFont() — defaults to 'ttf'.
|
|
149
155
|
* 'woff' and 'woff2' transcode to web-compressed flavours.
|
|
150
|
-
* 'otf'
|
|
156
|
+
* 'otf' requires a CFF/CFF2 source (no outline conversion is done) and throws for TrueType outlines.
|
|
151
157
|
*/
|
|
152
158
|
export declare type OutputFormat = 'ttf' | 'otf' | 'woff' | 'woff2';
|
|
153
159
|
|
|
154
160
|
/** Pin an axis at a single value — the axis is locked and removed from the output font's design space */
|
|
155
161
|
export declare type PinnedAxis = number;
|
|
156
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Groups a customer's selected named instances into clampFont outputs so that no output's range
|
|
165
|
+
* contains a named instance the customer did not select.
|
|
166
|
+
*
|
|
167
|
+
* Selections merge into one output only when their combined range holds no unselected named
|
|
168
|
+
* instance: Regular + Medium + SemiBold + Bold → one output; Regular + Bold alone → two outputs
|
|
169
|
+
* (each pinned to its own instance). Output names come from compactName(), optionally prefixed
|
|
170
|
+
* with `family`.
|
|
171
|
+
*
|
|
172
|
+
* @param font - The font's axes and named instances, from getInstances()
|
|
173
|
+
* @param selected - Names of the instances the customer bought (must match exactly)
|
|
174
|
+
* @param family - Optional family name to prefix each output name with, e.g. "Encode Sans"
|
|
175
|
+
* @returns OutputConfig entries ready for clampFont(), sorted along the primary axis
|
|
176
|
+
* @throws If a selected name is not a named instance of the font
|
|
177
|
+
*/
|
|
178
|
+
export declare function planOutputs(font: FontInstancesResult, selected: string[], family?: string): OutputConfig[];
|
|
179
|
+
|
|
157
180
|
/** @deprecated Use OutputConfig instead */
|
|
158
181
|
export declare type SubfamilyConfig = OutputConfig;
|
|
159
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Lists the named instances that a single output built from `names` would include without being selected,
|
|
185
|
+
* e.g. ['Medium', 'SemiBold'] for Regular + Bold. An empty array means the output is purchase-safe.
|
|
186
|
+
*
|
|
187
|
+
* @param font - The font's axes and named instances, from getInstances()
|
|
188
|
+
* @param names - The instance names one output would hull
|
|
189
|
+
*/
|
|
190
|
+
export declare function unboughtInstances(font: FontInstancesResult, names: string[]): string[];
|
|
191
|
+
|
|
160
192
|
export { }
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { createRequire as
|
|
2
|
-
const
|
|
3
|
-
let
|
|
4
|
-
async function
|
|
5
|
-
return
|
|
1
|
+
import { createRequire as j } from "node:module";
|
|
2
|
+
const R = j(import.meta.url), { preparePyodide: d, PyodideFile: x } = R("@web-alchemy/fonttools/src/pyodide.js");
|
|
3
|
+
let T = null;
|
|
4
|
+
async function M() {
|
|
5
|
+
return T || (T = d().then(
|
|
6
6
|
(t) => t.runPythonAsync(`
|
|
7
7
|
from fontTools.ttLib import TTFont
|
|
8
8
|
|
|
@@ -13,10 +13,10 @@ def vf_convert_flavor(file_options):
|
|
|
13
13
|
|
|
14
14
|
vf_convert_flavor
|
|
15
15
|
`)
|
|
16
|
-
)),
|
|
16
|
+
)), T;
|
|
17
17
|
}
|
|
18
|
-
async function
|
|
19
|
-
const a = await
|
|
18
|
+
async function P(t, o) {
|
|
19
|
+
const a = await d(), e = new x({ pyodide: a }), n = new x({ pyodide: a });
|
|
20
20
|
try {
|
|
21
21
|
await e.upload(t);
|
|
22
22
|
const i = /* @__PURE__ */ new Map([
|
|
@@ -24,7 +24,7 @@ async function V(t, o) {
|
|
|
24
24
|
["output-file", n.filename],
|
|
25
25
|
["flavor", o]
|
|
26
26
|
]);
|
|
27
|
-
return (await
|
|
27
|
+
return (await M())(i), n.download();
|
|
28
28
|
} finally {
|
|
29
29
|
try {
|
|
30
30
|
e.delete();
|
|
@@ -36,15 +36,15 @@ async function V(t, o) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
async function
|
|
40
|
-
return
|
|
39
|
+
async function L(t) {
|
|
40
|
+
return P(t, "woff");
|
|
41
41
|
}
|
|
42
|
-
async function
|
|
43
|
-
return
|
|
42
|
+
async function $(t) {
|
|
43
|
+
return P(t, "woff2");
|
|
44
44
|
}
|
|
45
|
-
let
|
|
46
|
-
async function
|
|
47
|
-
return
|
|
45
|
+
let b = null;
|
|
46
|
+
async function k() {
|
|
47
|
+
return b || (b = d().then(
|
|
48
48
|
(t) => t.runPythonAsync(`
|
|
49
49
|
import json
|
|
50
50
|
from fontTools.ttLib import TTFont
|
|
@@ -81,13 +81,13 @@ def get_instances_fn(input_file):
|
|
|
81
81
|
|
|
82
82
|
get_instances_fn
|
|
83
83
|
`)
|
|
84
|
-
)),
|
|
84
|
+
)), b;
|
|
85
85
|
}
|
|
86
|
-
async function
|
|
87
|
-
const o = await
|
|
86
|
+
async function E(t) {
|
|
87
|
+
const o = await d(), a = t instanceof ArrayBuffer ? new Uint8Array(t) : t, e = new x({ pyodide: o });
|
|
88
88
|
try {
|
|
89
89
|
await e.upload(a);
|
|
90
|
-
const i = (await
|
|
90
|
+
const i = (await k())(e.filename);
|
|
91
91
|
return JSON.parse(i);
|
|
92
92
|
} finally {
|
|
93
93
|
try {
|
|
@@ -96,9 +96,9 @@ async function j(t) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
let
|
|
100
|
-
async function
|
|
101
|
-
return
|
|
99
|
+
let F = null, S = null, V = null, I = null, D = null;
|
|
100
|
+
async function U() {
|
|
101
|
+
return F || (F = d().then(
|
|
102
102
|
(t) => t.runPythonAsync(`
|
|
103
103
|
from fontTools import ttLib
|
|
104
104
|
|
|
@@ -153,10 +153,10 @@ def patch_font_names_fn(file_options):
|
|
|
153
153
|
|
|
154
154
|
patch_font_names_fn
|
|
155
155
|
`)
|
|
156
|
-
)),
|
|
156
|
+
)), F;
|
|
157
157
|
}
|
|
158
|
-
async function
|
|
159
|
-
return
|
|
158
|
+
async function C() {
|
|
159
|
+
return S || (S = d().then(
|
|
160
160
|
(t) => t.runPythonAsync(`
|
|
161
161
|
from fontTools.ttLib import TTFont
|
|
162
162
|
from fontTools.varLib import instancer
|
|
@@ -187,10 +187,10 @@ def vf_clamp_instantiate(file_options, axes_json):
|
|
|
187
187
|
|
|
188
188
|
vf_clamp_instantiate
|
|
189
189
|
`)
|
|
190
|
-
)),
|
|
190
|
+
)), S;
|
|
191
191
|
}
|
|
192
|
-
async function
|
|
193
|
-
return
|
|
192
|
+
async function z() {
|
|
193
|
+
return V || (V = d().then(
|
|
194
194
|
(t) => t.runPythonAsync(`
|
|
195
195
|
from fontTools.ttLib import TTFont
|
|
196
196
|
|
|
@@ -249,10 +249,10 @@ def vf_clamp_normalize_wght(file_options, new_min_str):
|
|
|
249
249
|
|
|
250
250
|
vf_clamp_normalize_wght
|
|
251
251
|
`)
|
|
252
|
-
)),
|
|
252
|
+
)), V;
|
|
253
253
|
}
|
|
254
|
-
async function
|
|
255
|
-
return
|
|
254
|
+
async function B() {
|
|
255
|
+
return I || (I = d().then(
|
|
256
256
|
(t) => t.runPythonAsync(`
|
|
257
257
|
from fontTools.ttLib import TTFont
|
|
258
258
|
|
|
@@ -312,10 +312,10 @@ def vf_clamp_prune_stat(file_options):
|
|
|
312
312
|
|
|
313
313
|
vf_clamp_prune_stat
|
|
314
314
|
`)
|
|
315
|
-
)),
|
|
315
|
+
)), I;
|
|
316
316
|
}
|
|
317
|
-
async function
|
|
318
|
-
return
|
|
317
|
+
async function W() {
|
|
318
|
+
return D || (D = d().then(
|
|
319
319
|
(t) => t.runPythonAsync(`
|
|
320
320
|
from fontTools.ttLib import TTFont
|
|
321
321
|
|
|
@@ -362,26 +362,26 @@ def vf_clamp_update_os2(file_options):
|
|
|
362
362
|
|
|
363
363
|
vf_clamp_update_os2
|
|
364
364
|
`)
|
|
365
|
-
)),
|
|
365
|
+
)), D;
|
|
366
366
|
}
|
|
367
|
-
function
|
|
367
|
+
function q(t) {
|
|
368
368
|
return t.replace(/[^A-Za-z0-9 -]/g, "").trim().split(/\s+/).join("-").replace(/^-+|-+$/g, "").slice(0, 63);
|
|
369
369
|
}
|
|
370
|
-
async function
|
|
370
|
+
async function G(t, o) {
|
|
371
371
|
if (!o) return t;
|
|
372
|
-
const a = await
|
|
372
|
+
const a = await d(), e = new x({ pyodide: a }), n = new x({ pyodide: a });
|
|
373
373
|
try {
|
|
374
|
-
const i =
|
|
374
|
+
const i = q(o);
|
|
375
375
|
await e.upload(t);
|
|
376
|
-
const
|
|
376
|
+
const s = /* @__PURE__ */ new Map([
|
|
377
377
|
["input-file", e.filename],
|
|
378
378
|
["output-file", n.filename],
|
|
379
379
|
["family-name", o],
|
|
380
380
|
["postscript-name", i]
|
|
381
381
|
]);
|
|
382
|
-
return (await
|
|
382
|
+
return (await U())(s), n.download();
|
|
383
383
|
} catch (i) {
|
|
384
|
-
|
|
384
|
+
throw Object.assign(new Error(`vf-clamp: name table patching failed for "${o}"`), { cause: i });
|
|
385
385
|
} finally {
|
|
386
386
|
try {
|
|
387
387
|
e.delete();
|
|
@@ -393,18 +393,18 @@ async function z(t, o) {
|
|
|
393
393
|
}
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
|
-
function
|
|
396
|
+
function H(t) {
|
|
397
397
|
return typeof t == "number" ? t : t === null ? null : [t.min, t.max];
|
|
398
398
|
}
|
|
399
|
-
async function
|
|
400
|
-
const a = await
|
|
399
|
+
async function J(t, o) {
|
|
400
|
+
const a = await d(), e = new x({ pyodide: a }), n = new x({ pyodide: a });
|
|
401
401
|
try {
|
|
402
402
|
await e.upload(t);
|
|
403
403
|
const i = /* @__PURE__ */ new Map([
|
|
404
404
|
["input-file", e.filename],
|
|
405
405
|
["output-file", n.filename]
|
|
406
406
|
]);
|
|
407
|
-
return (await
|
|
407
|
+
return (await C())(i, JSON.stringify(o)), n.download();
|
|
408
408
|
} finally {
|
|
409
409
|
try {
|
|
410
410
|
e.delete();
|
|
@@ -416,17 +416,17 @@ async function E(t, o) {
|
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
|
-
async function
|
|
420
|
-
const o = await
|
|
419
|
+
async function Z(t) {
|
|
420
|
+
const o = await d(), a = new x({ pyodide: o }), e = new x({ pyodide: o });
|
|
421
421
|
try {
|
|
422
422
|
await a.upload(t);
|
|
423
423
|
const n = /* @__PURE__ */ new Map([
|
|
424
424
|
["input-file", a.filename],
|
|
425
425
|
["output-file", e.filename]
|
|
426
426
|
]);
|
|
427
|
-
return (await
|
|
427
|
+
return (await B())(n), e.download();
|
|
428
428
|
} catch (n) {
|
|
429
|
-
|
|
429
|
+
throw Object.assign(new Error("vf-clamp: STAT pruning failed"), { cause: n });
|
|
430
430
|
} finally {
|
|
431
431
|
try {
|
|
432
432
|
a.delete();
|
|
@@ -438,17 +438,17 @@ async function W(t) {
|
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
|
-
async function
|
|
442
|
-
const o = await
|
|
441
|
+
async function K(t) {
|
|
442
|
+
const o = await d(), a = new x({ pyodide: o }), e = new x({ pyodide: o });
|
|
443
443
|
try {
|
|
444
444
|
await a.upload(t);
|
|
445
445
|
const n = /* @__PURE__ */ new Map([
|
|
446
446
|
["input-file", a.filename],
|
|
447
447
|
["output-file", e.filename]
|
|
448
448
|
]);
|
|
449
|
-
return (await
|
|
449
|
+
return (await W())(n), e.download();
|
|
450
450
|
} catch (n) {
|
|
451
|
-
|
|
451
|
+
throw Object.assign(new Error("vf-clamp: OS/2 and macStyle update failed"), { cause: n });
|
|
452
452
|
} finally {
|
|
453
453
|
try {
|
|
454
454
|
a.delete();
|
|
@@ -460,17 +460,17 @@ async function q(t) {
|
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
}
|
|
463
|
-
async function
|
|
464
|
-
const a = await
|
|
463
|
+
async function Q(t, o) {
|
|
464
|
+
const a = await d(), e = new x({ pyodide: a }), n = new x({ pyodide: a });
|
|
465
465
|
try {
|
|
466
466
|
await e.upload(t);
|
|
467
467
|
const i = /* @__PURE__ */ new Map([
|
|
468
468
|
["input-file", e.filename],
|
|
469
469
|
["output-file", n.filename]
|
|
470
470
|
]);
|
|
471
|
-
return (await
|
|
471
|
+
return (await z())(i, String(o)), n.download();
|
|
472
472
|
} catch (i) {
|
|
473
|
-
|
|
473
|
+
throw Object.assign(new Error("vf-clamp: wght normalisation failed"), { cause: i });
|
|
474
474
|
} finally {
|
|
475
475
|
try {
|
|
476
476
|
e.delete();
|
|
@@ -482,57 +482,60 @@ async function G(t, o) {
|
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
484
|
}
|
|
485
|
-
function
|
|
485
|
+
function X(t, o) {
|
|
486
486
|
const a = new Map(o.map((i) => [i.name, i])), e = {};
|
|
487
487
|
for (const i of t) {
|
|
488
|
-
const
|
|
489
|
-
if (!
|
|
490
|
-
for (const [
|
|
491
|
-
e[
|
|
488
|
+
const s = a.get(i);
|
|
489
|
+
if (!s) throw new Error(`Named instance "${i}" not found in font`);
|
|
490
|
+
for (const [f, c] of Object.entries(s.coordinates))
|
|
491
|
+
e[f] ? (e[f].min = Math.min(e[f].min, c), e[f].max = Math.max(e[f].max, c)) : e[f] = { min: c, max: c };
|
|
492
492
|
}
|
|
493
493
|
const n = {};
|
|
494
|
-
for (const [i, { min:
|
|
495
|
-
n[i] =
|
|
494
|
+
for (const [i, { min: s, max: f }] of Object.entries(e))
|
|
495
|
+
n[i] = s === f ? s : { min: s, max: f };
|
|
496
496
|
return n;
|
|
497
497
|
}
|
|
498
|
-
|
|
499
|
-
|
|
498
|
+
function Y(t) {
|
|
499
|
+
return t.length >= 4 && t[0] === 79 && t[1] === 84 && t[2] === 84 && t[3] === 79;
|
|
500
|
+
}
|
|
501
|
+
async function at(t, o) {
|
|
502
|
+
var f, c, w;
|
|
500
503
|
if (o.outputs.length === 0) return [];
|
|
501
|
-
const a = t instanceof ArrayBuffer ? new Uint8Array(t) : t, e = o.format ?? "ttf";
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (d.default < _.min || d.default > _.max) {
|
|
519
|
-
const S = Math.max(_.min, Math.min(_.max, d.default));
|
|
504
|
+
const a = t instanceof ArrayBuffer ? new Uint8Array(t) : t, e = o.format ?? "ttf", { instances: n, axes: i } = await E(a), s = [];
|
|
505
|
+
for (const m of o.outputs) {
|
|
506
|
+
let _ = {};
|
|
507
|
+
if ((f = m.instances) != null && f.length && (_ = X(m.instances, n)), m.axes && (_ = { ..._, ...m.axes }), o.strict && ((c = m.instances) != null && c.length)) {
|
|
508
|
+
const p = new Set(m.instances), g = n.filter((h) => !p.has(h.name) && i.every((y) => {
|
|
509
|
+
const A = h.coordinates[y.tag] ?? y.default, v = _[y.tag];
|
|
510
|
+
return v == null ? !0 : typeof v == "number" ? A === v : A >= v.min && A <= v.max;
|
|
511
|
+
})).map((h) => h.name);
|
|
512
|
+
if (g.length)
|
|
513
|
+
throw new Error(`vf-clamp: output "${m.name || m.instances.join(", ")}" would include unselected instances (${g.join(", ")}); use planOutputs() to split the selection`);
|
|
514
|
+
}
|
|
515
|
+
for (const p of i) {
|
|
516
|
+
const g = _[p.tag];
|
|
517
|
+
if (g != null && typeof g == "object") {
|
|
518
|
+
const h = g;
|
|
519
|
+
if (p.default < h.min || p.default > h.max) {
|
|
520
|
+
const y = Math.max(h.min, Math.min(h.max, p.default));
|
|
520
521
|
console.warn(
|
|
521
|
-
`vf-clamp: axis "${
|
|
522
|
+
`vf-clamp: axis "${p.tag}" default (${p.default}) is outside restricted range [${h.min}, ${h.max}] — will be clamped to ${y}`
|
|
522
523
|
);
|
|
523
524
|
}
|
|
524
525
|
}
|
|
525
526
|
}
|
|
526
|
-
const
|
|
527
|
-
for (const [
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
let
|
|
531
|
-
|
|
527
|
+
const r = {};
|
|
528
|
+
for (const [p, g] of Object.entries(_))
|
|
529
|
+
g !== null && (r[p] = H(g));
|
|
530
|
+
const u = m.name || ((w = m.instances) != null && w.length ? m.instances.length === 1 ? m.instances[0] : `${m.instances[0]}-${m.instances[m.instances.length - 1]}` : "output");
|
|
531
|
+
let l = await J(a, r);
|
|
532
|
+
if (e === "otf" && !Y(l))
|
|
533
|
+
throw new Error("vf-clamp: format 'otf' needs a CFF/CFF2 source font; this font has TrueType outlines — use 'ttf'");
|
|
534
|
+
l = await Z(l), o.normalizeWeightAxis && (l = await Q(l, 100)), l = await K(l), l = await G(l, u), e === "woff2" ? l = await $(l) : e === "woff" && (l = await L(l)), s.push({ name: u, buffer: l, format: e });
|
|
532
535
|
}
|
|
533
|
-
return
|
|
536
|
+
return s;
|
|
534
537
|
}
|
|
535
|
-
function
|
|
538
|
+
function tt(t, o) {
|
|
536
539
|
if (t === o) return t;
|
|
537
540
|
const a = t.split(" "), e = o.split(" ");
|
|
538
541
|
let n = 0;
|
|
@@ -541,13 +544,75 @@ function K(t, o) {
|
|
|
541
544
|
let i = 0;
|
|
542
545
|
for (; i < a.length - n && i < e.length - n && a[a.length - 1 - i] === e[e.length - 1 - i]; )
|
|
543
546
|
i++;
|
|
544
|
-
const
|
|
545
|
-
return [
|
|
547
|
+
const s = a.slice(0, n).join(" "), f = a.slice(n, a.length - (i || 0)).join(" "), c = e.slice(n, e.length - (i || 0)).join(" "), w = i > 0 ? a.slice(a.length - i).join(" ") : "", m = f && c ? `${f}-${c}` : f || c;
|
|
548
|
+
return [s, m, w].filter(Boolean).join(" ");
|
|
549
|
+
}
|
|
550
|
+
function O(t, o) {
|
|
551
|
+
const a = {};
|
|
552
|
+
for (const e of o) {
|
|
553
|
+
const n = t.map((i) => i.coordinates[e.tag] ?? e.default);
|
|
554
|
+
a[e.tag] = { min: Math.min(...n), max: Math.max(...n) };
|
|
555
|
+
}
|
|
556
|
+
return a;
|
|
557
|
+
}
|
|
558
|
+
function N(t, o, a, e) {
|
|
559
|
+
return o.filter((n) => a.has(n.name) ? !1 : e.every((i) => {
|
|
560
|
+
const s = n.coordinates[i.tag] ?? i.default, f = t[i.tag];
|
|
561
|
+
return !f || s >= f.min && s <= f.max;
|
|
562
|
+
}));
|
|
563
|
+
}
|
|
564
|
+
function et(t) {
|
|
565
|
+
return t.reduce((o, a) => !o || a.maximum - a.minimum > o.maximum - o.minimum ? a : o, void 0);
|
|
566
|
+
}
|
|
567
|
+
function it(t, o, a) {
|
|
568
|
+
const { axes: e, instances: n } = t, i = new Map(n.map((r) => [r.name, r])), s = [];
|
|
569
|
+
for (const r of new Set(o)) {
|
|
570
|
+
const u = i.get(r);
|
|
571
|
+
if (!u) throw new Error(`Named instance "${r}" not found in font`);
|
|
572
|
+
s.push(u);
|
|
573
|
+
}
|
|
574
|
+
if (!s.length) return [];
|
|
575
|
+
const f = new Set(s.map((r) => r.name));
|
|
576
|
+
let c;
|
|
577
|
+
if (!N(O(s, e), n, f, e).length)
|
|
578
|
+
c = [s];
|
|
579
|
+
else {
|
|
580
|
+
c = s.map((u) => [u]);
|
|
581
|
+
let r = !0;
|
|
582
|
+
for (; r; ) {
|
|
583
|
+
r = !1;
|
|
584
|
+
t: for (let u = 0; u < c.length; u++)
|
|
585
|
+
for (let l = u + 1; l < c.length; l++) {
|
|
586
|
+
const p = [...c[u], ...c[l]];
|
|
587
|
+
if (!N(O(p, e), n, f, e).length) {
|
|
588
|
+
c[u] = p, c.splice(l, 1), r = !0;
|
|
589
|
+
break t;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const w = et(e), m = w ? [w, ...e.filter((r) => r.tag !== w.tag)] : e, _ = (r, u) => {
|
|
595
|
+
for (const l of m) {
|
|
596
|
+
const p = (r.coordinates[l.tag] ?? l.default) - (u.coordinates[l.tag] ?? l.default);
|
|
597
|
+
if (p) return p;
|
|
598
|
+
}
|
|
599
|
+
return 0;
|
|
600
|
+
};
|
|
601
|
+
return c = c.map((r) => [...r].sort(_)).sort((r, u) => _(r[0], u[0])), c.map((r) => {
|
|
602
|
+
const u = r[0], l = r[r.length - 1], p = r.length === 1 ? u.name : tt(u.name, l.name);
|
|
603
|
+
return { name: a ? `${a} ${p}` : p, instances: r.map((g) => g.name) };
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
function ot(t, o) {
|
|
607
|
+
const a = new Map(t.instances.map((n) => [n.name, n])), e = o.map((n) => a.get(n)).filter((n) => !!n);
|
|
608
|
+
return e.length ? N(O(e, t.axes), t.instances, new Set(o), t.axes).map((n) => n.name) : [];
|
|
546
609
|
}
|
|
547
610
|
export {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
611
|
+
at as clampFont,
|
|
612
|
+
tt as compactName,
|
|
613
|
+
L as convertToWoff,
|
|
614
|
+
$ as convertToWoff2,
|
|
615
|
+
E as getInstances,
|
|
616
|
+
it as planOutputs,
|
|
617
|
+
ot as unboughtInstances
|
|
553
618
|
};
|