fontisan 0.4.43 → 0.4.45

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.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.bug-fixes/12-type1-generators-duck-typing.md +80 -0
  3. data/TODO.bug-fixes/13-hint-extractor-duck-typing.md +30 -0
  4. data/TODO.bug-fixes/14-font-interface-type-checks.md +82 -0
  5. data/TODO.bug-fixes/15-glyph-type-checks-and-simple-glyph-points-bug.md +49 -0
  6. data/TODO.bug-fixes/16-variation-table-checks.md +31 -0
  7. data/TODO.bug-fixes/17-ufo-and-stitcher-cleanup.md +32 -0
  8. data/TODO.bug-fixes/18-generic-value-and-io-checks.md +56 -0
  9. data/TODO.bug-fixes/19-serialization-migration.md +52 -0
  10. data/TODO.bug-fixes/20-variation-validator-type-checks.md +29 -0
  11. data/TODO.bug-fixes/21-xml-builder-send.md +24 -0
  12. data/TODO.bug-fixes/22-type1-converter-font-info.md +25 -0
  13. data/TODO.bug-fixes/23-boundary-type-checks.md +31 -0
  14. data/TODO.bug-fixes/24-validator-dynamic-field-key.md +25 -0
  15. data/TODO.bug-fixes/25-serialization-migration.md +49 -0
  16. data/TODO.bug-fixes/26-variation-preserver-spec-migration.md +25 -0
  17. data/TODO.bug-fixes/27-table-class-registry.md +50 -0
  18. data/TODO.bug-fixes/28-spec-doubles-cleanup.md +40 -0
  19. data/TODO.bug-fixes/29-strategy-registries.md +30 -0
  20. data/TODO.bug-fixes/30-eliminate-send-in-specs.md +36 -0
  21. data/TODO.bug-fixes/README.md +27 -0
  22. data/lib/fontisan/binary/base_record.rb +13 -12
  23. data/lib/fontisan/collection/builder.rb +3 -5
  24. data/lib/fontisan/collection/dfont_builder.rb +4 -11
  25. data/lib/fontisan/collection/writer.rb +1 -1
  26. data/lib/fontisan/commands/base_command.rb +1 -1
  27. data/lib/fontisan/commands/info_command.rb +1 -1
  28. data/lib/fontisan/commands/pack_command.rb +4 -4
  29. data/lib/fontisan/commands/unpack_command.rb +1 -1
  30. data/lib/fontisan/commands/validate_command.rb +1 -1
  31. data/lib/fontisan/conversion_options.rb +1 -1
  32. data/lib/fontisan/converters/format_converter.rb +3 -3
  33. data/lib/fontisan/converters/glyf_table_builder.rb +1 -1
  34. data/lib/fontisan/converters/outline_converter.rb +2 -6
  35. data/lib/fontisan/converters/table_copier.rb +4 -12
  36. data/lib/fontisan/converters/type1_converter.rb +13 -38
  37. data/lib/fontisan/converters/woff2_encoder.rb +8 -10
  38. data/lib/fontisan/converters/woff_writer.rb +3 -3
  39. data/lib/fontisan/export/exporter.rb +8 -9
  40. data/lib/fontisan/export/table_serializer.rb +71 -186
  41. data/lib/fontisan/export/transformers/font_to_ttx.rb +18 -18
  42. data/lib/fontisan/export/transformers/head_transformer.rb +1 -1
  43. data/lib/fontisan/export/transformers/hhea_transformer.rb +1 -1
  44. data/lib/fontisan/export/transformers/maxp_transformer.rb +1 -1
  45. data/lib/fontisan/export/transformers/name_transformer.rb +1 -1
  46. data/lib/fontisan/export/transformers/os2_transformer.rb +1 -1
  47. data/lib/fontisan/export/transformers/post_transformer.rb +1 -1
  48. data/lib/fontisan/export/ttx_generator.rb +36 -37
  49. data/lib/fontisan/font_writer.rb +1 -1
  50. data/lib/fontisan/glyph_accessor.rb +7 -9
  51. data/lib/fontisan/hints/hint_converter.rb +1 -1
  52. data/lib/fontisan/hints/postscript_hint_extractor.rb +24 -64
  53. data/lib/fontisan/hints/truetype_hint_extractor.rb +1 -1
  54. data/lib/fontisan/models/ttx/tables/binary_table.rb +1 -1
  55. data/lib/fontisan/optimizers/charstring_rewriter.rb +1 -1
  56. data/lib/fontisan/optimizers/pattern_analyzer.rb +1 -1
  57. data/lib/fontisan/parsers/dfont_parser.rb +2 -2
  58. data/lib/fontisan/pipeline/transformation_pipeline.rb +1 -1
  59. data/lib/fontisan/sfnt_font.rb +2 -0
  60. data/lib/fontisan/sfnt_source.rb +28 -0
  61. data/lib/fontisan/stitcher/source.rb +8 -10
  62. data/lib/fontisan/subset/builder.rb +2 -2
  63. data/lib/fontisan/subset/table_strategy/cff2.rb +2 -3
  64. data/lib/fontisan/svg_to_glyf/assembler.rb +59 -20
  65. data/lib/fontisan/svg_to_glyf/document.rb +28 -9
  66. data/lib/fontisan/svg_to_glyf/geometry/normalizer.rb +34 -17
  67. data/lib/fontisan/tables/cff/charstring.rb +3 -0
  68. data/lib/fontisan/tables/cff/dict.rb +6 -3
  69. data/lib/fontisan/tables/cff/index.rb +1 -1
  70. data/lib/fontisan/tables/cff/private_dict_writer.rb +2 -2
  71. data/lib/fontisan/tables/cff/table_builder.rb +1 -1
  72. data/lib/fontisan/tables/cff.rb +3 -0
  73. data/lib/fontisan/tables/cff2/table_builder.rb +3 -5
  74. data/lib/fontisan/tables/cff2.rb +3 -0
  75. data/lib/fontisan/tables/cmap.rb +3 -0
  76. data/lib/fontisan/tables/cvar.rb +3 -0
  77. data/lib/fontisan/tables/fvar.rb +3 -0
  78. data/lib/fontisan/tables/glyf/simple_glyph.rb +9 -0
  79. data/lib/fontisan/tables/glyf.rb +12 -8
  80. data/lib/fontisan/tables/glyf_table.rb +1 -1
  81. data/lib/fontisan/tables/gvar.rb +3 -0
  82. data/lib/fontisan/tables/head.rb +3 -0
  83. data/lib/fontisan/tables/hhea.rb +3 -0
  84. data/lib/fontisan/tables/hmtx.rb +3 -7
  85. data/lib/fontisan/tables/hvar.rb +4 -0
  86. data/lib/fontisan/tables/loca.rb +3 -0
  87. data/lib/fontisan/tables/maxp.rb +3 -0
  88. data/lib/fontisan/tables/mvar.rb +3 -0
  89. data/lib/fontisan/tables/name.rb +3 -0
  90. data/lib/fontisan/tables/os2.rb +3 -0
  91. data/lib/fontisan/tables/post.rb +3 -0
  92. data/lib/fontisan/tables/registry.rb +71 -0
  93. data/lib/fontisan/tables/vvar.rb +3 -0
  94. data/lib/fontisan/tables.rb +2 -0
  95. data/lib/fontisan/type1/afm_generator.rb +12 -65
  96. data/lib/fontisan/type1/charstring_converter.rb +3 -3
  97. data/lib/fontisan/type1/conversion_options.rb +3 -2
  98. data/lib/fontisan/type1/decryptor.rb +1 -1
  99. data/lib/fontisan/type1/generator.rb +14 -9
  100. data/lib/fontisan/type1/inf_generator.rb +10 -35
  101. data/lib/fontisan/type1/pfa_generator.rb +36 -26
  102. data/lib/fontisan/type1/pfb_generator.rb +24 -12
  103. data/lib/fontisan/type1/pfm_generator.rb +14 -31
  104. data/lib/fontisan/type1/ttf_to_type1_converter.rb +30 -24
  105. data/lib/fontisan/ufo/bounds.rb +91 -0
  106. data/lib/fontisan/ufo/compile/fvar.rb +2 -2
  107. data/lib/fontisan/ufo/contour.rb +5 -0
  108. data/lib/fontisan/ufo/convert/from_bin_data.rb +15 -29
  109. data/lib/fontisan/ufo/info.rb +4 -0
  110. data/lib/fontisan/ufo/point.rb +0 -7
  111. data/lib/fontisan/ufo.rb +1 -0
  112. data/lib/fontisan/utilities/brotli_wrapper.rb +3 -5
  113. data/lib/fontisan/utilities/padding.rb +9 -8
  114. data/lib/fontisan/validation/collection_validator.rb +2 -2
  115. data/lib/fontisan/validators/validator.rb +4 -4
  116. data/lib/fontisan/variable/delta_applicator.rb +4 -11
  117. data/lib/fontisan/variable/metric_delta_processor.rb +3 -3
  118. data/lib/fontisan/variable/static_font_builder.rb +19 -23
  119. data/lib/fontisan/variation/converter.rb +1 -1
  120. data/lib/fontisan/variation/instance_font_wrapper.rb +41 -0
  121. data/lib/fontisan/variation/instance_writer.rb +19 -17
  122. data/lib/fontisan/variation/metrics_adjuster.rb +16 -21
  123. data/lib/fontisan/variation/validator.rb +26 -38
  124. data/lib/fontisan/variation/variable_svg_generator.rb +0 -48
  125. data/lib/fontisan/variation/variation_preserver.rb +2 -3
  126. data/lib/fontisan/variation.rb +1 -0
  127. data/lib/fontisan/version.rb +1 -1
  128. data/lib/fontisan/woff2/sfnt_checksum.rb +1 -1
  129. data/lib/fontisan/woff2/table_transformer.rb +22 -29
  130. data/lib/fontisan/woff2_font.rb +11 -10
  131. data/lib/fontisan/woff_font.rb +2 -0
  132. data/lib/fontisan.rb +1 -0
  133. metadata +25 -2
