@liiift-studio/sanity-font-manager 2.7.0 → 2.8.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 +695 -437
- package/dist/index.js +2330 -7084
- package/dist/index.mjs +2331 -7085
- package/package.json +84 -83
- package/src/components/BatchUploadFonts.jsx +655 -655
- package/src/components/BulkActions.jsx +99 -99
- package/src/components/ExistingDocumentResolver.jsx +152 -152
- package/src/components/FontReviewCard.jsx +455 -455
- 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 +267 -267
- 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 +674 -674
- 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/UploadModal.jsx +309 -309
- package/src/components/UploadScriptsComponent.jsx +539 -539
- package/src/components/UploadStep1Settings.jsx +272 -272
- package/src/components/UploadStep2Review.jsx +478 -478
- package/src/components/UploadStep3Execute.jsx +234 -234
- package/src/components/UploadStep3bInstances.jsx +396 -396
- package/src/components/UploadSummary.jsx +196 -196
- 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 +120 -120
- package/src/schema/openTypeField.js +1995 -1995
- package/src/schema/styleCountField.js +12 -12
- package/src/schema/stylesField.js +302 -302
- package/src/schema/stylisticSetField.js +301 -301
- package/src/utils/buildUploadPlan.js +326 -326
- package/src/utils/executeUploadPlan.js +430 -430
- package/src/utils/executionReducer.js +56 -56
- package/src/utils/fontHelpers.js +281 -281
- package/src/utils/generateCssFile.js +207 -207
- package/src/utils/generateFontData.js +98 -98
- 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 +101 -101
- package/src/utils/parseFont.js +56 -56
- package/src/utils/parseVariableFontInstances.js +301 -301
- package/src/utils/planReducer.js +531 -531
- package/src/utils/planTypes.js +183 -183
- package/src/utils/processFontFiles.js +530 -530
- package/src/utils/regenerateFontData.js +146 -146
- package/src/utils/resolveExistingFont.js +87 -87
- package/src/utils/retitleFontEntries.js +154 -154
- package/src/utils/sanitizeForSanityId.js +65 -65
- package/src/utils/setupDecompressors.js +27 -27
- package/src/utils/updateFontPrices.js +94 -94
- package/src/utils/updateTypefaceDocument.js +162 -162
- package/src/utils/uploadFontFiles.js +405 -405
- package/src/utils/utils.js +24 -24
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
// Parses font files and returns name/subfamily metadata groupings without writing to Sanity
|
|
2
|
-
|
|
3
|
-
import { parseFont } from './parseFont';
|
|
4
|
-
import { getNameString, getVariationAxes } from './fontHelpers';
|
|
5
|
-
import slugify from 'slugify';
|
|
6
|
-
|
|
7
|
-
/** Reads font files and returns name/subfamily metadata without writing to Sanity */
|
|
8
|
-
export async function getEmptyFontKit({ title, files, weightKeywordList, italicKeywordList }) {
|
|
9
|
-
|
|
10
|
-
let fontNames = {};
|
|
11
|
-
let subfamilies = {};
|
|
12
|
-
|
|
13
|
-
for (var i = 0; i < files.length; i++) {
|
|
14
|
-
|
|
15
|
-
const file = files[i];
|
|
16
|
-
const fontBuffer = await readFontFile(file);
|
|
17
|
-
const font = await parseFont(fontBuffer, file.name);
|
|
18
|
-
|
|
19
|
-
let weightName = getNameString(font, 17) || getNameString(font, 2) || '';
|
|
20
|
-
|
|
21
|
-
const axes = getVariationAxes(font);
|
|
22
|
-
let variableFont = axes !== null;
|
|
23
|
-
const familyName = getNameString(font, 1);
|
|
24
|
-
const fullName = getNameString(font, 4);
|
|
25
|
-
let subfamilyName = familyName.toLowerCase().trim().replace(title.toLowerCase().trim(), '').trim();
|
|
26
|
-
let fontTitle = fullName.toLowerCase().trim();
|
|
27
|
-
|
|
28
|
-
weightKeywordList.forEach(keyword => {
|
|
29
|
-
const kw = keyword.toLowerCase().trim();
|
|
30
|
-
|
|
31
|
-
if (fontTitle.includes(kw)) {
|
|
32
|
-
fontTitle = fontTitle.replace(kw, '');
|
|
33
|
-
}
|
|
34
|
-
if (subfamilyName.includes(kw)) {
|
|
35
|
-
subfamilyName = subfamilyName.replace(kw, '');
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
italicKeywordList.forEach(keyword => {
|
|
40
|
-
const kw = keyword.toLowerCase().trim();
|
|
41
|
-
|
|
42
|
-
if (subfamilyName.includes(kw)) {
|
|
43
|
-
subfamilyName = subfamilyName.replace(kw, '');
|
|
44
|
-
}
|
|
45
|
-
if (fontTitle.includes(kw)) {
|
|
46
|
-
fontTitle = fontTitle.replace(kw, '');
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
fontTitle = fontTitle.trim().split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
51
|
-
|
|
52
|
-
subfamilyName = subfamilyName.trim();
|
|
53
|
-
subfamilyName = (subfamilyName == '') ? 'Regular' : subfamilyName.split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let id = slugify(fontTitle.toLowerCase().trim());
|
|
57
|
-
if (variableFont && !id.endsWith('-vf')) {
|
|
58
|
-
id = id + '-vf';
|
|
59
|
-
fontTitle = fontTitle + ' VF';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// add subfamily to list
|
|
63
|
-
if (!subfamilies[id]) {
|
|
64
|
-
subfamilies[id] = [subfamilyName];
|
|
65
|
-
} else if (subfamilies[id].indexOf(subfamilyName) == -1) {
|
|
66
|
-
subfamilies[id] = [...subfamilies[id], subfamilyName];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!fontNames[id]) {
|
|
70
|
-
fontNames[id] = [{
|
|
71
|
-
file: file.name,
|
|
72
|
-
fullName: fullName,
|
|
73
|
-
familyName: familyName,
|
|
74
|
-
subFamily: subfamilyName,
|
|
75
|
-
}];
|
|
76
|
-
} else if (fontNames[id].indexOf(file.name) == -1) {
|
|
77
|
-
fontNames[id].push({
|
|
78
|
-
file: file.name,
|
|
79
|
-
fullName: fullName,
|
|
80
|
-
familyName: familyName,
|
|
81
|
-
subFamily: subfamilyName,
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
console.log('Font names:', fontNames);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Reads a font file and returns its content as an ArrayBuffer */
|
|
90
|
-
const readFontFile = (file) => {
|
|
91
|
-
return new Promise((resolve, reject) => {
|
|
92
|
-
const reader = new FileReader();
|
|
93
|
-
|
|
94
|
-
reader.onload = (event) => {
|
|
95
|
-
resolve(event.target.result);
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
reader.onerror = (error) => { reject(error); };
|
|
99
|
-
reader.readAsArrayBuffer(file);
|
|
100
|
-
});
|
|
101
|
-
};
|
|
1
|
+
// Parses font files and returns name/subfamily metadata groupings without writing to Sanity
|
|
2
|
+
|
|
3
|
+
import { parseFont } from './parseFont';
|
|
4
|
+
import { getNameString, getVariationAxes } from './fontHelpers';
|
|
5
|
+
import slugify from 'slugify';
|
|
6
|
+
|
|
7
|
+
/** Reads font files and returns name/subfamily metadata without writing to Sanity */
|
|
8
|
+
export async function getEmptyFontKit({ title, files, weightKeywordList, italicKeywordList }) {
|
|
9
|
+
|
|
10
|
+
let fontNames = {};
|
|
11
|
+
let subfamilies = {};
|
|
12
|
+
|
|
13
|
+
for (var i = 0; i < files.length; i++) {
|
|
14
|
+
|
|
15
|
+
const file = files[i];
|
|
16
|
+
const fontBuffer = await readFontFile(file);
|
|
17
|
+
const font = await parseFont(fontBuffer, file.name);
|
|
18
|
+
|
|
19
|
+
let weightName = getNameString(font, 17) || getNameString(font, 2) || '';
|
|
20
|
+
|
|
21
|
+
const axes = getVariationAxes(font);
|
|
22
|
+
let variableFont = axes !== null;
|
|
23
|
+
const familyName = getNameString(font, 1);
|
|
24
|
+
const fullName = getNameString(font, 4);
|
|
25
|
+
let subfamilyName = familyName.toLowerCase().trim().replace(title.toLowerCase().trim(), '').trim();
|
|
26
|
+
let fontTitle = fullName.toLowerCase().trim();
|
|
27
|
+
|
|
28
|
+
weightKeywordList.forEach(keyword => {
|
|
29
|
+
const kw = keyword.toLowerCase().trim();
|
|
30
|
+
|
|
31
|
+
if (fontTitle.includes(kw)) {
|
|
32
|
+
fontTitle = fontTitle.replace(kw, '');
|
|
33
|
+
}
|
|
34
|
+
if (subfamilyName.includes(kw)) {
|
|
35
|
+
subfamilyName = subfamilyName.replace(kw, '');
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
italicKeywordList.forEach(keyword => {
|
|
40
|
+
const kw = keyword.toLowerCase().trim();
|
|
41
|
+
|
|
42
|
+
if (subfamilyName.includes(kw)) {
|
|
43
|
+
subfamilyName = subfamilyName.replace(kw, '');
|
|
44
|
+
}
|
|
45
|
+
if (fontTitle.includes(kw)) {
|
|
46
|
+
fontTitle = fontTitle.replace(kw, '');
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
fontTitle = fontTitle.trim().split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
51
|
+
|
|
52
|
+
subfamilyName = subfamilyName.trim();
|
|
53
|
+
subfamilyName = (subfamilyName == '') ? 'Regular' : subfamilyName.split(' ').map(word => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
let id = slugify(fontTitle.toLowerCase().trim());
|
|
57
|
+
if (variableFont && !id.endsWith('-vf')) {
|
|
58
|
+
id = id + '-vf';
|
|
59
|
+
fontTitle = fontTitle + ' VF';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// add subfamily to list
|
|
63
|
+
if (!subfamilies[id]) {
|
|
64
|
+
subfamilies[id] = [subfamilyName];
|
|
65
|
+
} else if (subfamilies[id].indexOf(subfamilyName) == -1) {
|
|
66
|
+
subfamilies[id] = [...subfamilies[id], subfamilyName];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!fontNames[id]) {
|
|
70
|
+
fontNames[id] = [{
|
|
71
|
+
file: file.name,
|
|
72
|
+
fullName: fullName,
|
|
73
|
+
familyName: familyName,
|
|
74
|
+
subFamily: subfamilyName,
|
|
75
|
+
}];
|
|
76
|
+
} else if (fontNames[id].indexOf(file.name) == -1) {
|
|
77
|
+
fontNames[id].push({
|
|
78
|
+
file: file.name,
|
|
79
|
+
fullName: fullName,
|
|
80
|
+
familyName: familyName,
|
|
81
|
+
subFamily: subfamilyName,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log('Font names:', fontNames);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Reads a font file and returns its content as an ArrayBuffer */
|
|
90
|
+
const readFontFile = (file) => {
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
const reader = new FileReader();
|
|
93
|
+
|
|
94
|
+
reader.onload = (event) => {
|
|
95
|
+
resolve(event.target.result);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
reader.onerror = (error) => { reject(error); };
|
|
99
|
+
reader.readAsArrayBuffer(file);
|
|
100
|
+
});
|
|
101
|
+
};
|
package/src/utils/parseFont.js
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
// Async font parser — decompressor globals must be set before lib-font is imported
|
|
2
|
-
|
|
3
|
-
import './setupDecompressors.js';
|
|
4
|
-
import { Font } from 'lib-font';
|
|
5
|
-
|
|
6
|
-
/** Maximum font file size accepted for parsing (50 MB) */
|
|
7
|
-
const MAX_FONT_FILE_SIZE = 50 * 1024 * 1024;
|
|
8
|
-
|
|
9
|
-
/** Parse timeout — prevents hanging if lib-font silently fails (30 seconds) */
|
|
10
|
-
const PARSE_TIMEOUT_MS = 30000;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Parse a font file from an ArrayBuffer.
|
|
14
|
-
* Returns a lib-font Font object with all tables accessible via font.opentype.tables.*.
|
|
15
|
-
*
|
|
16
|
-
* @param {ArrayBuffer} buffer - Raw font file bytes
|
|
17
|
-
* @param {string} filename - Original filename (used for format detection by lib-font)
|
|
18
|
-
* @returns {Promise<import('lib-font').Font>} Parsed lib-font Font object
|
|
19
|
-
* @throws {Error} If the file exceeds MAX_FONT_FILE_SIZE, parsing fails, or times out
|
|
20
|
-
*/
|
|
21
|
-
export async function parseFont(buffer, filename) {
|
|
22
|
-
if (buffer.byteLength > MAX_FONT_FILE_SIZE) {
|
|
23
|
-
throw new Error(`Font file exceeds ${MAX_FONT_FILE_SIZE / 1024 / 1024}MB limit: ${filename} (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB)`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return new Promise((resolve, reject) => {
|
|
27
|
-
let settled = false;
|
|
28
|
-
|
|
29
|
-
const settle = (fn) => (...args) => {
|
|
30
|
-
if (!settled) {
|
|
31
|
-
settled = true;
|
|
32
|
-
clearTimeout(timer);
|
|
33
|
-
fn(...args);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Timeout guard — prevents infinite hang if lib-font fails silently
|
|
38
|
-
const timer = setTimeout(() => {
|
|
39
|
-
settle(reject)(new Error(`Parsing timed out for ${filename} (${PARSE_TIMEOUT_MS / 1000}s). The file may be corrupted or in an unsupported format.`));
|
|
40
|
-
}, PARSE_TIMEOUT_MS);
|
|
41
|
-
|
|
42
|
-
const font = new Font('font', { skipStyleSheet: true });
|
|
43
|
-
font.onload = settle((evt) => resolve(evt.detail.font));
|
|
44
|
-
font.onerror = settle((evt) => {
|
|
45
|
-
const msg = evt.detail?.message || `Failed to parse ${filename}`;
|
|
46
|
-
reject(new Error(msg));
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
font.fromDataBuffer(buffer, filename);
|
|
51
|
-
} catch (err) {
|
|
52
|
-
// Catches synchronous throws from WOFF2 constructor when brotli is missing
|
|
53
|
-
settle(reject)(new Error(`${filename}: ${err.message}`));
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
}
|
|
1
|
+
// Async font parser — decompressor globals must be set before lib-font is imported
|
|
2
|
+
|
|
3
|
+
import './setupDecompressors.js';
|
|
4
|
+
import { Font } from 'lib-font';
|
|
5
|
+
|
|
6
|
+
/** Maximum font file size accepted for parsing (50 MB) */
|
|
7
|
+
const MAX_FONT_FILE_SIZE = 50 * 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
/** Parse timeout — prevents hanging if lib-font silently fails (30 seconds) */
|
|
10
|
+
const PARSE_TIMEOUT_MS = 30000;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse a font file from an ArrayBuffer.
|
|
14
|
+
* Returns a lib-font Font object with all tables accessible via font.opentype.tables.*.
|
|
15
|
+
*
|
|
16
|
+
* @param {ArrayBuffer} buffer - Raw font file bytes
|
|
17
|
+
* @param {string} filename - Original filename (used for format detection by lib-font)
|
|
18
|
+
* @returns {Promise<import('lib-font').Font>} Parsed lib-font Font object
|
|
19
|
+
* @throws {Error} If the file exceeds MAX_FONT_FILE_SIZE, parsing fails, or times out
|
|
20
|
+
*/
|
|
21
|
+
export async function parseFont(buffer, filename) {
|
|
22
|
+
if (buffer.byteLength > MAX_FONT_FILE_SIZE) {
|
|
23
|
+
throw new Error(`Font file exceeds ${MAX_FONT_FILE_SIZE / 1024 / 1024}MB limit: ${filename} (${(buffer.byteLength / 1024 / 1024).toFixed(1)}MB)`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
let settled = false;
|
|
28
|
+
|
|
29
|
+
const settle = (fn) => (...args) => {
|
|
30
|
+
if (!settled) {
|
|
31
|
+
settled = true;
|
|
32
|
+
clearTimeout(timer);
|
|
33
|
+
fn(...args);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Timeout guard — prevents infinite hang if lib-font fails silently
|
|
38
|
+
const timer = setTimeout(() => {
|
|
39
|
+
settle(reject)(new Error(`Parsing timed out for ${filename} (${PARSE_TIMEOUT_MS / 1000}s). The file may be corrupted or in an unsupported format.`));
|
|
40
|
+
}, PARSE_TIMEOUT_MS);
|
|
41
|
+
|
|
42
|
+
const font = new Font('font', { skipStyleSheet: true });
|
|
43
|
+
font.onload = settle((evt) => resolve(evt.detail.font));
|
|
44
|
+
font.onerror = settle((evt) => {
|
|
45
|
+
const msg = evt.detail?.message || `Failed to parse ${filename}`;
|
|
46
|
+
reject(new Error(msg));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
font.fromDataBuffer(buffer, filename);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
// Catches synchronous throws from WOFF2 constructor when brotli is missing
|
|
53
|
+
settle(reject)(new Error(`${filename}: ${err.message}`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|