fontisan 0.4.7 → 0.4.9

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.adoc +116 -2
  4. data/docs/.vitepress/config.ts +1 -7
  5. data/docs/COLLECTION_VALIDATION.adoc +61 -0
  6. data/docs/STITCHER_GUIDE.adoc +138 -1
  7. data/docs/cli/convert.md +44 -2
  8. data/docs/cli/index.md +21 -28
  9. data/docs/cli/validate-collection.md +95 -0
  10. data/docs/cli/validate.md +8 -0
  11. data/docs/index.md +0 -2
  12. data/lib/fontisan/cli.rb +29 -8
  13. data/lib/fontisan/collection/reader/stats.rb +23 -0
  14. data/lib/fontisan/collection/reader.rb +90 -0
  15. data/lib/fontisan/collection.rb +1 -0
  16. data/lib/fontisan/commands/convert_command.rb +96 -18
  17. data/lib/fontisan/commands/multi_format_output.rb +59 -0
  18. data/lib/fontisan/commands/validate_collection_command.rb +121 -0
  19. data/lib/fontisan/commands.rb +2 -0
  20. data/lib/fontisan/error.rb +25 -0
  21. data/lib/fontisan/models.rb +0 -1
  22. data/lib/fontisan/stitcher/collection_result.rb +18 -0
  23. data/lib/fontisan/stitcher/partition_strategy/base.rb +23 -0
  24. data/lib/fontisan/stitcher/partition_strategy/blueprint.rb +24 -0
  25. data/lib/fontisan/stitcher/partition_strategy/by_plane.rb +131 -0
  26. data/lib/fontisan/stitcher/partition_strategy/partition.rb +24 -0
  27. data/lib/fontisan/stitcher/partition_strategy.rb +22 -0
  28. data/lib/fontisan/stitcher.rb +44 -10
  29. data/lib/fontisan/ufo/compile/name.rb +2 -2
  30. data/lib/fontisan/ufo/info.rb +48 -0
  31. data/lib/fontisan/unicode/plane.rb +56 -0
  32. data/lib/fontisan/unicode.rb +17 -0
  33. data/lib/fontisan/version.rb +1 -1
  34. data/lib/fontisan.rb +2 -2
  35. metadata +14 -18
  36. data/docs/cli/audit.md +0 -337
  37. data/lib/fontisan/cldr/aggregator.rb +0 -33
  38. data/lib/fontisan/cldr/cache_manager.rb +0 -110
  39. data/lib/fontisan/cldr/config.rb +0 -59
  40. data/lib/fontisan/cldr/download_error.rb +0 -9
  41. data/lib/fontisan/cldr/downloader.rb +0 -79
  42. data/lib/fontisan/cldr/error.rb +0 -8
  43. data/lib/fontisan/cldr/index.rb +0 -64
  44. data/lib/fontisan/cldr/index_builder.rb +0 -72
  45. data/lib/fontisan/cldr/unicode_set_parser.rb +0 -189
  46. data/lib/fontisan/cldr/unknown_version_error.rb +0 -9
  47. data/lib/fontisan/cldr/version_resolver.rb +0 -91
  48. data/lib/fontisan/cldr.rb +0 -23
  49. data/lib/fontisan/cli/cldr_cli.rb +0 -85
  50. data/lib/fontisan/config/cldr.yml +0 -22
  51. data/lib/fontisan/models/cldr/language_coverage.rb +0 -31
  52. data/lib/fontisan/models/cldr.rb +0 -12
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ class Stitcher
5
+ # Per-subfont stats computed from the loaded, on-disk subfont (not the
6
+ # in-memory UFO target — the compiler may add glyphs, e.g. .notdef).
7
+ SubfontStats = Struct.new(:name, :glyph_count, :codepoint_count,
8
+ keyword_init: true)
9
+
10
+ # Return value of {Stitcher#write_collection}. Carries the output path,
11
+ # total bytes, and one {SubfontStats} per declared subfont.
12
+ CollectionResult = Struct.new(:path, :bytes, :subfonts, keyword_init: true) do
13
+ def face_count
14
+ subfonts.size
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ class Stitcher
5
+ module PartitionStrategy
6
+ # Abstract base. Concrete partitioners (ByPlane, ByBlock, …)
7
+ # implement {#call} and return a {Blueprint}.
8
+ class Base
9
+ # Default cap: 65,535 − .notdef − safety margin. Matches the
10
+ # Stitcher's own +GlyphLimit+ cap for TTF.
11
+ DEFAULT_CAP = 65_484
12
+
13
+ # @param cp_map [Hash{Integer=>Object}] codepoint → donor label
14
+ # @param cap [Integer] max codepoints per partition
15
+ # @return [Blueprint]
16
+ def call(cp_map, cap: DEFAULT_CAP)
17
+ raise NotImplementedError,
18
+ "#{self.class} must implement #call"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ class Stitcher
5
+ module PartitionStrategy
6
+ # The result of partitioning a codepoint set: an ordered list of
7
+ # {Partition}s. Applying a blueprint to a Stitcher declares one
8
+ # subfont per partition.
9
+ Blueprint = Struct.new(:partitions, keyword_init: true) do
10
+ # @param stitcher [Fontisan::Stitcher]
11
+ # @return [Array<Symbol>] names of subfonts declared
12
+ def apply_to(stitcher)
13
+ partitions.each { |p| p.apply_to(stitcher) }
14
+ partitions.map(&:name)
15
+ end
16
+
17
+ # @return [Array<Symbol>]
18
+ def names
19
+ partitions.map(&:name)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ class Stitcher
5
+ module PartitionStrategy
6
+ # Partition codepoints by Unicode plane (BMP, SMP, SIP, …).
7
+ #
8
+ # For each plane with codepoints:
9
+ # - if the count fits under +cap+, emit one partition named
10
+ # +:plane_<n>+ (e.g. +:plane_0+, +:plane_2+);
11
+ # - otherwise sub-split using the large CJK extension block
12
+ # boundaries, naming partitions +:plane_<n>_a+, +:plane_<n>_b+,
13
+ # and so on.
14
+ #
15
+ # If a single CJK extension block alone exceeds +cap+, raises
16
+ # {Fontisan::GlyphLimitExceededError} — the partitioner cannot
17
+ # satisfy the cap and the caller must use a smaller cap, split
18
+ # manually, or switch to a format with a higher glyph limit.
19
+ class ByPlane < Base
20
+ # @param cp_map [Hash{Integer=>Object}] codepoint → donor label
21
+ # @param cap [Integer] max codepoints per partition
22
+ # @return [Blueprint]
23
+ def call(cp_map, cap: DEFAULT_CAP)
24
+ grouped = group_by_plane(cp_map)
25
+ partitions = grouped.flat_map do |plane_num, entries|
26
+ build_partitions_for_plane(plane_num, entries, cap)
27
+ end
28
+ Blueprint.new(partitions: partitions)
29
+ end
30
+
31
+ private
32
+
33
+ def group_by_plane(cp_map)
34
+ cp_map.group_by { |cp, _label| Fontisan::Unicode::Plane.of(cp) }
35
+ end
36
+
37
+ def build_partitions_for_plane(plane_num, entries, cap)
38
+ if entries.size <= cap
39
+ return [single_partition(plane_num, entries)]
40
+ end
41
+
42
+ sub_split_by_block(plane_num, entries, cap)
43
+ end
44
+
45
+ def single_partition(plane_num, entries)
46
+ Partition.new(
47
+ name: :"plane_#{plane_num}",
48
+ cps: entries.map(&:first),
49
+ donor_map: entries.to_h,
50
+ )
51
+ end
52
+
53
+ # When a plane overflows +cap+, carve it along the large CJK
54
+ # extension block boundaries. The +:other+ bucket (everything
55
+ # outside the known mega-blocks) is split into chunks of +cap+,
56
+ # while each known large block becomes one partition atomically —
57
+ # if a single block alone exceeds +cap+, we cannot sub-split it
58
+ # (its codepoints are contiguous and we don't have finer-grained
59
+ # boundaries to use), so raise.
60
+ def sub_split_by_block(plane_num, entries, cap)
61
+ buckets = bucket_by_large_block(entries)
62
+ partitions = []
63
+ suffix = "a"
64
+
65
+ buckets.each do |label, bucket_entries|
66
+ if large_block?(label) && bucket_entries.size > cap
67
+ raise PartitionCapExceededError.new(
68
+ block_label: label,
69
+ actual: bucket_entries.size,
70
+ cap: cap,
71
+ )
72
+ end
73
+
74
+ chunks(bucket_entries, cap).each do |chunk|
75
+ partitions << Partition.new(
76
+ name: :"plane_#{plane_num}_#{suffix}",
77
+ cps: chunk.map(&:first),
78
+ donor_map: chunk.to_h,
79
+ )
80
+ suffix = suffix.succ
81
+ end
82
+ end
83
+ partitions
84
+ end
85
+
86
+ def large_block?(label)
87
+ Fontisan::Unicode::Plane::LARGE_CJK_BLOCKS.key?(label)
88
+ end
89
+
90
+ # Split +entries+ into sub-arrays of at most +cap+ each.
91
+ def chunks(entries, cap)
92
+ entries.each_slice(cap).to_a
93
+ end
94
+
95
+ # Bucket entries by which large CJK block (or :other) they fall
96
+ # into. Entries outside any known large block go into :other,
97
+ # which is then packed into its own partition(s).
98
+ def bucket_by_large_block(entries)
99
+ buckets = { other: [] }
100
+ Fontisan::Unicode::Plane::LARGE_CJK_BLOCKS.each_key do |label|
101
+ buckets[label] = []
102
+ end
103
+
104
+ entries.each do |cp, label|
105
+ block = find_large_block(cp)
106
+ if block
107
+ buckets[block] << [cp, label]
108
+ else
109
+ buckets[:other] << [cp, label]
110
+ end
111
+ end
112
+
113
+ # Drop empty buckets; preserve order (other first if non-empty,
114
+ # then CJK blocks in declaration order).
115
+ ordered = []
116
+ ordered << [:other, buckets[:other]] unless buckets[:other].empty?
117
+ Fontisan::Unicode::Plane::LARGE_CJK_BLOCKS.each_key do |label|
118
+ ordered << [label, buckets[label]] unless buckets[label].empty?
119
+ end
120
+ ordered
121
+ end
122
+
123
+ def find_large_block(codepoint)
124
+ Fontisan::Unicode::Plane::LARGE_CJK_BLOCKS.find do |_label, range|
125
+ range.include?(codepoint)
126
+ end&.first
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ class Stitcher
5
+ module PartitionStrategy
6
+ # One slice of a partitioned codepoint set. Each partition names
7
+ # a target subfont and the codepoints + donor mapping that should
8
+ # land in it.
9
+ #
10
+ # {#apply_to} pushes the partition's bindings into a Stitcher via
11
+ # +include_codepoints_map+ (TODO 68). If that API is unavailable
12
+ # (older Stitcher), it falls back to one +include_codepoints+
13
+ # call per donor — but the preferred path is the map API.
14
+ Partition = Struct.new(:name, :cps, :donor_map, keyword_init: true) do
15
+ # @param stitcher [Fontisan::Stitcher]
16
+ # @return [void]
17
+ def apply_to(stitcher)
18
+ slice = cps.to_h { |cp| [cp, donor_map[cp]] }
19
+ stitcher.include_codepoints_map(slice, into: name)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ class Stitcher
5
+ # Namespace for codepoint partitioners that split a codepoint set
6
+ # across named subfonts while respecting the format's glyph cap.
7
+ #
8
+ # A partitioner is a strategy object with a single entry point
9
+ # (`.partition`) that takes a +{codepoint => donor}+ map and returns
10
+ # a {Blueprint}. The blueprint is then applied to a Stitcher via
11
+ # {Blueprint#apply_to}.
12
+ #
13
+ # Adding a new partitioner = adding a new file + a new entry here.
14
+ # No edits to existing partitioners required (open/closed).
15
+ module PartitionStrategy
16
+ autoload :Base, "fontisan/stitcher/partition_strategy/base"
17
+ autoload :Blueprint, "fontisan/stitcher/partition_strategy/blueprint"
18
+ autoload :Partition, "fontisan/stitcher/partition_strategy/partition"
19
+ autoload :ByPlane, "fontisan/stitcher/partition_strategy/by_plane"
20
+ end
21
+ end
22
+ end
@@ -25,11 +25,20 @@ module Fontisan
25
25
  # stitcher.include_range(0x4E00..0x9FFF, from: :noto_cjk, into: :cjk)
