fontisan 0.4.6 → 0.4.8

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/BUG-stitcher-drops-isolated-cps.md +58 -0
  3. data/BUG-stitcher-drops-plane1-codepoints.md +310 -0
  4. data/BUG-stitcher-gid-cap-65535.md +110 -0
  5. data/CHANGELOG.md +119 -0
  6. data/README.adoc +121 -68
  7. data/benchmark/compile_benchmark.rb +70 -0
  8. data/docs/.vitepress/config.ts +0 -7
  9. data/docs/CFF2_SUPPORT.adoc +184 -0
  10. data/docs/STITCHER_GUIDE.adoc +151 -0
  11. data/docs/SVG_TO_GLYF.adoc +118 -0
  12. data/docs/UFO_COMPILATION.adoc +119 -0
  13. data/docs/cli/index.md +5 -28
  14. data/docs/index.md +0 -2
  15. data/lib/fontisan/cli.rb +29 -8
  16. data/lib/fontisan/collection/reader/stats.rb +23 -0
  17. data/lib/fontisan/collection/reader.rb +90 -0
  18. data/lib/fontisan/collection/writer.rb +5 -6
  19. data/lib/fontisan/collection.rb +1 -0
  20. data/lib/fontisan/commands/convert_command.rb +96 -18
  21. data/lib/fontisan/commands/multi_format_output.rb +59 -0
  22. data/lib/fontisan/commands/validate_collection_command.rb +121 -0
  23. data/lib/fontisan/commands.rb +2 -0
  24. data/lib/fontisan/error.rb +56 -0
  25. data/lib/fontisan/models.rb +0 -1
  26. data/lib/fontisan/stitcher/collection_result.rb +18 -0
  27. data/lib/fontisan/stitcher/deduplicator.rb +47 -0
  28. data/lib/fontisan/stitcher/glyph_limit.rb +53 -0
  29. data/lib/fontisan/stitcher/glyph_signature.rb +51 -0
  30. data/lib/fontisan/stitcher/partition_strategy/base.rb +23 -0
  31. data/lib/fontisan/stitcher/partition_strategy/blueprint.rb +24 -0
  32. data/lib/fontisan/stitcher/partition_strategy/by_plane.rb +131 -0
  33. data/lib/fontisan/stitcher/partition_strategy/partition.rb +24 -0
  34. data/lib/fontisan/stitcher/partition_strategy.rb +22 -0
  35. data/lib/fontisan/stitcher.rb +223 -168
  36. data/lib/fontisan/svg_to_glyf/assembler.rb +132 -0
  37. data/lib/fontisan/svg_to_glyf/document.rb +83 -0
  38. data/lib/fontisan/svg_to_glyf/geometry/affine_transform.rb +112 -0
  39. data/lib/fontisan/svg_to_glyf/geometry/normalizer.rb +45 -0
  40. data/lib/fontisan/svg_to_glyf/geometry/transform_parser.rb +91 -0
  41. data/lib/fontisan/svg_to_glyf/geometry.rb +13 -0
  42. data/lib/fontisan/svg_to_glyf/path/command.rb +18 -0
  43. data/lib/fontisan/svg_to_glyf/path/contour_builder.rb +140 -0
  44. data/lib/fontisan/svg_to_glyf/path/parser.rb +98 -0
  45. data/lib/fontisan/svg_to_glyf/path/state.rb +79 -0
  46. data/lib/fontisan/svg_to_glyf/path.rb +14 -0
  47. data/lib/fontisan/svg_to_glyf.rb +62 -0
  48. data/lib/fontisan/tables/cff/cff2_charstring_builder.rb +216 -0
  49. data/lib/fontisan/tables/cff.rb +1 -0
  50. data/lib/fontisan/tables/cff2/dict_encoder.rb +94 -0
  51. data/lib/fontisan/tables/cff2/fd_select.rb +69 -0
  52. data/lib/fontisan/tables/cff2/header.rb +34 -0
  53. data/lib/fontisan/tables/cff2/index_builder.rb +79 -0
  54. data/lib/fontisan/tables/cff2.rb +4 -0
  55. data/lib/fontisan/ufo/compile/cbdt_cblc.rb +103 -0
  56. data/lib/fontisan/ufo/compile/cff2.rb +181 -0
  57. data/lib/fontisan/ufo/compile/cff2_subroutines.rb +39 -0
  58. data/lib/fontisan/ufo/compile/colr.rb +80 -0
  59. data/lib/fontisan/ufo/compile/cpal.rb +61 -0
  60. data/lib/fontisan/ufo/compile/math.rb +143 -0
  61. data/lib/fontisan/ufo/compile/meta.rb +51 -0
  62. data/lib/fontisan/ufo/compile/name.rb +2 -2
  63. data/lib/fontisan/ufo/compile/otf2_compiler.rb +46 -0
  64. data/lib/fontisan/ufo/compile/sbix.rb +99 -0
  65. data/lib/fontisan/ufo/compile/svg_table.rb +60 -0
  66. data/lib/fontisan/ufo/compile/variable_otf.rb +75 -0
  67. data/lib/fontisan/ufo/compile.rb +11 -0
  68. data/lib/fontisan/ufo/info.rb +48 -0
  69. data/lib/fontisan/unicode/plane.rb +56 -0
  70. data/lib/fontisan/unicode.rb +17 -0
  71. data/lib/fontisan/version.rb +1 -1
  72. data/lib/fontisan.rb +5 -2
  73. metadata +53 -19
  74. data/docs/cli/audit.md +0 -337
  75. data/lib/fontisan/cldr/aggregator.rb +0 -33
  76. data/lib/fontisan/cldr/cache_manager.rb +0 -110
  77. data/lib/fontisan/cldr/config.rb +0 -59
  78. data/lib/fontisan/cldr/download_error.rb +0 -9
  79. data/lib/fontisan/cldr/downloader.rb +0 -79
  80. data/lib/fontisan/cldr/error.rb +0 -8
  81. data/lib/fontisan/cldr/index.rb +0 -64
  82. data/lib/fontisan/cldr/index_builder.rb +0 -72
  83. data/lib/fontisan/cldr/unicode_set_parser.rb +0 -189
  84. data/lib/fontisan/cldr/unknown_version_error.rb +0 -9
  85. data/lib/fontisan/cldr/version_resolver.rb +0 -91
  86. data/lib/fontisan/cldr.rb +0 -23
  87. data/lib/fontisan/cli/cldr_cli.rb +0 -85
  88. data/lib/fontisan/config/cldr.yml +0 -22
  89. data/lib/fontisan/models/cldr/language_coverage.rb +0 -31
  90. data/lib/fontisan/models/cldr.rb +0 -12
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Ufo
5
+ module Compile
6
+ # Builds the OpenType `sbix` (Apple Bitmap) table.
7
+ #
8
+ # sbix stores bitmap strikes (PNG/JPEG) at multiple sizes. Each
9
+ # strike is a set of glyph-to-bitmap mappings at a specific ppem
10
+ # and resolution.
11
+ #
12
+ # Layout:
13
+ # Header (8 bytes):
14
+ # uint16 version (= 1)
15
+ # uint16 flags
16
+ # uint32 numStrikes
17
+ # Offset32 strikeOffsets[numStrikes]
18
+ #
19
+ # Strike (per ppem):
20
+ # uint16 ppem
21
+ # uint16 resolution (ppi)
22
+ # Offset32 glyphDataOffsets[numGlyphs + 1]
23
+ #
24
+ # Glyph Data (per glyph):
25
+ # int16 originOffsetX
26
+ # int16 originOffsetY
27
+ # uint16 graphicType ("png " or "jpeg")
28
+ # uint8 bitmapData[]
29
+ #
30
+ # @see https://learn.microsoft.com/en-us/typography/opentype/spec/sbix
31
+ module Sbix
32
+ VERSION = 1
33
+ PNG_TYPE = "png "
34
+ JPEG_TYPE = "jpeg"
35
+
36
+ # @param strikes [Array<Hash>] each with :ppem, :resolution,
37
+ # and :glyphs (Array<Hash> with :origin_x, :origin_y,
38
+ # :graphic_type, :data per glyph)
39
+ # @param num_glyphs [Integer] total glyph count
40
+ # @return [String, nil] sbix bytes, or nil if no strikes
41
+ def self.build(strikes:, num_glyphs:)
42
+ return nil if strikes.nil? || strikes.empty?
43
+
44
+ num_strikes = strikes.size
45
+ header_size = 4 + 4 + (num_strikes * 4) # version + flags + offsets
46
+
47
+ strike_bytes = strikes.map { |s| build_strike(s, num_glyphs) }
48
+
49
+ io = +""
50
+ io << [VERSION, 0, num_strikes].pack("nnN")
51
+
52
+ offset = header_size
53
+ strike_bytes.each do |s|
54
+ io << [offset].pack("N")
55
+ offset += s.bytesize
56
+ io << s
57
+ end
58
+ io
59
+ end
60
+
61
+ def self.build_strike(strike, num_glyphs)
62
+ glyphs = strike[:glyphs] || []
63
+ ppem = strike[:ppem] || 0
64
+ resolution = strike[:resolution] || 72
65
+
66
+ # Offset table: ppem(2) + resolution(2) + (numGlyphs+1) × offset(4)
67
+ offset_table_size = 4 + ((num_glyphs + 1) * 4)
68
+
69
+ offsets = []
70
+ glyph_data = +""
71
+ current_offset = offset_table_size
72
+
73
+ num_glyphs.times do |gid|
74
+ glyph = glyphs[gid]
75
+ if glyph && glyph[:data]
76
+ offsets << current_offset
77
+ data = glyph[:data].b
78
+ glyph_data << [glyph[:origin_x] || 0, glyph[:origin_y] || 0].pack("nn")
79
+ glyph_data << (glyph[:graphic_type] || PNG_TYPE).ljust(4)[0, 4]
80
+ glyph_data << data
81
+ current_offset += 8 + data.bytesize
82
+ else
83
+ offsets << current_offset
84
+ end
85
+ end
86
+ offsets << current_offset # sentinel
87
+
88
+ io = +""
89
+ io << [ppem, resolution].pack("nn")
90
+ offsets.each { |o| io << [o].pack("N") }
91
+ io << glyph_data
92
+ io
93
+ end
94
+
95
+ private_class_method :build_strike
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Ufo
5
+ module Compile
6
+ # Builds the OpenType `SVG ` table for SVG-in-OpenType color glyphs.
7
+ #
8
+ # Layout:
9
+ # Header (10 bytes):
10
+ # uint16 version (= 0)
11
+ # Offset32 documentListOffset (= 10, immediately after header)
12
+ # uint32 reserved (= 0)
13
+ #
14
+ # Document List:
15
+ # uint16 numEntries
16
+ # DocumentRecord[numEntries]:
17
+ # uint16 startGlyphID
18
+ # uint16 endGlyphID
19
+ # Offset32 svgDocOffset (from start of SVG table)
20
+ # uint32 svgDocLength
21
+ #
22
+ # SVG Documents (raw XML, optionally gzipped)
23
+ #
24
+ # @see https://learn.microsoft.com/en-us/typography/opentype/spec/svg
25
+ module SvgTable
26
+ VERSION = 0
27
+ HEADER_SIZE = 10
28
+ DOCUMENT_RECORD_SIZE = 12
29
+
30
+ # @param entries [Array<Hash>] each with :start_gid (Integer),
31
+ # :end_gid (Integer), :svg (String — raw SVG XML)
32
+ # @return [String, nil] SVG table bytes, or nil if no entries
33
+ def self.build(entries:)
34
+ return nil if entries.nil? || entries.empty?
35
+
36
+ num_entries = entries.size
37
+ doc_list_size = 2 + (num_entries * DOCUMENT_RECORD_SIZE)
38
+ doc_data_offset = HEADER_SIZE + doc_list_size
39
+
40
+ header = [VERSION, HEADER_SIZE, 0].pack("nNN")
41
+
42
+ records = +""
43
+ docs = +""
44
+ offset = doc_data_offset
45
+ entries.each do |entry|
46
+ svg_bytes = entry[:svg].b
47
+ records << [entry[:start_gid], entry[:end_gid],
48
+ offset, svg_bytes.bytesize].pack("nnNN")
49
+ docs << svg_bytes
50
+ offset += svg_bytes.bytesize
51
+ end
52
+
53
+ doc_list = [num_entries].pack("n") + records
54
+
55
+ header + doc_list + docs
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tmpdir"
4
+
5
+ module Fontisan
6
+ module Ufo
7
+ module Compile
8
+ # VariableOtf — produces a variable OTF with CFF2 outlines.
9
+ #
10
+ # The orchestrator assembles a default-master UFO plus variation
11
+ # masters into a variable OpenType font with:
12
+ # - CFF2 outlines (static — blend/vsindex operators are TODO 07/18)
13
+ # - fvar (axes + instances)
14
+ # - STAT (style attributes)
15
+ # - avar (axis remapping, when maps are provided)
16
+ #
17
+ # NOTE: the CFF2 table currently contains static outlines (no
18
+ # VariationStore, no blend operators). Full variable CFF2 requires
19
+ # TODO 07 (blend/vsindex) and TODO 18 (blend integration) to be
20
+ # wired into the charstring builder.
21
+ #
22
+ # @see https://learn.microsoft.com/en-us/typography/opentype/spec/cff2
23
+ class VariableOtf
24
+ # @param default_font [Fontisan::Ufo::Font] the default master
25
+ # @param master_fonts [Array<Fontisan::Ufo::Font>] variation masters
26
+ # @param axes [Array<Hash>] fvar axes (tag, min, default, max, name_id)
27
+ # @param instances [Array<Hash>] named instances
28
+ def initialize(default_font, master_fonts: [], axes: [], instances: [])
29
+ @default = default_font
30
+ @masters = master_fonts
31
+ @axes = axes
32
+ @instances = instances
33
+ end
34
+
35
+ # Compile the variable OTF directly to the output path.
36
+ # @param output_path [String] output file path
37
+ # @return [String] the output path
38
+ def compile(output_path:)
39
+ tables = build_tables
40
+ Fontisan::FontWriter.write_to_file(
41
+ tables.transform_values { |t| t.is_a?(String) ? t : t.to_binary_s },
42
+ output_path,
43
+ sfnt_version: 0x4F54544F,
44
+ )
45
+ output_path
46
+ end
47
+
48
+ # Build all tables for the variable OTF.
49
+ # @return [Hash<String,String>] table tag → bytes
50
+ def build_tables
51
+ glyphs = @default.glyphs.values
52
+
53
+ tables = {
54
+ "head" => Head.build(@default, glyphs: glyphs, loca_format: Head::LOCA_FORMAT_LONG),
55
+ "hhea" => Hhea.build(@default, glyphs: glyphs),
56
+ "maxp" => Maxp.build(@default, glyphs: glyphs, version: Maxp::VERSION_OPEN_TYPE),
57
+ "OS/2" => Os2.build(@default, glyphs: glyphs),
58
+ "name" => Name.build(@default),
59
+ "post" => Post.build(@default),
60
+ "hmtx" => Hmtx.build(@default, glyphs: glyphs),
61
+ "cmap" => Cmap.build(@default, glyphs: glyphs),
62
+ "CFF2" => Cff2.build(@default, glyphs: glyphs),
63
+ "fvar" => Fvar.build(@default, axes: @axes, instances: @instances),
64
+ "STAT" => Stat.build(axes: @axes),
65
+ }
66
+
67
+ avar_bytes = Avar.build(axes: @axes)
68
+ tables["avar"] = avar_bytes if avar_bytes
69
+
70
+ tables
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -46,6 +46,17 @@ module Fontisan
46
46
  autoload :Cmap, "fontisan/ufo/compile/cmap"