@@ -178,8 +178,6 @@ module Fontisan
178
178
  # @param tables [Hash<String, String>] Font tables
179
179
  # @return [Object] Font-like object
180
180
  def build_font_from_tables(tables)
181
- # Create a simple font wrapper that implements the minimal
182
- # interface needed by SvgGenerator
183
181
  InstanceFontWrapper.new(@font, tables)
184
182
  end
185
183
 
@@ -214,52 +212,6 @@ module Fontisan
214
212
  end
215
213
  coords
216
214
  end
217
-
218
- # Wrapper class for instance font tables
219
- #
220
- # Provides minimal interface needed by SvgGenerator while using
221
- # instance tables instead of original font tables.
222
- class InstanceFontWrapper
223
- # @return [Hash<String, String>] Font tables
224
- attr_reader :table_data
225
-
226
- # Initialize wrapper
227
- #
228
- # @param original_font [Object] Original variable font
229
- # @param instance_tables [Hash<String, String>] Instance tables
230
- def initialize(original_font, instance_tables)
231
- @original_font = original_font
232
- @table_data = instance_tables
233
- end
234
-
235
- # Get table by tag
236
- #
237
- # @param tag [String] Table tag
238
- # @return [Object, nil] Table or nil
239
- def table(tag)
240
- # Use instance table if available, otherwise fall back to original
241
- if @table_data.key?(tag)
242
- end
243
- @original_font.table(tag)
244
- end
245
-
246
- # Check if table exists
247
- #
248
- # @param tag [String] Table tag
249
- # @return [Boolean] True if table exists
250
- def has_table?(tag)
251
- @table_data.key?(tag) || @original_font.has_table?(tag)
252
- end
253
-
254
- # Forward other methods to original font
255
- def method_missing(method, ...)
256
- @original_font.send(method, ...)
257
- end
258
-
259
- def respond_to_missing?(method, include_private = false)
260
- @original_font.respond_to?(method, include_private) || super
261
- end
262
- end
263
215
  end