26
26
  # stitcher.write_collection("out.otc", format: :otf2)
27
27
  class Stitcher
28
- autoload :Source, "fontisan/stitcher/source"
29
- autoload :Selector, "fontisan/stitcher/selector"
30
- autoload :GlyphSignature, "fontisan/stitcher/glyph_signature"
31
- autoload :Deduplicator, "fontisan/stitcher/deduplicator"
32
- autoload :GlyphLimit, "fontisan/stitcher/glyph_limit"
28
+ autoload :Source, "fontisan/stitcher/source"
29
+ autoload :Selector, "fontisan/stitcher/selector"
30
+ autoload :GlyphSignature, "fontisan/stitcher/glyph_signature"
31
+ autoload :Deduplicator, "fontisan/stitcher/deduplicator"
32
+ autoload :GlyphLimit, "fontisan/stitcher/glyph_limit"
33
+ autoload :CollectionResult, "fontisan/stitcher/collection_result"
34
+ autoload :SubfontStats, "fontisan/stitcher/collection_result"
35
+ autoload :PartitionStrategy, "fontisan/stitcher/partition_strategy"
36
+
37
+ # Internal: pairs a compiled loaded font with its stats so
38
+ # +write_collection+ can build the collection and the result from a
39
+ # single compilation pass.
40
+ CompiledSubfont = Struct.new(:name, :font, :stats, keyword_init: true)
41
+ private_constant :CompiledSubfont
33
42
 