47
47
  autoload :GlyfLoca, "fontisan/ufo/compile/glyf_loca"
48
48
  autoload :Cff, "fontisan/ufo/compile/cff"
49
+ autoload :Cff2, "fontisan/ufo/compile/cff2"
50
+ autoload :Otf2Compiler, "fontisan/ufo/compile/otf2_compiler"
51
+ autoload :Cpal, "fontisan/ufo/compile/cpal"
52
+ autoload :Meta, "fontisan/ufo/compile/meta"
53
+ autoload :SvgTable, "fontisan/ufo/compile/svg_table"
54
+ autoload :Sbix, "fontisan/ufo/compile/sbix"
55
+ autoload :MathTable, "fontisan/ufo/compile/math"
56
+ autoload :CbdtCblc, "fontisan/ufo/compile/cbdt_cblc"
57
+ autoload :Colr, "fontisan/ufo/compile/colr"
58
+ autoload :Cff2Subrs, "fontisan/ufo/compile/cff2_subroutines"
59
+ autoload :VariableOtf, "fontisan/ufo/compile/variable_otf"
49
60
  autoload :Avar, "fontisan/ufo/compile/avar"
50
61
  autoload :Hvar, "fontisan/ufo/compile/hvar"
51
62
  autoload :Mvar, "fontisan/ufo/compile/mvar"
