fontisan 0.4.20 → 0.4.21

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7eea2bdc6a2142dc7aec297e4753ea530f9b66d1e89f7cd4a83d8582bd511a8b
4
- data.tar.gz: 2d6c594a71b01982f226e2c1db4f952280f167aaf2bc404b8bbfc2ef95bfea79
3
+ metadata.gz: 55632b625474e82c1cf9573ad6fce2de32f5636823701023abee87fc8817d8fc
4
+ data.tar.gz: 5a26b3428874b792c101484dbed0468c25eb9f8eb7644a2c0ffff61c8f2baf6a
5
5
  SHA512:
6
- metadata.gz: 41e75040b3020c6ddcd22ca1567c8a7d44c6511d6fbdd886e6712de23b4ae7146a85fb64ef135c0e3aa73af3054c9c622315663c1bbd60ca744092cc2c6c6b0b
7
- data.tar.gz: 7ffb7e4f4a4546d3c42f18ef92566693ca8250e287b307011afa8cd782769c328917eb7929c1bb664581b9c82fafa1355801893e08b8cddda60f222c68e467cc
6
+ metadata.gz: e7334f4be856d9a86712a0215c3671c7148d623649d4b81d232aa5f70b8aee71e1e55047a2780f48a7018721026e13dc99f0fff6dcbeda49801f88a832cbf99e
7
+ data.tar.gz: a6432a0a4db97bdfedd597f5f79f40be36b0fd5d450f21e43319023a85b3e331377b2ee7e002fceb9f90c2e9e9a9ee4c1489ebd25e295058e5605d5be0482f57
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-07-07 23:48:55 UTC using RuboCop version 1.86.1.
3
+ # on 2026-07-08 06:50:36 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,7 +12,7 @@ Gemspec/RequiredRubyVersion:
12
12
  - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
13
13
  - 'fontisan.gemspec'
14
14
 
15
- # Offense count: 2102
15
+ # Offense count: 2110
16
16
  # This cop supports safe autocorrection (--autocorrect).
17
17
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
18
18
  # URISchemes: http, https
@@ -95,7 +95,7 @@ Lint/UselessConstantScoping:
95
95
  - 'lib/fontisan/conversion_options.rb'
96
96
  - 'lib/fontisan/type1/charstrings.rb'
97
97
 
98
- # Offense count: 703
98
+ # Offense count: 704
99
99
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
100
100
  Metrics/AbcSize:
101
101
  Enabled: false
@@ -123,7 +123,7 @@ Metrics/CollectionLiteralLength:
123
123
  Metrics/CyclomaticComplexity:
124
124
  Enabled: false
125
125
 
126
- # Offense count: 977
126
+ # Offense count: 978
127
127
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
128
128
  Metrics/MethodLength:
129
129
  Max: 135
@@ -221,7 +221,7 @@ RSpec/DescribeMethod:
221
221
  - 'spec/fontisan/type1/seac_expander_spec.rb'
222
222
  - 'spec/fontisan/type1_real_fonts_spec.rb'
223
223
 
224
- # Offense count: 1664
224
+ # Offense count: 1666
225
225
  # Configuration parameters: CountAsOne.
226
226
  RSpec/ExampleLength:
227
227
  Max: 66
@@ -298,7 +298,7 @@ RSpec/MultipleDescribes:
298
298
  - 'spec/fontisan/utils/thread_pool_spec.rb'
299
299
  - 'spec/fontisan/variation/cache_spec.rb'
300
300
 
301
- # Offense count: 2102
301
+ # Offense count: 2103
302
302
  RSpec/MultipleExpectations:
303
303
  Max: 19
304
304
 
@@ -330,7 +330,7 @@ RSpec/RepeatedExample:
330
330
  - 'spec/fontisan/metrics_calculator_spec.rb'
331
331
  - 'spec/fontisan/tables/os2_spec.rb'
332
332
 
333
- # Offense count: 47
333
+ # Offense count: 48
334
334
  # Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
335
335
  # SupportedInflectors: default, active_support
336
336
  RSpec/SpecFilePathFormat:
@@ -139,6 +139,15 @@ module Fontisan
139
139
  cbdts.first
140
140
  end
141
141
 