34
43
  DEFAULT_DEDUPLICATE = true
35
44
 
@@ -54,6 +63,13 @@ module Fontisan
54
63
  Selector::Codepoints.new(codepoints).apply(source(from), @subfonts[into])
55
64
  end
56
65
 
66
+ def include_codepoints_map(cp_map, into:)
67
+ cp_map.to_h
68
+ .group_by { |_cp, label| label }
69
+ .transform_values { |pairs| pairs.map(&:first).sort }
70
+ .each { |label, cps| include_codepoints(cps, from: label, into: into) }
71
+ end
72
+
57
73
  def include_gid(donor_gid, from:, into:)
58
74
  Selector::Gid.new(donor_gid).apply(source(from), @subfonts[into])
59
75
  end
@@ -89,13 +105,19 @@ module Fontisan
89
105
  raise ArgumentError, "no subfonts declared" if @subfonts.empty?
90
106
 
91
107
  compiled = @subfonts.keys.map do |name|
92
- compile_subfont_to_loaded_font(name, format: format)
108
+ compile_subfont_with_stats(name, format: format)
93
109
  end
110
+ fonts = compiled.map(&:font)
94
111
 
95
112
  collection_format = collection_format_for(format)
96
- Collection::Builder.new(compiled, format: collection_format,
97
- optimize: true).build_to_file(path)
98
- path
113
+ Collection::Builder.new(fonts, format: collection_format,
114
+ optimize: true).build_to_file(path)
115
+
116
+ CollectionResult.new(
117
+ path: path,
118
+ bytes: File.size(path),
119
+ subfonts: compiled.map(&:stats),
120
+ )
99
121
  end
