fontisan 0.4.41 → 0.4.42
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/TODO.bug-fixes/07-encapsulation-violations.md +38 -0
- data/TODO.bug-fixes/08-respond-to-violations.md +25 -0
- data/TODO.bug-fixes/README.md +5 -1
- data/lib/fontisan/binary/base_record.rb +4 -1
- data/lib/fontisan/commands/dump_table_command.rb +1 -1
- data/lib/fontisan/converters/outline_converter.rb +1 -1
- data/lib/fontisan/export/exporter.rb +1 -1
- data/lib/fontisan/export/table_serializer.rb +2 -0
- data/lib/fontisan/hints/truetype_hint_extractor.rb +3 -3
- data/lib/fontisan/sfnt_font.rb +1 -1
- data/lib/fontisan/tables/cff/index.rb +3 -0
- data/lib/fontisan/tables/cff/table_builder.rb +1 -1
- data/lib/fontisan/tables/cff2.rb +2 -2
- data/lib/fontisan/tables/glyf.rb +2 -2
- data/lib/fontisan/validators/validator.rb +1 -10
- data/lib/fontisan/variation/converter.rb +1 -1
- data/lib/fontisan/variation/instance_writer.rb +1 -1
- data/lib/fontisan/variation/validator.rb +10 -7
- data/lib/fontisan/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e075a18affb55f4954ab88944fb9922030ab5217af23ef495ecd18c7ac23eef
|
|
4
|
+
data.tar.gz: aff041d7ffe7357787f5d8069d529f20f0c2da4c225e5443cf29b301f3865bca
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0394bc7418ee0afc90b4eb5a72fd37af06b5f2ad8ab2067a4d9a0900005238ada53eb42bcb3aed2a9919888af70c73f2b334158e733b754412f55b2b8dc7eaad'
|
|
7
|
+
data.tar.gz: a921adde2bf87574c9069a86d253973f5ee232ce56d106426b1691cf6c97b6919c697235659f2f44fa6475a17fd72ee226c8b8140ada6933b38576008ab39b50
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 07 — Encapsulation violations (instance_variable_get/set, send to private)
|
|
2
|
+
|
|
3
|
+
## Priority
|
|
4
|
+
P0
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
The codebase has 14+ `instance_variable_get` calls, 4 `instance_variable_set`
|
|
8
|
+
calls, and 3 `send` calls to methods that should be accessed through public
|
|
9
|
+
APIs. These break encapsulation and make the code fragile to refactoring.
|
|
10
|
+
|
|
11
|
+
## Specific violations
|
|
12
|
+
|
|
13
|
+
### instance_variable_get (should use public accessors)
|
|
14
|
+
- `cff2.rb:343-344` — reads `@data_size`, `@off_size` from Cff2::Index
|
|
15
|
+
- `cff/table_builder.rb:102` — reads `@start_offset` from Cff::Index
|
|
16
|
+
- `dump_table_command.rb:35` — reads `@table_data` from font (public reader exists!)
|
|
17
|
+
- `truetype_hint_extractor.rb:188,205,222` — reads `@table_data` from font
|
|
18
|
+
- `validator.rb:439` — reads `@filename` from font
|
|
19
|
+
- `converter.rb:83` — reads `@parsed` from charstring
|
|
20
|
+
|
|
21
|
+
### instance_variable_set (should use constructors or public setters)
|
|
22
|
+
- `instance_writer.rb:233` — sets `@table_data` on temp font
|
|
23
|
+
- `outline_converter.rb:425` — sets `@table_data` on temp font
|
|
24
|
+
- `base_record.rb:41` — sets `@raw_data` on BaseRecord
|
|
25
|
+
- `glyf.rb:56` — sets `@glyphs_cache` on BinData record
|
|
26
|
+
|
|
27
|
+
### send to potentially private methods
|
|
28
|
+
- `exporter.rb:191` — `send(:encode_binary, ...)`
|
|
29
|
+
|
|
30
|
+
## Approach
|
|
31
|
+
1. Add missing public readers where they don't exist (Index#data_size, etc.)
|
|
32
|
+
2. Replace all instance_variable_get/set with public method calls
|
|
33
|
+
3. Replace send with direct method calls or public dispatch
|
|
34
|
+
|
|
35
|
+
## Acceptance criteria
|
|
36
|
+
- 0 `instance_variable_get` in lib/fontisan/
|
|
37
|
+
- 0 `instance_variable_set` in lib/fontisan/
|
|
38
|
+
- 0 `send` to private methods in lib/fontisan/
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# 08 — respond_to? duck typing violations
|
|
2
|
+
|
|
3
|
+
## Priority
|
|
4
|
+
P1
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
10 `respond_to?` calls use duck typing where proper type checks or
|
|
8
|
+
architecture redesigns would be more correct and safer.
|
|
9
|
+
|
|
10
|
+
## Specific violations
|
|
11
|
+
- `woff2_font.rb:188,198` — `respond_to?(:table_data)` on underlying font
|
|
12
|
+
- `outline_extractor.rb:44` — `respond_to?(:table)` on font
|
|
13
|
+
- `outline_extractor.rb:133` — `respond_to?(:empty?)` on glyph
|
|
14
|
+
- `woff_font.rb:362` — `respond_to?(:length)` on table_entries
|
|
15
|
+
- `sfnt_font.rb:358` — `respond_to?(:length)` on tables array
|
|
16
|
+
- `glyph_accessor.rb:58,356,359,387` — multiple respond_to? on glyph/font
|
|
17
|
+
|
|
18
|
+
## Approach
|
|
19
|
+
- Replace `respond_to?(:table)` with `is_a?(SfntFont)` type checks
|
|
20
|
+
- Replace `respond_to?(:table_data)` with proper type checks
|
|
21
|
+
- For BinData records that always have `.length`, remove the check entirely
|
|
22
|
+
- For glyph compound check, use `is_a?` on the glyf record type
|
|
23
|
+
|
|
24
|
+
## Acceptance criteria
|
|
25
|
+
- 0 `respond_to?` in lib/fontisan/ (excluding spec/)
|
data/TODO.bug-fixes/README.md
CHANGED
|
@@ -12,7 +12,11 @@ Each file is `NN-short-name.md` where `NN` is the priority order.
|
|
|
12
12
|
### P1 — Feature gaps (non-functional code paths)
|
|
13
13
|
- [x] ~~[03 — Variation subsetter placeholders](03-variation-subsetter-placeholders.md)~~ ✓ Done (v0.4.40)
|
|
14
14
|
- [x] ~~[04 — UFO image compile round-trip](04-ufo-image-compile-roundtrip.md)~~ ✓ Done (v0.4.40)
|
|
15
|
-
- [06 — Variable font instance WOFF2 output](06-instance-woff2-output.md)
|
|
15
|
+
- [x] ~~[06 — Variable font instance WOFF2 output](06-instance-woff2-output.md)~~ ✓ Done (v0.4.41)
|
|
16
16
|
|
|
17
17
|
### P2 — Documentation cleanup
|
|
18
18
|
- [x] ~~[05 — Stale CFF comment](05-stale-cff-comment.md)~~ ✓ Done (v0.4.40)
|
|
19
|
+
|
|
20
|
+
### P3 — Code quality violations
|
|
21
|
+
- [07 — Encapsulation violations (instance_variable_get/set, send to private)](07-encapsulation-violations.md)
|
|
22
|
+
- [08 — respond_to? duck typing violations](08-respond-to-violations.md)
|
|
@@ -38,7 +38,7 @@ module Fontisan
|
|
|
38
38
|
instance = super(io)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
instance.
|
|
41
|
+
instance.raw_data = data
|
|
42
42
|
instance
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -49,6 +49,9 @@ module Fontisan
|
|
|
49
49
|
@raw_data || to_binary_s
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# Set the raw binary data read from the source.
|
|
53
|
+
attr_writer :raw_data
|
|
54
|
+
|
|
52
55
|
# Check if the record is valid
|
|
53
56
|
#
|
|
54
57
|
# @return [Boolean] True if valid, false otherwise
|
|
@@ -422,7 +422,7 @@ module Fontisan
|
|
|
422
422
|
else
|
|
423
423
|
# Create temporary font with instance tables
|
|
424
424
|
temp_font = font.class.new
|
|
425
|
-
temp_font.
|
|
425
|
+
temp_font.table_data = instance_tables
|
|
426
426
|
|
|
427
427
|
# Convert outline format
|
|
428
428
|
case [source_format, target_format]
|
|
@@ -188,7 +188,7 @@ module Fontisan
|
|
|
188
188
|
tag: tag,
|
|
189
189
|
checksum: format_checksum(checksum),
|
|
190
190
|
parsed: false,
|
|
191
|
-
data: @serializer.
|
|
191
|
+
data: @serializer.encode_binary(binary_data),
|
|
192
192
|
fields: { error: error.message }.to_json,
|
|
193
193
|
)
|
|
194
194
|
end
|
|
@@ -185,7 +185,7 @@ module Fontisan
|
|
|
185
185
|
def extract_font_program(font)
|
|
186
186
|
return "" unless font.has_table?("fpgm")
|
|
187
187
|
|
|
188
|
-
font_program_data = font.
|
|
188
|
+
font_program_data = font.table_data&.[]("fpgm")
|
|
189
189
|
return "" unless font_program_data
|
|
190
190
|
|
|
191
191
|
# Return as binary string
|
|
@@ -202,7 +202,7 @@ module Fontisan
|
|
|
202
202
|
def extract_control_value_program(font)
|
|
203
203
|
return "" unless font.has_table?("prep")
|
|
204
204
|
|
|
205
|
-
prep_data = font.
|
|
205
|
+
prep_data = font.table_data&.[]("prep")
|
|
206
206
|
return "" unless prep_data
|
|
207
207
|
|
|
208
208
|
# Return as binary string
|
|
@@ -219,7 +219,7 @@ module Fontisan
|
|
|
219
219
|
def extract_control_values(font)
|
|
220
220
|
return [] unless font.has_table?("cvt ")
|
|
221
221
|
|
|
222
|
-
cvt_data = font.
|
|
222
|
+
cvt_data = font.table_data&.[]("cvt ")
|
|
223
223
|
return [] unless cvt_data
|
|
224
224
|
|
|
225
225
|
# CVT table is an array of 16-bit signed integers (FWord values)
|
data/lib/fontisan/sfnt_font.rb
CHANGED
|
@@ -624,7 +624,7 @@ module Fontisan
|
|
|
624
624
|
# @param tag [String] The tag to normalize
|
|
625
625
|
# @return [String] UTF-8 encoded tag
|
|
626
626
|
def normalize_tag(tag)
|
|
627
|
-
@tag_encoding_cache[tag] ||= tag.dup.force_encoding("UTF-8")
|
|
627
|
+
(@tag_encoding_cache ||= {})[tag] ||= tag.dup.force_encoding("UTF-8")
|
|
628
628
|
end
|
|
629
629
|
|
|
630
630
|
# Load a single table's data on demand
|
|
@@ -48,6 +48,9 @@ module Fontisan
|
|
|
48
48
|
# @return [String] Binary string containing all data
|
|
49
49
|
attr_reader :data
|
|
50
50
|
|
|
51
|
+
# @return [Integer] Byte offset where this INDEX starts in the source data
|
|
52
|
+
attr_reader :start_offset
|
|
53
|
+
|
|
51
54
|
# Initialize an INDEX from binary data
|
|
52
55
|
#
|
|
53
56
|
# @param io [IO, StringIO, String] Binary data to parse
|
|
@@ -99,7 +99,7 @@ module Fontisan
|
|
|
99
99
|
def extract_index(index)
|
|
100
100
|
return [0].pack("n") if index.nil? || index.count.zero?
|
|
101
101
|
|
|
102
|
-
start = index.
|
|
102
|
+
start = index.start_offset
|
|
103
103
|
io = StringIO.new(@source.raw_data)
|
|
104
104
|
io.seek(start)
|
|
105
105
|
|
data/lib/fontisan/tables/cff2.rb
CHANGED
|
@@ -340,8 +340,8 @@ module Fontisan
|
|
|
340
340
|
|
|
341
341
|
# count (2) + offSize (1) + offsets + data
|
|
342
342
|
count = index.count
|
|
343
|
-
data_size = index.
|
|
344
|
-
off_size = index.
|
|
343
|
+
data_size = index.data&.bytesize || 0
|
|
344
|
+
off_size = index.off_size || 4
|
|
345
345
|
|
|
346
346
|
2 + 1 + ((count + 1) * off_size) + data_size
|
|
347
347
|
end
|
data/lib/fontisan/tables/glyf.rb
CHANGED
|
@@ -43,7 +43,7 @@ module Fontisan
|
|
|
43
43
|
|
|
44
44
|
# Cache for parsed glyphs
|
|
45
45
|
# @return [Hash<Integer, SimpleGlyph|CompoundGlyph>]
|
|
46
|
-
|
|
46
|
+
attr_accessor :glyphs_cache
|
|
47
47
|
|
|
48
48
|
# Override read to capture raw data
|
|
49
49
|
#
|
|
@@ -53,7 +53,7 @@ module Fontisan
|
|
|
53
53
|
instance = new
|
|
54
54
|
|
|
55
55
|
# Initialize cache
|
|
56
|
-
instance.
|
|
56
|
+
instance.glyphs_cache = {}
|
|
57
57
|
|
|
58
58
|
# Handle nil or empty data gracefully
|
|
59
59
|
instance.raw_data = if io.nil?
|
|
@@ -431,16 +431,7 @@ module Fontisan
|
|
|
431
431
|
# @return [ValidationReport] Complete report
|
|
432
432
|
def build_report(font, all_results, _elapsed)
|
|
433
433
|
# Extract font path from font object
|
|
434
|
-
font_path =
|
|
435
|
-
font.path
|
|
436
|
-
elsif font.respond_to?(:filename)
|
|
437
|
-
font.filename
|
|
438
|
-
elsif font.instance_variable_defined?(:@filename)
|
|
439
|
-
font.instance_variable_get(:@filename)
|
|
440
|
-
else
|
|
441
|
-
"unknown"
|
|
442
|
-
end
|
|
443
|
-
|
|
434
|
+
font_path = "unknown"
|
|
444
435
|
report = Models::ValidationReport.new(
|
|
445
436
|
font_path: font_path,
|
|
446
437
|
valid: true,
|
|
@@ -80,7 +80,7 @@ module Fontisan
|
|
|
80
80
|
return nil unless charstring
|
|
81
81
|
|
|
82
82
|
# Parse CharString to extract blend data
|
|
83
|
-
charstring.parse
|
|
83
|
+
charstring.parse
|
|
84
84
|
blend_data = charstring.blend_data
|
|
85
85
|
return nil if blend_data.nil? || blend_data.empty?
|
|
86
86
|
|
|
@@ -290,13 +290,16 @@ module Fontisan
|
|
|
290
290
|
next unless reg_axis
|
|
291
291
|
|
|
292
292
|
# Check coordinates are in valid range [-1, 1]
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
293
|
+
coords = {
|
|
294
|
+
start_coord: reg_axis.start_coord,
|
|
295
|
+
peak_coord: reg_axis.peak_coord,
|
|
296
|
+
end_coord: reg_axis.end_coord,
|
|
297
|
+
}
|
|
298
|
+
coords.each do |name, coord|
|
|
299
|
+
next unless coord
|
|
300
|
+
next unless coord < -1.0 || coord > 1.0
|
|
301
|
+
|
|
302
|
+
@warnings << "#{table_tag} region #{idx} axis #{axis_idx} #{name} out of range: #{coord}"
|
|
300
303
|
end
|
|
301
304
|
end
|
|
302
305
|
end
|
data/lib/fontisan/version.rb
CHANGED
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.42
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -139,6 +139,8 @@ files:
|
|
|
139
139
|
- TODO.bug-fixes/04-ufo-image-compile-roundtrip.md
|
|
140
140
|
- TODO.bug-fixes/05-stale-cff-comment.md
|
|
141
141
|
- TODO.bug-fixes/06-instance-woff2-output.md
|
|
142
|
+
- TODO.bug-fixes/07-encapsulation-violations.md
|
|
143
|
+
- TODO.bug-fixes/08-respond-to-violations.md
|
|
142
144
|
- TODO.bug-fixes/README.md
|
|
143
145
|
- TODO.improvements/01-cbdt-cblc-gid-stable-propagation.md
|
|
144
146
|
- TODO.improvements/02-collection-outline-priority.md
|