@liiift-studio/sanity-font-manager 2.7.1 → 2.9.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/dist/index.js +13005 -1454
- package/dist/index.mjs +12879 -1343
- package/package.json +3 -1
- package/src/index.js +4 -0
- package/src/schema/cedarsProfileField.js +82 -0
- package/src/utils/computeCedarsProfile.js +19 -0
- package/src/utils/generateFontData.js +20 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liiift-studio/sanity-font-manager",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.0",
|
|
4
4
|
"description": "Sanity Studio plugin — full font management suite with batch upload, format conversion, metadata extraction, CSS generation, collection/pair generation, and script variant support. Supports Sanity v3, v4, and v5.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Liiift Studio",
|
|
@@ -50,7 +50,9 @@
|
|
|
50
50
|
"link:all": "npm link && cd ../../../sites/darden/sanity && npm link @liiift-studio/sanity-font-manager && cd ../../tdf/sanity && npm link @liiift-studio/sanity-font-manager && cd ../../mckl/cms && npm link @liiift-studio/sanity-font-manager"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
+
"@liiift-studio/cedars-engine": "^1.0.0",
|
|
53
54
|
"base-64": "^1.0.0",
|
|
55
|
+
"lib-font": "^3.0.1",
|
|
54
56
|
"nanoid": "^5.0.0",
|
|
55
57
|
"slugify": "^1.6.6"
|
|
56
58
|
},
|
package/src/index.js
CHANGED
|
@@ -110,6 +110,10 @@ export { openTypeField, createOpenTypeField } from './schema/openTypeField.js';
|
|
|
110
110
|
export { styleCountField } from './schema/styleCountField.js';
|
|
111
111
|
export { stylisticSetField } from './schema/stylisticSetField.js';
|
|
112
112
|
export { createStylesField } from './schema/stylesField.js';
|
|
113
|
+
export { cedarsProfileField, createCedarsProfileField } from './schema/cedarsProfileField.js';
|
|
114
|
+
|
|
115
|
+
// CEDARS+ profile computation (auto-tags typefaces on upload)
|
|
116
|
+
export { computeCedarsProfile } from './utils/computeCedarsProfile.js';
|
|
113
117
|
|
|
114
118
|
// Keyword utilities
|
|
115
119
|
export {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Sanity schema field for the auto-computed CEDARS+ typographic profile.
|
|
2
|
+
// Read-only; populated on font upload by computeCedarsProfile (see utils).
|
|
3
|
+
|
|
4
|
+
// The six CEDARS axes, in canonical order — shared by the scores and labels groups.
|
|
5
|
+
const AXES = ['contrast', 'energy', 'details', 'axis', 'rhythm', 'structure'];
|
|
6
|
+
|
|
7
|
+
// Build a title-cased label from an axis key (e.g. 'contrast' -> 'Contrast').
|
|
8
|
+
function titleCase(key) {
|
|
9
|
+
return key.charAt(0).toUpperCase() + key.slice(1);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Number sub-fields (0..100) for each axis, used by the scores group.
|
|
13
|
+
const scoreFields = AXES.map((key) => ({
|
|
14
|
+
title: titleCase(key),
|
|
15
|
+
name: key,
|
|
16
|
+
type: 'number',
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
// String sub-fields (human-readable descriptor) for each axis, used by the labels group.
|
|
20
|
+
const labelFields = AXES.map((key) => ({
|
|
21
|
+
title: titleCase(key),
|
|
22
|
+
name: key,
|
|
23
|
+
type: 'string',
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
// The CEDARS+ profile field definition. Attach to a `font` document schema.
|
|
27
|
+
export const cedarsProfileField = {
|
|
28
|
+
title: 'CEDARS+ Profile',
|
|
29
|
+
name: 'cedarsPlus',
|
|
30
|
+
type: 'object',
|
|
31
|
+
description:
|
|
32
|
+
'Auto-computed typographic profile from the font outlines — Contrast, Energy, Details, Axis, Rhythm, Structure (0–100 each). Read-only; set on upload.',
|
|
33
|
+
readOnly: true,
|
|
34
|
+
options: { collapsible: true, collapsed: true },
|
|
35
|
+
fields: [
|
|
36
|
+
{
|
|
37
|
+
title: 'Scores',
|
|
38
|
+
name: 'scores',
|
|
39
|
+
type: 'object',
|
|
40
|
+
options: { collapsible: true, collapsed: false },
|
|
41
|
+
fields: scoreFields,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
title: 'Labels',
|
|
45
|
+
name: 'labels',
|
|
46
|
+
type: 'object',
|
|
47
|
+
options: { collapsible: true, collapsed: true },
|
|
48
|
+
fields: labelFields,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
title: '+ Descriptors',
|
|
52
|
+
name: 'plus',
|
|
53
|
+
type: 'object',
|
|
54
|
+
options: { collapsible: true, collapsed: true },
|
|
55
|
+
fields: [
|
|
56
|
+
{ title: 'x-height / cap-height', name: 'xHeightRatio', type: 'number' },
|
|
57
|
+
{ title: 'cap-height / em', name: 'capHeightRatio', type: 'number' },
|
|
58
|
+
{ title: 'Monospaced', name: 'isMonospaced', type: 'boolean' },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
// Normalized [C,E,D,A,R,S] 0..1 vector — kept for similarity search / export.
|
|
63
|
+
title: 'Vector',
|
|
64
|
+
name: 'vector',
|
|
65
|
+
type: 'array',
|
|
66
|
+
of: [{ type: 'number' }],
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Factory to create a customised CEDARS+ profile field.
|
|
73
|
+
* @param {object} [options]
|
|
74
|
+
* @param {boolean} [options.readOnly=true] - Whether the field is read-only in the Studio.
|
|
75
|
+
* @param {string} [options.group] - Optional field group to assign the field to.
|
|
76
|
+
* @returns {object} Sanity field definition
|
|
77
|
+
*/
|
|
78
|
+
export function createCedarsProfileField({ readOnly = true, group } = {}) {
|
|
79
|
+
const field = { ...cedarsProfileField, readOnly };
|
|
80
|
+
if (group) field.group = group;
|
|
81
|
+
return field;
|
|
82
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Computes a font's CEDARS+ profile from its raw bytes, shaped for the Sanity
|
|
2
|
+
// `cedarsPlus` field. Thin wrapper around @liiift-studio/cedars-engine.
|
|
3
|
+
import { analyzeFont } from '@liiift-studio/cedars-engine';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compute the CEDARS+ profile (Contrast, Energy, Details, Axis, Rhythm, Structure)
|
|
7
|
+
* for a font from its raw outline geometry.
|
|
8
|
+
* @param {ArrayBuffer} buffer - The font file bytes (OTF/TTF/WOFF).
|
|
9
|
+
* @returns {{ scores: object, labels: object, plus: object, vector: number[] }}
|
|
10
|
+
*/
|
|
11
|
+
export function computeCedarsProfile(buffer) {
|
|
12
|
+
const profile = analyzeFont(buffer);
|
|
13
|
+
return {
|
|
14
|
+
scores: profile.scores,
|
|
15
|
+
labels: profile.labels,
|
|
16
|
+
plus: profile.plus,
|
|
17
|
+
vector: profile.vector,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Extracts metadata, metrics, glyph count, OpenType features, and variable axes from a font and optionally patches the Sanity font document
|
|
2
2
|
|
|
3
3
|
import { parseFont } from './parseFont';
|
|
4
|
+
import { computeCedarsProfile } from './computeCedarsProfile';
|
|
4
5
|
import {
|
|
5
6
|
getFontMetadata,
|
|
6
7
|
getFontMetrics,
|
|
@@ -43,10 +44,12 @@ export default async function generateFontData({ fileInput, url, fontKit, fontId
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
let font = fontKit;
|
|
47
|
+
// Hoisted so the CEDARS+ step below can reuse the raw bytes when we fetched them.
|
|
48
|
+
let fontBuffer = null;
|
|
46
49
|
if (!fontKit || fontKit == null) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
font = await parseFont(
|
|
50
|
+
const res = await fetch(srcUrl);
|
|
51
|
+
fontBuffer = await res.arrayBuffer();
|
|
52
|
+
font = await parseFont(fontBuffer, `${fontId}.ttf`);
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
const variableAxes = getVariationAxes(font);
|
|
@@ -81,6 +84,19 @@ export default async function generateFontData({ fileInput, url, fontKit, fontId
|
|
|
81
84
|
variableFont = true;
|
|
82
85
|
}
|
|
83
86
|
|
|
87
|
+
// CEDARS+ profile — best-effort enrichment. Wrapped so a failure here can never
|
|
88
|
+
// block the font upload; the raw bytes are fetched only if we don't already have them.
|
|
89
|
+
let cedarsPlus = null;
|
|
90
|
+
try {
|
|
91
|
+
if (!fontBuffer) {
|
|
92
|
+
const res = await fetch(srcUrl);
|
|
93
|
+
fontBuffer = await res.arrayBuffer();
|
|
94
|
+
}
|
|
95
|
+
cedarsPlus = computeCedarsProfile(fontBuffer);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.warn('CEDARS profile computation failed:', err?.message || err);
|
|
98
|
+
}
|
|
99
|
+
|
|
84
100
|
let patch = {
|
|
85
101
|
metrics: metrics,
|
|
86
102
|
metaData: metaData,
|
|
@@ -90,6 +106,7 @@ export default async function generateFontData({ fileInput, url, fontKit, fontId
|
|
|
90
106
|
glyphCount: glyphCount,
|
|
91
107
|
opentypeFeatures: { chars: opentypeFeatures },
|
|
92
108
|
characterSet: { chars: characterSet },
|
|
109
|
+
...(cedarsPlus ? { cedarsPlus } : {}),
|
|
93
110
|
};
|
|
94
111
|
|
|
95
112
|
console.log('Font data patch:', Object.keys(patch));
|