100
122
 
101
123
  private
@@ -140,6 +162,10 @@ module Fontisan
140
162
  end
141
163
 
142
164
  def compile_subfont_to_loaded_font(subfont_name, format:)
165
+ compile_subfont_with_stats(subfont_name, format: format).font
166
+ end
167
+
168
+ def compile_subfont_with_stats(subfont_name, format:)
143
169
  target = build_target_for(subfont_name)
144
170
  GlyphLimit.check!(target.glyphs.size, format: format)
145
171
 
@@ -148,7 +174,15 @@ module Fontisan
148
174
  sub_path = File.join(dir, "sub#{subfont_name}#{ext}")
149
175
  compiler = compiler_for(format)
150
176
  compiler.new(target).compile(output_path: sub_path)
151
- return Fontisan::FontLoader.load(sub_path)
177
+ propagate_cbdt_tables(sub_path) if cbdt_source
178
+
179
+ loaded = Fontisan::FontLoader.load(sub_path)
180
+ stats = SubfontStats.new(
181
+ name: subfont_name,
182
+ glyph_count: loaded.table("maxp")&.num_glyphs || 0,
183
+ codepoint_count: (loaded.table("cmap")&.unicode_mappings || {}).size,
184
+ )
185
+ CompiledSubfont.new(name: subfont_name, font: loaded, stats: stats)
152
186
  end