@@ -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.6"
4
+ VERSION = "0.4.8"
5
5
  end
data/lib/fontisan.rb CHANGED
@@ -70,10 +70,12 @@ module Fontisan
70
70
  autoload :CorruptedVariationDataError, "fontisan/error"
71
71
  autoload :InvalidVariationDataError, "fontisan/error"
72
72
  autoload :VariationDataCorruptedError, "fontisan/error"
73
+ autoload :MultipleCbdtSourcesError, "fontisan/error"
74
+ autoload :GlyphLimitExceededError, "fontisan/error"
75
+ autoload :PartitionCapExceededError, "fontisan/error"
73
76
 
74
77
  # Namespace hubs (each hub declares its own child autoloads)
75
78
  autoload :Binary, "fontisan/binary"
76
- autoload :Cldr, "fontisan/cldr"
77
79
  autoload :Collection, "fontisan/collection"
78
80
  autoload :Commands, "fontisan/commands"
79
81
  autoload :Converters, "fontisan/converters"
@@ -86,9 +88,11 @@ module Fontisan
86
88
  autoload :Pipeline, "fontisan/pipeline"
87
89
  autoload :Subset, "fontisan/subset"
88
90
  autoload :Svg, "fontisan/svg"
91
+ autoload :SvgToGlyf, "fontisan/svg_to_glyf"
89
92
  autoload :Tables, "fontisan/tables"