142
+ # Safe variant of #cbdt_source: returns nil instead of raising when
143
+ # the project has multiple CBDT sources. Used inside the per-subfont
144
+ # glyph-copy pass where raising mid-compile would leave partial state.
145
+ def safe_cbdt_source
146
+ cbdt_source
147
+ rescue MultipleCbdtSourcesError
148
+ nil
149
+ end
150
+
142
151
  def build_target_for(subfont_name)
143
152
  bindings = @subfonts[subfont_name] || []
144
153
  target = Ufo::Font.new
@@ -171,15 +180,22 @@ module Fontisan
171
180
  def assign_gids_and_copy_glyphs(bindings, target, deduplicator)
172
181
  cbdt = safe_cbdt_source
173
182
 
174
- if cbdt
175
- add_all_cbdt_glyphs(cbdt, target)
176
- else
177
- add_notdef_from(bindings, target, deduplicator)
178
- end
183
+ # Glyph ordering matters: Cmap.build uses first-wins semantics
184
+ # (Cmap.build docstring), so outline donors must be stitched
185
+ # before CBDT placeholders. Otherwise the empty CBDT placeholder
186
+ # would land at a lower GID and win the cp→gid mapping for any
187
+ # codepoint covered by both donors, hiding the real outline glyph.
188
+ # CBDT bitmap data still propagates via propagate_cbdt_tables.
189
+ add_notdef_from(bindings, target, deduplicator)
190
+ add_outline_glyphs(bindings, target, deduplicator,
191
+ skip_sources: cbdt ? [cbdt] : [])
192
+ add_all_cbdt_glyphs(cbdt, target) if cbdt
193
+ end
179
194
 
195
+ def add_outline_glyphs(bindings, target, deduplicator, skip_sources:)
180
196
  sorted_bindings(bindings).each do |binding|
181
197
  next if binding[:donor_gid].zero?
182
- next if cbdt && binding[:source].equal?(cbdt)
198
+ next if skip_sources.any? { |s| s.equal?(binding[:source]) }
183
199
 
184
200
  glyph = binding[:source].glyph_for_gid(binding[:donor_gid])
185
201
  next unless glyph
@@ -197,13 +213,6 @@ module Fontisan
197
213
  end
198
214
  end
199
215
 
200
- def safe_cbdt_source
201
- cbdts = @sources.values.select { |s| s.bitmap_mode == :cbdt }
202
- cbdts.size == 1 ? cbdts.first : nil
203
- rescue MultipleCbdtSourcesError
204
- nil
205
- end
206
-
207
216
  def sorted_bindings(bindings)
208
217
  bindings.sort_by { |b| [b[:codepoint] || Float::INFINITY, b[:donor_gid]] }
209
218
  end
@@ -11,6 +11,14 @@ module Fontisan
11
11
  # Both subtables share the same segment list (capped to BMP for
12
12
  # format 4); format 4 is required by Windows even though format
13
13
  # 12 is more capable.
14
+ #
15
+ # Contract — first-wins: when multiple glyphs declare the same
16
+ # codepoint, the glyph at the LOWER GID wins the cp→gid mapping.
17
+ # Callers that want one donor to outrank another for a shared
18
+ # codepoint (e.g. outline donor vs. CBDT placeholder) MUST order
19
+ # the higher-priority glyphs earlier in the `glyphs:` array.
20
+ # Stitcher relies on this for outline-first stitching.
21
+ #
14
22
  # @see https://learn.microsoft.com/en-us/typography/opentype/spec/cmap
15
23
  module Cmap
16
24
  PLATFORM_WINDOWS = 3
@@ -195,7 +195,7 @@ module Fontisan
195
195
 
196
196
  def self.build_empty(glyph_count)
197
197
  # Minimal gvar with no variation data
198
- header = [VERSION_MAJOR, VERSION_MINOR, 0, 0, 0, glyph_count, 1, glyph_count * 1 + 16].pack("nnnnnnNN")
198
+ header = [VERSION_MAJOR, VERSION_MINOR, 0, 0, 0, glyph_count, 1, glyph_count + 16].pack("nnnnnnNN")
199
199
  offsets = Array.new(glyph_count + 1, 0).pack("C*")
200
200
  header + offsets
201
201
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontisan
4
- VERSION = "0.4.20"
4
+ VERSION = "0.4.21"
5
5
  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.20
4
+ version: 0.4.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.