fontisan 0.4.18 → 0.4.19
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 +21 -110
- data/lib/fontisan/collection/writer.rb +2 -16
- data/lib/fontisan/converters/glyf_table_builder.rb +1 -1
- data/lib/fontisan/converters/type1_converter.rb +2 -4
- data/lib/fontisan/converters/woff2_encoder.rb +8 -4
- data/lib/fontisan/converters/woff_writer.rb +2 -7
- data/lib/fontisan/font_writer.rb +3 -9
- data/lib/fontisan/sfnt_builder.rb +1 -1
- data/lib/fontisan/sfnt_font.rb +1 -1
- data/lib/fontisan/tables/head.rb +30 -7
- data/lib/fontisan/ufo/compile/head.rb +2 -4
- data/lib/fontisan/utilities/padding.rb +60 -0
- data/lib/fontisan/utilities.rb +1 -0
- data/lib/fontisan/variable/table_updater.rb +1 -3
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/encoder_rules.rb +11 -10
- data/lib/fontisan/woff2/sfnt_checksum.rb +39 -41
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ec589024241926918e56eb58fbe63cd2b14cc0d82fd92908337f7f2f7ef5c076
|
|
4
|
+
data.tar.gz: f3ef0a193655de4790717559c7bce5722f5d9e7b5548bc5b313cf400a3b2121d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b83304cf80e5f7a55ee98717b0f3bec987ad81cef329d0f1d2ab9ab91ecd66db58f6e2ed5eada8c3b4049665e3a37240a26477dbd25405a586d1f9ca7fc7dd61
|
|
7
|
+
data.tar.gz: ebc0f3e2c752e7752452280d83e4220aabb12c23a4766e431dbc3e13b480b85b0259a929656919da42e9993a8bd2354a9283fbef21a95ba49b666c54c8d0812b
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2026-
|
|
3
|
+
# on 2026-07-07 11:03:20 UTC using RuboCop version 1.86.1.
|
|
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,84 +12,20 @@ Gemspec/RequiredRubyVersion:
|
|
|
12
12
|
- 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
|
|
13
13
|
- 'fontisan.gemspec'
|
|
14
14
|
|
|
15
|
-
# Offense count:
|
|
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/cldr/unicode_set_parser.rb'
|
|
22
|
-
- 'lib/fontisan/hints.rb'
|
|
23
|
-
- 'lib/fontisan/models.rb'
|
|
24
|
-
- 'lib/fontisan/pipeline.rb'
|
|
25
|
-
- 'lib/fontisan/pipeline/strategies.rb'
|
|
26
|
-
- 'lib/fontisan/tables.rb'
|
|
27
|
-
- 'lib/fontisan/tables/cff.rb'
|
|
28
|
-
- 'spec/fontisan/cldr/config_spec.rb'
|
|
29
|
-
- 'spec/fontisan/cldr/unicode_set_parser_spec.rb'
|
|
30
|
-
- 'spec/fontisan/cli/cldr_cli_spec.rb'
|
|
31
|
-
|
|
32
|
-
# Offense count: 2
|
|
33
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
34
|
-
# Configuration parameters: EnforcedStyleAlignWith.
|
|
35
|
-
# SupportedStylesAlignWith: either, start_of_block, start_of_line
|
|
36
|
-
Layout/BlockAlignment:
|
|
37
|
-
Exclude:
|
|
38
|
-
- 'lib/fontisan/cldr/unicode_set_parser.rb'
|
|
39
|
-
- 'spec/fontisan/cldr/index_spec.rb'
|
|
40
|
-
|
|
41
|
-
# Offense count: 2
|
|
42
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
43
|
-
Layout/BlockEndNewline:
|
|
44
|
-
Exclude:
|
|
45
|
-
- 'lib/fontisan/cldr/unicode_set_parser.rb'
|
|
46
|
-
- 'spec/fontisan/cldr/index_spec.rb'
|
|
47
|
-
|
|
48
|
-
# Offense count: 2
|
|
49
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
50
|
-
Layout/EmptyLines:
|
|
51
|
-
Exclude:
|
|
52
|
-
- 'lib/fontisan/cli.rb'
|
|
53
|
-
|
|
54
|
-
# Offense count: 4
|
|
55
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
56
|
-
# Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns.
|
|
57
|
-
# SupportedStylesAlignWith: start_of_line, relative_to_receiver
|
|
58
|
-
Layout/IndentationWidth:
|
|
59
|
-
Exclude:
|
|
60
|
-
- 'lib/fontisan/cldr/unicode_set_parser.rb'
|
|
61
|
-
- 'spec/fontisan/cldr/index_spec.rb'
|
|
62
|
-
|
|
63
|
-
# Offense count: 1641
|
|
15
|
+
# Offense count: 2090
|
|
64
16
|
# This cop supports safe autocorrection (--autocorrect).
|
|
65
17
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
|
|
66
18
|
# URISchemes: http, https
|
|
67
19
|
Layout/LineLength:
|
|
68
20
|
Enabled: false
|
|
69
21
|
|
|
70
|
-
# Offense count: 21
|
|
71
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
72
|
-
# Configuration parameters: AllowInHeredoc.
|
|
73
|
-
Layout/TrailingWhitespace:
|
|
74
|
-
Exclude:
|
|
75
|
-
- 'lib/fontisan/cldr/unicode_set_parser.rb'
|
|
76
|
-
- 'lib/fontisan/hints.rb'
|
|
77
|
-
- 'lib/fontisan/models.rb'
|
|
78
|
-
- 'lib/fontisan/pipeline.rb'
|
|
79
|
-
- 'lib/fontisan/pipeline/strategies.rb'
|
|
80
|
-
- 'lib/fontisan/tables.rb'
|
|
81
|
-
- 'lib/fontisan/tables/cff.rb'
|
|
82
|
-
- 'spec/fontisan/cldr/config_spec.rb'
|
|
83
|
-
- 'spec/fontisan/cldr/unicode_set_parser_spec.rb'
|
|
84
|
-
- 'spec/fontisan/cli/cldr_cli_spec.rb'
|
|
85
|
-
|
|
86
22
|
# Offense count: 2
|
|
87
23
|
Lint/CopDirectiveSyntax:
|
|
88
24
|
Exclude:
|
|
89
25
|
- 'lib/fontisan/woff2_font.rb'
|
|
90
26
|
- 'spec/fontisan/variation/cache_spec.rb'
|
|
91
27
|
|
|
92
|
-
# Offense count:
|
|
28
|
+
# Offense count: 52
|
|
93
29
|
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
|
|
94
30
|
Lint/DuplicateBranch:
|
|
95
31
|
Enabled: false
|
|
@@ -142,7 +78,7 @@ Lint/StructNewOverride:
|
|
|
142
78
|
Exclude:
|
|
143
79
|
- 'lib/fontisan/optimizers/pattern_analyzer.rb'
|
|
144
80
|
|
|
145
|
-
# Offense count:
|
|
81
|
+
# Offense count: 5
|
|
146
82
|
# This cop supports safe autocorrection (--autocorrect).
|
|
147
83
|
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
|
|
148
84
|
# NotImplementedExceptions: NotImplementedError
|
|
@@ -150,9 +86,7 @@ Lint/UnusedMethodArgument:
|
|
|
150
86
|
Exclude:
|
|
151
87
|
- 'lib/fontisan.rb'
|
|
152
88
|
- 'lib/fontisan/tables/cff2/private_dict_blend_handler.rb'
|
|
153
|
-
- 'lib/fontisan/utilities/brotli_wrapper.rb'
|
|
154
89
|
- 'lib/fontisan/variation/table_accessor.rb'
|
|
155
|
-
- 'lib/fontisan/woff2/glyf_transformer.rb'
|
|
156
90
|
- 'lib/fontisan/woff_font.rb'
|
|
157
91
|
|
|
158
92
|
# Offense count: 5
|
|
@@ -161,21 +95,21 @@ Lint/UselessConstantScoping:
|
|
|
161
95
|
- 'lib/fontisan/conversion_options.rb'
|
|
162
96
|
- 'lib/fontisan/type1/charstrings.rb'
|
|
163
97
|
|
|
164
|
-
# Offense count:
|
|
98
|
+
# Offense count: 703
|
|
165
99
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
|
166
100
|
Metrics/AbcSize:
|
|
167
101
|
Enabled: false
|
|
168
102
|
|
|
169
|
-
# Offense count:
|
|
103
|
+
# Offense count: 36
|
|
170
104
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
|
|
171
105
|
# AllowedMethods: refine
|
|
172
106
|
Metrics/BlockLength:
|
|
173
107
|
Max: 104
|
|
174
108
|
|
|
175
|
-
# Offense count:
|
|
109
|
+
# Offense count: 4
|
|
176
110
|
# Configuration parameters: CountBlocks, CountModifierForms.
|
|
177
111
|
Metrics/BlockNesting:
|
|
178
|
-
Max:
|
|
112
|
+
Max: 4
|
|
179
113
|
|
|
180
114
|
# Offense count: 2
|
|
181
115
|
# Configuration parameters: LengthThreshold.
|
|
@@ -184,28 +118,28 @@ Metrics/CollectionLiteralLength:
|
|
|
184
118
|
- 'lib/fontisan/type1/agl.rb'
|
|
185
119
|
- 'lib/fontisan/type1/encodings.rb'
|
|
186
120
|
|
|
187
|
-
# Offense count:
|
|
121
|
+
# Offense count: 366
|
|
188
122
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
189
123
|
Metrics/CyclomaticComplexity:
|
|
190
124
|
Enabled: false
|
|
191
125
|
|
|
192
|
-
# Offense count:
|
|
126
|
+
# Offense count: 977
|
|
193
127
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
194
128
|
Metrics/MethodLength:
|
|
195
129
|
Max: 135
|
|
196
130
|
|
|
197
|
-
# Offense count:
|
|
131
|
+
# Offense count: 27
|
|
198
132
|
# Configuration parameters: CountKeywordArgs.
|
|
199
133
|
Metrics/ParameterLists:
|
|
200
134
|
Max: 39
|
|
201
135
|
MaxOptionalParameters: 4
|
|
202
136
|
|
|
203
|
-
# Offense count:
|
|
137
|
+
# Offense count: 278
|
|
204
138
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
|
205
139
|
Metrics/PerceivedComplexity:
|
|
206
140
|
Enabled: false
|
|
207
141
|
|
|
208
|
-
# Offense count:
|
|
142
|
+
# Offense count: 208
|
|
209
143
|
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
|
210
144
|
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
|
|
211
145
|
Naming/MethodParameterName:
|
|
@@ -248,7 +182,7 @@ Naming/VariableName:
|
|
|
248
182
|
- 'lib/fontisan/type1/pfm_generator.rb'
|
|
249
183
|
- 'lib/fontisan/type1/pfm_parser.rb'
|
|
250
184
|
|
|
251
|
-
# Offense count:
|
|
185
|
+
# Offense count: 120
|
|
252
186
|
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
|
253
187
|
# SupportedStyles: snake_case, normalcase, non_integer
|
|
254
188
|
# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
|
|
@@ -265,13 +199,13 @@ RSpec/AnyInstance:
|
|
|
265
199
|
- 'spec/fontisan/variation/parallel_generator_spec.rb'
|
|
266
200
|
- 'spec/integration/format_conversion_spec.rb'
|
|
267
201
|
|
|
268
|
-
# Offense count:
|
|
202
|
+
# Offense count: 127
|
|
269
203
|
# Configuration parameters: Prefixes, AllowedPatterns.
|
|
270
204
|
# Prefixes: when, with, without
|
|
271
205
|
RSpec/ContextWording:
|
|
272
206
|
Enabled: false
|
|
273
207
|
|
|
274
|
-
# Offense count:
|
|
208
|
+
# Offense count: 38
|
|
275
209
|
# Configuration parameters: IgnoredMetadata.
|
|
276
210
|
RSpec/DescribeClass:
|
|
277
211
|
Enabled: false
|
|
@@ -287,7 +221,7 @@ RSpec/DescribeMethod:
|
|
|
287
221
|
- 'spec/fontisan/type1/seac_expander_spec.rb'
|
|
288
222
|
- 'spec/fontisan/type1_real_fonts_spec.rb'
|
|
289
223
|
|
|
290
|
-
# Offense count:
|
|
224
|
+
# Offense count: 1660
|
|
291
225
|
# Configuration parameters: CountAsOne.
|
|
292
226
|
RSpec/ExampleLength:
|
|
293
227
|
Max: 66
|
|
@@ -364,11 +298,11 @@ RSpec/MultipleDescribes:
|
|
|
364
298
|
- 'spec/fontisan/utils/thread_pool_spec.rb'
|
|
365
299
|
- 'spec/fontisan/variation/cache_spec.rb'
|
|
366
300
|
|
|
367
|
-
# Offense count:
|
|
301
|
+
# Offense count: 2097
|
|
368
302
|
RSpec/MultipleExpectations:
|
|
369
303
|
Max: 19
|
|
370
304
|
|
|
371
|
-
# Offense count:
|
|
305
|
+
# Offense count: 157
|
|
372
306
|
# Configuration parameters: AllowSubject.
|
|
373
307
|
RSpec/MultipleMemoizedHelpers:
|
|
374
308
|
Max: 13
|
|
@@ -396,7 +330,7 @@ RSpec/RepeatedExample:
|
|
|
396
330
|
- 'spec/fontisan/metrics_calculator_spec.rb'
|
|
397
331
|
- 'spec/fontisan/tables/os2_spec.rb'
|
|
398
332
|
|
|
399
|
-
# Offense count:
|
|
333
|
+
# Offense count: 47
|
|
400
334
|
# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
|
|
401
335
|
# SupportedInflectors: default, active_support
|
|
402
336
|
RSpec/SpecFilePathFormat:
|
|
@@ -417,7 +351,7 @@ RSpec/SubjectStub:
|
|
|
417
351
|
Exclude:
|
|
418
352
|
- 'spec/fontisan/variation/delta_applier_spec.rb'
|
|
419
353
|
|
|
420
|
-
# Offense count:
|
|
354
|
+
# Offense count: 422
|
|
421
355
|
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
|
|
422
356
|
RSpec/VerifiedDoubles:
|
|
423
357
|
Enabled: false
|
|
@@ -427,23 +361,6 @@ Rake/MethodDefinitionInTask:
|
|
|
427
361
|
Exclude:
|
|
428
362
|
- 'Rakefile'
|
|
429
363
|
|
|
430
|
-
# Offense count: 2
|
|
431
|
-
Security/Open:
|
|
432
|
-
Exclude:
|
|
433
|
-
- 'Rakefile'
|
|
434
|
-
|
|
435
|
-
# Offense count: 2
|
|
436
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
437
|
-
# Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
|
|
438
|
-
# SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
|
|
439
|
-
# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
|
|
440
|
-
# FunctionalMethods: let, let!, subject, watch
|
|
441
|
-
# AllowedMethods: lambda, proc, it
|
|
442
|
-
Style/BlockDelimiters:
|
|
443
|
-
Exclude:
|
|
444
|
-
- 'lib/fontisan/cldr/unicode_set_parser.rb'
|
|
445
|
-
- 'spec/fontisan/cldr/index_spec.rb'
|
|
446
|
-
|
|
447
364
|
# Offense count: 6
|
|
448
365
|
# This cop supports safe autocorrection (--autocorrect).
|
|
449
366
|
Style/ComparableClamp:
|
|
@@ -478,12 +395,6 @@ Style/HashLikeCase:
|
|
|
478
395
|
- 'lib/fontisan/commands/unpack_command.rb'
|
|
479
396
|
- 'lib/fontisan/models/validation_report.rb'
|
|
480
397
|
|
|
481
|
-
# Offense count: 9
|
|
482
|
-
# This cop supports safe autocorrection (--autocorrect).
|
|
483
|
-
Style/MultilineIfModifier:
|
|
484
|
-
Exclude:
|
|
485
|
-
- 'lib/fontisan/cldr/unicode_set_parser.rb'
|
|
486
|
-
|
|
487
398
|
# Offense count: 1
|
|
488
399
|
# Configuration parameters: AllowedMethods.
|
|
489
400
|
# AllowedMethods: respond_to_missing?
|
|
@@ -212,7 +212,7 @@ module Fontisan
|
|
|
212
212
|
binary << table_info[:data]
|
|
213
213
|
|
|
214
214
|
# Pad to 4-byte boundary
|
|
215
|
-
padding =
|
|
215
|
+
padding = Utilities::Padding.boundary(table_info[:data])
|
|
216
216
|
binary << ("\x00" * padding) if padding.positive?
|
|
217
217
|
end
|
|
218
218
|
end
|
|
@@ -244,26 +244,12 @@ module Fontisan
|
|
|
244
244
|
binary << ("\x00" * padding_needed)
|
|
245
245
|
end
|
|
246
246
|
|
|
247
|
-
# Calculate padding needed for 4-byte alignment
|
|
248
|
-
#
|
|
249
|
-
# @param size [Integer] Current size
|
|
250
|
-
# @return [Integer] Padding bytes needed
|
|
251
|
-
def calculate_padding(size)
|
|
252
|
-
remainder = size % 4
|
|
253
|
-
return 0 if remainder.zero?
|
|
254
|
-
|
|
255
|
-
4 - remainder
|
|
256
|
-
end
|
|
257
|
-
|
|
258
247
|
# Calculate table checksum
|
|
259
248
|
#
|
|
260
249
|
# @param data [String] Table data
|
|
261
250
|
# @return [Integer] Checksum
|
|
262
251
|
def calculate_table_checksum(data)
|
|
263
|
-
|
|
264
|
-
padded_data = data.dup
|
|
265
|
-
padding_length = calculate_padding(data.bytesize)
|
|
266
|
-
padded_data << ("\x00" * padding_length) if padding_length.positive?
|
|
252
|
+
padded_data = Utilities::Padding.pad(data)
|
|
267
253
|
|
|
268
254
|
# Sum all uint32 values
|
|
269
255
|
sum = 0
|
|
@@ -571,12 +571,10 @@ module Fontisan
|
|
|
571
571
|
data << [1000].pack("n")
|
|
572
572
|
|
|
573
573
|
# Created (LONGDATETIME) - use current time
|
|
574
|
-
|
|
575
|
-
data << [created_seconds].pack("Q>")
|
|
574
|
+
data << [Tables::Head.now_longdatetime].pack("Q>")
|
|
576
575
|
|
|
577
576
|
# Modified (LONGDATETIME) - use current time
|
|
578
|
-
|
|
579
|
-
data << [modified_seconds].pack("Q>")
|
|
577
|
+
data << [Tables::Head.now_longdatetime].pack("Q>")
|
|
580
578
|
|
|
581
579
|
# Bounding box (int16 each)
|
|
582
580
|
data << [font_bbox[0]].pack("s>") # x_min
|
|
@@ -418,14 +418,12 @@ module Fontisan
|
|
|
418
418
|
size += 16 if glyf_transform # synthetic loca directory entry
|
|
419
419
|
|
|
420
420
|
table_data.each_value do |data|
|
|
421
|
-
size += data
|
|
422
|
-
size += (4 - (data.bytesize % 4)) % 4 # pad to 4-byte boundary
|
|
421
|
+
size += Utilities::Padding.aligned_size(data)
|
|
423
422
|
end
|
|
424
423
|
|
|
425
424
|
if glyf_transform
|
|
426
425
|
loca_len = glyf_transform[:loca_orig_length]
|
|
427
|
-
size += loca_len
|
|
428
|
-
size += (4 - (loca_len % 4)) % 4
|
|
426
|
+
size += Utilities::Padding.aligned_size(loca_len)
|
|
429
427
|
end
|
|
430
428
|
|
|
431
429
|
size
|
|
@@ -504,6 +502,12 @@ module Fontisan
|
|
|
504
502
|
end
|
|
505
503
|
|
|
506
504
|
woff2_data << compressed_data
|
|
505
|
+
# Per WOFF2 spec section 5.3, the table directory + compressed
|
|
506
|
+
# font block MUST be padded with null bytes so the total file
|
|
507
|
+
# length is a multiple of 4. fontTools' WOFF2Writer does the
|
|
508
|
+
# same: `pad(directory + compressedFont, size=4)`.
|
|
509
|
+
pad = Utilities::Padding.boundary(woff2_data)
|
|
510
|
+
woff2_data << ("\x00" * pad) if pad.positive?
|
|
507
511
|
update_woff2_length!(woff2_data)
|
|
508
512
|
woff2_data
|
|
509
513
|
end
|
|
@@ -234,11 +234,6 @@ module Fontisan
|
|
|
234
234
|
}
|
|
235
235
|
end
|
|
236
236
|
|
|
237
|
-
# Padding bytes needed to bring `length` up to a 4-byte boundary.
|
|
238
|
-
def padding_to_4(length)
|
|
239
|
-
(4 - (length % 4)) % 4
|
|
240
|
-
end
|
|
241
|
-
|
|
242
237
|
# Assemble complete WOFF binary.
|
|
243
238
|
#
|
|
244
239
|
# Computes the full layout (offsets + padding) up front so the header
|
|
@@ -262,7 +257,7 @@ module Fontisan
|
|
|
262
257
|
entries = []
|
|
263
258
|
cursor = data_start
|
|
264
259
|
compressed_tables.each do |tag, info|
|
|
265
|
-
pad =
|
|
260
|
+
pad = Utilities::Padding.boundary(info[:compressed_length])
|
|
266
261
|
entries << { tag:, info:, offset: cursor, pad_bytes: "\x00" * pad }
|
|
267
262
|
cursor += info[:compressed_length] + pad
|
|
268
263
|
end
|
|
@@ -284,7 +279,7 @@ module Fontisan
|
|
|
284
279
|
sfnt_header_size = 12
|
|
285
280
|
sfnt_dir_size = num_tables * 16
|
|
286
281
|
sfnt_tables_size = compressed_tables.values.sum do |t|
|
|
287
|
-
|
|
282
|
+
Utilities::Padding.aligned_size(t[:original_length])
|
|
288
283
|
end
|
|
289
284
|
total_sfnt_size = sfnt_header_size + sfnt_dir_size + sfnt_tables_size
|
|
290
285
|
|
data/lib/fontisan/font_writer.rb
CHANGED
|
@@ -165,7 +165,7 @@ module Fontisan
|
|
|
165
165
|
checksum = calculate_table_checksum(data)
|
|
166
166
|
|
|
167
167
|
# Calculate padding to 4-byte boundary
|
|
168
|
-
padding_length = (
|
|
168
|
+
padding_length = Utilities::Padding.boundary(data)
|
|
169
169
|
padding = "\0" * padding_length
|
|
170
170
|
|
|
171
171
|
entries << {
|
|
@@ -235,10 +235,7 @@ module Fontisan
|
|
|
235
235
|
# @param data [String] Table binary data
|
|
236
236
|
# @return [Integer] Table checksum
|
|
237
237
|
def calculate_table_checksum(data)
|
|
238
|
-
|
|
239
|
-
padded_data = data.dup
|
|
240
|
-
padding_length = (4 - (data.bytesize % 4)) % 4
|
|
241
|
-
padded_data << ("\0" * padding_length) if padding_length.positive?
|
|
238
|
+
padded_data = Utilities::Padding.pad(data)
|
|
242
239
|
|
|
243
240
|
# Sum all uint32 values
|
|
244
241
|
sum = 0
|
|
@@ -285,10 +282,7 @@ module Fontisan
|
|
|
285
282
|
# @param data [String] Complete font binary
|
|
286
283
|
# @return [Integer] Font checksum
|
|
287
284
|
def calculate_font_checksum(data)
|
|
288
|
-
|
|
289
|
-
padded_data = data.dup
|
|
290
|
-
padding_length = (4 - (data.bytesize % 4)) % 4
|
|
291
|
-
padded_data << ("\0" * padding_length) if padding_length.positive?
|
|
285
|
+
padded_data = Utilities::Padding.pad(data)
|
|
292
286
|
|
|
293
287
|
# Sum all uint32 values
|
|
294
288
|
sum = 0
|
|
@@ -34,7 +34,7 @@ module Fontisan
|
|
|
34
34
|
records = tables.map do |tag, data|
|
|
35
35
|
length = data.bytesize
|
|
36
36
|
checksum = Utilities::ChecksumCalculator.calculate_table_checksum(data)
|
|
37
|
-
padding = (
|
|
37
|
+
padding = Utilities::Padding.boundary(length)
|
|
38
38
|
record = { tag:, checksum:, offset: cursor, length:, data:, padding: }
|
|
39
39
|
cursor += length + padding
|
|
40
40
|
record
|
data/lib/fontisan/sfnt_font.rb
CHANGED
|
@@ -714,7 +714,7 @@ module Fontisan
|
|
|
714
714
|
io.write(data)
|
|
715
715
|
|
|
716
716
|
# Add padding to align to 4-byte boundary
|
|
717
|
-
padding =
|
|
717
|
+
padding = Utilities::Padding.boundary(io.pos)
|
|
718
718
|
io.write(PADDING_BYTES[0, padding]) if padding.positive?
|
|
719
719
|
|
|
720
720
|
# Zero out checksumAdjustment field in head table
|
data/lib/fontisan/tables/head.rb
CHANGED
|
@@ -28,6 +28,12 @@ module Fontisan
|
|
|
28
28
|
# rejects WOFF2 files where it is not set.
|
|
29
29
|
FLAG_LOSSLESS_MODIFYING = 0x0800
|
|
30
30
|
|
|
31
|
+
# Difference between 1904-01-01 00:00:00 UTC (Mac epoch, used by
|
|
32
|
+
# LONGDATETIME fields) and 1970-01-01 00:00:00 UTC (Unix epoch).
|
|
33
|
+
# Single source of truth for converting between the two — head's
|
|
34
|
+
# created/modified fields are LONGDATETIME.
|
|
35
|
+
MAC_EPOCH_OFFSET = 2_082_844_800
|
|
36
|
+
|
|
31
37
|
# Version as 16.16 fixed-point (stored as int32)
|
|
32
38
|
int32 :version_raw
|
|
33
39
|
|
|
@@ -73,14 +79,14 @@ module Fontisan
|
|
|
73
79
|
#
|
|
74
80
|
# @return [Time] Creation time
|
|
75
81
|
def created
|
|
76
|
-
longdatetime_to_time(created_raw)
|
|
82
|
+
self.class.longdatetime_to_time(created_raw)
|
|
77
83
|
end
|
|
78
84
|
|
|
79
85
|
# Convert modified timestamp to Time object
|
|
80
86
|
#
|
|
81
87
|
# @return [Time] Modification time
|
|
82
88
|
def modified
|
|
83
|
-
longdatetime_to_time(modified_raw)
|
|
89
|
+
self.class.longdatetime_to_time(modified_raw)
|
|
84
90
|
end
|
|
85
91
|
|
|
86
92
|
# Validate that the magic number is correct
|
|
@@ -150,6 +156,26 @@ module Fontisan
|
|
|
150
156
|
glyph_data_format.zero?
|
|
151
157
|
end
|
|
152
158
|
|
|
159
|
+
# Current wall clock time as a LONGDATETIME integer (seconds since
|
|
160
|
+
# Mac epoch). Used wherever a `created`/`modified` timestamp needs
|
|
161
|
+
# to be set to a meaningful "now" value (WOFF2 encoder, Type 1
|
|
162
|
+
# converter, UFO compile fallback).
|
|
163
|
+
#
|
|
164
|
+
# @return [Integer] Seconds since 1904-01-01 00:00:00 UTC
|
|
165
|
+
def self.now_longdatetime
|
|
166
|
+
to_longdatetime(Time.now)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Convert a Unix-epoch `Time` (or Integer seconds) to a LONGDATETIME
|
|
170
|
+
# integer (Mac-epoch seconds). Symmetric with `#longdatetime_to_time`
|
|
171
|
+
# — pair them whenever you convert across the epoch boundary.
|
|
172
|
+
#
|
|
173
|
+
# @param time [Time, Integer] Unix-epoch time, default `Time.now`
|
|
174
|
+
# @return [Integer] Seconds since 1904-01-01 00:00:00 UTC
|
|
175
|
+
def self.to_longdatetime(time = Time.now)
|
|
176
|
+
time.to_i + MAC_EPOCH_OFFSET
|
|
177
|
+
end
|
|
178
|
+
|
|
153
179
|
# Validate magic number and raise error if invalid
|
|
154
180
|
#
|
|
155
181
|
# @raise [Fontisan::CorruptedTableError] If magic number is invalid
|
|
@@ -167,15 +193,12 @@ module Fontisan
|
|
|
167
193
|
# Alias for backward compatibility
|
|
168
194
|
alias validate! validate_magic_number!
|
|
169
195
|
|
|
170
|
-
private
|
|
171
|
-
|
|
172
196
|
# Convert LONGDATETIME to Ruby Time
|
|
173
197
|
#
|
|
174
198
|
# @param seconds [Integer] Seconds since 1904-01-01 00:00:00
|
|
175
199
|
# @return [Time] Ruby Time object
|
|
176
|
-
def longdatetime_to_time(seconds)
|
|
177
|
-
|
|
178
|
-
Time.at(seconds - 2_082_844_800)
|
|
200
|
+
def self.longdatetime_to_time(seconds)
|
|
201
|
+
Time.at(seconds - MAC_EPOCH_OFFSET)
|
|
179
202
|
end
|
|
180
203
|
end
|
|
181
204
|
end
|
|
@@ -14,8 +14,6 @@ module Fontisan
|
|
|
14
14
|
LOCA_FORMAT_SHORT = 0
|
|
15
15
|
LOCA_FORMAT_LONG = 1
|
|
16
16
|
MAC_STYLE_REGULAR = 0
|
|
17
|
-
# Seconds between 1904-01-01 (Mac epoch) and 1970-01-01 (Unix epoch)
|
|
18
|
-
MAC_EPOCH_OFFSET = 2_082_844_800
|
|
19
17
|
|
|
20
18
|
# @param font [Fontisan::Ufo::Font]
|
|
21
19
|
# @param glyphs [Array<Fontisan::Ufo::Glyph>] (unused here;
|
|
@@ -76,7 +74,7 @@ module Fontisan
|
|
|
76
74
|
# OpenType head.created/modified are LONGDATETIME seconds
|
|
77
75
|
# since 1904-01-01. If the UFO field is absent, use now.
|
|
78
76
|
def self.time_to_longdatetime(value)
|
|
79
|
-
return
|
|
77
|
+
return Tables::Head.now_longdatetime if value.nil?
|
|
80
78
|
|
|
81
79
|
require "time"
|
|
82
80
|
parsed = nil
|
|
@@ -89,7 +87,7 @@ module Fontisan
|
|
|
89
87
|
parsed = Time.now
|
|
90
88
|
end
|
|
91
89
|
end
|
|
92
|
-
parsed
|
|
90
|
+
Tables::Head.to_longdatetime(parsed)
|
|
93
91
|
end
|
|
94
92
|
private_class_method :time_to_longdatetime
|
|
95
93
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fontisan
|
|
4
|
+
module Utilities
|
|
5
|
+
# Stateless helpers for SFNT byte-alignment padding.
|
|
6
|
+
#
|
|
7
|
+
# OpenType and WOFF/WOFF2 require table data and certain directory
|
|
8
|
+
# blocks to be aligned to 4-byte boundaries with trailing null
|
|
9
|
+
# bytes. The same `(N - (size % N)) % N` arithmetic was duplicated
|
|
10
|
+
# across nine call sites before extraction; this module is the
|
|
11
|
+
# single source of truth.
|
|
12
|
+
#
|
|
13
|
+
# @example Get padding length
|
|
14
|
+
# Padding.boundary(13) # => 3
|
|
15
|
+
# Padding.boundary(13, boundary: 4) # => 3
|
|
16
|
+
# Padding.boundary(16, boundary: 4) # => 0
|
|
17
|
+
#
|
|
18
|
+
# @example Get padded bytes
|
|
19
|
+
# Padding.pad("abc") # => "abc\x00"
|
|
20
|
+
# Padding.pad("abcd") # => "abcd"
|
|
21
|
+
module Padding
|
|
22
|
+
# Count of trailing pad bytes needed to align `size` to `boundary`.
|
|
23
|
+
#
|
|
24
|
+
# @param size [Integer, #bytesize] Either an Integer size, or any
|
|
25
|
+
# object responding to `#bytesize` (e.g. a String).
|
|
26
|
+
# @param boundary [Integer] Alignment boundary in bytes (default
|
|
27
|
+
# `Constants::TABLE_ALIGNMENT`).
|
|
28
|
+
# @return [Integer] Number of pad bytes in 0...boundary-1
|
|
29
|
+
def self.boundary(size, boundary: Constants::TABLE_ALIGNMENT)
|
|
30
|
+
length = size.respond_to?(:bytesize) ? size.bytesize : size
|
|
31
|
+
(boundary - (length % boundary)) % boundary
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Return `bytes` followed by null padding to the requested boundary.
|
|
35
|
+
#
|
|
36
|
+
# @param bytes [String] Binary string to pad.
|
|
37
|
+
# @param boundary [Integer] Alignment boundary in bytes.
|
|
38
|
+
# @return [String] The original string if already aligned, else a
|
|
39
|
+
# new binary string with trailing nulls.
|
|
40
|
+
def self.pad(bytes, boundary: Constants::TABLE_ALIGNMENT)
|
|
41
|
+
pad_count = boundary(bytes, boundary:)
|
|
42
|
+
return bytes if pad_count.zero?
|
|
43
|
+
|
|
44
|
+
out = bytes.dup.force_encoding(Encoding::BINARY)
|
|
45
|
+
out << ("\x00" * pad_count)
|
|
46
|
+
out
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Aligned size of `size` after padding to `boundary`.
|
|
50
|
+
#
|
|
51
|
+
# @param size [Integer, #bytesize] Original size or bytes-like.
|
|
52
|
+
# @param boundary [Integer] Alignment boundary in bytes.
|
|
53
|
+
# @return [Integer] Size after alignment (multiple of `boundary`).
|
|
54
|
+
def self.aligned_size(size, boundary: Constants::TABLE_ALIGNMENT)
|
|
55
|
+
length = size.respond_to?(:bytesize) ? size.bytesize : size
|
|
56
|
+
length + boundary(length, boundary:)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/fontisan/utilities.rb
CHANGED
|
@@ -168,9 +168,7 @@ module Fontisan
|
|
|
168
168
|
_old_modified = io.read(8) # Skip old modified timestamp
|
|
169
169
|
rest = io.read # Remaining data
|
|
170
170
|
|
|
171
|
-
|
|
172
|
-
# Difference between 1904 and 1970 (Unix epoch) is 2082844800 seconds
|
|
173
|
-
longdatetime = timestamp.to_i + 2_082_844_800
|
|
171
|
+
longdatetime = Tables::Head.to_longdatetime(timestamp)
|
|
174
172
|
|
|
175
173
|
# Build updated head binary
|
|
176
174
|
output = String.new(encoding: Encoding::BINARY)
|
data/lib/fontisan/version.rb
CHANGED
|
@@ -80,24 +80,25 @@ module Fontisan
|
|
|
80
80
|
table_data["head"] = head.to_binary_s
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
#
|
|
83
|
+
# Set head.modified to the current time.
|
|
84
84
|
#
|
|
85
|
-
# Chrome's OTS rejects fonts whose modified
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
85
|
+
# Chrome's OTS rejects WOFF2 fonts whose head.modified is not
|
|
86
|
+
# meaningfully later than head.created. Source fonts frequently
|
|
87
|
+
# carry modified == created (or modified within seconds of it),
|
|
88
|
+
# which Chrome rejects. Setting modified to the actual current
|
|
89
|
+
# wall-clock time matches fontTools' behaviour (it sets
|
|
90
|
+
# head.modified = timestampNow() on every save) and gives Chrome
|
|
91
|
+
# a delta large enough to accept.
|
|
89
92
|
#
|
|
90
|
-
# Must be called BEFORE checksum recompute so the modified bytes
|
|
91
|
-
# included in the checksum.
|
|
93
|
+
# Must be called BEFORE checksum recompute so the modified bytes
|
|
94
|
+
# are included in the checksum.
|
|
92
95
|
#
|
|
93
96
|
# @param table_data [Hash{String => String}]
|
|
94
97
|
def self.touch_head_modified!(table_data)
|
|
95
98
|
return unless table_data.key?("head")
|
|
96
99
|
|
|
97
100
|
head = Tables::Head.read(table_data["head"])
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
head.modified_raw = head.created_raw + 1
|
|
101
|
+
head.modified_raw = Tables::Head.now_longdatetime
|
|
101
102
|
table_data["head"] = head.to_binary_s
|
|
102
103
|
end
|
|
103
104
|
end
|
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string: true
|
|
2
2
|
|
|
3
3
|
module Fontisan
|
|
4
4
|
module Woff2
|
|
5
|
-
# Computes head.checksumAdjustment for the SFNT
|
|
5
|
+
# Computes `head.checksumAdjustment` for the SFNT the WOFF2 decoder
|
|
6
6
|
# reconstructs at runtime.
|
|
7
7
|
#
|
|
8
|
-
# Per OpenType spec,
|
|
9
|
-
# uint32-wise sum of the entire SFNT file (offset
|
|
10
|
-
# directory + all tables) equals the magic
|
|
11
|
-
# validates this; a stale value from the
|
|
12
|
-
# with "Failed to convert WOFF 2.0
|
|
13
|
-
# been modified (head.flags bit 11,
|
|
8
|
+
# Per OpenType spec ("Calculating Checksums"), checksumAdjustment
|
|
9
|
+
# must be set so the uint32-wise sum of the entire SFNT file (offset
|
|
10
|
+
# table + table directory + all tables) equals the magic
|
|
11
|
+
# 0xB1B0AFBA. Chrome's OTS validates this; a stale value from the
|
|
12
|
+
# source font causes rejection with "Failed to convert WOFF 2.0
|
|
13
|
+
# font to SFNT" once any table has been modified (head.flags bit 11,
|
|
14
|
+
# glyf/loca transformation, etc.).
|
|
14
15
|
#
|
|
15
|
-
#
|
|
16
|
+
# Delegates the per-table uint32 sum to {Utilities::ChecksumCalculator}
|
|
17
|
+
# — that is the single source of truth for the OpenType checksum
|
|
18
|
+
# algorithm. This class orchestrates the per-table layout: building
|
|
19
|
+
# the offset table + table directory bytes, zeroing head's
|
|
20
|
+
# checksumAdjustment field for its own checksum per spec, then
|
|
21
|
+
# subtracting the total from the magic.
|
|
16
22
|
class SfntChecksum
|
|
17
|
-
# Per-table data the checksum is computed over.
|
|
18
|
-
# table tag, bytes is the (already reconstructed, post-transform)
|
|
19
|
-
# table data the decoder will see.
|
|
23
|
+
# Per-table data the checksum is computed over.
|
|
20
24
|
Table = Struct.new(:tag, :bytes, keyword_init: true)
|
|
21
25
|
|
|
22
26
|
# @param flavor [Integer] sfnt version (0x00010000 for TrueType,
|
|
23
27
|
# 0x4F54544F for CFF)
|
|
24
28
|
# @param tables [Array<Table>] reconstructed tables in alphabetical
|
|
25
|
-
#
|
|
29
|
+
# tag order
|
|
26
30
|
def initialize(flavor:, tables:)
|
|
27
31
|
@flavor = flavor
|
|
28
32
|
@tables = tables
|
|
@@ -32,12 +36,13 @@ module Fontisan
|
|
|
32
36
|
#
|
|
33
37
|
# @return [Integer]
|
|
34
38
|
def adjustment
|
|
35
|
-
(
|
|
39
|
+
Utilities::ChecksumCalculator.calculate_adjustment(total_checksum)
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
private
|
|
39
43
|
|
|
40
|
-
# Whole-SFNT checksum with head.checksumAdjustment treated as 0
|
|
44
|
+
# Whole-SFNT checksum with head.checksumAdjustment treated as 0
|
|
45
|
+
# (per spec, head's own checksum field uses that placeholder).
|
|
41
46
|
def total_checksum
|
|
42
47
|
sum = offset_table_checksum
|
|
43
48
|
sum = (sum + directory_checksum) & 0xFFFFFFFF
|
|
@@ -50,58 +55,51 @@ module Fontisan
|
|
|
50
55
|
# Checksum of the 12-byte offset table (sfnt version + numTables +
|
|
51
56
|
# searchRange + entrySelector + rangeShift).
|
|
52
57
|
def offset_table_checksum
|
|
58
|
+
Utilities::ChecksumCalculator.calculate_table_checksum(offset_table_bytes)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def offset_table_bytes
|
|
53
62
|
n = @tables.size
|
|
54
63
|
search_range = (2**Integer(Math.log2(n))) * 16
|
|
55
64
|
entry_selector = Integer(Math.log2(n))
|
|
56
65
|
range_shift = n * 16 - search_range
|
|
57
|
-
|
|
66
|
+
[@flavor, n, search_range, entry_selector, range_shift]
|
|
58
67
|
.pack("I>S>S>S>S>")
|
|
59
|
-
|
|
68
|
+
.ljust(12, "\x00")
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
# Checksum of the table directory (one 16-byte entry per table).
|
|
63
72
|
# Each entry is tag(4) + checksum(4) + offset(4) + length(4).
|
|
64
73
|
def directory_checksum
|
|
74
|
+
Utilities::ChecksumCalculator.calculate_table_checksum(directory_bytes)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def directory_bytes
|
|
78
|
+
cursor = offset_table_bytes.bytesize + @tables.size * 16
|
|
65
79
|
offsets = {}
|
|
66
|
-
cursor = 12 + @tables.size * 16
|
|
67
80
|
@tables.each do |t|
|
|
68
81
|
offsets[t.tag] = cursor
|
|
69
|
-
cursor +=
|
|
82
|
+
cursor += Utilities::Padding.aligned_size(t.bytes)
|
|
70
83
|
end
|
|
84
|
+
|
|
71
85
|
bytes = String.new(encoding: Encoding::BINARY)
|
|
72
86
|
@tables.each do |t|
|
|
73
|
-
|
|
74
|
-
bytes << tag_bytes
|
|
87
|
+
bytes << t.tag.encode(Encoding::BINARY).ljust(4, " ")
|
|
75
88
|
bytes << [per_table_checksum(t)].pack("I>")
|
|
76
89
|
bytes << [offsets[t.tag]].pack("I>")
|
|
77
90
|
bytes << [t.bytes.bytesize].pack("I>")
|
|
78
91
|
end
|
|
79
|
-
|
|
92
|
+
bytes
|
|
80
93
|
end
|
|
81
94
|
|
|
82
95
|
# OpenType per-table checksum. The head table's checksumAdjustment
|
|
83
|
-
# field (offset 8-11) is treated as zero per spec.
|
|
84
|
-
# zero-padded to a 4-byte boundary for checksum, even when their
|
|
85
|
-
# actual length is shorter.
|
|
96
|
+
# field (offset 8-11) is treated as zero per spec.
|
|
86
97
|
def per_table_checksum(table)
|
|
87
|
-
|
|
98
|
+
data = table.bytes
|
|
88
99
|
if table.tag == "head"
|
|
89
|
-
|
|
100
|
+
data = "#{data.byteslice(0, 8)}#{['00000000'].pack('H8')}#{data.byteslice(12..)}"
|
|
90
101
|
end
|
|
91
|
-
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def uint32_sum(bytes)
|
|
95
|
-
bytes.unpack("I>*").sum & 0xFFFFFFFF
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def pad_to_4(bytes)
|
|
99
|
-
pad = (-bytes.bytesize) % 4
|
|
100
|
-
pad.zero? ? bytes : bytes + ("\x00" * pad)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def padded_size(size)
|
|
104
|
-
(size + 3) & ~3
|
|
102
|
+
Utilities::ChecksumCalculator.calculate_table_checksum(data)
|
|
105
103
|
end
|
|
106
104
|
end
|
|
107
105
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fontisan
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.19
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -673,6 +673,7 @@ files:
|
|
|
673
673
|
- lib/fontisan/utilities.rb
|
|
674
674
|
- lib/fontisan/utilities/brotli_wrapper.rb
|
|
675
675
|
- lib/fontisan/utilities/checksum_calculator.rb
|
|
676
|
+
- lib/fontisan/utilities/padding.rb
|
|
676
677
|
- lib/fontisan/utils.rb
|
|
677
678
|
- lib/fontisan/utils/thread_pool.rb
|
|
678
679
|
- lib/fontisan/validation.rb
|