90
93
  autoload :Type1, "fontisan/type1"
91
94
  autoload :Ufo, "fontisan/ufo"
95
+ autoload :Unicode, "fontisan/unicode"
92
96
  autoload :Utilities, "fontisan/utilities"
93
97
  autoload :Utils, "fontisan/utils"
94
98
  autoload :Validation, "fontisan/validation"
@@ -117,7 +121,6 @@ module Fontisan
117
121
  autoload :TrueTypeFont, "fontisan/true_type_font"
118
122
  autoload :TrueTypeFontExtensions, "fontisan/true_type_font_extensions"
119
123
  autoload :Type1Font, "fontisan/type1_font"
120
- autoload :CldrCli, "fontisan/cli/cldr_cli"
121
124
  autoload :Woff2Font, "fontisan/woff2_font"
122
125
  autoload :WoffFont, "fontisan/woff_font"
123
126
 
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.6
4
+ version: 0.4.8
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-01 00:00:00.000000000 Z
11
+ date: 2026-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -125,11 +125,15 @@ files:
125
125
  - ".rspec"
126
126
  - ".rubocop.yml"
127
127
  - ".rubocop_todo.yml"
128
+ - BUG-stitcher-drops-isolated-cps.md
129
+ - BUG-stitcher-drops-plane1-codepoints.md
130
+ - BUG-stitcher-gid-cap-65535.md
128
131
  - CHANGELOG.md
