@liiift-studio/sanity-font-manager 2.3.19 → 2.4.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 +437 -437
- package/dist/index.js +103 -48
- package/dist/index.mjs +103 -48
- package/package.json +85 -85
- package/src/components/BatchUploadFonts.jsx +640 -639
- package/src/components/FontScriptUploaderComponent.jsx +463 -463
- package/src/components/GenerateCollectionsPairsComponent.jsx +259 -259
- package/src/components/KeyValueInput.jsx +95 -95
- package/src/components/KeyValueReferenceInput.jsx +254 -254
- package/src/components/NestedObjectArraySelector.jsx +146 -146
- package/src/components/PriceInput.jsx +26 -26
- package/src/components/PrimaryCollectionGeneratorTypeface.jsx +116 -116
- package/src/components/RegenerateSubfamiliesComponent.jsx +185 -185
- package/src/components/SetOTF.jsx +87 -87
- package/src/components/SingleUploaderTool.jsx +673 -673
- package/src/components/StatusDisplay.jsx +26 -26
- package/src/components/StyleCountInput.jsx +16 -16
- package/src/components/UpdateScriptsComponent.jsx +76 -76
- package/src/components/UploadButton.jsx +43 -43
- package/src/components/UploadScriptsComponent.jsx +537 -537
- package/src/components/VariableInstanceReferencesInput.jsx +190 -190
- package/src/hooks/useNestedObjects.js +92 -92
- package/src/hooks/useSanityClient.js +9 -9
- package/src/index.js +70 -70
- package/src/schema/openTypeField.js +1945 -1945
- package/src/schema/styleCountField.js +12 -12
- package/src/schema/stylesField.js +268 -268
- package/src/schema/stylisticSetField.js +301 -301
- package/src/utils/generateCssFile.js +205 -205
- package/src/utils/generateFontData.js +145 -145
- package/src/utils/generateFontFile.js +38 -38
- package/src/utils/generateKeywords.js +185 -185
- package/src/utils/generateSubset.js +45 -45
- package/src/utils/getEmptyFontKit.js +99 -99
- package/src/utils/parseVariableFontInstances.js +211 -211
- package/src/utils/processFontFiles.js +487 -477
- package/src/utils/regenerateFontData.js +146 -146
- package/src/utils/sanitizeForSanityId.js +65 -65
- package/src/utils/updateFontPrices.js +94 -94
- package/src/utils/updateTypefaceDocument.js +149 -160
- package/src/utils/uploadFontFiles.js +115 -26
- package/src/utils/utils.js +24 -24
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
// Builds weight and italic keyword lists with abbreviation expansions for parsing font subfamily names
|
|
2
|
-
|
|
3
|
-
const coreWeights = ["Hairline", "ExtraThin", "Thin", "Mager", "Maigre", "ExtraLight", "Light", "Chiaro", "Lite", "Leicht", "Demi", "Book", "Buch", "Regular", "Normal", "Medium", "Stark", "Thick", "Kräftig", "Viertelfett", "Halbfett", "Dreiviertelfett", "Dark", "Bold", "Neretto", "Gras", "Fett", "Extrafett", "Black", "Nero", "Heavy", "Nerissimo", "Ultra", "Fat", "Poster"];
|
|
4
|
-
const modifiers = ["Demi", "Semi", "Extra", "Ultra", "Super", "Plus"];
|
|
5
|
-
|
|
6
|
-
const coreItalics = ["Italic", "Slant", "Oblique", "Cursive", "Rotalic", "Reverse", "Crab Claw", "Crabclaw", "South Paw", "Southpaw", "Backwards", "Backslant", "Backslanted", "Back Slant"];
|
|
7
|
-
|
|
8
|
-
/** All known abbreviation-to-canonical-name mappings, sorted alphabetically */
|
|
9
|
-
const alternativeSpelling = {
|
|
10
|
-
Backslant: ["Bsl"],
|
|
11
|
-
Backwards: ["Bck"],
|
|
12
|
-
Black: ["Blak", "Blk"],
|
|
13
|
-
Bold: ["Bd", "Bld"], // B omitted — too ambiguous
|
|
14
|
-
Condensed: ["Cond", "Cnd"],
|
|
15
|
-
Crabclaw: ["Crab", "Claw"],
|
|
16
|
-
Cursive: ["Cur"],
|
|
17
|
-
Dark: ["Drk"],
|
|
18
|
-
Expanded: ["Exp"],
|
|
19
|
-
Extra: ["Xt", "Xtra", "Xtr", "X"], // X omitted as standalone — too ambiguous
|
|
20
|
-
ExtraBlack: ["Xblk", "XBlk", "Xblck", "XBlck"],
|
|
21
|
-
ExtraBold: ["Xbd", "XBd", "Xbld", "XBld", "Xbold", "XBold", "ExBold", "Exbold", "Exbd", "ExBd", "Exbld", "ExBld"],
|
|
22
|
-
ExtraCondensed: ["XCond", "Xcnd"],
|
|
23
|
-
ExtraExpanded: ["XExp"],
|
|
24
|
-
ExtraLight: ["Xlight", "XLight", "Xlt", "XLt", "Xlgt", "XLgt", "Xl", "XL", "Xlght", "XLght"],
|
|
25
|
-
ExtraThin: ["Xthin", "Xthn", "Xth", "XThin", "XThn", "XTh", "XT"],
|
|
26
|
-
Extended: ["Ext"],
|
|
27
|
-
Hairline: ["Hl", "Hln", "Hlnn", "Hlnne", "Hlnnne"],
|
|
28
|
-
Italic: ["Ital", "It"],
|
|
29
|
-
Light: ["Lt", "Lght"],
|
|
30
|
-
Medium: ["Med", "Md", "md", "med"],
|
|
31
|
-
Oblique: ["Obl"],
|
|
32
|
-
Plus: ["Pls"],
|
|
33
|
-
Regular: ["Reg", "Rg"],
|
|
34
|
-
Reverse: ["Rev"],
|
|
35
|
-
Rotalic: ["Rot"],
|
|
36
|
-
SemiBold: ["SmBd", "Sb", "Sbd", "Sbld", "Sbold", "Semibd", "SemiBd", "Semibld", "SemiBld", "semiBd", "semiBld"],
|
|
37
|
-
Slant: ["Sl"],
|
|
38
|
-
Southpaw: ["South", "Paw"],
|
|
39
|
-
Super: ["Supr"],
|
|
40
|
-
Thin: ["Thn"],
|
|
41
|
-
Ultra: ["Ult", "Ultre", "Ul", "Ulta"],
|
|
42
|
-
XX: ["XXt", "XXtra", "XXtr", "XX"],
|
|
43
|
-
XXBlack: ["XXblk", "XXBlk", "XXblck", "XXBlck"],
|
|
44
|
-
XXLight: ["XXlight", "XXLight", "XXlt", "XXLt", "XXlgt", "XXLgt", "XXl", "XXL", "XXlght", "XXLght"],
|
|
45
|
-
XXX: ["XXXt", "XXXtra", "XXXtr", "XXX"],
|
|
46
|
-
XXXLight: ["XXXlight", "XXXLight", "XXXlt", "XXXLt", "XXXlgt", "XXXLgt", "XXXl", "XXXL", "XXXlght", "XXXLght"],
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/** Maps an abbreviated font name word back to its canonical weight/style name */
|
|
50
|
-
export function reverseSpellingLookup(str) {
|
|
51
|
-
// Exact match first to avoid partial collisions
|
|
52
|
-
let exactMatch = "";
|
|
53
|
-
Object.keys(alternativeSpelling).forEach(function (key) {
|
|
54
|
-
alternativeSpelling[key].forEach(function (alternative) {
|
|
55
|
-
if (str === alternative) {
|
|
56
|
-
exactMatch = key;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
if (exactMatch) return exactMatch;
|
|
61
|
-
|
|
62
|
-
// Fall back to longest word-boundary match
|
|
63
|
-
let result = "";
|
|
64
|
-
let longestMatch = 0;
|
|
65
|
-
Object.keys(alternativeSpelling).forEach(function (key) {
|
|
66
|
-
alternativeSpelling[key].forEach(function (alternative) {
|
|
67
|
-
const regex = new RegExp(`\\b${alternative}\\b`);
|
|
68
|
-
if (regex.test(str) && alternative.length > longestMatch) {
|
|
69
|
-
result = key;
|
|
70
|
-
longestMatch = alternative.length;
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Expands each word in a string from abbreviation to its canonical weight/style name */
|
|
78
|
-
export function expandAbbreviations(str) {
|
|
79
|
-
if (!str) return str;
|
|
80
|
-
return str.split(' ')
|
|
81
|
-
.map(word => {
|
|
82
|
-
const expanded = reverseSpellingLookup(word);
|
|
83
|
-
return expanded || word;
|
|
84
|
-
})
|
|
85
|
-
.join(' ');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Removes weight and style keywords from a string, returning only the remainder */
|
|
89
|
-
export function removeWeightNames(str) {
|
|
90
|
-
if (!str) return str;
|
|
91
|
-
return str.split(' ')
|
|
92
|
-
.map(word => {
|
|
93
|
-
coreWeights.forEach((weight) => {
|
|
94
|
-
if (word === weight) word = "";
|
|
95
|
-
modifiers.forEach((modifier) => {
|
|
96
|
-
if (word === modifier || modifier + weight === word) word = "";
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
const expanded = reverseSpellingLookup(word);
|
|
100
|
-
if (expanded) return "";
|
|
101
|
-
return word;
|
|
102
|
-
})
|
|
103
|
-
.join(' ')
|
|
104
|
-
.trim();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** Generates comprehensive weight and italic keyword lists including all alternative spellings */
|
|
108
|
-
export function generateStyleKeywords() {
|
|
109
|
-
let weightKeywordList = [];
|
|
110
|
-
let italicKeywordList = [];
|
|
111
|
-
|
|
112
|
-
// Start with all core weights
|
|
113
|
-
weightKeywordList = [...coreWeights];
|
|
114
|
-
|
|
115
|
-
// Add all modifier + weight combinations
|
|
116
|
-
modifiers.forEach(modifier => {
|
|
117
|
-
coreWeights.forEach(weight => {
|
|
118
|
-
weightKeywordList.push(modifier + weight);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Add standalone modifiers
|
|
123
|
-
weightKeywordList = [...weightKeywordList, ...modifiers];
|
|
124
|
-
|
|
125
|
-
// Set up italic keywords
|
|
126
|
-
italicKeywordList = [...coreItalics];
|
|
127
|
-
|
|
128
|
-
// Expand weight list with alternative spellings
|
|
129
|
-
weightKeywordList = weightKeywordList.map(function (el) {
|
|
130
|
-
var newEls = [];
|
|
131
|
-
Object.keys(alternativeSpelling).forEach(function (key) {
|
|
132
|
-
if (el.indexOf(key) !== -1) {
|
|
133
|
-
alternativeSpelling[key].forEach(function (alternative) {
|
|
134
|
-
let newSpelling = el.replace(key, alternative);
|
|
135
|
-
newEls.push(newSpelling);
|
|
136
|
-
Object.keys(alternativeSpelling).forEach(function (key2) {
|
|
137
|
-
if (newSpelling.indexOf(key2) !== -1) {
|
|
138
|
-
alternativeSpelling[key2].forEach(function (alternative2) {
|
|
139
|
-
let newSpelling2 = newSpelling.replace(key2, alternative2);
|
|
140
|
-
newEls.push(newSpelling2);
|
|
141
|
-
Object.keys(alternativeSpelling).forEach(function (key3) {
|
|
142
|
-
if (newSpelling2.indexOf(key3) !== -1) {
|
|
143
|
-
alternativeSpelling[key3].forEach(function (alternative3) {
|
|
144
|
-
newEls.push(newSpelling2.replace(key3, alternative3));
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
newEls.push(el);
|
|
155
|
-
return newEls;
|
|
156
|
-
}).reduce(function (a, b) {
|
|
157
|
-
return a.concat(b);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Expand italic list with alternative spellings
|
|
161
|
-
italicKeywordList = italicKeywordList.map(function (el) {
|
|
162
|
-
var newEls = [];
|
|
163
|
-
Object.keys(alternativeSpelling).forEach(function (key) {
|
|
164
|
-
if (el.indexOf(key) !== -1) {
|
|
165
|
-
alternativeSpelling[key].forEach(function (alternative) {
|
|
166
|
-
newEls.push(el.replace(key, alternative));
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
newEls.push(el);
|
|
171
|
-
return newEls;
|
|
172
|
-
}).reduce(function (a, b) {
|
|
173
|
-
return a.concat(b);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Sort longest to shortest so more specific matches win
|
|
177
|
-
weightKeywordList = weightKeywordList.sort((a, b) => b.length - a.length);
|
|
178
|
-
italicKeywordList = italicKeywordList.sort((a, b) => b.length - a.length);
|
|
179
|
-
|
|
180
|
-
// Deduplicate
|
|
181
|
-
weightKeywordList = weightKeywordList.filter((item, pos) => weightKeywordList.indexOf(item) === pos);
|
|
182
|
-
italicKeywordList = italicKeywordList.filter((item, pos) => italicKeywordList.indexOf(item) === pos);
|
|
183
|
-
|
|
184
|
-
return { weightKeywordList, italicKeywordList };
|
|
185
|
-
}
|
|
1
|
+
// Builds weight and italic keyword lists with abbreviation expansions for parsing font subfamily names
|
|
2
|
+
|
|
3
|
+
const coreWeights = ["Hairline", "ExtraThin", "Thin", "Mager", "Maigre", "ExtraLight", "Light", "Chiaro", "Lite", "Leicht", "Demi", "Book", "Buch", "Regular", "Normal", "Medium", "Stark", "Thick", "Kräftig", "Viertelfett", "Halbfett", "Dreiviertelfett", "Dark", "Bold", "Neretto", "Gras", "Fett", "Extrafett", "Black", "Nero", "Heavy", "Nerissimo", "Ultra", "Fat", "Poster"];
|
|
4
|
+
const modifiers = ["Demi", "Semi", "Extra", "Ultra", "Super", "Plus"];
|
|
5
|
+
|
|
6
|
+
const coreItalics = ["Italic", "Slant", "Oblique", "Cursive", "Rotalic", "Reverse", "Crab Claw", "Crabclaw", "South Paw", "Southpaw", "Backwards", "Backslant", "Backslanted", "Back Slant"];
|
|
7
|
+
|
|
8
|
+
/** All known abbreviation-to-canonical-name mappings, sorted alphabetically */
|
|
9
|
+
const alternativeSpelling = {
|
|
10
|
+
Backslant: ["Bsl"],
|
|
11
|
+
Backwards: ["Bck"],
|
|
12
|
+
Black: ["Blak", "Blk"],
|
|
13
|
+
Bold: ["Bd", "Bld"], // B omitted — too ambiguous
|
|
14
|
+
Condensed: ["Cond", "Cnd"],
|
|
15
|
+
Crabclaw: ["Crab", "Claw"],
|
|
16
|
+
Cursive: ["Cur"],
|
|
17
|
+
Dark: ["Drk"],
|
|
18
|
+
Expanded: ["Exp"],
|
|
19
|
+
Extra: ["Xt", "Xtra", "Xtr", "X"], // X omitted as standalone — too ambiguous
|
|
20
|
+
ExtraBlack: ["Xblk", "XBlk", "Xblck", "XBlck"],
|
|
21
|
+
ExtraBold: ["Xbd", "XBd", "Xbld", "XBld", "Xbold", "XBold", "ExBold", "Exbold", "Exbd", "ExBd", "Exbld", "ExBld"],
|
|
22
|
+
ExtraCondensed: ["XCond", "Xcnd"],
|
|
23
|
+
ExtraExpanded: ["XExp"],
|
|
24
|
+
ExtraLight: ["Xlight", "XLight", "Xlt", "XLt", "Xlgt", "XLgt", "Xl", "XL", "Xlght", "XLght"],
|
|
25
|
+
ExtraThin: ["Xthin", "Xthn", "Xth", "XThin", "XThn", "XTh", "XT"],
|
|
26
|
+
Extended: ["Ext"],
|
|
27
|
+
Hairline: ["Hl", "Hln", "Hlnn", "Hlnne", "Hlnnne"],
|
|
28
|
+
Italic: ["Ital", "It"],
|
|
29
|
+
Light: ["Lt", "Lght"],
|
|
30
|
+
Medium: ["Med", "Md", "md", "med"],
|
|
31
|
+
Oblique: ["Obl"],
|
|
32
|
+
Plus: ["Pls"],
|
|
33
|
+
Regular: ["Reg", "Rg"],
|
|
34
|
+
Reverse: ["Rev"],
|
|
35
|
+
Rotalic: ["Rot"],
|
|
36
|
+
SemiBold: ["SmBd", "Sb", "Sbd", "Sbld", "Sbold", "Semibd", "SemiBd", "Semibld", "SemiBld", "semiBd", "semiBld"],
|
|
37
|
+
Slant: ["Sl"],
|
|
38
|
+
Southpaw: ["South", "Paw"],
|
|
39
|
+
Super: ["Supr"],
|
|
40
|
+
Thin: ["Thn"],
|
|
41
|
+
Ultra: ["Ult", "Ultre", "Ul", "Ulta"],
|
|
42
|
+
XX: ["XXt", "XXtra", "XXtr", "XX"],
|
|
43
|
+
XXBlack: ["XXblk", "XXBlk", "XXblck", "XXBlck"],
|
|
44
|
+
XXLight: ["XXlight", "XXLight", "XXlt", "XXLt", "XXlgt", "XXLgt", "XXl", "XXL", "XXlght", "XXLght"],
|
|
45
|
+
XXX: ["XXXt", "XXXtra", "XXXtr", "XXX"],
|
|
46
|
+
XXXLight: ["XXXlight", "XXXLight", "XXXlt", "XXXLt", "XXXlgt", "XXXLgt", "XXXl", "XXXL", "XXXlght", "XXXLght"],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Maps an abbreviated font name word back to its canonical weight/style name */
|
|
50
|
+
export function reverseSpellingLookup(str) {
|
|
51
|
+
// Exact match first to avoid partial collisions
|
|
52
|
+
let exactMatch = "";
|
|
53
|
+
Object.keys(alternativeSpelling).forEach(function (key) {
|
|
54
|
+
alternativeSpelling[key].forEach(function (alternative) {
|
|
55
|
+
if (str === alternative) {
|
|
56
|
+
exactMatch = key;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
if (exactMatch) return exactMatch;
|
|
61
|
+
|
|
62
|
+
// Fall back to longest word-boundary match
|
|
63
|
+
let result = "";
|
|
64
|
+
let longestMatch = 0;
|
|
65
|
+
Object.keys(alternativeSpelling).forEach(function (key) {
|
|
66
|
+
alternativeSpelling[key].forEach(function (alternative) {
|
|
67
|
+
const regex = new RegExp(`\\b${alternative}\\b`);
|
|
68
|
+
if (regex.test(str) && alternative.length > longestMatch) {
|
|
69
|
+
result = key;
|
|
70
|
+
longestMatch = alternative.length;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Expands each word in a string from abbreviation to its canonical weight/style name */
|
|
78
|
+
export function expandAbbreviations(str) {
|
|
79
|
+
if (!str) return str;
|
|
80
|
+
return str.split(' ')
|
|
81
|
+
.map(word => {
|
|
82
|
+
const expanded = reverseSpellingLookup(word);
|
|
83
|
+
return expanded || word;
|
|
84
|
+
})
|
|
85
|
+
.join(' ');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Removes weight and style keywords from a string, returning only the remainder */
|
|
89
|
+
export function removeWeightNames(str) {
|
|
90
|
+
if (!str) return str;
|
|
91
|
+
return str.split(' ')
|
|
92
|
+
.map(word => {
|
|
93
|
+
coreWeights.forEach((weight) => {
|
|
94
|
+
if (word === weight) word = "";
|
|
95
|
+
modifiers.forEach((modifier) => {
|
|
96
|
+
if (word === modifier || modifier + weight === word) word = "";
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
const expanded = reverseSpellingLookup(word);
|
|
100
|
+
if (expanded) return "";
|
|
101
|
+
return word;
|
|
102
|
+
})
|
|
103
|
+
.join(' ')
|
|
104
|
+
.trim();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Generates comprehensive weight and italic keyword lists including all alternative spellings */
|
|
108
|
+
export function generateStyleKeywords() {
|
|
109
|
+
let weightKeywordList = [];
|
|
110
|
+
let italicKeywordList = [];
|
|
111
|
+
|
|
112
|
+
// Start with all core weights
|
|
113
|
+
weightKeywordList = [...coreWeights];
|
|
114
|
+
|
|
115
|
+
// Add all modifier + weight combinations
|
|
116
|
+
modifiers.forEach(modifier => {
|
|
117
|
+
coreWeights.forEach(weight => {
|
|
118
|
+
weightKeywordList.push(modifier + weight);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Add standalone modifiers
|
|
123
|
+
weightKeywordList = [...weightKeywordList, ...modifiers];
|
|
124
|
+
|
|
125
|
+
// Set up italic keywords
|
|
126
|
+
italicKeywordList = [...coreItalics];
|
|
127
|
+
|
|
128
|
+
// Expand weight list with alternative spellings
|
|
129
|
+
weightKeywordList = weightKeywordList.map(function (el) {
|
|
130
|
+
var newEls = [];
|
|
131
|
+
Object.keys(alternativeSpelling).forEach(function (key) {
|
|
132
|
+
if (el.indexOf(key) !== -1) {
|
|
133
|
+
alternativeSpelling[key].forEach(function (alternative) {
|
|
134
|
+
let newSpelling = el.replace(key, alternative);
|
|
135
|
+
newEls.push(newSpelling);
|
|
136
|
+
Object.keys(alternativeSpelling).forEach(function (key2) {
|
|
137
|
+
if (newSpelling.indexOf(key2) !== -1) {
|
|
138
|
+
alternativeSpelling[key2].forEach(function (alternative2) {
|
|
139
|
+
let newSpelling2 = newSpelling.replace(key2, alternative2);
|
|
140
|
+
newEls.push(newSpelling2);
|
|
141
|
+
Object.keys(alternativeSpelling).forEach(function (key3) {
|
|
142
|
+
if (newSpelling2.indexOf(key3) !== -1) {
|
|
143
|
+
alternativeSpelling[key3].forEach(function (alternative3) {
|
|
144
|
+
newEls.push(newSpelling2.replace(key3, alternative3));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
newEls.push(el);
|
|
155
|
+
return newEls;
|
|
156
|
+
}).reduce(function (a, b) {
|
|
157
|
+
return a.concat(b);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Expand italic list with alternative spellings
|
|
161
|
+
italicKeywordList = italicKeywordList.map(function (el) {
|
|
162
|
+
var newEls = [];
|
|
163
|
+
Object.keys(alternativeSpelling).forEach(function (key) {
|
|
164
|
+
if (el.indexOf(key) !== -1) {
|
|
165
|
+
alternativeSpelling[key].forEach(function (alternative) {
|
|
166
|
+
newEls.push(el.replace(key, alternative));
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
newEls.push(el);
|
|
171
|
+
return newEls;
|
|
172
|
+
}).reduce(function (a, b) {
|
|
173
|
+
return a.concat(b);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Sort longest to shortest so more specific matches win
|
|
177
|
+
weightKeywordList = weightKeywordList.sort((a, b) => b.length - a.length);
|
|
178
|
+
italicKeywordList = italicKeywordList.sort((a, b) => b.length - a.length);
|
|
179
|
+
|
|
180
|
+
// Deduplicate
|
|
181
|
+
weightKeywordList = weightKeywordList.filter((item, pos) => weightKeywordList.indexOf(item) === pos);
|
|
182
|
+
italicKeywordList = italicKeywordList.filter((item, pos) => italicKeywordList.indexOf(item) === pos);
|
|
183
|
+
|
|
184
|
+
return { weightKeywordList, italicKeywordList };
|
|
185
|
+
}
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
// Requests DS-WEB fingerprinted WOFF2 and display subset generation from an existing WOFF2 via fontWorker
|
|
2
|
-
// The server subsets the WOFF2 directly — no TTF conversion involved.
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Calls fontWorker to generate a display subset WOFF2 and subset CSS from an existing WOFF2 URL.
|
|
6
|
-
* Patches fileInput.woff2_subset and fileInput.css_subset on the Sanity document directly.
|
|
7
|
-
* @param {object} params
|
|
8
|
-
* @param {string} params.woff2Url - CDN URL of the existing WOFF2 to subset
|
|
9
|
-
* @param {string} params.filename - base filename (no extension) for the generated assets
|
|
10
|
-
* @param {string} params.documentId
|
|
11
|
-
* @param {string} params.documentTitle
|
|
12
|
-
* @param {boolean} params.documentVariableFont
|
|
13
|
-
* @param {string} params.documentStyle
|
|
14
|
-
* @param {string} params.documentWeight
|
|
15
|
-
* @returns {Promise<number>} 1 on success, -1 on network error
|
|
16
|
-
*/
|
|
17
|
-
export default async function generateSubset({
|
|
18
|
-
woff2Url,
|
|
19
|
-
filename,
|
|
20
|
-
documentId,
|
|
21
|
-
documentTitle,
|
|
22
|
-
documentVariableFont,
|
|
23
|
-
documentStyle,
|
|
24
|
-
documentWeight,
|
|
25
|
-
}) {
|
|
26
|
-
await fetch(`${process.env.SANITY_STUDIO_SITE_URL}/api/sanity/fontWorker`, {
|
|
27
|
-
method: 'POST',
|
|
28
|
-
mode: 'no-cors',
|
|
29
|
-
headers: { 'Content-Type': 'application/json' },
|
|
30
|
-
body: JSON.stringify({
|
|
31
|
-
code: 'generate-subset',
|
|
32
|
-
woff2Url,
|
|
33
|
-
filename,
|
|
34
|
-
documentId,
|
|
35
|
-
documentTitle,
|
|
36
|
-
documentVariableFont,
|
|
37
|
-
documentStyle,
|
|
38
|
-
documentWeight,
|
|
39
|
-
})
|
|
40
|
-
}).catch(e => {
|
|
41
|
-
console.warn('Subset generation call failed:', e.message);
|
|
42
|
-
return -1;
|
|
43
|
-
});
|
|
44
|
-
return 1;
|
|
45
|
-
}
|
|
1
|
+
// Requests DS-WEB fingerprinted WOFF2 and display subset generation from an existing WOFF2 via fontWorker
|
|
2
|
+
// The server subsets the WOFF2 directly — no TTF conversion involved.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Calls fontWorker to generate a display subset WOFF2 and subset CSS from an existing WOFF2 URL.
|
|
6
|
+
* Patches fileInput.woff2_subset and fileInput.css_subset on the Sanity document directly.
|
|
7
|
+
* @param {object} params
|
|
8
|
+
* @param {string} params.woff2Url - CDN URL of the existing WOFF2 to subset
|
|
9
|
+
* @param {string} params.filename - base filename (no extension) for the generated assets
|
|
10
|
+
* @param {string} params.documentId
|
|
11
|
+
* @param {string} params.documentTitle
|
|
12
|
+
* @param {boolean} params.documentVariableFont
|
|
13
|
+
* @param {string} params.documentStyle
|
|
14
|
+
* @param {string} params.documentWeight
|
|
15
|
+
* @returns {Promise<number>} 1 on success, -1 on network error
|
|
16
|
+
*/
|
|
17
|
+
export default async function generateSubset({
|
|
18
|
+
woff2Url,
|
|
19
|
+
filename,
|
|
20
|
+
documentId,
|
|
21
|
+
documentTitle,
|
|
22
|
+
documentVariableFont,
|
|
23
|
+
documentStyle,
|
|
24
|
+
documentWeight,
|
|
25
|
+
}) {
|
|
26
|
+
await fetch(`${process.env.SANITY_STUDIO_SITE_URL}/api/sanity/fontWorker`, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
mode: 'no-cors',
|
|
29
|
+
headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
code: 'generate-subset',
|
|
32
|
+
woff2Url,
|
|
33
|
+
filename,
|
|
34
|
+
documentId,
|
|
35
|
+
documentTitle,
|
|
36
|
+
documentVariableFont,
|
|
37
|
+
documentStyle,
|
|
38
|
+
documentWeight,
|
|
39
|
+
})
|
|
40
|
+
}).catch(e => {
|
|
41
|
+
console.warn('Subset generation call failed:', e.message);
|
|
42
|
+
return -1;
|
|
43
|
+
});
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
// Returns a zeroed-out fontkit-shaped placeholder object used when no font binary is available
|
|
2
|
-
import * as fontkit from 'fontkit';
|
|
3
|
-
import slugify from 'slugify';
|
|
4
|
-
|
|
5
|
-
/** Reads font files and returns name/subfamily metadata without writing to Sanity */
|
|
6
|
-
export async function getEmptyFontKit({ title, files, weightKeywordList, italicKeywordList }) {
|
|
7
|
-
|
|
8
|
-
let fontNames = {};
|
|
9
|
-
let subfamilies = {};
|
|
10
|
-
|
|
11
|
-
for (var i = 0; i < files.length; i++) {
|
|
12
|
-
|
|
13
|
-
const file = files[i];
|
|
14
|
-
const fontBuffer = await readFontFile(file);
|
|
15
|
-
const font = fontkit.create(fontBuffer);
|
|
16
|
-
|
|
17
|
-
let weightName = font?.name?.records?.preferredSubfamily ? font?.name?.records?.preferredSubfamily : font?.name?.records?.fontSubfamily;
|
|
18
|
-
weightName = weightName?.en ? weightName.en : weightName.constructor == Object ? weightName[Object.keys(weightName)[0]] : weightName;
|
|
19
|
-
|
|
20
|
-
let variableFont = font?.variationAxes && Object.keys(font.variationAxes).length > 0 ? true : false;
|
|
21
|
-
let subfamilyName = font.familyName.toLowerCase().trim().replace(title.toLowerCase().trim(), '').trim();
|
|
22
|
-
let fontTitle = font?.fullName.toLowerCase().trim();
|
|
23
|
-
|
|
24
|
-
weightKeywordList.forEach(keyword => {
|
|
25
|
-
const kw = keyword.toLowerCase().trim();
|
|
26
|
-
|
|
27
|
-
if (fontTitle.includes(kw)) {
|
|
28
|
-
fontTitle = fontTitle.replace(kw, '');
|
|
29
|
-
}
|
|
30
|
-
if (subfamilyName.includes(kw)) {
|
|
31
|
-
subfamilyName = subfamilyName.replace(kw, '');
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
italicKeywordList.forEach(keyword => {
|
|
36
|
-
const kw = keyword.toLowerCase().trim();
|
|
37
|
-
|
|
38
|
-
if (subfamilyName.includes(kw)) {
|
|
39
|
-
subfamilyName = subfamilyName.replace(kw, '');
|
|
40
|
-
}
|
|
41
|
-
if (fontTitle.includes(kw)) {
|
|
42
|
-
fontTitle = fontTitle.replace(kw, '');
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
fontTitle = fontTitle.trim().split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
47
|
-
|
|
48
|
-
subfamilyName = subfamilyName.trim();
|
|
49
|
-
subfamilyName = (subfamilyName == '') ? 'Regular' : subfamilyName.split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
let id = slugify(fontTitle.toLowerCase().trim());
|
|
53
|
-
if (variableFont && !id.endsWith('-vf')) {
|
|
54
|
-
id = id + '-vf';
|
|
55
|
-
fontTitle = fontTitle + ' VF';
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// add subfamily to list
|
|
59
|
-
if (!subfamilies[id]) {
|
|
60
|
-
subfamilies[id] = [subfamilyName];
|
|
61
|
-
} else if (subfamilies[id].indexOf(subfamilyName) == -1) {
|
|
62
|
-
subfamilies[id] = [...subfamilies[id], subfamilyName];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!fontNames[id]) {
|
|
66
|
-
fontNames[id] = [{
|
|
67
|
-
file: file.name,
|
|
68
|
-
fullName: font.fullName,
|
|
69
|
-
familyName: font.familyName,
|
|
70
|
-
subFamily: subfamilyName,
|
|
71
|
-
}];
|
|
72
|
-
} else if (fontNames[id].indexOf(file.name) == -1) {
|
|
73
|
-
fontNames[id].push({
|
|
74
|
-
file: file.name,
|
|
75
|
-
fullName: font.fullName,
|
|
76
|
-
familyName: font.familyName,
|
|
77
|
-
subFamily: subfamilyName,
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
console.log('font names : ', fontNames);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Reads a font file and returns its content as a Uint8Array */
|
|
88
|
-
const readFontFile = (file) => {
|
|
89
|
-
return new Promise((resolve, reject) => {
|
|
90
|
-
const reader = new FileReader();
|
|
91
|
-
|
|
92
|
-
reader.onload = (event) => {
|
|
93
|
-
resolve(new Uint8Array(event.target.result));
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
reader.onerror = (error) => { reject(error); };
|
|
97
|
-
reader.readAsArrayBuffer(file);
|
|
98
|
-
});
|
|
99
|
-
};
|
|
1
|
+
// Returns a zeroed-out fontkit-shaped placeholder object used when no font binary is available
|
|
2
|
+
import * as fontkit from 'fontkit';
|
|
3
|
+
import slugify from 'slugify';
|
|
4
|
+
|
|
5
|
+
/** Reads font files and returns name/subfamily metadata without writing to Sanity */
|
|
6
|
+
export async function getEmptyFontKit({ title, files, weightKeywordList, italicKeywordList }) {
|
|
7
|
+
|
|
8
|
+
let fontNames = {};
|
|
9
|
+
let subfamilies = {};
|
|
10
|
+
|
|
11
|
+
for (var i = 0; i < files.length; i++) {
|
|
12
|
+
|
|
13
|
+
const file = files[i];
|
|
14
|
+
const fontBuffer = await readFontFile(file);
|
|
15
|
+
const font = fontkit.create(fontBuffer);
|
|
16
|
+
|
|
17
|
+
let weightName = font?.name?.records?.preferredSubfamily ? font?.name?.records?.preferredSubfamily : font?.name?.records?.fontSubfamily;
|
|
18
|
+
weightName = weightName?.en ? weightName.en : weightName.constructor == Object ? weightName[Object.keys(weightName)[0]] : weightName;
|
|
19
|
+
|
|
20
|
+
let variableFont = font?.variationAxes && Object.keys(font.variationAxes).length > 0 ? true : false;
|
|
21
|
+
let subfamilyName = font.familyName.toLowerCase().trim().replace(title.toLowerCase().trim(), '').trim();
|
|
22
|
+
let fontTitle = font?.fullName.toLowerCase().trim();
|
|
23
|
+
|
|
24
|
+
weightKeywordList.forEach(keyword => {
|
|
25
|
+
const kw = keyword.toLowerCase().trim();
|
|
26
|
+
|
|
27
|
+
if (fontTitle.includes(kw)) {
|
|
28
|
+
fontTitle = fontTitle.replace(kw, '');
|
|
29
|
+
}
|
|
30
|
+
if (subfamilyName.includes(kw)) {
|
|
31
|
+
subfamilyName = subfamilyName.replace(kw, '');
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
italicKeywordList.forEach(keyword => {
|
|
36
|
+
const kw = keyword.toLowerCase().trim();
|
|
37
|
+
|
|
38
|
+
if (subfamilyName.includes(kw)) {
|
|
39
|
+
subfamilyName = subfamilyName.replace(kw, '');
|
|
40
|
+
}
|
|
41
|
+
if (fontTitle.includes(kw)) {
|
|
42
|
+
fontTitle = fontTitle.replace(kw, '');
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
fontTitle = fontTitle.trim().split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
47
|
+
|
|
48
|
+
subfamilyName = subfamilyName.trim();
|
|
49
|
+
subfamilyName = (subfamilyName == '') ? 'Regular' : subfamilyName.split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
let id = slugify(fontTitle.toLowerCase().trim());
|
|
53
|
+
if (variableFont && !id.endsWith('-vf')) {
|
|
54
|
+
id = id + '-vf';
|
|
55
|
+
fontTitle = fontTitle + ' VF';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// add subfamily to list
|
|
59
|
+
if (!subfamilies[id]) {
|
|
60
|
+
subfamilies[id] = [subfamilyName];
|
|
61
|
+
} else if (subfamilies[id].indexOf(subfamilyName) == -1) {
|
|
62
|
+
subfamilies[id] = [...subfamilies[id], subfamilyName];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!fontNames[id]) {
|
|
66
|
+
fontNames[id] = [{
|
|
67
|
+
file: file.name,
|
|
68
|
+
fullName: font.fullName,
|
|
69
|
+
familyName: font.familyName,
|
|
70
|
+
subFamily: subfamilyName,
|
|
71
|
+
}];
|
|
72
|
+
} else if (fontNames[id].indexOf(file.name) == -1) {
|
|
73
|
+
fontNames[id].push({
|
|
74
|
+
file: file.name,
|
|
75
|
+
fullName: font.fullName,
|
|
76
|
+
familyName: font.familyName,
|
|
77
|
+
subFamily: subfamilyName,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log('font names : ', fontNames);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Reads a font file and returns its content as a Uint8Array */
|
|
88
|
+
const readFontFile = (file) => {
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const reader = new FileReader();
|
|
91
|
+
|
|
92
|
+
reader.onload = (event) => {
|
|
93
|
+
resolve(new Uint8Array(event.target.result));
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
reader.onerror = (error) => { reject(error); };
|
|
97
|
+
reader.readAsArrayBuffer(file);
|
|
98
|
+
});
|
|
99
|
+
};
|