264
216
  end
265
217
  end
@@ -148,10 +148,9 @@ module Fontisan
148
148
  def validate_source!
149
149
  raise ArgumentError, "Source font cannot be nil" if @source_font.nil?
150
150
 
151
- unless @source_font.respond_to?(:has_table?) &&
152
- @source_font.respond_to?(:table_data)
151
+ unless @source_font.is_a?(SfntSource)
153
152
  raise ArgumentError,
154
- "Source font must respond to :has_table? and :table_data"
153
+ "Source font must be an SfntSource instance"
155
154
  end
156
155
 
157
156
  if @target_tables.nil?
@@ -13,6 +13,7 @@ module Fontisan
13
13
  autoload :DeltaParser, "fontisan/variation/delta_parser"
14
14
  autoload :Inspector, "fontisan/variation/inspector"
15
15
  autoload :InstanceGenerator, "fontisan/variation/instance_generator"
16
+ autoload :InstanceFontWrapper, "fontisan/variation/instance_font_wrapper"
16
17
  autoload :InstanceWriter, "fontisan/variation/instance_writer"
17
18
  autoload :Interpolator, "fontisan/variation/interpolator"
18
19
  autoload :MetricsAdjuster, "fontisan/variation/metrics_adjuster"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontisan
4
- VERSION = "0.4.43"
4
+ VERSION = "0.4.45"
5
5
  end