129
132
  - Gemfile
130
133
  - LICENSE
131
134
  - README.adoc
132
135
  - Rakefile
136
+ - benchmark/compile_benchmark.rb
133
137
  - benchmark/variation_quick_bench.rb
134
138
  - docs/.gitignore
135
139
  - docs/.vitepress/config.ts
@@ -141,12 +145,16 @@ files:
141
145
  - docs/.vitepress/theme/index.ts
142
146
  - docs/.vitepress/theme/style.css
143
147
  - docs/APPLE_LEGACY_FONTS.adoc
148
+ - docs/CFF2_SUPPORT.adoc
144
149
  - docs/COLLECTION_VALIDATION.adoc
145
150
  - docs/COLOR_FONTS.adoc
146
151
  - docs/CONVERSION_GUIDE.adoc
147
152
  - docs/EXTRACT_TTC_MIGRATION.md
148
153
  - docs/FONT_HINTING.adoc
154
+ - docs/STITCHER_GUIDE.adoc
155
+ - docs/SVG_TO_GLYF.adoc
149
156
  - docs/TYPE1_FONTS.adoc
157
+ - docs/UFO_COMPILATION.adoc
150
158
  - docs/VALIDATION.adoc
151
159
  - docs/VARIABLE_FONT_OPERATIONS.adoc
