fontisan 0.2.13 → 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 +56 -196
- data/Gemfile +1 -1
- 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/info_command.rb +5 -5
- data/lib/fontisan/commands/scripts_command.rb +0 -1
- data/lib/fontisan/converters/format_converter.rb +2 -1
- data/lib/fontisan/converters/type1_converter.rb +65 -60
- data/lib/fontisan/hints/hint_converter.rb +2 -1
- data/lib/fontisan/loading_modes.rb +0 -2
- data/lib/fontisan/open_type_font.rb +0 -40
- data/lib/fontisan/sfnt_font.rb +41 -22
- data/lib/fontisan/tables/glyf/compound_glyph.rb +0 -1
- data/lib/fontisan/true_type_collection.rb +8 -8
- data/lib/fontisan/true_type_font.rb +1 -59
- data/lib/fontisan/type1/afm_parser.rb +2 -1
- data/lib/fontisan/type1/cff_to_type1_converter.rb +24 -19
- data/lib/fontisan/type1/private_dict.rb +28 -7
- data/lib/fontisan/type1/seac_expander.rb +22 -17
- 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/woff2_font.rb +2 -2
- data/lib/fontisan/woff_font.rb +3 -3
- data/lib/fontisan.rb +3 -2
- metadata +122 -4
|
@@ -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
|
+
```
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Color Fonts Overview
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Color Fonts Overview
|
|
6
|
+
|
|
7
|
+
Fontisan supports multiple color font formats for modern typography.
|
|
8
|
+
|
|
9
|
+
## Supported Formats
|
|
10
|
+
|
|
11
|
+
| Format | Tables | Description |
|
|
12
|
+
|--------|--------|-------------|
|
|
13
|
+
| COLR/CPAL | COLR, CPAL | Layered vector glyphs |
|
|
14
|
+
| sbix | sbix | Bitmap images (Apple) |
|
|
15
|
+
| CBDT/CBLC | CBDT, CBLC | Bitmap images (Google) |
|
|
16
|
+
| SVG | SVG | SVG embedded in font |
|
|
17
|
+
|
|
18
|
+
## COLR/CPAL
|
|
19
|
+
|
|
20
|
+
COLR/CPAL uses layered vector glyphs with palette-based coloring.
|
|
21
|
+
|
|
22
|
+
### Advantages
|
|
23
|
+
|
|
24
|
+
- **Scalable** — Vectors scale to any size
|
|
25
|
+
- **Small file size** — Efficient compression
|
|
26
|
+
- **Variable support** — Works with variable fonts
|
|
27
|
+
- **Wide support** — Modern browsers and apps
|
|
28
|
+
|
|
29
|
+
### Structure
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
COLR table:
|
|
33
|
+
- Glyph layers (which glyphs make up each color glyph)
|
|
34
|
+
- Layer order (back to front)
|
|
35
|
+
|
|
36
|
+
CPAL table:
|
|
37
|
+
- Color palettes (sets of colors)
|
|
38
|
+
- Color values (RGBA for each palette entry)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## sbix
|
|
42
|
+
|
|
43
|
+
sbix stores bitmap images at multiple resolutions (Apple format).
|
|
44
|
+
|
|
45
|
+
### Advantages
|
|
46
|
+
|
|
47
|
+
- **Photo-realistic** — Any image quality
|
|
48
|
+
- **Multiple sizes** — Different bitmaps for different sizes
|
|
49
|
+
|
|
50
|
+
### Structure
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
sbix table:
|
|
54
|
+
- Strikes (image sets at different sizes)
|
|
55
|
+
- Per-glyph images (PNG, JPG, etc.)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## CBDT/CBLC
|
|
59
|
+
|
|
60
|
+
CBDT/CBLC stores bitmap images (Google/Android format).
|
|
61
|
+
|
|
62
|
+
### Advantages
|
|
63
|
+
|
|
64
|
+
- **Android support** — Primary Android bitmap format
|
|
65
|
+
- **Multiple sizes** — Different bitmaps per ppem
|
|
66
|
+
|
|
67
|
+
### Structure
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
CBLC table:
|
|
71
|
+
- Location data (where each bitmap is)
|
|
72
|
+
- Size information
|
|
73
|
+
|
|
74
|
+
CBDT table:
|
|
75
|
+
- Bitmap data (actual images)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## SVG Color Fonts
|
|
79
|
+
|
|
80
|
+
SVG fonts embed complete SVG documents.
|
|
81
|
+
|
|
82
|
+
### Advantages
|
|
83
|
+
|
|
84
|
+
- **Full SVG features** — Gradients, effects, etc.
|
|
85
|
+
- **Standalone** — No external resources
|
|
86
|
+
|
|
87
|
+
### Limitations
|
|
88
|
+
|
|
89
|
+
- **Large file size** — XML overhead
|
|
90
|
+
- **Limited support** — Not all browsers
|
|
91
|
+
|
|
92
|
+
## Guides
|
|
93
|
+
|
|
94
|
+
- [COLR/CPAL](/guide/color-fonts/colr-cpal) — Vector layered color fonts
|
|
95
|
+
- [Bitmaps](/guide/color-fonts/bitmaps) — sbix and CBDT/CBLC
|
|
96
|
+
- [SVG Color](/guide/color-fonts/svg) — SVG-based color fonts
|
|
97
|
+
|
|
98
|
+
## Quick Start
|
|
99
|
+
|
|
100
|
+
### Check for Color Tables
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
font = Fontisan::FontLoader.load('font.ttf')
|
|
104
|
+
|
|
105
|
+
if font.tables['COLR']
|
|
106
|
+
puts "COLR/CPAL color font"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if font.tables['sbix']
|
|
110
|
+
puts "sbix bitmap font"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
if font.tables['CBDT']
|
|
114
|
+
puts "CBDT/CBLC bitmap font"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if font.tables['SVG']
|
|
118
|
+
puts "SVG color font"
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Get Color Information
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
colr = font.tables['COLR']
|
|
126
|
+
cpal = font.tables['CPAL']
|
|
127
|
+
|
|
128
|
+
if colr && cpal
|
|
129
|
+
# Get base glyphs (color emoji)
|
|
130
|
+
colr.base_glyphs.each do |glyph_id|
|
|
131
|
+
puts "Base glyph: #{glyph_id}"
|
|
132
|
+
puts " Layers: #{colr.layers_for(glyph_id).length}"
|
|
133
|
+
|
|
134
|
+
colr.layers_for(glyph_id).each do |layer|
|
|
135
|
+
color = cpal.color(layer[:palette_index])
|
|
136
|
+
puts " Layer #{layer[:glyph_id]}: #{color}"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
```
|