@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.
Files changed (42) hide show
  1. package/README.md +437 -437
  2. package/dist/index.js +103 -48
  3. package/dist/index.mjs +103 -48
  4. package/package.json +85 -85
  5. package/src/components/BatchUploadFonts.jsx +640 -639
  6. package/src/components/FontScriptUploaderComponent.jsx +463 -463
  7. package/src/components/GenerateCollectionsPairsComponent.jsx +259 -259
  8. package/src/components/KeyValueInput.jsx +95 -95
  9. package/src/components/KeyValueReferenceInput.jsx +254 -254
  10. package/src/components/NestedObjectArraySelector.jsx +146 -146
  11. package/src/components/PriceInput.jsx +26 -26
  12. package/src/components/PrimaryCollectionGeneratorTypeface.jsx +116 -116
  13. package/src/components/RegenerateSubfamiliesComponent.jsx +185 -185
  14. package/src/components/SetOTF.jsx +87 -87
  15. package/src/components/SingleUploaderTool.jsx +673 -673
  16. package/src/components/StatusDisplay.jsx +26 -26
  17. package/src/components/StyleCountInput.jsx +16 -16
  18. package/src/components/UpdateScriptsComponent.jsx +76 -76
  19. package/src/components/UploadButton.jsx +43 -43
  20. package/src/components/UploadScriptsComponent.jsx +537 -537
  21. package/src/components/VariableInstanceReferencesInput.jsx +190 -190
  22. package/src/hooks/useNestedObjects.js +92 -92
  23. package/src/hooks/useSanityClient.js +9 -9
  24. package/src/index.js +70 -70
  25. package/src/schema/openTypeField.js +1945 -1945
  26. package/src/schema/styleCountField.js +12 -12
  27. package/src/schema/stylesField.js +268 -268
  28. package/src/schema/stylisticSetField.js +301 -301
  29. package/src/utils/generateCssFile.js +205 -205
  30. package/src/utils/generateFontData.js +145 -145
  31. package/src/utils/generateFontFile.js +38 -38
  32. package/src/utils/generateKeywords.js +185 -185
  33. package/src/utils/generateSubset.js +45 -45
  34. package/src/utils/getEmptyFontKit.js +99 -99
  35. package/src/utils/parseVariableFontInstances.js +211 -211
  36. package/src/utils/processFontFiles.js +487 -477
  37. package/src/utils/regenerateFontData.js +146 -146
  38. package/src/utils/sanitizeForSanityId.js +65 -65
  39. package/src/utils/updateFontPrices.js +94 -94
  40. package/src/utils/updateTypefaceDocument.js +149 -160
  41. package/src/utils/uploadFontFiles.js +115 -26
  42. 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
+ };