@@ -79,7 +79,7 @@ module Fontisan
79
79
  offsets = {}
80
80
  @tables.each do |t|
81
81
  offsets[t.tag] = cursor
82
- cursor += Utilities::Padding.aligned_size(t.bytes)
82
+ cursor += Utilities::Padding.aligned_size(t.bytes.bytesize)
83
83
  end
84
84
 
85
85
  bytes = String.new(encoding: Encoding::BINARY)
@@ -32,41 +32,34 @@ module Fontisan
32
32
  #
33
33
  # @param tag [String] Table tag
34
34
  # @return [String, nil] Transformed table data
35
+ # Per-tag transform method dispatch.
36
+ TRANSFORM_DISPATCH = {
37
+ "glyf" => :transform_glyf,
38
+ "loca" => :transform_loca,
39
+ "hmtx" => :transform_hmtx,
40
+ }.freeze
41
+
42
+ # Per-tag transform version constant.
43
+ TRANSFORM_VERSIONS = {
44
+ "glyf" => Directory::TRANSFORM_GLYF_LOCA,
45
+ "loca" => Directory::TRANSFORM_GLYF_LOCA,
46
+ "hmtx" => Directory::TRANSFORM_HMTX,
47
+ }.freeze
48
+
35
49
  def transform_table(tag)
36
- case tag
37
- when "glyf"
38
- transform_glyf
39
- when "loca"
40
- transform_loca
41
- when "hmtx"
42
- transform_hmtx
43
- else
44
- # No transformation, return original data
45
- get_table_data(tag)
46
- end
50
+ transform_method = TRANSFORM_DISPATCH[tag]
51
+ return method(transform_method).call if transform_method
52
+
53
+ # No transformation, return original data
54
+ get_table_data(tag)
47
55
  end
48
56
 
49
- # Check if a table can be transformed
50
- #
51
- # @param tag [String] Table tag
52
- # @return [Boolean] True if table supports transformation
53
57
  def transformable?(tag)
54
- %w[glyf loca hmtx].include?(tag)
58
+ TRANSFORM_DISPATCH.key?(tag)
55
59
  end
56
60
 
57
- # Determine transformation version for a table
58
- #
59
- # @param tag [String] Table tag
60
- # @return [Integer] Transformation version
61
61
  def transformation_version(tag)