153
187
  end
154
188
 
@@ -31,7 +31,7 @@ module Fontisan
31
31
  family = font.info.family_name || "Untitled"
32
32
  subfamily = font.info.style_name || "Regular"
33
33
  ps_name = font.info.postscript_font_name || "#{family}-#{subfamily}"
34
- full_name = "#{family} #{subfamily}".strip
34
+ full_name = font.info.postscript_full_name || "#{family} #{subfamily}".strip
35
35
  major = font.info.version_major || 0
36
36
  minor = font.info.version_minor || 0
37
37
  version_str = "Version #{major}.#{minor}"
@@ -72,7 +72,7 @@ module Fontisan
72
72
 
73
73
  header + body + storage
74
74
  end
75
- private_class_method :format0_bytes, :default_records
75
+ private_class_method :format0_bytes
76
76
  end
77
77
  end
78
78
  end
@@ -41,6 +41,54 @@ module Fontisan
41
41
  end
42
42
  end
43
43
 
44
+ # Build a +Ufo::Info+ for one subfont of a collection. The family
45
+ # name embeds the subfont name (e.g. +"MyFont CJK"+), and the
46
+ # PostScript name uses the hyphenated form (e.g. +"MyFont-CJK"+).
47
+ #
48
+ # +version+ is parsed into +version_major+ / +version_minor+ per
49
+ # the UFO major.minor shape (patch is dropped).
50
+ #
51
+ # +trademark+ is stored under +extras["openTypeNameTrademark"]+
52
+ # because it is not in +STANDARD_FIELDS+ yet — see TODO 71
53
+ # out-of-scope follow-up.
54
+ #
55
+ # @param family [String] collection-wide family name
56
+ # @param subfont [String, Symbol] subfont identifier (appended to family)
57
+ # @param version [String, Integer] e.g. "0.1", "1", "1.2.3"
58
+ # @param subfamily [String] OpenType subfamily (default "Regular")
59
+ # @param copyright [String, nil]
60
+ # @param trademark [String, nil]
61
+ # @return [Info]
62
+ def self.for_subfont(family:, subfont:, version:,
63
+ subfamily: "Regular",
64
+ copyright: nil, trademark: nil)
65
+ major, minor = parse_version(version)
66
+ subfont_str = subfont.to_s
67
+ values = {
68
+ family_name: "#{family} #{subfont_str}",
69
+ style_name: subfamily,
70
+ version_major: major,
71
+ version_minor: minor,
72
+ postscript_font_name: "#{family}-#{subfont_str}",
73
+ postscript_full_name: "#{family} #{subfont_str}",
74
+ }
75
+ values[:copyright] = copyright if copyright
76
+ values["openTypeNameTrademark"] = trademark if trademark
77
+ new(values)
78
+ end
79
+
80
+ # @param version [String, Integer]
81
+ # @return [Array(Integer, Integer)] [major, minor]
82
+ def self.parse_version(version)
83
+ case version.to_s
84
+ when /\A(\d+)\.(\d+)\.\d+\z/ then [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i]
85
+ when /\A(\d+)\.(\d+)\z/ then [Regexp.last_match(1).to_i, Regexp.last_match(2).to_i]
86
+ when /\A(\d+)\z/ then [Regexp.last_match(1).to_i, 0]
87
+ else [0, 0]
88
+ end
89
+ end
90
+ private_class_method :parse_version
91
+
44
92
  # @return [Hash] a Hash<String, Object> suitable for emit() to