152
160
  - docs/WOFF_WOFF2_FORMATS.adoc
@@ -165,7 +173,6 @@ files:
165
173
  - docs/api/validators/font-validator.md
166
174
  - docs/api/validators/helper.md
167
175
  - docs/api/validators/profile.md
168
- - docs/cli/audit.md
169
176
  - docs/cli/convert.md
170
177
  - docs/cli/dump-table.md
171
178
  - docs/cli/export.md
@@ -269,24 +276,13 @@ files:
269
276
  - lib/fontisan/binary.rb
270
277
  - lib/fontisan/binary/base_record.rb
271
278
  - lib/fontisan/binary/structures.rb
272
- - lib/fontisan/cldr.rb
273
- - lib/fontisan/cldr/aggregator.rb
274
- - lib/fontisan/cldr/cache_manager.rb
275
- - lib/fontisan/cldr/config.rb
276
- - lib/fontisan/cldr/download_error.rb
277
- - lib/fontisan/cldr/downloader.rb
278
- - lib/fontisan/cldr/error.rb
279
- - lib/fontisan/cldr/index.rb
280
- - lib/fontisan/cldr/index_builder.rb
281
- - lib/fontisan/cldr/unicode_set_parser.rb
282
- - lib/fontisan/cldr/unknown_version_error.rb
283
- - lib/fontisan/cldr/version_resolver.rb
284
279
  - lib/fontisan/cli.rb
285
- - lib/fontisan/cli/cldr_cli.rb
286
280
  - lib/fontisan/collection.rb
287
281
  - lib/fontisan/collection/builder.rb
288
282
  - lib/fontisan/collection/dfont_builder.rb
289
283
  - lib/fontisan/collection/offset_calculator.rb
284
+ - lib/fontisan/collection/reader.rb
285
+ - lib/fontisan/collection/reader/stats.rb
290
286
  - lib/fontisan/collection/shared_logic.rb
291
287
  - lib/fontisan/collection/table_analyzer.rb
292
288
  - lib/fontisan/collection/table_deduplicator.rb
@@ -301,6 +297,7 @@ files:
301
297
  - lib/fontisan/commands/info_command.rb
302
298
  - lib/fontisan/commands/instance_command.rb
303
299
  - lib/fontisan/commands/ls_command.rb
300
+ - lib/fontisan/commands/multi_format_output.rb
304
301
  - lib/fontisan/commands/optical_size_command.rb