62
- case tag
63
- when "glyf", "loca"
64
- Directory::TRANSFORM_GLYF_LOCA
65
- when "hmtx"
66
- Directory::TRANSFORM_HMTX
67
- else
68
- Directory::TRANSFORM_NONE
69
- end
62
+ TRANSFORM_VERSIONS.fetch(tag, Directory::TRANSFORM_NONE)
70
63
  end
71
64
 
72
65
  private
@@ -150,7 +143,7 @@ module Fontisan
150
143
  # @param tag [String] Table tag
151
144
  # @return [String, nil] Table data or nil if not found
152
145
  def get_table_data(tag)
153
- return nil unless font.respond_to?(:table_data)
146
+ return nil unless font.is_a?(SfntSource)
154
147
 
155
148
  font.table_data[tag]
156
149
  end
@@ -42,21 +42,20 @@ module Fontisan
42
42
  (@flags & 0x3F) != 0x3F && KNOWN_TAGS[tag_index]&.start_with?(/glyf|loca|hmtx/)
43
43
  end
44
44
 
45
+ TRANSFORM_VERSIONS = {
46
+ "glyf" => TRANSFORM_GLYF_LOCA,
47
+ "loca" => TRANSFORM_GLYF_LOCA,
48
+ "hmtx" => TRANSFORM_HMTX,
49
+ }.freeze
50
+
45
51
  # Get transform version for this table
46
52
  def transform_version
47
53
  return TRANSFORM_NONE unless transformed?
48
54
 
49
- case tag
50
- when "glyf", "loca"
51
- TRANSFORM_GLYF_LOCA
52
- when "hmtx"
53
- TRANSFORM_HMTX
54
- else
55
- TRANSFORM_NONE
56
- end
55
+ TRANSFORM_VERSIONS.fetch(tag, TRANSFORM_NONE)
57
56
  end
58
57
 
59
- private
58
+ # Methods below were private; made public for testability (TODO 30)
60
59
 
61
60
  def tag_index
62
61
  @flags & 0x3F
@@ -73,6 +72,8 @@ module Fontisan
73
72
  # puts font.header.flavor
74
73
  # puts font.table_names
75
74
  class Woff2Font
75
+ include SfntSource
76
+
76
77
  # High-level pipeline format identifier. Owned by the font class so the
77
78
  # conversion pipeline can dispatch without case statements (OCP).
78
79
  #
@@ -725,7 +726,7 @@ module Fontisan
725
726
  sfnt_data[head_offset + 8, 4] = [adjustment].pack("N")
726
727
  end
727
728
 
728
- private
729
+ # Methods below were private; made public for testability (TODO 30)
729
730
 
730
731
  # Read variable-length UIntBase128 integer from IO
731
732
  def read_uint_base128(io)
@@ -53,6 +53,8 @@ module Fontisan
53
53
  # woff.to_ttf("output.ttf") # if TrueType flavored
54
54
  # woff.to_otf("output.otf") # if CFF flavored
55
55
  class WoffFont < BinData::Record
56
+ include SfntSource
57
+
56
58
  # High-level pipeline format identifier. Owned by the font class so the
57
59
  # conversion pipeline can dispatch without case statements (OCP).
58
60
  #
data/lib/fontisan.rb CHANGED
@@ -116,6 +116,7 @@ module Fontisan
116
116
  autoload :OpenTypeFontExtensions, "fontisan/open_type_font_extensions"
117
117
  autoload :OutlineExtractor, "fontisan/outline_extractor"
118
118
  autoload :SfntFont, "fontisan/sfnt_font"
119
+ autoload :SfntSource, "fontisan/sfnt_source"
119
120
  autoload :SfntTable, "fontisan/sfnt_table"
120
121
  autoload :SfntBuilder, "fontisan/sfnt_builder"
121
122
  autoload :Stitcher, "fontisan/stitcher"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fontisan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.43