45
93
  # serialize back to plist. Keys are in camelCase per UFO 3.
46
94
  def to_plist
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Unicode
5
+ # Unicode plane metadata. A plane is a contiguous range of 65,536
6
+ # codepoints; +cp >> 16+ is the plane number.
7
+ #
8
+ # Plane labels follow the Unicode standard names. Unassigned planes
9
+ # fall back to +"Plane_N"+. This is a pure-data module — no I/O, no
10
+ # state, no dependency on tables or stitcher.
11
+ module Plane
12
+ BMP = 0
13
+ SMP = 1
14
+ SIP = 2
15
+ TIP = 3
16
+ SSP = 14
17
+
18
+ # The handful of mega-blocks large enough to overflow a single
19
+ # plane's 65,535-glyph cap when partitioning by plane. Range +
20
+ # label only — full Unicode Blocks.txt data lives in +Block+
21
+ # (follow-up).
22
+ LARGE_CJK_BLOCKS = {
23
+ "CJK_Ext_B" => 0x2A700..0x2B73F,
24
+ "CJK_Ext_C" => 0x2B740..0x2B81F,
25
+ "CJK_Ext_D" => 0x2B820..0x2CEAF,
26
+ "CJK_Ext_E" => 0x2CEB0..0x2EBEF,
27
+ "CJK_Ext_F" => 0x2EBF0..0x2EE5F,
28
+ }.freeze
29
+
30
+ # @param codepoint [Integer]
31
+ # @return [Integer] plane number (0..16)
32
+ def self.of(codepoint)
33
+ codepoint >> 16
34
+ end
35
+
36
+ # @param plane [Integer]
37
+ # @return [String] human-readable plane label
38
+ def self.label(plane)
39
+ case plane
40
+ when BMP then "BMP"
41
+ when SMP then "SMP"
42
+ when SIP then "SIP"
43
+ when TIP then "TIP"
44
+ when SSP then "SSP"
45
+ else "Plane_#{plane}"
46
+ end
47
+ end
48
+
49
+ # @param codepoint [Integer]
50
+ # @return [String] label of the plane containing +codepoint+
51
+ def self.label_of(codepoint)
52
+ label(of(codepoint))
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Namespace hub for Unicode metadata used by font tooling.
4
+ #
5
+ # Plane/Block/Script metadata is a separate concern from font format
6
+ # parsing — it is pure Unicode knowledge. Keeping it under its own
7
+ # namespace (rather than scattering it across +Tables+ or +Stitcher+)
8
+ # keeps the data MECE: one concept, one home.
9
+ #
10
+ # Each metadata concept (Plane, Block, Script) lives in its own file so
11
+ # that adding a new concept is additive (open/closed).
12
+
13
+ module Fontisan
14
+ module Unicode
15
+ autoload :Plane, "fontisan/unicode/plane"
16
+ end
17
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontisan
4
- VERSION = "0.4.7"
4
+ VERSION = "0.4.9"
5
5
  end
