fontisan 0.2.14 → 0.2.16
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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +79 -4
- data/docs/.gitignore +17 -0
- data/docs/.vitepress/config.ts +317 -0
- data/docs/.vitepress/theme/components/ApiMethod.vue +127 -0
- data/docs/.vitepress/theme/components/Badge.vue +51 -0
- data/docs/.vitepress/theme/components/FeatureComparison.vue +87 -0
- data/docs/.vitepress/theme/components/HeroCodeBlock.vue +98 -0
- data/docs/.vitepress/theme/components/WithinHero.vue +30 -0
- data/docs/.vitepress/theme/index.ts +22 -0
- data/docs/.vitepress/theme/style.css +330 -0
- data/docs/api/conversion-options.md +141 -0
- data/docs/api/converters/curve-converter.md +34 -0
- data/docs/api/converters/hint-converter.md +34 -0
- data/docs/api/converters/outline-converter.md +27 -0
- data/docs/api/font-loader.md +111 -0
- data/docs/api/font-writer.md +103 -0
- data/docs/api/index.md +79 -0
- data/docs/api/models/glyph-accessor.md +43 -0
- data/docs/api/models/glyph.md +40 -0
- data/docs/api/models/table-analyzer.md +35 -0
- data/docs/api/sfnt-font.md +53 -0
- data/docs/api/type1-font.md +43 -0
- data/docs/api/validators/font-validator.md +31 -0
- data/docs/api/validators/helper.md +36 -0
- data/docs/api/validators/profile.md +39 -0
- data/docs/cli/convert.md +87 -0
- data/docs/cli/dump-table.md +110 -0
- data/docs/cli/export.md +176 -0
- data/docs/cli/features.md +124 -0
- data/docs/cli/glyphs.md +90 -0
- data/docs/cli/index.md +208 -0
- data/docs/cli/info.md +254 -0
- data/docs/cli/instance.md +122 -0
- data/docs/cli/ls.md +95 -0
- data/docs/cli/optical-size.md +94 -0
- data/docs/cli/pack.md +125 -0
- data/docs/cli/scripts.md +105 -0
- data/docs/cli/subset.md +39 -0
- data/docs/cli/tables.md +84 -0
- data/docs/cli/unicode.md +101 -0
- data/docs/cli/validate.md +48 -0
- data/docs/cli/variable.md +126 -0
- data/docs/cli/version.md +46 -0
- data/docs/guide/cli/convert.md +108 -0
- data/docs/guide/cli/export.md +138 -0
- data/docs/guide/cli/index.md +99 -0
- data/docs/guide/cli/info.md +144 -0
- data/docs/guide/cli/pack.md +155 -0
- data/docs/guide/cli/subset.md +118 -0
- data/docs/guide/cli/validate.md +139 -0
- data/docs/guide/color-fonts/bitmaps.md +177 -0
- data/docs/guide/color-fonts/colr-cpal.md +175 -0
- data/docs/guide/color-fonts/index.md +140 -0
- data/docs/guide/color-fonts/svg.md +154 -0
- data/docs/guide/color.md +51 -0
- data/docs/guide/comparisons/font-validator.md +222 -0
- data/docs/guide/comparisons/fonttools.md +200 -0
- data/docs/guide/comparisons/index.md +83 -0
- data/docs/guide/comparisons/lcdf-typetools.md +205 -0
- data/docs/guide/contributing.md +279 -0
- data/docs/guide/conversion/collections.md +251 -0
- data/docs/guide/conversion/curves.md +246 -0
- data/docs/guide/conversion/index.md +157 -0
- data/docs/guide/conversion/options.md +251 -0
- data/docs/guide/conversion/ttf-otf.md +184 -0
- data/docs/guide/conversion/type1.md +208 -0
- data/docs/guide/conversion/web.md +240 -0
- data/docs/guide/conversion.md +39 -0
- data/docs/guide/formats/collections.md +147 -0
- data/docs/guide/formats/dfont.md +99 -0
- data/docs/guide/formats/index.md +65 -0
- data/docs/guide/formats/otf.md +103 -0
- data/docs/guide/formats/svg.md +97 -0
- data/docs/guide/formats/ttf.md +105 -0
- data/docs/guide/formats/type1.md +118 -0
- data/docs/guide/formats/woff.md +115 -0
- data/docs/guide/hinting/autohint.md +141 -0
- data/docs/guide/hinting/conversion.md +161 -0
- data/docs/guide/hinting/index.md +86 -0
- data/docs/guide/hinting/postscript.md +149 -0
- data/docs/guide/hinting/truetype.md +135 -0
- data/docs/guide/hinting.md +44 -0
- data/docs/guide/index.md +152 -0
- data/docs/guide/installation.md +116 -0
- data/docs/guide/migrations/extract-ttc.md +549 -0
- data/docs/guide/migrations/font-validator.md +260 -0
- data/docs/guide/migrations/fonttools.md +208 -0
- data/docs/guide/migrations/index.md +64 -0
- data/docs/guide/migrations/otfinfo.md +197 -0
- data/docs/guide/quick-start.md +204 -0
- data/docs/guide/type1.md +58 -0
- data/docs/guide/universal-outline.md +151 -0
- data/docs/guide/validation/custom.md +195 -0
- data/docs/guide/validation/helpers.md +188 -0
- data/docs/guide/validation/index.md +132 -0
- data/docs/guide/validation/profiles.md +156 -0
- data/docs/guide/validation.md +47 -0
- data/docs/guide/variable-fonts/advanced.md +231 -0
- data/docs/guide/variable-fonts/axes.md +209 -0
- data/docs/guide/variable-fonts/conversion.md +197 -0
- data/docs/guide/variable-fonts/index.md +84 -0
- data/docs/guide/variable-fonts/instances.md +187 -0
- data/docs/guide/variable-fonts/named-instances.md +194 -0
- data/docs/guide/variable-fonts/static.md +168 -0
- data/docs/guide/variable.md +58 -0
- data/docs/guide/woff.md +59 -0
- data/docs/index.md +136 -0
- data/docs/lychee.toml +37 -0
- data/docs/package-lock.json +2560 -0
- data/docs/package.json +15 -0
- data/docs/public/apple-touch-icon.png +0 -0
- data/docs/public/favicon-96x96.png +0 -0
- data/docs/public/favicon.ico +0 -0
- data/docs/public/favicon.svg +1 -0
- data/docs/public/logo-full.svg +1 -0
- data/docs/public/logo.svg +1 -0
- data/docs/public/site.webmanifest +21 -0
- data/docs/public/web-app-manifest-192x192.png +0 -0
- data/docs/public/web-app-manifest-512x512.png +0 -0
- data/fontisan.gemspec +1 -1
- data/lib/fontisan/commands/features_command.rb +0 -1
- data/lib/fontisan/commands/scripts_command.rb +0 -1
- data/lib/fontisan/loading_modes.rb +0 -2
- data/lib/fontisan/sfnt_font.rb +4 -3
- data/lib/fontisan/tables/glyf/compound_glyph.rb +0 -1
- data/lib/fontisan/variable/delta_applicator.rb +3 -3
- data/lib/fontisan/variation/optimizer.rb +0 -1
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan.rb +3 -2
- metadata +122 -4
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: subset
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# subset
|
|
6
|
+
|
|
7
|
+
Create font subsets.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
fontisan subset FONT [options]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Arguments
|
|
16
|
+
|
|
17
|
+
| Argument | Description |
|
|
18
|
+
|----------|-------------|
|
|
19
|
+
| `FONT` | Input font file |
|
|
20
|
+
|
|
21
|
+
## Options
|
|
22
|
+
|
|
23
|
+
| Option | Description |
|
|
24
|
+
|--------|-------------|
|
|
25
|
+
| `--chars TEXT` | Characters to include |
|
|
26
|
+
| `--chars-file PATH` | File with characters |
|
|
27
|
+
| `--unicodes RANGE` | Unicode ranges |
|
|
28
|
+
| `--glyphs LIST` | Glyph names/IDs |
|
|
29
|
+
| `--output PATH` | Output file |
|
|
30
|
+
| `--output-format FORMAT` | Output format |
|
|
31
|
+
| `--retain-gids` | Retain glyph IDs |
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
### By Characters
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Subset to specific characters
|
|
39
|
+
fontisan subset font.ttf --chars "ABCDEFabcdef0123456789" --output subset.ttf
|
|
40
|
+
|
|
41
|
+
# Subset to ASCII
|
|
42
|
+
fontisan subset font.ttf --chars "$(printf '%s' {a..z} {A..Z} {0..9})" --output ascii.ttf
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### By Unicode Range
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Basic Latin only
|
|
49
|
+
fontisan subset font.ttf --unicodes "U+0000-007F" --output basic-latin.ttf
|
|
50
|
+
|
|
51
|
+
# Latin + Latin-1
|
|
52
|
+
fontisan subset font.ttf --unicodes "U+0000-00FF" --output latin1.ttf
|
|
53
|
+
|
|
54
|
+
# Multiple ranges
|
|
55
|
+
fontisan subset font.ttf --unicodes "U+0000-007F,U+00A0-00FF" --output extended.ttf
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### By Glyph Names
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Specific glyphs
|
|
62
|
+
fontisan subset font.ttf --glyphs "A,B,C,a,b,c,zero,one,two" --output subset.ttf
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### From File
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Read characters from file
|
|
69
|
+
echo "Hello World" > chars.txt
|
|
70
|
+
fontisan subset font.ttf --chars-file chars.txt --output subset.ttf
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Retain Glyph IDs
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Keep original glyph IDs
|
|
77
|
+
fontisan subset font.ttf --chars "ABC" --retain-gids --output subset.ttf
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### With Format Conversion
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Subset and convert to WOFF2
|
|
84
|
+
fontisan subset font.ttf --chars "ABC" --output-format woff2 --output subset.woff2
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Subsetting Strategies
|
|
88
|
+
|
|
89
|
+
### Web Font Optimization
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Subset to page content
|
|
93
|
+
fontisan subset font.ttf --chars "$(cat content.html)" --output web-font.woff2
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Latin Only
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Standard Latin subset
|
|
100
|
+
fontisan subset font.ttf --unicodes "U+0000-024F,U+1E00-1EFF" --output latin.woff2
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Character Set Presets
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# ASCII
|
|
107
|
+
fontisan subset font.ttf --unicodes "U+0000-007F" --output ascii.ttf
|
|
108
|
+
|
|
109
|
+
# Basic Multilingual Plane (BMP)
|
|
110
|
+
fontisan subset font.ttf --unicodes "U+0000-FFFF" --output bmp.ttf
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Notes
|
|
114
|
+
|
|
115
|
+
- Subsetting removes unused glyphs and tables
|
|
116
|
+
- CFF fonts may require special handling
|
|
117
|
+
- Variable fonts are subset while preserving variation
|
|
118
|
+
- OpenType features are preserved for included glyphs
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: validate
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# validate
|
|
6
|
+
|
|
7
|
+
Validate fonts for correctness.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
fontisan validate FONT [options]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Arguments
|
|
16
|
+
|
|
17
|
+
| Argument | Description |
|
|
18
|
+
|----------|-------------|
|
|
19
|
+
| `FONT` | Font file to validate |
|
|
20
|
+
| `FONT ...` | Multiple files |
|
|
21
|
+
|
|
22
|
+
## Options
|
|
23
|
+
|
|
24
|
+
| Option | Description |
|
|
25
|
+
|--------|-------------|
|
|
26
|
+
| `-t, --profile PROFILE` | Validation profile |
|
|
27
|
+
| `-S, --summary` | Show summary only |
|
|
28
|
+
| `-T, --table` | Table format output |
|
|
29
|
+
| `--list` | List available profiles |
|
|
30
|
+
|
|
31
|
+
## Profiles
|
|
32
|
+
|
|
33
|
+
| Profile | Description |
|
|
34
|
+
|---------|-------------|
|
|
35
|
+
| `indexability` | Fast font discovery |
|
|
36
|
+
| `usability` | Installation compatibility |
|
|
37
|
+
| `production` | Comprehensive quality (default) |
|
|
38
|
+
| `web` | Web embedding readiness |
|
|
39
|
+
| `spec_compliance` | Full spec compliance |
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
### Basic Validation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
fontisan validate font.ttf
|
|
47
|
+
|
|
48
|
+
# Font: font.ttf
|
|
49
|
+
# Status: VALID
|
|
50
|
+
#
|
|
51
|
+
# Summary:
|
|
52
|
+
# Checks performed: 37
|
|
53
|
+
# Passed: 37
|
|
54
|
+
# Failed: 0
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### With Profile
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
fontisan validate font.ttf -t web
|
|
61
|
+
|
|
62
|
+
# Font: font.ttf
|
|
63
|
+
# Profile: web
|
|
64
|
+
# Status: INVALID
|
|
65
|
+
#
|
|
66
|
+
# Summary:
|
|
67
|
+
# Checks performed: 18
|
|
68
|
+
# Passed: 17
|
|
69
|
+
# Failed: 1
|
|
70
|
+
#
|
|
71
|
+
# Failed checks:
|
|
72
|
+
# web_font_tables - Missing required GSUB table
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Summary Only
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
fontisan validate font.ttf -S
|
|
79
|
+
|
|
80
|
+
# font.ttf: VALID (37/37 checks passed)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Table Format
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
fontisan validate font.ttf -T
|
|
87
|
+
|
|
88
|
+
# CHECK_ID | STATUS | SEVERITY | TABLE
|
|
89
|
+
# --------------------------------------------------
|
|
90
|
+
# required_tables | PASS | error | N/A
|
|
91
|
+
# name_version | PASS | error | name
|
|
92
|
+
# family_name | PASS | error | name
|
|
93
|
+
# head_magic | PASS | error | head
|
|
94
|
+
# ...
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Multiple Files
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
fontisan validate fonts/*.ttf
|
|
101
|
+
|
|
102
|
+
# fonts/regular.ttf: VALID
|
|
103
|
+
# fonts/bold.ttf: VALID
|
|
104
|
+
# fonts/italic.ttf: INVALID (1 error)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### List Profiles
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
fontisan validate --list
|
|
111
|
+
|
|
112
|
+
# Available validation profiles:
|
|
113
|
+
# indexability - Fast validation for font discovery
|
|
114
|
+
# usability - Basic usability for installation
|
|
115
|
+
# production - Comprehensive quality checks
|
|
116
|
+
# web - Web embedding and optimization
|
|
117
|
+
# spec_compliance - Full OpenType spec compliance
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Exit Codes
|
|
121
|
+
|
|
122
|
+
| Code | Meaning |
|
|
123
|
+
|------|---------|
|
|
124
|
+
| 0 | Valid |
|
|
125
|
+
| 1 | Invalid (errors found) |
|
|
126
|
+
| 2 | Error (file not found, etc.) |
|
|
127
|
+
|
|
128
|
+
## Use in Scripts
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Validate before release
|
|
132
|
+
fontisan validate font.ttf -t production
|
|
133
|
+
if [ $? -eq 0 ]; then
|
|
134
|
+
echo "Ready for release"
|
|
135
|
+
else
|
|
136
|
+
echo "Validation failed"
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
```
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Bitmap Color Fonts
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Bitmap Color Fonts
|
|
6
|
+
|
|
7
|
+
Bitmap color fonts store pixel images at specific sizes.
|
|
8
|
+
|
|
9
|
+
## Formats
|
|
10
|
+
|
|
11
|
+
| Format | Tables | Platform |
|
|
12
|
+
|--------|--------|----------|
|
|
13
|
+
| sbix | sbix | Apple |
|
|
14
|
+
| CBDT/CBLC | CBDT, CBLC | Google/Android |
|
|
15
|
+
|
|
16
|
+
## sbix (Apple)
|
|
17
|
+
|
|
18
|
+
sbix stores bitmap images at multiple resolutions.
|
|
19
|
+
|
|
20
|
+
### Structure
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
sbix = font.tables['sbix']
|
|
24
|
+
|
|
25
|
+
# Strikes (image sets at different sizes)
|
|
26
|
+
sbix.strikes.each do |strike|
|
|
27
|
+
puts "Strike at #{strike.ppem} ppem"
|
|
28
|
+
|
|
29
|
+
strike.glyphs.each do |glyph_id, image|
|
|
30
|
+
puts " Glyph #{glyph_id}: #{image.format}"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Image Formats
|
|
36
|
+
|
|
37
|
+
- `png ` — PNG image
|
|
38
|
+
- `jpg ` — JPEG image
|
|
39
|
+
- `tiff` — TIFF image
|
|
40
|
+
- `dupe` — Duplicate of another glyph
|
|
41
|
+
|
|
42
|
+
### Accessing Images
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
sbix = font.tables['sbix']
|
|
46
|
+
strike = sbix.strikes.first
|
|
47
|
+
|
|
48
|
+
glyph_id = 42
|
|
49
|
+
image = strike.image_for(glyph_id)
|
|
50
|
+
|
|
51
|
+
puts "Format: #{image.format}"
|
|
52
|
+
puts "Data size: #{image.data.length} bytes"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## CBDT/CBLC (Android)
|
|
56
|
+
|
|
57
|
+
CBDT/CBLC stores bitmap images for Android.
|
|
58
|
+
|
|
59
|
+
### Structure
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
cbdt = font.tables['CBDT']
|
|
63
|
+
cblc = font.tables['CBLC']
|
|
64
|
+
|
|
65
|
+
# CBLC has location data
|
|
66
|
+
cblc.strikes.each do |strike|
|
|
67
|
+
puts "Strike at #{strike.ppem}x#{strike.ppem}"
|
|
68
|
+
|
|
69
|
+
strike.glyph_ranges.each do |range|
|
|
70
|
+
puts " Glyphs #{range.start}..#{range.end}"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# CBDT has actual bitmap data
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Bitmap Formats
|
|
78
|
+
|
|
79
|
+
- **Small metrics** — For small glyphs
|
|
80
|
+
- **Big metrics** — For larger glyphs
|
|
81
|
+
- **Bit aligned** — 1, 2, 4, or 8 bits per pixel
|
|
82
|
+
|
|
83
|
+
### Accessing Bitmaps
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
cbdt = font.tables['CBDT']
|
|
87
|
+
cblc = font.tables['CBLC']
|
|
88
|
+
|
|
89
|
+
glyph_id = 42
|
|
90
|
+
strike_index = 0
|
|
91
|
+
|
|
92
|
+
# Get bitmap location
|
|
93
|
+
location = cblc.location(strike_index, glyph_id)
|
|
94
|
+
|
|
95
|
+
if location
|
|
96
|
+
# Get bitmap data
|
|
97
|
+
bitmap = cbdt.bitmap(location)
|
|
98
|
+
puts "Size: #{bitmap.width}x#{bitmap.height}"
|
|
99
|
+
puts "Data: #{bitmap.data.length} bytes"
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Working with Bitmaps
|
|
104
|
+
|
|
105
|
+
### List Available Sizes
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
sbix = font.tables['sbix']
|
|
109
|
+
|
|
110
|
+
if sbix
|
|
111
|
+
puts "Available sizes:"
|
|
112
|
+
sbix.strikes.each do |strike|
|
|
113
|
+
puts " #{strike.ppem} ppem"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Export Bitmaps
|
|
119
|
+
|
|
120
|
+
```ruby
|
|
121
|
+
sbix = font.tables['sbix']
|
|
122
|
+
strike = sbix.strikes.first
|
|
123
|
+
|
|
124
|
+
sbix.glyphs.each do |glyph_id, image|
|
|
125
|
+
if image.format == 'png '
|
|
126
|
+
File.write("glyph-#{glyph_id}.png", image.data)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Conversion
|
|
132
|
+
|
|
133
|
+
### Preserve Bitmaps
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Bitmaps preserved during same-format conversion
|
|
137
|
+
fontisan convert bitmap-font.ttf --to ttf --output bitmap-font.ttf
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Remove Bitmaps
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Create outline-only version
|
|
144
|
+
fontisan convert bitmap-font.ttf --to ttf --no-bitmaps --output outline-only.ttf
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Comparison
|
|
148
|
+
|
|
149
|
+
| Feature | sbix | CBDT/CBLC |
|
|
150
|
+
|---------|------|-----------|
|
|
151
|
+
| Platform | Apple | Android |
|
|
152
|
+
| Image formats | PNG, JPEG, TIFF | Raw bitmap |
|
|
153
|
+
| Scalability | No | No |
|
|
154
|
+
| File size | Larger | Smaller |
|
|
155
|
+
| Quality | Photo-realistic | Limited |
|
|
156
|
+
|
|
157
|
+
## Limitations
|
|
158
|
+
|
|
159
|
+
### Bitmap Scaling
|
|
160
|
+
|
|
161
|
+
Bitmap fonts don't scale well:
|
|
162
|
+
- Each strike is designed for a specific ppem
|
|
163
|
+
- Scaling up causes pixelation
|
|
164
|
+
- Scaling down causes aliasing
|
|
165
|
+
|
|
166
|
+
### Variable Fonts
|
|
167
|
+
|
|
168
|
+
Bitmap fonts don't work with variation:
|
|
169
|
+
- No interpolation possible
|
|
170
|
+
- Must provide strikes for each instance
|
|
171
|
+
|
|
172
|
+
## Best Practices
|
|
173
|
+
|
|
174
|
+
1. **Provide multiple strikes** — Cover common sizes
|
|
175
|
+
2. **Use PNG for sbix** — Best quality/size tradeoff
|
|
176
|
+
3. **Test on target platforms** — Rendering varies
|
|
177
|
+
4. **Consider outline fallback** — For unavailable sizes
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: COLR/CPAL Color Fonts
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# COLR/CPAL Color Fonts
|
|
6
|
+
|
|
7
|
+
COLR/CPAL is a modern color font format using layered vector glyphs.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
- **COLR** — Defines glyph layers and their order
|
|
12
|
+
- **CPAL** — Defines color palettes
|
|
13
|
+
|
|
14
|
+
## Structure
|
|
15
|
+
|
|
16
|
+
### COLR Table
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
colr = font.tables['COLR']
|
|
20
|
+
|
|
21
|
+
# Base glyphs (color emoji)
|
|
22
|
+
base_glyphs = colr.base_glyphs
|
|
23
|
+
|
|
24
|
+
# Layers for a base glyph
|
|
25
|
+
layers = colr.layers_for(glyph_id)
|
|
26
|
+
# Returns array of { glyph_id:, palette_index: }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### CPAL Table
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
cpal = font.tables['CPAL']
|
|
33
|
+
|
|
34
|
+
# Number of palettes
|
|
35
|
+
num_palettes = cpal.num_palettes
|
|
36
|
+
|
|
37
|
+
# Colors in a palette
|
|
38
|
+
palette_colors = cpal.palette(0) # First palette
|
|
39
|
+
|
|
40
|
+
# Get specific color
|
|
41
|
+
color = cpal.color(palette_index)
|
|
42
|
+
# Returns { r:, g:, b:, a: }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Working with COLR/CPAL
|
|
46
|
+
|
|
47
|
+
### List Color Glyphs
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
font = Fontisan::FontLoader.load('color-font.ttf')
|
|
51
|
+
colr = font.tables['COLR']
|
|
52
|
+
|
|
53
|
+
colr.base_glyphs.each do |glyph_id|
|
|
54
|
+
glyph_name = font.glyph_name(glyph_id)
|
|
55
|
+
puts "#{glyph_name} (#{glyph_id})"
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Get Layer Colors
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
colr = font.tables['COLR']
|
|
63
|
+
cpal = font.tables['CPAL']
|
|
64
|
+
|
|
65
|
+
colr.base_glyphs.each do |glyph_id|
|
|
66
|
+
puts "Glyph #{glyph_id}:"
|
|
67
|
+
|
|
68
|
+
colr.layers_for(glyph_id).each do |layer|
|
|
69
|
+
color = cpal.color(layer[:palette_index])
|
|
70
|
+
layer_name = font.glyph_name(layer[:glyph_id])
|
|
71
|
+
puts " #{layer_name}: rgba(#{color[:r]}, #{color[:g]}, #{color[:b]}, #{color[:a]})"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Color Palettes
|
|
77
|
+
|
|
78
|
+
### Default Palette
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
cpal = font.tables['CPAL']
|
|
82
|
+
|
|
83
|
+
# First palette (default)
|
|
84
|
+
default_palette = cpal.palette(0)
|
|
85
|
+
puts "Default palette has #{default_palette.length} colors"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Multiple Palettes
|
|
89
|
+
|
|
90
|
+
Some fonts have alternative color schemes:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
cpal = font.tables['CPAL']
|
|
94
|
+
|
|
95
|
+
(0...cpal.num_palettes).each do |palette_index|
|
|
96
|
+
puts "Palette #{palette_index}:"
|
|
97
|
+
cpal.palette(palette_index).each_with_index do |color, i|
|
|
98
|
+
puts " Color #{i}: rgba(#{color[:r]}, #{color[:g]}, #{color[:b]}, #{color[:a]})"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Conversion
|
|
104
|
+
|
|
105
|
+
### Preserve COLR/CPAL
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# COLR/CPAL is preserved during same-format conversion
|
|
109
|
+
fontisan convert color-font.ttf --to otf --output color-font.otf
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Flatten to PNG
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Export color glyphs as PNG images
|
|
116
|
+
fontisan export color-font.ttf --format png --output ./images/
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Creating COLR/CPAL
|
|
120
|
+
|
|
121
|
+
Currently Fontisan focuses on reading and preserving COLR/CPAL. For creation, consider:
|
|
122
|
+
|
|
123
|
+
1. Design glyphs in layers
|
|
124
|
+
2. Assign palette indices
|
|
125
|
+
3. Define color palettes
|
|
126
|
+
4. Use font editor (Glyphs, FontForge)
|
|
127
|
+
|
|
128
|
+
## Browser Support
|
|
129
|
+
|
|
130
|
+
| Browser | Version |
|
|
131
|
+
|---------|---------|
|
|
132
|
+
| Chrome | 98+ |
|
|
133
|
+
| Firefox | 105+ |
|
|
134
|
+
| Safari | 15.4+ |
|
|
135
|
+
| Edge | 98+ |
|
|
136
|
+
|
|
137
|
+
## Validation
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# Validate color font
|
|
141
|
+
fontisan validate color-font.ttf
|
|
142
|
+
|
|
143
|
+
# Check COLR structure
|
|
144
|
+
fontisan info color-font.ttf
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Example
|
|
148
|
+
|
|
149
|
+
### Analyze Color Emoji Font
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
font = Fontisan::FontLoader.load('emoji.ttf')
|
|
153
|
+
colr = font.tables['COLR']
|
|
154
|
+
cpal = font.tables['CPAL']
|
|
155
|
+
|
|
156
|
+
puts "Color glyphs: #{colr.base_glyphs.length}"
|
|
157
|
+
puts "Palettes: #{cpal.num_palettes}"
|
|
158
|
+
|
|
159
|
+
# Find glyph for specific character
|
|
160
|
+
cmap = font.tables['cmap']
|
|
161
|
+
glyph_id = cmap.glyph_id_for('😀')
|
|
162
|
+
|
|
163
|
+
if colr.base_glyphs.include?(glyph_id)
|
|
164
|
+
puts "😀 is a color glyph"
|
|
165
|
+
|
|
166
|
+
layers = colr.layers_for(glyph_id)
|
|
167
|
+
puts "Composed of #{layers.length} layers:"
|
|
168
|
+
|
|
169
|
+
layers.each do |layer|
|
|
170
|
+
layer_name = font.glyph_name(layer[:glyph_id])
|
|
171
|
+
color = cpal.color(layer[:palette_index])
|
|
172
|
+
puts " #{layer_name}: rgba(#{color[:r]}, #{color[:g]}, #{color[:b]})"
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
```
|