4
+ version: 0.4.45
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-12 00:00:00.000000000 Z
11
+ date: 2026-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -144,6 +144,25 @@ files:
144
144
  - TODO.bug-fixes/09-type1-cff-seac-expansion.md
145
145
  - TODO.bug-fixes/10-ttf-type1-composite-glyphs.md
146
146
  - TODO.bug-fixes/11-remaining-respond-to.md
147
+ - TODO.bug-fixes/12-type1-generators-duck-typing.md
148
+ - TODO.bug-fixes/13-hint-extractor-duck-typing.md
149
+ - TODO.bug-fixes/14-font-interface-type-checks.md
150
+ - TODO.bug-fixes/15-glyph-type-checks-and-simple-glyph-points-bug.md
151
+ - TODO.bug-fixes/16-variation-table-checks.md
152
+ - TODO.bug-fixes/17-ufo-and-stitcher-cleanup.md
153
+ - TODO.bug-fixes/18-generic-value-and-io-checks.md
154
+ - TODO.bug-fixes/19-serialization-migration.md
155
+ - TODO.bug-fixes/20-variation-validator-type-checks.md
156
+ - TODO.bug-fixes/21-xml-builder-send.md
157
+ - TODO.bug-fixes/22-type1-converter-font-info.md
158
+ - TODO.bug-fixes/23-boundary-type-checks.md
159
+ - TODO.bug-fixes/24-validator-dynamic-field-key.md
160
+ - TODO.bug-fixes/25-serialization-migration.md
161
+ - TODO.bug-fixes/26-variation-preserver-spec-migration.md
162
+ - TODO.bug-fixes/27-table-class-registry.md
163
+ - TODO.bug-fixes/28-spec-doubles-cleanup.md
164
+ - TODO.bug-fixes/29-strategy-registries.md
165
+ - TODO.bug-fixes/30-eliminate-send-in-specs.md
147
166
  - TODO.bug-fixes/README.md
148
167
  - TODO.improvements/01-cbdt-cblc-gid-stable-propagation.md
149
168
  - TODO.improvements/02-collection-outline-priority.md
@@ -490,6 +509,7 @@ files:
490
509
  - lib/fontisan/pipeline/variation_resolver.rb
491
510
  - lib/fontisan/sfnt_builder.rb
492
511
  - lib/fontisan/sfnt_font.rb
512
+ - lib/fontisan/sfnt_source.rb
493
513
  - lib/fontisan/sfnt_table.rb
494
514
  - lib/fontisan/stitcher.rb
495
515
  - lib/fontisan/stitcher/cbdt_propagator.rb
@@ -645,6 +665,7 @@ files:
645
665
  - lib/fontisan/tables/os2_table.rb
646
666
  - lib/fontisan/tables/post.rb
647
667
  - lib/fontisan/tables/post_table.rb
668
+ - lib/fontisan/tables/registry.rb
648
669
  - lib/fontisan/tables/sbix.rb
649
670
  - lib/fontisan/tables/svg.rb
650
671
  - lib/fontisan/tables/variation_common.rb
@@ -678,6 +699,7 @@ files:
678
699
  - lib/fontisan/type1_font.rb
679
700
  - lib/fontisan/ufo.rb
680
701
  - lib/fontisan/ufo/anchor.rb
702
+ - lib/fontisan/ufo/bounds.rb
681
703
  - lib/fontisan/ufo/cli.rb
682
704
  - lib/fontisan/ufo/compile.rb
683
705
  - lib/fontisan/ufo/compile/avar.rb
@@ -800,6 +822,7 @@ files:
800
822
  - lib/fontisan/variation/delta_applier.rb
801
823
  - lib/fontisan/variation/delta_parser.rb
802
824
  - lib/fontisan/variation/inspector.rb
825
+ - lib/fontisan/variation/instance_font_wrapper.rb
803
826
  - lib/fontisan/variation/instance_generator.rb
804
827
  - lib/fontisan/variation/instance_writer.rb
805
828
  - lib/fontisan/variation/interpolator.rb