305
302
  - lib/fontisan/commands/pack_command.rb
306
303
  - lib/fontisan/commands/scripts_command.rb
@@ -308,9 +305,9 @@ files:
308
305
  - lib/fontisan/commands/tables_command.rb
309
306
  - lib/fontisan/commands/unicode_command.rb
310
307
  - lib/fontisan/commands/unpack_command.rb
308
+ - lib/fontisan/commands/validate_collection_command.rb
311
309
  - lib/fontisan/commands/validate_command.rb
312
310
  - lib/fontisan/commands/variable_command.rb
313
- - lib/fontisan/config/cldr.yml
314
311
  - lib/fontisan/config/collection_settings.yml
315
312
  - lib/fontisan/config/conversion_matrix.yml
316
313
  - lib/fontisan/config/export_settings.yml
@@ -371,8 +368,6 @@ files:
371
368
  - lib/fontisan/models/all_scripts_features_info.rb
372
369
  - lib/fontisan/models/bitmap_glyph.rb
373
370
  - lib/fontisan/models/bitmap_strike.rb
374
- - lib/fontisan/models/cldr.rb
375
- - lib/fontisan/models/cldr/language_coverage.rb
376
371
  - lib/fontisan/models/collection_brief_info.rb
377
372
  - lib/fontisan/models/collection_font_summary.rb
378
373
  - lib/fontisan/models/collection_info.rb
@@ -436,6 +431,15 @@ files:
436
431
  - lib/fontisan/sfnt_font.rb
437
432
  - lib/fontisan/sfnt_table.rb
438
433
  - lib/fontisan/stitcher.rb
434
+ - lib/fontisan/stitcher/collection_result.rb
435
+ - lib/fontisan/stitcher/deduplicator.rb
436
+ - lib/fontisan/stitcher/glyph_limit.rb
437
+ - lib/fontisan/stitcher/glyph_signature.rb
438
+ - lib/fontisan/stitcher/partition_strategy.rb
439
+ - lib/fontisan/stitcher/partition_strategy/base.rb
440
+ - lib/fontisan/stitcher/partition_strategy/blueprint.rb
441
+ - lib/fontisan/stitcher/partition_strategy/by_plane.rb
442
+ - lib/fontisan/stitcher/partition_strategy/partition.rb
439
443
  - lib/fontisan/stitcher/selector.rb
440
444
  - lib/fontisan/stitcher/selector/codepoints.rb
441
445
  - lib/fontisan/stitcher/selector/gid.rb
@@ -453,10 +457,23 @@ files:
453
457
  - lib/fontisan/svg/font_generator.rb
454
458
  - lib/fontisan/svg/glyph_generator.rb
455
459
  - lib/fontisan/svg/view_box_calculator.rb
460
+ - lib/fontisan/svg_to_glyf.rb
461
+ - lib/fontisan/svg_to_glyf/assembler.rb
462
+ - lib/fontisan/svg_to_glyf/document.rb
463
+ - lib/fontisan/svg_to_glyf/geometry.rb
464
+ - lib/fontisan/svg_to_glyf/geometry/affine_transform.rb
465
+ - lib/fontisan/svg_to_glyf/geometry/normalizer.rb
466
+ - lib/fontisan/svg_to_glyf/geometry/transform_parser.rb
467
+ - lib/fontisan/svg_to_glyf/path.rb
468
+ - lib/fontisan/svg_to_glyf/path/command.rb
469
+ - lib/fontisan/svg_to_glyf/path/contour_builder.rb
470
+ - lib/fontisan/svg_to_glyf/path/parser.rb
471
+ - lib/fontisan/svg_to_glyf/path/state.rb
456
472
  - lib/fontisan/tables.rb
457
473
  - lib/fontisan/tables/cbdt.rb
458
474
  - lib/fontisan/tables/cblc.rb
459
475
  - lib/fontisan/tables/cff.rb