data/lib/fontisan.rb CHANGED
@@ -72,10 +72,10 @@ module Fontisan
72
72
  autoload :VariationDataCorruptedError, "fontisan/error"
73
73
  autoload :MultipleCbdtSourcesError, "fontisan/error"
74
74
  autoload :GlyphLimitExceededError, "fontisan/error"
75
+ autoload :PartitionCapExceededError, "fontisan/error"
75
76
 
76
77
  # Namespace hubs (each hub declares its own child autoloads)
77
78
  autoload :Binary, "fontisan/binary"
78
- autoload :Cldr, "fontisan/cldr"
79
79
  autoload :Collection, "fontisan/collection"
80
80
  autoload :Commands, "fontisan/commands"
81
81
  autoload :Converters, "fontisan/converters"
@@ -92,6 +92,7 @@ module Fontisan
92
92
  autoload :Tables, "fontisan/tables"
93
93
  autoload :Type1, "fontisan/type1"
94
94
  autoload :Ufo, "fontisan/ufo"
95
+ autoload :Unicode, "fontisan/unicode"
95
96
  autoload :Utilities, "fontisan/utilities"
96
97
  autoload :Utils, "fontisan/utils"
97
98
  autoload :Validation, "fontisan/validation"
@@ -120,7 +121,6 @@ module Fontisan
120
121
  autoload :TrueTypeFont, "fontisan/true_type_font"
121
122
  autoload :TrueTypeFontExtensions, "fontisan/true_type_font_extensions"
122
123
  autoload :Type1Font, "fontisan/type1_font"
123
- autoload :CldrCli, "fontisan/cli/cldr_cli"
124
124
  autoload :Woff2Font, "fontisan/woff2_font"
125
125
  autoload :WoffFont, "fontisan/woff_font"
126
126
 
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.7
4
+ version: 0.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -173,7 +173,6 @@ files:
173
173
  - docs/api/validators/font-validator.md
174
174
  - docs/api/validators/helper.md
175
175
  - docs/api/validators/profile.md
176
- - docs/cli/audit.md
177
176
  - docs/cli/convert.md
178
177
  - docs/cli/dump-table.md
179
178
  - docs/cli/export.md
@@ -189,6 +188,7 @@ files:
189
188
  - docs/cli/subset.md
190
189
  - docs/cli/tables.md
191
190
  - docs/cli/unicode.md
191
+ - docs/cli/validate-collection.md
192
192
  - docs/cli/validate.md
193
193
  - docs/cli/variable.md
194
194
  - docs/cli/version.md
@@ -277,24 +277,13 @@ files:
277
277
  - lib/fontisan/binary.rb
278
278
  - lib/fontisan/binary/base_record.rb
279
279
  - lib/fontisan/binary/structures.rb
280
- - lib/fontisan/cldr.rb
281
- - lib/fontisan/cldr/aggregator.rb
282
- - lib/fontisan/cldr/cache_manager.rb
283
- - lib/fontisan/cldr/config.rb
284
- - lib/fontisan/cldr/download_error.rb
285
- - lib/fontisan/cldr/downloader.rb
286
- - lib/fontisan/cldr/error.rb
287
- - lib/fontisan/cldr/index.rb
288
- - lib/fontisan/cldr/index_builder.rb
289
- - lib/fontisan/cldr/unicode_set_parser.rb
290
- - lib/fontisan/cldr/unknown_version_error.rb
291
- - lib/fontisan/cldr/version_resolver.rb
292
280
  - lib/fontisan/cli.rb
293
- - lib/fontisan/cli/cldr_cli.rb
294
281
  - lib/fontisan/collection.rb
