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.
- checksums.yaml +4 -4
- data/BUG-stitcher-drops-isolated-cps.md +58 -0
- data/BUG-stitcher-drops-plane1-codepoints.md +310 -0
- data/BUG-stitcher-gid-cap-65535.md +110 -0
- data/CHANGELOG.md +119 -0
- data/README.adoc +121 -68
- data/benchmark/compile_benchmark.rb +70 -0
- data/docs/.vitepress/config.ts +0 -7
- data/docs/CFF2_SUPPORT.adoc +184 -0
- data/docs/STITCHER_GUIDE.adoc +151 -0
- data/docs/SVG_TO_GLYF.adoc +118 -0
- data/docs/UFO_COMPILATION.adoc +119 -0
- data/docs/cli/index.md +5 -28
- data/docs/index.md +0 -2
- data/lib/fontisan/cli.rb +29 -8
- data/lib/fontisan/collection/reader/stats.rb +23 -0
- data/lib/fontisan/collection/reader.rb +90 -0
- data/lib/fontisan/collection/writer.rb +5 -6
- data/lib/fontisan/collection.rb +1 -0
- data/lib/fontisan/commands/convert_command.rb +96 -18
- data/lib/fontisan/commands/multi_format_output.rb +59 -0
- data/lib/fontisan/commands/validate_collection_command.rb +121 -0
- data/lib/fontisan/commands.rb +2 -0
- data/lib/fontisan/error.rb +56 -0
- data/lib/fontisan/models.rb +0 -1
- data/lib/fontisan/stitcher/collection_result.rb +18 -0
- data/lib/fontisan/stitcher/deduplicator.rb +47 -0
- data/lib/fontisan/stitcher/glyph_limit.rb +53 -0
- data/lib/fontisan/stitcher/glyph_signature.rb +51 -0
- data/lib/fontisan/stitcher/partition_strategy/base.rb +23 -0
- data/lib/fontisan/stitcher/partition_strategy/blueprint.rb +24 -0
- data/lib/fontisan/stitcher/partition_strategy/by_plane.rb +131 -0
- data/lib/fontisan/stitcher/partition_strategy/partition.rb +24 -0
- data/lib/fontisan/stitcher/partition_strategy.rb +22 -0
- data/lib/fontisan/stitcher.rb +223 -168
- data/lib/fontisan/svg_to_glyf/assembler.rb +132 -0
- data/lib/fontisan/svg_to_glyf/document.rb +83 -0
- data/lib/fontisan/svg_to_glyf/geometry/affine_transform.rb +112 -0
- data/lib/fontisan/svg_to_glyf/geometry/normalizer.rb +45 -0
- data/lib/fontisan/svg_to_glyf/geometry/transform_parser.rb +91 -0
- data/lib/fontisan/svg_to_glyf/geometry.rb +13 -0
- data/lib/fontisan/svg_to_glyf/path/command.rb +18 -0
- data/lib/fontisan/svg_to_glyf/path/contour_builder.rb +140 -0
- data/lib/fontisan/svg_to_glyf/path/parser.rb +98 -0
- data/lib/fontisan/svg_to_glyf/path/state.rb +79 -0
- data/lib/fontisan/svg_to_glyf/path.rb +14 -0
- data/lib/fontisan/svg_to_glyf.rb +62 -0
- data/lib/fontisan/tables/cff/cff2_charstring_builder.rb +216 -0
- data/lib/fontisan/tables/cff.rb +1 -0
- data/lib/fontisan/tables/cff2/dict_encoder.rb +94 -0
- data/lib/fontisan/tables/cff2/fd_select.rb +69 -0
- data/lib/fontisan/tables/cff2/header.rb +34 -0
- data/lib/fontisan/tables/cff2/index_builder.rb +79 -0
- data/lib/fontisan/tables/cff2.rb +4 -0
- data/lib/fontisan/ufo/compile/cbdt_cblc.rb +103 -0
- data/lib/fontisan/ufo/compile/cff2.rb +181 -0
- data/lib/fontisan/ufo/compile/cff2_subroutines.rb +39 -0
- data/lib/fontisan/ufo/compile/colr.rb +80 -0
- data/lib/fontisan/ufo/compile/cpal.rb +61 -0
- data/lib/fontisan/ufo/compile/math.rb +143 -0
- data/lib/fontisan/ufo/compile/meta.rb +51 -0
- data/lib/fontisan/ufo/compile/name.rb +2 -2
- data/lib/fontisan/ufo/compile/otf2_compiler.rb +46 -0
- data/lib/fontisan/ufo/compile/sbix.rb +99 -0
- data/lib/fontisan/ufo/compile/svg_table.rb +60 -0
- data/lib/fontisan/ufo/compile/variable_otf.rb +75 -0
- data/lib/fontisan/ufo/compile.rb +11 -0
- data/lib/fontisan/ufo/info.rb +48 -0
- data/lib/fontisan/unicode/plane.rb +56 -0
- data/lib/fontisan/unicode.rb +17 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan.rb +5 -2
- metadata +53 -19
- data/docs/cli/audit.md +0 -337
- data/lib/fontisan/cldr/aggregator.rb +0 -33
- data/lib/fontisan/cldr/cache_manager.rb +0 -110
- data/lib/fontisan/cldr/config.rb +0 -59
- data/lib/fontisan/cldr/download_error.rb +0 -9
- data/lib/fontisan/cldr/downloader.rb +0 -79
- data/lib/fontisan/cldr/error.rb +0 -8
- data/lib/fontisan/cldr/index.rb +0 -64
- data/lib/fontisan/cldr/index_builder.rb +0 -72
- data/lib/fontisan/cldr/unicode_set_parser.rb +0 -189
- data/lib/fontisan/cldr/unknown_version_error.rb +0 -9
- data/lib/fontisan/cldr/version_resolver.rb +0 -91
- data/lib/fontisan/cldr.rb +0 -23
- data/lib/fontisan/cli/cldr_cli.rb +0 -85
- data/lib/fontisan/config/cldr.yml +0 -22
- data/lib/fontisan/models/cldr/language_coverage.rb +0 -31
- 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
|
data/lib/fontisan/ufo/compile.rb
CHANGED
|
@@ -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"
|
data/lib/fontisan/ufo/info.rb
CHANGED
|
@@ -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
|
data/lib/fontisan/version.rb
CHANGED
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.
|
|
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-
|
|
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
|