476
+ - lib/fontisan/tables/cff/cff2_charstring_builder.rb
460
477
  - lib/fontisan/tables/cff/cff_glyph.rb
461
478
  - lib/fontisan/tables/cff/charset.rb
462
479
  - lib/fontisan/tables/cff/charstring.rb
@@ -479,6 +496,10 @@ files:
479
496
  - lib/fontisan/tables/cff2.rb
480
497
  - lib/fontisan/tables/cff2/blend_operator.rb
481
498
  - lib/fontisan/tables/cff2/charstring_parser.rb
499
+ - lib/fontisan/tables/cff2/dict_encoder.rb
500
+ - lib/fontisan/tables/cff2/fd_select.rb
501
+ - lib/fontisan/tables/cff2/header.rb
502
+ - lib/fontisan/tables/cff2/index_builder.rb
482
503
  - lib/fontisan/tables/cff2/operand_stack.rb
483
504
  - lib/fontisan/tables/cff2/private_dict_blend_handler.rb
484
505
  - lib/fontisan/tables/cff2/region_matcher.rb
@@ -557,8 +578,13 @@ files:
557
578
  - lib/fontisan/ufo/compile.rb
558
579
  - lib/fontisan/ufo/compile/avar.rb
559
580
  - lib/fontisan/ufo/compile/base_compiler.rb
581
+ - lib/fontisan/ufo/compile/cbdt_cblc.rb
560
582
  - lib/fontisan/ufo/compile/cff.rb
583
+ - lib/fontisan/ufo/compile/cff2.rb
584
+ - lib/fontisan/ufo/compile/cff2_subroutines.rb
561
585
  - lib/fontisan/ufo/compile/cmap.rb
586
+ - lib/fontisan/ufo/compile/colr.rb
587
+ - lib/fontisan/ufo/compile/cpal.rb
562
588
  - lib/fontisan/ufo/compile/filters.rb
563
589
  - lib/fontisan/ufo/compile/filters/cubic_to_quadratic.rb
564
590
  - lib/fontisan/ufo/compile/filters/decompose_components.rb
@@ -573,14 +599,20 @@ files:
573
599
  - lib/fontisan/ufo/compile/hmtx.rb
574
600
  - lib/fontisan/ufo/compile/hvar.rb
575
601
  - lib/fontisan/ufo/compile/item_variation_store.rb
602
+ - lib/fontisan/ufo/compile/math.rb
576
603
  - lib/fontisan/ufo/compile/maxp.rb
604
+ - lib/fontisan/ufo/compile/meta.rb
577
605
  - lib/fontisan/ufo/compile/mvar.rb
578
606
  - lib/fontisan/ufo/compile/name.rb
579
607
  - lib/fontisan/ufo/compile/os2.rb
608
+ - lib/fontisan/ufo/compile/otf2_compiler.rb
580
609
  - lib/fontisan/ufo/compile/otf_compiler.rb
581
610
  - lib/fontisan/ufo/compile/post.rb
611
+ - lib/fontisan/ufo/compile/sbix.rb
582
612
  - lib/fontisan/ufo/compile/stat.rb
613
+ - lib/fontisan/ufo/compile/svg_table.rb
583
614
  - lib/fontisan/ufo/compile/ttf_compiler.rb
615
+ - lib/fontisan/ufo/compile/variable_otf.rb
584
616
  - lib/fontisan/ufo/compile/variable_ttf.rb
585
617
  - lib/fontisan/ufo/component.rb
586
618
  - lib/fontisan/ufo/contour.rb
@@ -603,6 +635,8 @@ files:
603
635
  - lib/fontisan/ufo/reader.rb
604
636
  - lib/fontisan/ufo/transformation.rb
605
637
  - lib/fontisan/ufo/writer.rb
638
+ - lib/fontisan/unicode.rb
639
+ - lib/fontisan/unicode/plane.rb
606
640
  - lib/fontisan/utilities.rb
607
641
  - lib/fontisan/utilities/brotli_wrapper.rb
608
642
  - lib/fontisan/utilities/checksum_calculator.rb