295
282
  - lib/fontisan/collection/builder.rb
296
283
  - lib/fontisan/collection/dfont_builder.rb
297
284
  - lib/fontisan/collection/offset_calculator.rb
285
+ - lib/fontisan/collection/reader.rb
286
+ - lib/fontisan/collection/reader/stats.rb
298
287
  - lib/fontisan/collection/shared_logic.rb
299
288
  - lib/fontisan/collection/table_analyzer.rb
300
289
  - lib/fontisan/collection/table_deduplicator.rb
@@ -309,6 +298,7 @@ files:
309
298
  - lib/fontisan/commands/info_command.rb
310
299
  - lib/fontisan/commands/instance_command.rb
311
300
  - lib/fontisan/commands/ls_command.rb
301
+ - lib/fontisan/commands/multi_format_output.rb
312
302
  - lib/fontisan/commands/optical_size_command.rb
313
303
  - lib/fontisan/commands/pack_command.rb
314
304
  - lib/fontisan/commands/scripts_command.rb
@@ -316,9 +306,9 @@ files:
316
306
  - lib/fontisan/commands/tables_command.rb
317
307
  - lib/fontisan/commands/unicode_command.rb
318
308
  - lib/fontisan/commands/unpack_command.rb
309
+ - lib/fontisan/commands/validate_collection_command.rb
319
310
  - lib/fontisan/commands/validate_command.rb
320
311
  - lib/fontisan/commands/variable_command.rb
321
- - lib/fontisan/config/cldr.yml
322
312
  - lib/fontisan/config/collection_settings.yml
323
313
  - lib/fontisan/config/conversion_matrix.yml
324
314
  - lib/fontisan/config/export_settings.yml
@@ -379,8 +369,6 @@ files:
379
369
  - lib/fontisan/models/all_scripts_features_info.rb
380
370
  - lib/fontisan/models/bitmap_glyph.rb
381
371
  - lib/fontisan/models/bitmap_strike.rb
382
- - lib/fontisan/models/cldr.rb
383
- - lib/fontisan/models/cldr/language_coverage.rb
384
372
  - lib/fontisan/models/collection_brief_info.rb
385
373
  - lib/fontisan/models/collection_font_summary.rb
386
374
  - lib/fontisan/models/collection_info.rb
@@ -444,9 +432,15 @@ files:
444
432
  - lib/fontisan/sfnt_font.rb
445
433
  - lib/fontisan/sfnt_table.rb
446
434
  - lib/fontisan/stitcher.rb
435
+ - lib/fontisan/stitcher/collection_result.rb
447
436
  - lib/fontisan/stitcher/deduplicator.rb
448
437
  - lib/fontisan/stitcher/glyph_limit.rb
449
438
  - lib/fontisan/stitcher/glyph_signature.rb
439
+ - lib/fontisan/stitcher/partition_strategy.rb
440
+ - lib/fontisan/stitcher/partition_strategy/base.rb
441
+ - lib/fontisan/stitcher/partition_strategy/blueprint.rb
442
+ - lib/fontisan/stitcher/partition_strategy/by_plane.rb
443
+ - lib/fontisan/stitcher/partition_strategy/partition.rb
450
444
  - lib/fontisan/stitcher/selector.rb
451
445
  - lib/fontisan/stitcher/selector/codepoints.rb
452
446
  - lib/fontisan/stitcher/selector/gid.rb
@@ -642,6 +636,8 @@ files:
642
636
  - lib/fontisan/ufo/reader.rb
643
637
  - lib/fontisan/ufo/transformation.rb
644
638
  - lib/fontisan/ufo/writer.rb
639
+ - lib/fontisan/unicode.rb
640
+ - lib/fontisan/unicode/plane.rb
645
641
  - lib/fontisan/utilities.rb
646
642
  - lib/fontisan/utilities/brotli_wrapper.rb
647
643
  - lib/fontisan/utilities/checksum_calculator.rb