fontisan 0.2.3 → 0.2.4
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 +92 -40
- data/README.adoc +262 -3
- data/Rakefile +20 -7
- data/lib/fontisan/commands/base_command.rb +2 -19
- data/lib/fontisan/commands/convert_command.rb +16 -13
- data/lib/fontisan/commands/info_command.rb +88 -0
- data/lib/fontisan/config/conversion_matrix.yml +58 -20
- data/lib/fontisan/converters/outline_converter.rb +6 -3
- data/lib/fontisan/converters/svg_generator.rb +45 -0
- data/lib/fontisan/converters/woff2_encoder.rb +106 -13
- data/lib/fontisan/models/bitmap_glyph.rb +123 -0
- data/lib/fontisan/models/bitmap_strike.rb +94 -0
- data/lib/fontisan/models/color_glyph.rb +57 -0
- data/lib/fontisan/models/color_layer.rb +53 -0
- data/lib/fontisan/models/color_palette.rb +60 -0
- data/lib/fontisan/models/font_info.rb +26 -0
- data/lib/fontisan/models/svg_glyph.rb +89 -0
- data/lib/fontisan/open_type_font.rb +6 -0
- data/lib/fontisan/optimizers/charstring_rewriter.rb +19 -8
- data/lib/fontisan/optimizers/pattern_analyzer.rb +4 -2
- data/lib/fontisan/optimizers/subroutine_builder.rb +6 -5
- data/lib/fontisan/optimizers/subroutine_optimizer.rb +5 -2
- data/lib/fontisan/pipeline/output_writer.rb +2 -2
- data/lib/fontisan/tables/cbdt.rb +169 -0
- data/lib/fontisan/tables/cblc.rb +290 -0
- data/lib/fontisan/tables/cff.rb +6 -12
- data/lib/fontisan/tables/colr.rb +291 -0
- data/lib/fontisan/tables/cpal.rb +281 -0
- data/lib/fontisan/tables/glyf/glyph_builder.rb +5 -1
- data/lib/fontisan/tables/sbix.rb +379 -0
- data/lib/fontisan/tables/svg.rb +301 -0
- data/lib/fontisan/true_type_font.rb +6 -0
- data/lib/fontisan/validation/woff2_header_validator.rb +278 -0
- data/lib/fontisan/validation/woff2_table_validator.rb +270 -0
- data/lib/fontisan/validation/woff2_validator.rb +248 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/directory.rb +40 -11
- data/lib/fontisan/woff2/table_transformer.rb +506 -73
- data/lib/fontisan/woff2_font.rb +29 -9
- data/lib/fontisan/woff_font.rb +17 -4
- data/lib/fontisan.rb +12 -0
- metadata +17 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: efdc28872530847f76e7d586ed2ac859d0860275a9289936e742a467f6139799
|
|
4
|
+
data.tar.gz: b1af1580a3ef2d2b1dbb449c3c7295194497b358d8819d5f18496282b7730ed5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 533a181937b1b46ea380a14a9c108ba19678f9bf1c0fcb8a5b51f0e0fb43630c69180c74679ad91da49fa73d136ec783e69b460ce8f031834e3fc500ca666139
|
|
7
|
+
data.tar.gz: '09e9d4ff6a4694df5d6756dc9f4dc056f1951d66ca456079ad40336a41e32b804b15aa597f4089d20a20474a75faf680782c6cca2bf5ee8c2d5c30eb41e12623'
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on
|
|
3
|
+
# on 2026-01-03 02:11:10 UTC using RuboCop version 1.81.7.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
@@ -12,6 +12,14 @@ Gemspec/RequiredRubyVersion:
|
|
|
12
12
|
Exclude:
|
|
13
13
|
- 'fontisan.gemspec'
|
|
14
14
|
|
|
15
|
+
# Offense count: 1
|
|
16
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
17
|
+
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
|
18
|
+
# SupportedStyles: with_first_argument, with_fixed_indentation
|
|
19
|
+
Layout/ArgumentAlignment:
|
|
20
|
+
Exclude:
|
|
21
|
+
- 'lib/fontisan/converters/woff2_encoder.rb'
|
|
22
|
+
|
|
15
23
|
# Offense count: 1
|
|
16
24
|
# This cop supports safe autocorrection (--autocorrect).
|
|
17
25
|
Layout/EmptyLineAfterGuardClause:
|
|
@@ -24,21 +32,32 @@ Layout/EmptyLinesAroundExceptionHandlingKeywords:
|
|
|
24
32
|
Exclude:
|
|
25
33
|
- 'lib/fontisan/hints/hint_validator.rb'
|
|
26
34
|
|
|
27
|
-
# Offense count:
|
|
35
|
+
# Offense count: 23
|
|
28
36
|
# This cop supports safe autocorrection (--autocorrect).
|
|
29
37
|
# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
|
|
30
38
|
Layout/ExtraSpacing:
|
|
31
39
|
Exclude:
|
|
40
|
+
- 'lib/fontisan/commands/info_command.rb'
|
|
32
41
|
- 'lib/fontisan/hints/truetype_instruction_analyzer.rb'
|
|
42
|
+
- 'lib/fontisan/tables/sbix.rb'
|
|
43
|
+
- 'spec/fontisan/tables/sbix_spec.rb'
|
|
44
|
+
- 'spec/integration/color_emoji_fonts_spec.rb'
|
|
33
45
|
|
|
34
|
-
# Offense count:
|
|
46
|
+
# Offense count: 1270
|
|
35
47
|
# This cop supports safe autocorrection (--autocorrect).
|
|
36
48
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
|
|
37
49
|
# URISchemes: http, https
|
|
38
50
|
Layout/LineLength:
|
|
39
51
|
Enabled: false
|
|
40
52
|
|
|
41
|
-
# Offense count:
|
|
53
|
+
# Offense count: 18
|
|
54
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
55
|
+
# Configuration parameters: AllowInHeredoc.
|
|
56
|
+
Layout/TrailingWhitespace:
|
|
57
|
+
Exclude:
|
|
58
|
+
- 'spec/fontisan/converters/woff2_encoder_integration_spec.rb'
|
|
59
|
+
|
|
60
|
+
# Offense count: 27
|
|
42
61
|
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
|
|
43
62
|
Lint/DuplicateBranch:
|
|
44
63
|
Enabled: false
|
|
@@ -79,17 +98,26 @@ Lint/StructNewOverride:
|
|
|
79
98
|
Exclude:
|
|
80
99
|
- 'lib/fontisan/optimizers/pattern_analyzer.rb'
|
|
81
100
|
|
|
82
|
-
# Offense count:
|
|
101
|
+
# Offense count: 1
|
|
102
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
103
|
+
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
|
|
104
|
+
Lint/UnusedBlockArgument:
|
|
105
|
+
Exclude:
|
|
106
|
+
- 'spec/fontisan/tables/sbix_spec.rb'
|
|
107
|
+
|
|
108
|
+
# Offense count: 8
|
|
83
109
|
# This cop supports safe autocorrection (--autocorrect).
|
|
84
110
|
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
|
|
85
111
|
# NotImplementedExceptions: NotImplementedError
|
|
86
112
|
Lint/UnusedMethodArgument:
|
|
87
113
|
Exclude:
|
|
88
114
|
- 'lib/fontisan/font_loader.rb'
|
|
115
|
+
- 'lib/fontisan/optimizers/charstring_rewriter.rb'
|
|
89
116
|
- 'lib/fontisan/tables/cff2/private_dict_blend_handler.rb'
|
|
90
117
|
- 'lib/fontisan/utilities/brotli_wrapper.rb'
|
|
91
118
|
- 'lib/fontisan/variation/table_accessor.rb'
|
|
92
119
|
- 'lib/fontisan/woff2/glyf_transformer.rb'
|
|
120
|
+
- 'lib/fontisan/woff_font.rb'
|
|
93
121
|
|
|
94
122
|
# Offense count: 3
|
|
95
123
|
# This cop supports safe autocorrection (--autocorrect).
|
|
@@ -98,39 +126,39 @@ Lint/UselessAssignment:
|
|
|
98
126
|
- 'lib/fontisan/hints/truetype_instruction_analyzer.rb'
|
|
99
127
|
- 'spec/fontisan/hints/hint_round_trip_spec.rb'
|
|
100
128
|
|
|
101
|
-
# Offense count:
|
|
129
|
+
# Offense count: 462
|
|
102
130
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
103
131
|
Metrics/AbcSize:
|
|
104
132
|
Enabled: false
|
|
105
133
|
|
|
106
|
-
# Offense count:
|
|
134
|
+
# Offense count: 29
|
|
107
135
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
|
|
108
136
|
# AllowedMethods: refine
|
|
109
137
|
Metrics/BlockLength:
|
|
110
|
-
Max:
|
|
138
|
+
Max: 91
|
|
111
139
|
|
|
112
140
|
# Offense count: 8
|
|
113
141
|
# Configuration parameters: CountBlocks, CountModifierForms.
|
|
114
142
|
Metrics/BlockNesting:
|
|
115
143
|
Max: 5
|
|
116
144
|
|
|
117
|
-
# Offense count:
|
|
145
|
+
# Offense count: 240
|
|
118
146
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
119
147
|
Metrics/CyclomaticComplexity:
|
|
120
148
|
Enabled: false
|
|
121
149
|
|
|
122
|
-
# Offense count:
|
|
150
|
+
# Offense count: 699
|
|
123
151
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
124
152
|
Metrics/MethodLength:
|
|
125
153
|
Max: 135
|
|
126
154
|
|
|
127
|
-
# Offense count:
|
|
155
|
+
# Offense count: 18
|
|
128
156
|
# Configuration parameters: CountKeywordArgs.
|
|
129
157
|
Metrics/ParameterLists:
|
|
130
158
|
Max: 39
|
|
131
159
|
MaxOptionalParameters: 4
|
|
132
160
|
|
|
133
|
-
# Offense count:
|
|
161
|
+
# Offense count: 174
|
|
134
162
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
135
163
|
Metrics/PerceivedComplexity:
|
|
136
164
|
Enabled: false
|
|
@@ -145,29 +173,14 @@ Naming/MethodParameterName:
|
|
|
145
173
|
- 'lib/fontisan/tables/glyf/curve_converter.rb'
|
|
146
174
|
- 'lib/fontisan/variation/optimizer.rb'
|
|
147
175
|
|
|
148
|
-
# Offense count:
|
|
176
|
+
# Offense count: 84
|
|
149
177
|
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
|
150
178
|
# SupportedStyles: snake_case, normalcase, non_integer
|
|
151
179
|
# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
|
|
152
180
|
Naming/VariableNumber:
|
|
153
|
-
|
|
154
|
-
- 'lib/fontisan/models/ttx/tables/os2_table.rb'
|
|
155
|
-
- 'lib/fontisan/subset/table_subsetter.rb'
|
|
156
|
-
- 'lib/fontisan/tables/cff/charset.rb'
|
|
157
|
-
- 'lib/fontisan/tables/cff/encoding.rb'
|
|
158
|
-
- 'lib/fontisan/tables/cmap.rb'
|
|
159
|
-
- 'lib/fontisan/tables/glyf.rb'
|
|
160
|
-
- 'lib/fontisan/tables/glyf/compound_glyph.rb'
|
|
161
|
-
- 'lib/fontisan/tables/glyf/glyph_builder.rb'
|
|
162
|
-
- 'lib/fontisan/tables/glyf/simple_glyph.rb'
|
|
163
|
-
- 'spec/fontisan/collection/table_deduplicator_spec.rb'
|
|
164
|
-
- 'spec/fontisan/tables/cff/charset_spec.rb'
|
|
165
|
-
- 'spec/fontisan/tables/glyf/glyph_builder_spec.rb'
|
|
166
|
-
- 'spec/fontisan/tables/glyf_spec.rb'
|
|
167
|
-
- 'spec/fontisan/tables/hmtx_spec.rb'
|
|
168
|
-
- 'spec/fontisan/tables/maxp_spec.rb'
|
|
181
|
+
Enabled: false
|
|
169
182
|
|
|
170
|
-
# Offense count:
|
|
183
|
+
# Offense count: 25
|
|
171
184
|
# Configuration parameters: MinSize.
|
|
172
185
|
Performance/CollectionLiteralInLoop:
|
|
173
186
|
Exclude:
|
|
@@ -177,8 +190,10 @@ Performance/CollectionLiteralInLoop:
|
|
|
177
190
|
- 'lib/fontisan/tables/cff/private_dict_writer.rb'
|
|
178
191
|
- 'lib/fontisan/tables/cff2.rb'
|
|
179
192
|
- 'lib/fontisan/tables/cff2/region_matcher.rb'
|
|
193
|
+
- 'lib/fontisan/tables/sbix.rb'
|
|
180
194
|
- 'lib/fontisan/true_type_font.rb'
|
|
181
195
|
- 'lib/fontisan/variation/validator.rb'
|
|
196
|
+
- 'lib/fontisan/woff2/table_transformer.rb'
|
|
182
197
|
- 'spec/fontisan/cli_spec.rb'
|
|
183
198
|
- 'spec/fontisan/commands/glyphs_command_spec.rb'
|
|
184
199
|
- 'spec/fontisan/commands/info_command_spec.rb'
|
|
@@ -202,36 +217,40 @@ RSpec/AnyInstance:
|
|
|
202
217
|
- 'spec/fontisan/variation/parallel_generator_spec.rb'
|
|
203
218
|
- 'spec/integration/format_conversion_spec.rb'
|
|
204
219
|
|
|
205
|
-
# Offense count:
|
|
220
|
+
# Offense count: 105
|
|
206
221
|
# Configuration parameters: Prefixes, AllowedPatterns.
|
|
207
222
|
# Prefixes: when, with, without
|
|
208
223
|
RSpec/ContextWording:
|
|
209
224
|
Enabled: false
|
|
210
225
|
|
|
211
|
-
# Offense count:
|
|
226
|
+
# Offense count: 21
|
|
212
227
|
# Configuration parameters: IgnoredMetadata.
|
|
213
228
|
RSpec/DescribeClass:
|
|
214
229
|
Enabled: false
|
|
215
230
|
|
|
216
|
-
# Offense count:
|
|
231
|
+
# Offense count: 2
|
|
217
232
|
RSpec/DescribeMethod:
|
|
218
233
|
Exclude:
|
|
219
234
|
- 'spec/fontisan/collection/variable_font_builder_spec.rb'
|
|
235
|
+
- 'spec/fontisan/converters/woff2_encoder_integration_spec.rb'
|
|
220
236
|
|
|
221
|
-
# Offense count:
|
|
237
|
+
# Offense count: 1263
|
|
222
238
|
# Configuration parameters: CountAsOne.
|
|
223
239
|
RSpec/ExampleLength:
|
|
224
240
|
Max: 66
|
|
225
241
|
|
|
226
|
-
# Offense count:
|
|
242
|
+
# Offense count: 7
|
|
227
243
|
# This cop supports safe autocorrection (--autocorrect).
|
|
228
244
|
RSpec/ExpectActual:
|
|
229
245
|
Exclude:
|
|
230
246
|
- '**/spec/routing/**/*'
|
|
231
247
|
- 'spec/fontisan/commands/subset_command_spec.rb'
|
|
248
|
+
- 'spec/fontisan/converters/extended_woff2_spec.rb'
|
|
249
|
+
- 'spec/fontisan/converters/woff2_round_trip_spec.rb'
|
|
232
250
|
- 'spec/fontisan/subset/builder_spec.rb'
|
|
233
251
|
- 'spec/fontisan/utils/thread_pool_spec.rb'
|
|
234
252
|
- 'spec/fontisan/variation/validator_spec.rb'
|
|
253
|
+
- 'spec/fontisan/woff2/table_transformer_spec.rb'
|
|
235
254
|
|
|
236
255
|
# Offense count: 1
|
|
237
256
|
RSpec/IdenticalEqualityAssertion:
|
|
@@ -280,19 +299,20 @@ RSpec/MessageSpies:
|
|
|
280
299
|
- 'spec/fontisan/variation/instance_writer_spec.rb'
|
|
281
300
|
- 'spec/fontisan/woff2_font_spec.rb'
|
|
282
301
|
|
|
283
|
-
# Offense count:
|
|
302
|
+
# Offense count: 5
|
|
284
303
|
RSpec/MultipleDescribes:
|
|
285
304
|
Exclude:
|
|
286
305
|
- 'spec/fontisan/loading_modes_spec.rb'
|
|
287
306
|
- 'spec/fontisan/models/table_info_spec.rb'
|
|
288
307
|
- 'spec/fontisan/utils/thread_pool_spec.rb'
|
|
308
|
+
- 'spec/fontisan/validation/woff2_validator_spec.rb'
|
|
289
309
|
- 'spec/fontisan/variation/cache_spec.rb'
|
|
290
310
|
|
|
291
|
-
# Offense count:
|
|
311
|
+
# Offense count: 1606
|
|
292
312
|
RSpec/MultipleExpectations:
|
|
293
313
|
Max: 22
|
|
294
314
|
|
|
295
|
-
# Offense count:
|
|
315
|
+
# Offense count: 135
|
|
296
316
|
# Configuration parameters: AllowSubject.
|
|
297
317
|
RSpec/MultipleMemoizedHelpers:
|
|
298
318
|
Max: 13
|
|
@@ -375,7 +395,7 @@ Security/Open:
|
|
|
375
395
|
Exclude:
|
|
376
396
|
- 'Rakefile'
|
|
377
397
|
|
|
378
|
-
# Offense count:
|
|
398
|
+
# Offense count: 9
|
|
379
399
|
# This cop supports safe autocorrection (--autocorrect).
|
|
380
400
|
# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
|
|
381
401
|
# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
|
|
@@ -384,8 +404,11 @@ Security/Open:
|
|
|
384
404
|
# AllowedMethods: lambda, proc, it
|
|
385
405
|
Style/BlockDelimiters:
|
|
386
406
|
Exclude:
|
|
407
|
+
- 'spec/fontisan/converters/extended_woff2_spec.rb'
|
|
387
408
|
- 'spec/fontisan/hints/hint_validator_spec.rb'
|
|
388
409
|
- 'spec/fontisan/hints/truetype_instruction_analyzer_spec.rb'
|
|
410
|
+
- 'spec/fontisan/tables/colr_spec.rb'
|
|
411
|
+
- 'spec/fontisan/tables/cpal_spec.rb'
|
|
389
412
|
|
|
390
413
|
# Offense count: 1
|
|
391
414
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
@@ -427,6 +450,13 @@ Style/HashLikeCase:
|
|
|
427
450
|
- 'lib/fontisan/commands/unpack_command.rb'
|
|
428
451
|
- 'lib/fontisan/models/validation_report.rb'
|
|
429
452
|
|
|
453
|
+
# Offense count: 1
|
|
454
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
455
|
+
# Configuration parameters: AllowIfModifier.
|
|
456
|
+
Style/IfInsideElse:
|
|
457
|
+
Exclude:
|
|
458
|
+
- 'Rakefile'
|
|
459
|
+
|
|
430
460
|
# Offense count: 6
|
|
431
461
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
432
462
|
# Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns.
|
|
@@ -454,6 +484,13 @@ Style/SoleNestedConditional:
|
|
|
454
484
|
Exclude:
|
|
455
485
|
- 'lib/fontisan/hints/hint_validator.rb'
|
|
456
486
|
|
|
487
|
+
# Offense count: 4
|
|
488
|
+
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
489
|
+
# Configuration parameters: Mode.
|
|
490
|
+
Style/StringConcatenation:
|
|
491
|
+
Exclude:
|
|
492
|
+
- 'spec/fontisan/tables/cbdt_spec.rb'
|
|
493
|
+
|
|
457
494
|
# Offense count: 2
|
|
458
495
|
# This cop supports safe autocorrection (--autocorrect).
|
|
459
496
|
# Configuration parameters: .
|
|
@@ -462,13 +499,28 @@ Style/SymbolArray:
|
|
|
462
499
|
EnforcedStyle: percent
|
|
463
500
|
MinSize: 3
|
|
464
501
|
|
|
465
|
-
# Offense count:
|
|
502
|
+
# Offense count: 53
|
|
503
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
504
|
+
# Configuration parameters: EnforcedStyleForMultiline.
|
|
505
|
+
# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
|
|
506
|
+
Style/TrailingCommaInArguments:
|
|
507
|
+
Exclude:
|
|
508
|
+
- 'lib/fontisan/commands/info_command.rb'
|
|
509
|
+
- 'spec/fontisan/converters/woff2_encoder_integration_spec.rb'
|
|
510
|
+
- 'spec/fontisan/optimizers/charstring_rewriter_spec.rb'
|
|
511
|
+
- 'spec/fontisan/tables/cblc_spec.rb'
|
|
512
|
+
- 'spec/fontisan/tables/sbix_spec.rb'
|
|
513
|
+
- 'spec/integration/color_emoji_fonts_spec.rb'
|
|
514
|
+
|
|
515
|
+
# Offense count: 12
|
|
466
516
|
# This cop supports safe autocorrection (--autocorrect).
|
|
467
517
|
# Configuration parameters: EnforcedStyleForMultiline.
|
|
468
518
|
# SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
|
|
469
519
|
Style/TrailingCommaInArrayLiteral:
|
|
470
520
|
Exclude:
|
|
471
521
|
- 'spec/fontisan/hints/truetype_instruction_analyzer_spec.rb'
|
|
522
|
+
- 'spec/fontisan/tables/colr_spec.rb'
|
|
523
|
+
- 'spec/fontisan/tables/cpal_spec.rb'
|
|
472
524
|
- 'spec/fontisan/variation/parallel_generator_spec.rb'
|
|
473
525
|
|
|
474
526
|
# Offense count: 1
|
data/README.adoc
CHANGED
|
@@ -1300,6 +1300,173 @@ $ fontisan validate FONT.{ttc,otc}
|
|
|
1300
1300
|
NOTE: In `extract_ttc`, this was done with `extract_ttc --validate FONT.ttc`.
|
|
1301
1301
|
|
|
1302
1302
|
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
== Color font support
|
|
1306
|
+
|
|
1307
|
+
=== General
|
|
1308
|
+
|
|
1309
|
+
Fontisan provides comprehensive analysis of all color font formats defined in the OpenType specification.
|
|
1310
|
+
|
|
1311
|
+
=== Supported color font formats
|
|
1312
|
+
|
|
1313
|
+
Fontisan supports all four color font table formats:
|
|
1314
|
+
|
|
1315
|
+
COLR/CPAL:: Layered color glyphs with custom color palettes (Microsoft/Google standard)
|
|
1316
|
+
|
|
1317
|
+
SVG:: Embedded SVG graphics with gzip compression support (W3C standard)
|
|
1318
|
+
|
|
1319
|
+
CBDT/CBLC:: Bitmap glyphs at multiple ppem sizes (Google format)
|
|
1320
|
+
|
|
1321
|
+
sbix:: Bitmap graphics with PNG/JPEG/TIFF support (Apple format)
|
|
1322
|
+
|
|
1323
|
+
=== Analyzing color fonts
|
|
1324
|
+
|
|
1325
|
+
[source,ruby]
|
|
1326
|
+
----
|
|
1327
|
+
require 'fontisan'
|
|
1328
|
+
|
|
1329
|
+
# Load and analyze a color font
|
|
1330
|
+
info = Fontisan.info('emoji-font.ttf')
|
|
1331
|
+
|
|
1332
|
+
# Color glyph detection
|
|
1333
|
+
puts info.is_color_font # true if COLR/CPAL present
|
|
1334
|
+
puts info.has_svg_table # true if SVG table present
|
|
1335
|
+
puts info.has_bitmap_glyphs # true if CBDT/CBLC or sbix present
|
|
1336
|
+
|
|
1337
|
+
# Get color palette information (COLR/CPAL)
|
|
1338
|
+
if info.is_color_font
|
|
1339
|
+
puts "Color glyphs: #{info.color_glyphs}"
|
|
1340
|
+
puts "Color palettes: #{info.color_palettes}"
|
|
1341
|
+
puts "Colors per palette: #{info.colors_per_palette}"
|
|
1342
|
+
end
|
|
1343
|
+
|
|
1344
|
+
# Get SVG glyph information
|
|
1345
|
+
if info.has_svg_table
|
|
1346
|
+
puts "SVG glyphs: #{info.svg_glyph_count}"
|
|
1347
|
+
end
|
|
1348
|
+
|
|
1349
|
+
# Get bitmap information
|
|
1350
|
+
if info.has_bitmap_glyphs
|
|
1351
|
+
puts "Bitmap formats: #{info.bitmap_formats.join(', ')}"
|
|
1352
|
+
puts "Available sizes (ppem): #{info.bitmap_ppem_sizes.join(', ')}"
|
|
1353
|
+
|
|
1354
|
+
info.bitmap_strikes.each do |strike|
|
|
1355
|
+
puts "Strike at #{strike.ppem}ppem:"
|
|
1356
|
+
puts " - Glyphs: #{strike.num_glyphs}"
|
|
1357
|
+
puts " - Color depth: #{strike.color_depth}"
|
|
1358
|
+
end
|
|
1359
|
+
end
|
|
1360
|
+
----
|
|
1361
|
+
|
|
1362
|
+
=== Color font table access
|
|
1363
|
+
|
|
1364
|
+
Access color font tables directly:
|
|
1365
|
+
|
|
1366
|
+
[source,ruby]
|
|
1367
|
+
----
|
|
1368
|
+
font = Fontisan::FontLoader.load('emoji-font.ttf')
|
|
1369
|
+
|
|
1370
|
+
# Access COLR table (layered color)
|
|
1371
|
+
if font.has_table?('COLR')
|
|
1372
|
+
colr = font.table('COLR')
|
|
1373
|
+
puts "Color glyphs: #{colr.num_color_glyphs}"
|
|
1374
|
+
|
|
1375
|
+
# Get color layers for a glyph
|
|
1376
|
+
layers = colr.layers_for_glyph(42)
|
|
1377
|
+
layers.each do |layer|
|
|
1378
|
+
puts "Layer glyph: #{layer.glyph_id}, Palette index: #{layer.palette_index}"
|
|
1379
|
+
end
|
|
1380
|
+
end
|
|
1381
|
+
|
|
1382
|
+
# Access CPAL table (color palettes)
|
|
1383
|
+
if font.has_table?('CPAL')
|
|
1384
|
+
cpal = font.table('CPAL')
|
|
1385
|
+
|
|
1386
|
+
# Get colors from first palette
|
|
1387
|
+
palette = cpal.palette(0)
|
|
1388
|
+
palette.each_with_index do |color, i|
|
|
1389
|
+
puts "Color #{i}: R=#{color.red} G=#{color.green} B=#{color.blue} A=#{color.alpha}"
|
|
1390
|
+
end
|
|
1391
|
+
end
|
|
1392
|
+
|
|
1393
|
+
# Access SVG table
|
|
1394
|
+
if font.has_table?('SVG ')
|
|
1395
|
+
svg = font.table('SVG ')
|
|
1396
|
+
|
|
1397
|
+
# Get SVG document for glyph 100
|
|
1398
|
+
svg_doc = svg.svg_document_for_glyph(100)
|
|
1399
|
+
puts "Compressed: #{svg_doc.compressed?}"
|
|
1400
|
+
puts "SVG data: #{svg_doc.svg_data}"
|
|
1401
|
+
end
|
|
1402
|
+
|
|
1403
|
+
# Access bitmap tables
|
|
1404
|
+
if font.has_table?('CBLC')
|
|
1405
|
+
cblc = font.table('CBLC')
|
|
1406
|
+
puts "Available ppem sizes: #{cblc.ppem_sizes.join(', ')}"
|
|
1407
|
+
|
|
1408
|
+
# Check if glyph 50 has bitmap at 64ppem
|
|
1409
|
+
if cblc.has_bitmap_for_glyph?(50, 64)
|
|
1410
|
+
puts "Glyph 50 has bitmap at 64ppem"
|
|
1411
|
+
end
|
|
1412
|
+
end
|
|
1413
|
+
|
|
1414
|
+
if font.has_table?('sbix')
|
|
1415
|
+
sbix = font.table('sbix')
|
|
1416
|
+
|
|
1417
|
+
# Get bitmap data for glyph 42 at 128ppem
|
|
1418
|
+
glyph_data = sbix.glyph_data(42, 128)
|
|
1419
|
+
if glyph_data
|
|
1420
|
+
puts "Format: #{glyph_data[:graphic_type_name]}"
|
|
1421
|
+
puts "Origin: (#{glyph_data[:origin_x]}, #{glyph_data[:origin_y]})"
|
|
1422
|
+
puts "Data size: #{glyph_data[:data].length} bytes"
|
|
1423
|
+
end
|
|
1424
|
+
end
|
|
1425
|
+
----
|
|
1426
|
+
|
|
1427
|
+
=== Output formats
|
|
1428
|
+
|
|
1429
|
+
Color font information can be serialized to multiple formats:
|
|
1430
|
+
|
|
1431
|
+
[source,ruby]
|
|
1432
|
+
----
|
|
1433
|
+
info = Fontisan.info('emoji-font.ttf')
|
|
1434
|
+
|
|
1435
|
+
# YAML output
|
|
1436
|
+
puts info.to_yaml
|
|
1437
|
+
|
|
1438
|
+
# JSON output
|
|
1439
|
+
puts info.to_json
|
|
1440
|
+
|
|
1441
|
+
# XML output
|
|
1442
|
+
puts info.to_xml
|
|
1443
|
+
----
|
|
1444
|
+
|
|
1445
|
+
Example YAML output:
|
|
1446
|
+
|
|
1447
|
+
[source,yaml]
|
|
1448
|
+
----
|
|
1449
|
+
font_format: truetype
|
|
1450
|
+
family_name: "Emoji Font"
|
|
1451
|
+
is_color_font: true
|
|
1452
|
+
color_glyphs: 872
|
|
1453
|
+
color_palettes: 1
|
|
1454
|
+
colors_per_palette: 256
|
|
1455
|
+
has_svg_table: true
|
|
1456
|
+
svg_glyph_count: 872
|
|
1457
|
+
has_bitmap_glyphs: true
|
|
1458
|
+
bitmap_ppem_sizes: [16, 32, 64, 128, 256]
|
|
1459
|
+
bitmap_formats: ["PNG"]
|
|
1460
|
+
bitmap_strikes:
|
|
1461
|
+
- ppem: 128
|
|
1462
|
+
start_glyph_id: 1
|
|
1463
|
+
end_glyph_id: 872
|
|
1464
|
+
bit_depth: 32
|
|
1465
|
+
num_glyphs: 872
|
|
1466
|
+
color_depth: "32-bit (full color with alpha)"
|
|
1467
|
+
----
|
|
1468
|
+
|
|
1469
|
+
|
|
1303
1470
|
== Advanced features
|
|
1304
1471
|
|
|
1305
1472
|
Fontisan provides capabilities:
|
|
@@ -1389,6 +1556,9 @@ The loading mode can be queried at any time.
|
|
|
1389
1556
|
|
|
1390
1557
|
[source,ruby]
|
|
1391
1558
|
----
|
|
1559
|
+
# Check laziness
|
|
1560
|
+
font.lazy? # => true
|
|
1561
|
+
|
|
1392
1562
|
# Mode stored as font property
|
|
1393
1563
|
font.loading_mode # => :metadata or :full
|
|
1394
1564
|
|
|
@@ -1397,6 +1567,12 @@ font.table_available?(tag) # => boolean
|
|
|
1397
1567
|
|
|
1398
1568
|
# Access restricted based on mode
|
|
1399
1569
|
font.table(tag) # => Returns table or raises error
|
|
1570
|
+
|
|
1571
|
+
# Test lazy loading with an expensive operation
|
|
1572
|
+
glyphs = [] if font.subset_glyphs(lazy: true) { glyphs = font.subset_glyphs }
|
|
1573
|
+
glyphs.count # => Tests whether glyphs were already loaded
|
|
1574
|
+
font.table_available?("head") # => true (lazy loading enabled)
|
|
1575
|
+
font.table_available?("GSUB") # => false (lazy loading enabled)
|
|
1400
1576
|
----
|
|
1401
1577
|
|
|
1402
1578
|
|
|
@@ -1430,7 +1606,7 @@ fields:
|
|
|
1430
1606
|
`family_name`:: Font family name (nameID 1)
|
|
1431
1607
|
`subfamily_name`:: Font subfamily/style name (nameID 2)
|
|
1432
1608
|
`full_name`:: Full font name (nameID 4)
|
|
1433
|
-
`
|
|
1609
|
+
`postscript_name`:: PostScript name (nameID 6)
|
|
1434
1610
|
`preferred_family_name`:: Preferred family name (nameID 16, may be nil)
|
|
1435
1611
|
`preferred_subfamily_name`:: Preferred subfamily name (nameID 17, may be nil)
|
|
1436
1612
|
`units_per_em`:: Units per em from head table
|
|
@@ -1484,6 +1660,64 @@ font = Fontisan::FontLoader.load('font.ttf', mode: :full, lazy: false)
|
|
|
1484
1660
|
----
|
|
1485
1661
|
|
|
1486
1662
|
|
|
1663
|
+
=== Table preparedness
|
|
1664
|
+
|
|
1665
|
+
Every table uses preparedness to avoid lazy initialization overhead. Function
|
|
1666
|
+
`table_available?` was added to check if table prepared for access.
|
|
1667
|
+
|
|
1668
|
+
[source,ruby]
|
|
1669
|
+
----
|
|
1670
|
+
font = Fontisan::FontLoader.load('font.ttf')
|
|
1671
|
+
|
|
1672
|
+
# Check preparedness
|
|
1673
|
+
font.table_available?("head") # => true
|
|
1674
|
+
font.table_available?("GSUB") # => true
|
|
1675
|
+
font.table_available?("GPOS") # => true
|
|
1676
|
+
|
|
1677
|
+
# Force loading of a table
|
|
1678
|
+
head = font.table("head")
|
|
1679
|
+
puts head.metrics.units_per_em # => Asks explicitly for metric value
|
|
1680
|
+
puts head.metrics.weight_class # => Asks explicitly for metric value
|
|
1681
|
+
print head.ty # => Prints force loaded table
|
|
1682
|
+
|
|
1683
|
+
# Check preparedness after loading
|
|
1684
|
+
font.table_available?("head") # => true in cache
|
|
1685
|
+
font.table_available?("GSUB") # => true in cache
|
|
1686
|
+
font.table_available?("GPOS") # => true in cache
|
|
1687
|
+
----
|
|
1688
|
+
|
|
1689
|
+
A table not present in a font file is loaded lazily and `table_available?`
|
|
1690
|
+
returns `false` after loading this table or after calling directly `table` for
|
|
1691
|
+
the given tag:
|
|
1692
|
+
|
|
1693
|
+
[source,ruby]
|
|
1694
|
+
----
|
|
1695
|
+
font = Fontisan::FontLoader.load('font.ttf')
|
|
1696
|
+
|
|
1697
|
+
# Check preparedness
|
|
1698
|
+
font.standard_glyphs # => instantly loading glyphs (CFF and GDEF)
|
|
1699
|
+
font.candlestick_and_widget_chart # => instantly loading glyphs (Colr)
|
|
1700
|
+
font.txt_encoder # => instantly loading glyphs (OutlinedFont)
|
|
1701
|
+
font.table_available?("post") # => true (cached)
|
|
1702
|
+
|
|
1703
|
+
font.table_available?("OS/2") # => false (not loaded)
|
|
1704
|
+
font.table("OS/2") # => instant loading tables (lazy loading)
|
|
1705
|
+
font.table_available?("OS/2") # => true (cached)
|
|
1706
|
+
|
|
1707
|
+
font.table_available?("VORG") # => false (not loaded)
|
|
1708
|
+
font.table("VORG") # => instant loading tables (lazy loading)
|
|
1709
|
+
# Raises Fontisan::Tables::VorgTableNotInFont: Not a valid sfnt font or sfnt table VORG not included in input font
|
|
1710
|
+
font.table_available?("VORG") # => true (cached)
|
|
1711
|
+
|
|
1712
|
+
font.table_available?("GLYC") # => nil
|
|
1713
|
+
font.table("GLYC") # => instant loading tables (lazy loading)
|
|
1714
|
+
# Raises Fontisan::TableNotFound: Table GLYC not found in font
|
|
1715
|
+
font.table_available?("GLYC") # => nil (not cached)
|
|
1716
|
+
----
|
|
1717
|
+
|
|
1718
|
+
NOTE: If you do not need to check whether the table is present before access
|
|
1719
|
+
it is faster to access directly `table` method and catch error with validation
|
|
1720
|
+
than first call table_available? for the table not present in the font file.
|
|
1487
1721
|
|
|
1488
1722
|
|
|
1489
1723
|
== Outline format conversion
|
|
@@ -1599,6 +1833,31 @@ converter.supported_targets(:ttf)
|
|
|
1599
1833
|
# => [:ttf, :otf, :woff2, :svg]
|
|
1600
1834
|
----
|
|
1601
1835
|
|
|
1836
|
+
=== Convert into WOFF2
|
|
1837
|
+
|
|
1838
|
+
==== Validation
|
|
1839
|
+
|
|
1840
|
+
WOFF2 encoding can be validated automatically to ensure spec compliance:
|
|
1841
|
+
|
|
1842
|
+
.Validation example
|
|
1843
|
+
[example]
|
|
1844
|
+
====
|
|
1845
|
+
[source,ruby]
|
|
1846
|
+
----
|
|
1847
|
+
encoder = Fontisan::Converters::Woff2Encoder.new
|
|
1848
|
+
result = encoder.convert(font, {
|
|
1849
|
+
transform_tables: true,
|
|
1850
|
+
validate: true,
|
|
1851
|
+
validation_level: :strict # :strict, :standard, or :lenient
|
|
1852
|
+
})
|
|
1853
|
+
|
|
1854
|
+
report = result[:validation_report]
|
|
1855
|
+
puts "Valid: #{report.valid}"
|
|
1856
|
+
puts "Compression: #{report.info_issues.first.message}"
|
|
1857
|
+
----
|
|
1858
|
+
====
|
|
1859
|
+
|
|
1860
|
+
|
|
1602
1861
|
|
|
1603
1862
|
=== Validation
|
|
1604
1863
|
|
|
@@ -2117,7 +2376,7 @@ end
|
|
|
2117
2376
|
====
|
|
2118
2377
|
|
|
2119
2378
|
|
|
2120
|
-
== Round-
|
|
2379
|
+
== Round-trip validation
|
|
2121
2380
|
|
|
2122
2381
|
=== General
|
|
2123
2382
|
|
|
@@ -2190,7 +2449,7 @@ Loron.
|
|
|
2190
2449
|
|
|
2191
2450
|
Support for layered import CFF color glyphs rasterizing on demand, with
|
|
2192
2451
|
composite font support, a multi-layer color font represented by many
|
|
2193
|
-
CFF fonts stacked on top of each other.
|
|
2452
|
+
CFF fonts stacked on top of each other. ColorGlyph support contains
|
|
2194
2453
|
color glyphs, advanced color fonts glyphs and raster images (PNG or JPG)
|
|
2195
2454
|
combined with TrueType outlines.
|
|
2196
2455
|
|
data/Rakefile
CHANGED
|
@@ -87,6 +87,9 @@ namespace :fixtures do
|
|
|
87
87
|
|
|
88
88
|
# Create file tasks for each font
|
|
89
89
|
fonts.each do |name, config|
|
|
90
|
+
# Skip fonts that should not be downloaded (already committed)
|
|
91
|
+
next if config[:skip_download]
|
|
92
|
+
|
|
90
93
|
file config[:marker] do
|
|
91
94
|
if config[:single_file]
|
|
92
95
|
download_single_file(name, config[:url], config[:marker])
|
|
@@ -97,16 +100,26 @@ namespace :fixtures do
|
|
|
97
100
|
end
|
|
98
101
|
|
|
99
102
|
desc "Download all test fixture fonts"
|
|
100
|
-
task download: fonts.values.map { |config| config[:marker] }
|
|
103
|
+
task download: fonts.values.reject { |config| config[:skip_download] }.map { |config| config[:marker] }
|
|
101
104
|
|
|
102
105
|
desc "Clean downloaded fixtures"
|
|
103
106
|
task :clean do
|
|
104
|
-
fonts.values.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
fonts.values.reject { |config| config[:skip_download] }.each do |config|
|
|
108
|
+
if config[:single_file]
|
|
109
|
+
# For single files, just delete the marker file itself
|
|
110
|
+
if File.exist?(config[:marker])
|
|
111
|
+
puts "[fixtures:clean] Removing #{config[:marker]}..."
|
|
112
|
+
FileUtils.rm_f(config[:marker])
|
|
113
|
+
puts "[fixtures:clean] Removed #{config[:marker]}"
|
|
114
|
+
end
|
|
115
|
+
else
|
|
116
|
+
# For archives, delete the entire target directory
|
|
117
|
+
if File.exist?(config[:target_dir])
|
|
118
|
+
puts "[fixtures:clean] Removing #{config[:target_dir]}..."
|
|
119
|
+
FileUtils.rm_rf(config[:target_dir])
|
|
120
|
+
puts "[fixtures:clean] Removed #{config[:target_dir]}"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
110
123
|
end
|
|
111
124
|
end
|
|
112
125
|
end
|