var2stat 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- var2stat-0.2.2/.release-please-manifest.json +1 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/CHANGELOG.md +14 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/PKG-INFO +1 -1
- {var2stat-0.2.0 → var2stat-0.2.2}/pyproject.toml +1 -1
- {var2stat-0.2.0 → var2stat-0.2.2}/src/var2stat/config.py +3 -2
- {var2stat-0.2.0 → var2stat-0.2.2}/src/var2stat/generate.py +32 -35
- var2stat-0.2.2/src/var2stat/schema.json +182 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/uv.lock +1 -1
- var2stat-0.2.0/.release-please-manifest.json +0 -1
- var2stat-0.2.0/schema.json +0 -109
- {var2stat-0.2.0 → var2stat-0.2.2}/.github/workflows/publish.yml +0 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/.github/workflows/release.yml +0 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/.gitignore +0 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/.python-version +0 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/LICENSE +0 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/README.md +0 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/release-please-config.json +0 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/src/var2stat/__init__.py +0 -0
- {var2stat-0.2.0 → var2stat-0.2.2}/src/var2stat/__main__.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{".":"0.2.2"}
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.2](https://github.com/decipher3114/Var2Stat/compare/var2stat-v0.2.1...var2stat-v0.2.2) (2026-05-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* use standard naming convention ([48697b2](https://github.com/decipher3114/Var2Stat/commit/48697b204f2c4f58cdfae5e1d9f4e971673b9c07))
|
|
9
|
+
|
|
10
|
+
## [0.2.1](https://github.com/decipher3114/Var2Stat/compare/var2stat-v0.2.0...var2stat-v0.2.1) (2026-05-13)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* force canonical and valid name format ([4263ad3](https://github.com/decipher3114/Var2Stat/commit/4263ad306f92f163459cab390f22e3892896ee63))
|
|
16
|
+
|
|
3
17
|
## [0.2.0](https://github.com/decipher3114/Var2Stat/compare/var2stat-v0.1.1...var2stat-v0.2.0) (2026-04-13)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -7,7 +7,8 @@ from typing import Any, Dict
|
|
|
7
7
|
import click
|
|
8
8
|
from fontTools.ttLib import TTFont
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
SCHEMA_URL = "https://raw.githubusercontent.com/decipher3114/Var2Stat/refs/heads/main/var2stat/src/schema.json"
|
|
11
|
+
_SCHEMA_PATH = Path(__file__).resolve().with_name("schema.json")
|
|
11
12
|
|
|
12
13
|
WEIGHT_NAMES: Dict[int, str] = {
|
|
13
14
|
100: "Thin",
|
|
@@ -128,7 +129,7 @@ def create_config_structure(font_info: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
128
129
|
optimized_variants[variant_name] = optimized_variant
|
|
129
130
|
|
|
130
131
|
return {
|
|
131
|
-
"$schema":
|
|
132
|
+
"$schema": SCHEMA_URL,
|
|
132
133
|
"file": font_info["font_path"],
|
|
133
134
|
"font_name": font_info["font_name"],
|
|
134
135
|
"axes": global_axes,
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any, Dict
|
|
6
|
+
from typing import Any, Dict
|
|
7
7
|
|
|
8
8
|
import click
|
|
9
9
|
import jsonschema
|
|
10
10
|
from fontTools.ttLib import TTFont
|
|
11
11
|
from fontTools.varLib import instancer
|
|
12
12
|
|
|
13
|
-
SCHEMA_URL = "https://raw.githubusercontent.com/decipher3114/Var2Stat/refs/heads/main/schema.json"
|
|
14
|
-
|
|
15
13
|
|
|
16
14
|
def load_config(config_path: str) -> Dict[str, Any]:
|
|
17
15
|
if not os.path.exists(config_path):
|
|
@@ -24,20 +22,20 @@ def load_config(config_path: str) -> Dict[str, Any]:
|
|
|
24
22
|
except json.JSONDecodeError as e:
|
|
25
23
|
raise ValueError(f"Invalid JSON syntax at line {e.lineno}, column {e.colno}")
|
|
26
24
|
|
|
27
|
-
# Validate against
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
# Validate against the packaged schema.
|
|
26
|
+
schema_path = Path(__file__).resolve().with_name("schema.json")
|
|
27
|
+
with open(schema_path, "r", encoding="utf-8") as f:
|
|
28
|
+
schema = json.load(f)
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
jsonschema.validate(config_data, schema)
|
|
32
|
+
print("[INFO]: Config validated against schema")
|
|
33
|
+
except jsonschema.ValidationError as e:
|
|
34
|
+
error_msg = e.message
|
|
35
|
+
if e.absolute_path:
|
|
36
|
+
path_str = " -> ".join(str(p) for p in e.absolute_path)
|
|
37
|
+
error_msg += f" at {path_str}"
|
|
38
|
+
raise ValueError(error_msg)
|
|
41
39
|
|
|
42
40
|
return config_data
|
|
43
41
|
|
|
@@ -51,17 +49,14 @@ def remove_variation_tables(font: TTFont) -> None:
|
|
|
51
49
|
del font[table]
|
|
52
50
|
|
|
53
51
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if not canonical_font_name:
|
|
63
|
-
canonical_font_name = "Font"
|
|
64
|
-
return canonical_font_name
|
|
52
|
+
def canonicalize(value: str, fallback: str) -> str:
|
|
53
|
+
"""Canonicalize a string for filenames/PostScript names.
|
|
54
|
+
|
|
55
|
+
Removes all non-alphabetic characters (spaces, '-', '_', digits, punctuation, etc.).
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
canonical = "".join(ch for ch in (value or "").strip() if ch.isalpha())
|
|
59
|
+
return canonical or fallback
|
|
65
60
|
|
|
66
61
|
|
|
67
62
|
def encode(record, text: str) -> bytes:
|
|
@@ -73,8 +68,9 @@ def encode(record, text: str) -> bytes:
|
|
|
73
68
|
else:
|
|
74
69
|
return text.encode("utf-16-be")
|
|
75
70
|
|
|
71
|
+
|
|
76
72
|
def update_font_names(
|
|
77
|
-
font: TTFont, font_name: str, variant_name: str, axes: Dict[str,
|
|
73
|
+
font: TTFont, font_name: str, variant_name: str, axes: Dict[str, float | int]
|
|
78
74
|
) -> None:
|
|
79
75
|
# Update font metadata and naming information
|
|
80
76
|
name_table = font["name"]
|
|
@@ -96,7 +92,7 @@ def update_font_names(
|
|
|
96
92
|
record.string = encode(record, full_name)
|
|
97
93
|
|
|
98
94
|
elif record.nameID == 6: # PostScript name
|
|
99
|
-
ps_name = f"{font_name
|
|
95
|
+
ps_name = f"{canonicalize(font_name, 'Font')}-{canonicalize(variant_name, 'Variant')}"
|
|
100
96
|
record.string = encode(record, ps_name)
|
|
101
97
|
|
|
102
98
|
# Update OS/2 weight class
|
|
@@ -134,8 +130,8 @@ def generate_variant(
|
|
|
134
130
|
font: TTFont,
|
|
135
131
|
font_name: str,
|
|
136
132
|
variant_name: str,
|
|
137
|
-
variant_config: Dict[str,
|
|
138
|
-
global_axes: Dict[str,
|
|
133
|
+
variant_config: Dict[str, float | int],
|
|
134
|
+
global_axes: Dict[str, float | int | None],
|
|
139
135
|
font_defaults: Dict[str, float],
|
|
140
136
|
output_folder: str,
|
|
141
137
|
) -> bool:
|
|
@@ -162,8 +158,9 @@ def generate_variant(
|
|
|
162
158
|
update_font_names(static_font, font_name, variant_name, resolved_axes)
|
|
163
159
|
|
|
164
160
|
# Save font
|
|
165
|
-
canonical_font_name =
|
|
166
|
-
|
|
161
|
+
canonical_font_name = canonicalize(font_name, fallback="Font")
|
|
162
|
+
canonical_variant_name = canonicalize(variant_name, fallback="Variant")
|
|
163
|
+
output_filename = f"{canonical_font_name}-{canonical_variant_name}.ttf"
|
|
167
164
|
static_font.save(os.path.join(output_folder, output_filename))
|
|
168
165
|
|
|
169
166
|
return True
|
|
@@ -174,7 +171,7 @@ def generate_variant(
|
|
|
174
171
|
|
|
175
172
|
|
|
176
173
|
def resolve_output_directory(font_name: str) -> str:
|
|
177
|
-
canonical_font_name =
|
|
174
|
+
canonical_font_name = canonicalize(font_name, fallback="Font")
|
|
178
175
|
os.makedirs(canonical_font_name, exist_ok=True)
|
|
179
176
|
return canonical_font_name
|
|
180
177
|
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/decipher3114/Var2Stat",
|
|
4
|
+
"title": "Var2Stat Configuration",
|
|
5
|
+
"description": "Configuration schema for generating static fonts from variable fonts",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"file",
|
|
9
|
+
"font_name",
|
|
10
|
+
"axes",
|
|
11
|
+
"variants"
|
|
12
|
+
],
|
|
13
|
+
"definitions": {
|
|
14
|
+
"axes": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"wght": {
|
|
18
|
+
"type": [
|
|
19
|
+
"number",
|
|
20
|
+
"null"
|
|
21
|
+
],
|
|
22
|
+
"minimum": 1,
|
|
23
|
+
"maximum": 1000,
|
|
24
|
+
"description": "Weight axis (1-1000, typically 100-900)"
|
|
25
|
+
},
|
|
26
|
+
"wdth": {
|
|
27
|
+
"type": [
|
|
28
|
+
"number",
|
|
29
|
+
"null"
|
|
30
|
+
],
|
|
31
|
+
"minimum": 25,
|
|
32
|
+
"maximum": 200,
|
|
33
|
+
"description": "Width axis (25-200%, typically 50-200)"
|
|
34
|
+
},
|
|
35
|
+
"opsz": {
|
|
36
|
+
"type": [
|
|
37
|
+
"number",
|
|
38
|
+
"null"
|
|
39
|
+
],
|
|
40
|
+
"minimum": 6,
|
|
41
|
+
"maximum": 144,
|
|
42
|
+
"description": "Optical size axis (6-144pt)"
|
|
43
|
+
},
|
|
44
|
+
"slnt": {
|
|
45
|
+
"type": [
|
|
46
|
+
"number",
|
|
47
|
+
"null"
|
|
48
|
+
],
|
|
49
|
+
"minimum": -90,
|
|
50
|
+
"maximum": 90,
|
|
51
|
+
"description": "Slant axis (-90° to 90°)"
|
|
52
|
+
},
|
|
53
|
+
"ital": {
|
|
54
|
+
"anyOf": [
|
|
55
|
+
{
|
|
56
|
+
"enum": [
|
|
57
|
+
0,
|
|
58
|
+
1
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "null"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"description": "Italic axis (0=roman, 1=italic)"
|
|
66
|
+
},
|
|
67
|
+
"GRAD": {
|
|
68
|
+
"type": [
|
|
69
|
+
"number",
|
|
70
|
+
"null"
|
|
71
|
+
],
|
|
72
|
+
"minimum": -200,
|
|
73
|
+
"maximum": 300,
|
|
74
|
+
"description": "Grade axis (-200 to 300)"
|
|
75
|
+
},
|
|
76
|
+
"ROND": {
|
|
77
|
+
"type": [
|
|
78
|
+
"number",
|
|
79
|
+
"null"
|
|
80
|
+
],
|
|
81
|
+
"description": "Roundness axis"
|
|
82
|
+
},
|
|
83
|
+
"CASL": {
|
|
84
|
+
"type": [
|
|
85
|
+
"number",
|
|
86
|
+
"null"
|
|
87
|
+
],
|
|
88
|
+
"description": "Casual axis"
|
|
89
|
+
},
|
|
90
|
+
"CRSV": {
|
|
91
|
+
"type": [
|
|
92
|
+
"number",
|
|
93
|
+
"null"
|
|
94
|
+
],
|
|
95
|
+
"description": "Cursive axis"
|
|
96
|
+
},
|
|
97
|
+
"MONO": {
|
|
98
|
+
"type": [
|
|
99
|
+
"number",
|
|
100
|
+
"null"
|
|
101
|
+
],
|
|
102
|
+
"description": "Monospace axis"
|
|
103
|
+
},
|
|
104
|
+
"SOFT": {
|
|
105
|
+
"type": [
|
|
106
|
+
"number",
|
|
107
|
+
"null"
|
|
108
|
+
],
|
|
109
|
+
"description": "Softness axis"
|
|
110
|
+
},
|
|
111
|
+
"WONK": {
|
|
112
|
+
"type": [
|
|
113
|
+
"number",
|
|
114
|
+
"null"
|
|
115
|
+
],
|
|
116
|
+
"description": "Wonky axis"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"additionalProperties": {
|
|
120
|
+
"type": [
|
|
121
|
+
"number",
|
|
122
|
+
"null"
|
|
123
|
+
],
|
|
124
|
+
"description": "Custom axis value"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"properties": {
|
|
129
|
+
"$schema": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"description": "JSON schema reference"
|
|
132
|
+
},
|
|
133
|
+
"file": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"description": "Path to the variable font file",
|
|
136
|
+
"pattern": "\\.(ttf|otf)$"
|
|
137
|
+
},
|
|
138
|
+
"font_name": {
|
|
139
|
+
"type": "string",
|
|
140
|
+
"minLength": 1,
|
|
141
|
+
"allOf": [
|
|
142
|
+
{
|
|
143
|
+
"pattern": "^\\S(?:.*\\S)?$"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"pattern": ".*[0-9A-Za-z].*"
|
|
147
|
+
}
|
|
148
|
+
],
|
|
149
|
+
"description": "Target font family name for generated static fonts (non-empty after trimming; must contain at least one letter/digit)"
|
|
150
|
+
},
|
|
151
|
+
"axes": {
|
|
152
|
+
"allOf": [
|
|
153
|
+
{
|
|
154
|
+
"$ref": "#/definitions/axes"
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"description": "Global axis default values for all variants"
|
|
158
|
+
},
|
|
159
|
+
"variants": {
|
|
160
|
+
"type": "object",
|
|
161
|
+
"description": "Static font variants to generate (object keys are variant names)",
|
|
162
|
+
"minProperties": 1,
|
|
163
|
+
"propertyNames": {
|
|
164
|
+
"type": "string",
|
|
165
|
+
"minLength": 1,
|
|
166
|
+
"allOf": [
|
|
167
|
+
{
|
|
168
|
+
"pattern": "^\\S(?:.*\\S)?$"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"pattern": ".*[0-9A-Za-z].*"
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
"additionalProperties": {
|
|
176
|
+
"$ref": "#/definitions/axes",
|
|
177
|
+
"description": "Variant-specific axis values"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"additionalProperties": false
|
|
182
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{".":"0.2.0"}
|
var2stat-0.2.0/schema.json
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://github.com/decipher3114/Var2Stat",
|
|
4
|
-
"title": "Var2Stat Configuration",
|
|
5
|
-
"description": "Configuration schema for generating static fonts from variable fonts",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["file", "font_name", "axes", "variants"],
|
|
8
|
-
"definitions": {
|
|
9
|
-
"axes": {
|
|
10
|
-
"type": "object",
|
|
11
|
-
"properties": {
|
|
12
|
-
"wght": {
|
|
13
|
-
"type": ["number", "null"],
|
|
14
|
-
"minimum": 1,
|
|
15
|
-
"maximum": 1000,
|
|
16
|
-
"description": "Weight axis (1-1000, typically 100-900)"
|
|
17
|
-
},
|
|
18
|
-
"wdth": {
|
|
19
|
-
"type": ["number", "null"],
|
|
20
|
-
"minimum": 25,
|
|
21
|
-
"maximum": 200,
|
|
22
|
-
"description": "Width axis (25-200%, typically 50-200)"
|
|
23
|
-
},
|
|
24
|
-
"opsz": {
|
|
25
|
-
"type": ["number", "null"],
|
|
26
|
-
"minimum": 6,
|
|
27
|
-
"maximum": 144,
|
|
28
|
-
"description": "Optical size axis (6-144pt)"
|
|
29
|
-
},
|
|
30
|
-
"slnt": {
|
|
31
|
-
"type": ["number", "null"],
|
|
32
|
-
"minimum": -90,
|
|
33
|
-
"maximum": 90,
|
|
34
|
-
"description": "Slant axis (-90° to 90°)"
|
|
35
|
-
},
|
|
36
|
-
"ital": {
|
|
37
|
-
"anyOf": [{ "enum": [0, 1] }, { "type": "null" }],
|
|
38
|
-
"description": "Italic axis (0=roman, 1=italic)"
|
|
39
|
-
},
|
|
40
|
-
"GRAD": {
|
|
41
|
-
"type": ["number", "null"],
|
|
42
|
-
"minimum": -200,
|
|
43
|
-
"maximum": 300,
|
|
44
|
-
"description": "Grade axis (-200 to 300)"
|
|
45
|
-
},
|
|
46
|
-
"ROND": {
|
|
47
|
-
"type": ["number", "null"],
|
|
48
|
-
"description": "Roundness axis"
|
|
49
|
-
},
|
|
50
|
-
"CASL": {
|
|
51
|
-
"type": ["number", "null"],
|
|
52
|
-
"description": "Casual axis"
|
|
53
|
-
},
|
|
54
|
-
"CRSV": {
|
|
55
|
-
"type": ["number", "null"],
|
|
56
|
-
"description": "Cursive axis"
|
|
57
|
-
},
|
|
58
|
-
"MONO": {
|
|
59
|
-
"type": ["number", "null"],
|
|
60
|
-
"description": "Monospace axis"
|
|
61
|
-
},
|
|
62
|
-
"SOFT": {
|
|
63
|
-
"type": ["number", "null"],
|
|
64
|
-
"description": "Softness axis"
|
|
65
|
-
},
|
|
66
|
-
"WONK": {
|
|
67
|
-
"type": ["number", "null"],
|
|
68
|
-
"description": "Wonky axis"
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
"additionalProperties": {
|
|
72
|
-
"type": ["number", "null"],
|
|
73
|
-
"description": "Custom axis value"
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
"properties": {
|
|
78
|
-
"$schema": {
|
|
79
|
-
"type": "string",
|
|
80
|
-
"description": "JSON schema reference"
|
|
81
|
-
},
|
|
82
|
-
"file": {
|
|
83
|
-
"type": "string",
|
|
84
|
-
"description": "Path to the variable font file",
|
|
85
|
-
"pattern": "\\.(ttf|otf)$"
|
|
86
|
-
},
|
|
87
|
-
"font_name": {
|
|
88
|
-
"type": ["string", "null"],
|
|
89
|
-
"description": "Target font family name for generated static fonts"
|
|
90
|
-
},
|
|
91
|
-
"axes": {
|
|
92
|
-
"allOf": [{ "$ref": "#/definitions/axes" }],
|
|
93
|
-
"description": "Global axis default values for all variants"
|
|
94
|
-
},
|
|
95
|
-
"variants": {
|
|
96
|
-
"type": "object",
|
|
97
|
-
"description": "Static font variants to generate keys",
|
|
98
|
-
"minProperties": 1,
|
|
99
|
-
"patternProperties": {
|
|
100
|
-
"^[a-zA-Z ]+$": {
|
|
101
|
-
"$ref": "#/definitions/axes",
|
|
102
|
-
"description": "Variant-specific axis values"
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
"additionalProperties": false
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
"additionalProperties": false
|
|
109
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|