fontisan 0.4.23 → 0.4.24
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/.rubocop_todo.yml +240 -29
- data/Gemfile +6 -0
- data/Rakefile +9 -6
- data/benchmark/compile_benchmark.rb +13 -5
- data/docs/.vitepress/config.ts +1 -0
- data/docs/STITCHER_GUIDE.adoc +47 -3
- data/docs/api/layer.md +89 -0
- data/docs/cli/subset.md +25 -6
- data/docs/guide/cli/subset.md +31 -0
- data/docs/guide/color-fonts/bitmaps.md +17 -0
- data/lib/fontisan/cli.rb +2 -1
- data/lib/fontisan/collection/writer.rb +2 -1
- data/lib/fontisan/commands/validate_collection_command.rb +7 -1
- data/lib/fontisan/commands.rb +2 -1
- data/lib/fontisan/converters/collection_converter.rb +6 -1
- data/lib/fontisan/converters/svg_generator.rb +3 -1
- data/lib/fontisan/stitcher/collection_result.rb +2 -1
- data/lib/fontisan/stitcher/glyph_copier.rb +2 -1
- data/lib/fontisan/stitcher/partition_strategy/by_block.rb +3 -1
- data/lib/fontisan/stitcher/partition_strategy/by_script.rb +6 -2
- data/lib/fontisan/stitcher/source.rb +2 -1
- data/lib/fontisan/stitcher.rb +2 -1
- data/lib/fontisan/subset/table_strategy/glyf_loca_builder.rb +2 -1
- data/lib/fontisan/svg/standalone_glyph.rb +4 -1
- data/lib/fontisan/svg_to_glyf/assembler.rb +6 -3
- data/lib/fontisan/svg_to_glyf/geometry/transform_parser.rb +11 -4
- data/lib/fontisan/svg_to_glyf/geometry.rb +4 -2
- data/lib/fontisan/tables/cff/cff2_charstring_builder.rb +11 -5
- data/lib/fontisan/tables/cff.rb +2 -1
- data/lib/fontisan/tables/cff2/index_builder.rb +2 -1
- data/lib/fontisan/tables.rb +2 -1
- data/lib/fontisan/ufo/cli.rb +4 -1
- data/lib/fontisan/ufo/compile/cbdt_cblc.rb +2 -1
- data/lib/fontisan/ufo/compile/cff.rb +6 -2
- data/lib/fontisan/ufo/compile/cmap.rb +11 -5
- data/lib/fontisan/ufo/compile/cpal.rb +2 -1
- data/lib/fontisan/ufo/compile/feature_writers/gdef.rb +3 -1
- data/lib/fontisan/ufo/compile/feature_writers/mark_family_base.rb +3 -1
- data/lib/fontisan/ufo/compile/feature_writers.rb +2 -1
- data/lib/fontisan/ufo/compile/filters/cubic_to_quadratic.rb +4 -2
- data/lib/fontisan/ufo/compile/filters/remove_overlaps.rb +2 -1
- data/lib/fontisan/ufo/compile/fvar.rb +7 -3
- data/lib/fontisan/ufo/compile/glyf_loca.rb +4 -2
- data/lib/fontisan/ufo/compile/gvar.rb +5 -2
- data/lib/fontisan/ufo/compile/head.rb +2 -1
- data/lib/fontisan/ufo/compile/item_variation_store.rb +2 -1
- data/lib/fontisan/ufo/compile/name.rb +3 -1
- data/lib/fontisan/ufo/compile/sbix.rb +2 -1
- data/lib/fontisan/ufo/compile/stat.rb +6 -3
- data/lib/fontisan/ufo/convert/from_bin_data.rb +6 -2
- data/lib/fontisan/ufo/convert/to_dfont.rb +6 -2
- data/lib/fontisan/ufo/convert/to_otc.rb +4 -1
- data/lib/fontisan/ufo/convert/to_postscript.rb +8 -3
- data/lib/fontisan/ufo/convert/to_woff.rb +8 -3
- data/lib/fontisan/ufo/convert/to_woff2.rb +8 -3
- data/lib/fontisan/ufo/convert.rb +4 -1
- data/lib/fontisan/ufo/info.rb +4 -2
- data/lib/fontisan/ufo/plist.rb +7 -2
- data/lib/fontisan/ufo/reader.rb +3 -1
- data/lib/fontisan/ufo/writer.rb +13 -5
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/collection_decoder.rb +4 -1
- data/lib/fontisan/woff2/collection_encoder.rb +4 -1
- data/lib/fontisan/woff2/glyf_canonicalizer.rb +2 -1
- data/lib/fontisan/woff2/glyf_loca_reconstruct.rb +5 -2
- data/lib/fontisan/woff2/sfnt_checksum.rb +2 -1
- data/lib/fontisan.rb +0 -1
- metadata +2 -3
- data/lib/fontisan/tasks/fixture_downloader.rb +0 -162
- data/lib/fontisan/tasks.rb +0 -11
|
@@ -78,7 +78,9 @@ module Fontisan
|
|
|
78
78
|
def heuristic_class(glyph)
|
|
79
79
|
anchor_names = glyph.anchors.map(&:name)
|
|
80
80
|
mark_anchor_pattern = /\A_[a-zA-Z]+\z/
|
|
81
|
-
return 3 if anchor_names.any?
|
|
81
|
+
return 3 if anchor_names.any? do |n|
|
|
82
|
+
mark_anchor_pattern.match?(n.to_s)
|
|
83
|
+
end
|
|
82
84
|
return 2 if glyph.name.to_s.end_with?(".liga")
|
|
83
85
|
|
|
84
86
|
nil
|
|
@@ -28,7 +28,9 @@ module Fontisan
|
|
|
28
28
|
|
|
29
29
|
attachments[class_name] ||= new_class_entry
|
|
30
30
|
attachments[class_name][:marks][mark_name] = mark_anchor
|
|
31
|
-
bases.each
|
|
31
|
+
bases.each do |n, a|
|
|
32
|
+
attachments[class_name][base_bucket_key][n] = a
|
|
33
|
+
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
|
|
@@ -16,7 +16,8 @@ module Fontisan
|
|
|
16
16
|
# entry. No edits to the compiler.
|
|
17
17
|
module FeatureWriters
|
|
18
18
|
autoload :Base, "fontisan/ufo/compile/feature_writers/base"
|
|
19
|
-
autoload :MarkFamilyBase,
|
|
19
|
+
autoload :MarkFamilyBase,
|
|
20
|
+
"fontisan/ufo/compile/feature_writers/mark_family_base"
|
|
20
21
|
autoload :Kern, "fontisan/ufo/compile/feature_writers/kern"
|
|
21
22
|
autoload :Kern2, "fontisan/ufo/compile/feature_writers/kern2"
|
|
22
23
|
autoload :Gdef, "fontisan/ufo/compile/feature_writers/gdef"
|
|
@@ -134,8 +134,10 @@ module Fontisan
|
|
|
134
134
|
right_c2 = Point.new(x: r2x, y: r2y, type: "offcurve")
|
|
135
135
|
right_p3 = p3
|
|
136
136
|
|
|
137
|
-
left = subdivide_cubic(left_p0, left_c1, left_c2, left_p3,
|
|
138
|
-
|
|
137
|
+
left = subdivide_cubic(left_p0, left_c1, left_c2, left_p3,
|
|
138
|
+
tolerance)
|
|
139
|
+
right = subdivide_cubic(right_p0, right_c1, right_c2, right_p3,
|
|
140
|
+
tolerance)
|
|
139
141
|
|
|
140
142
|
# Drop the duplicated midpoint point between halves
|
|
141
143
|
left + right
|
|
@@ -56,7 +56,8 @@ module Fontisan
|
|
|
56
56
|
next if i == j || drop.include?(j)
|
|
57
57
|
|
|
58
58
|
# Drop inner when its bbox is fully inside outer's.
|
|
59
|
-
if contained_in?(bboxes[j],
|
|
59
|
+
if contained_in?(bboxes[j],
|
|
60
|
+
bboxes[i]) && (inner.points.size <= outer.points.size)
|
|
60
61
|
# Sanity: only drop if the point count is smaller —
|
|
61
62
|
# a fully-contained contour with MORE points is
|
|
62
63
|
# almost certainly the outer one in a malformed
|
|
@@ -28,7 +28,8 @@ module Fontisan
|
|
|
28
28
|
|
|
29
29
|
axes_bytes = build_axes(axes)
|
|
30
30
|
instances_bytes = build_instances(instances || [], axes.size)
|
|
31
|
-
header = build_header(axes.size, axes_bytes, instances_bytes,
|
|
31
|
+
header = build_header(axes.size, axes_bytes, instances_bytes,
|
|
32
|
+
instances)
|
|
32
33
|
header + axes_bytes + instances_bytes
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -38,7 +39,8 @@ module Fontisan
|
|
|
38
39
|
# majorVersion (2) + minorVersion (2) + axesArrayOffset (2)
|
|
39
40
|
# + reserved (2) + axisCount (2) + axisSize (2)
|
|
40
41
|
# + instanceCount (2) + instanceSize (2)
|
|
41
|
-
def self.build_header(axis_count, axes_bytes, _instances_bytes,
|
|
42
|
+
def self.build_header(axis_count, axes_bytes, _instances_bytes,
|
|
43
|
+
instances)
|
|
42
44
|
axes_offset = 16
|
|
43
45
|
axes_offset + axes_bytes.bytesize
|
|
44
46
|
|
|
@@ -86,7 +88,9 @@ module Fontisan
|
|
|
86
88
|
name_id = inst[:name_id] || inst["name_id"] || 0
|
|
87
89
|
flags = inst[:flags] || inst["flags"] || 0
|
|
88
90
|
coords = inst[:coords] || inst["coords"] || []
|
|
89
|
-
padded = coords.first(axis_count) + Array.new(
|
|
91
|
+
padded = coords.first(axis_count) + Array.new(
|
|
92
|
+
[axis_count - coords.size, 0].max, 0
|
|
93
|
+
)
|
|
90
94
|
padded = padded.first(axis_count).map { |c| fixed_value(c) }
|
|
91
95
|
|
|
92
96
|
io << [name_id, flags].pack("nn")
|
|
@@ -41,7 +41,8 @@ module Fontisan
|
|
|
41
41
|
offsets.map { |o| o / 2 }.pack("n*")
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
{ "glyf" => glyf_bytes, "loca" => loca_bytes,
|
|
44
|
+
{ "glyf" => glyf_bytes, "loca" => loca_bytes,
|
|
45
|
+
:loca_format => use_long ? 1 : 0 }
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
# Encode a single glyph into glyf bytes. Empty glyphs (no
|
|
@@ -138,7 +139,8 @@ module Fontisan
|
|
|
138
139
|
# nothing).
|
|
139
140
|
""
|
|
140
141
|
end
|
|
141
|
-
private_class_method :encode_glyph, :encode_simple, :encode_points,
|
|
142
|
+
private_class_method :encode_glyph, :encode_simple, :encode_points,
|
|
143
|
+
:encode_composite
|
|
142
144
|
end
|
|
143
145
|
end
|
|
144
146
|
end
|
|
@@ -121,7 +121,9 @@ module Fontisan
|
|
|
121
121
|
deltas = tuple[:deltas]
|
|
122
122
|
|
|
123
123
|
# Encode delta data
|
|
124
|
-
all_fit_int8 = deltas.all?
|
|
124
|
+
all_fit_int8 = deltas.all? do |dx, dy|
|
|
125
|
+
dx.between?(-127, 127) && dy.between?(-127, 127)
|
|
126
|
+
end
|
|
125
127
|
|
|
126
128
|
point_count = deltas.size
|
|
127
129
|
flags = point_count & POINT_COUNT_MASK
|
|
@@ -195,7 +197,8 @@ module Fontisan
|
|
|
195
197
|
|
|
196
198
|
def self.build_empty(glyph_count)
|
|
197
199
|
# Minimal gvar with no variation data
|
|
198
|
-
header = [VERSION_MAJOR, VERSION_MINOR, 0, 0, 0, glyph_count, 1,
|
|
200
|
+
header = [VERSION_MAJOR, VERSION_MINOR, 0, 0, 0, glyph_count, 1,
|
|
201
|
+
glyph_count + 16].pack("nnnnnnNN")
|
|
199
202
|
offsets = Array.new(glyph_count + 1, 0).pack("C*")
|
|
200
203
|
header + offsets
|
|
201
204
|
end
|
|
@@ -19,7 +19,8 @@ module Fontisan
|
|
|
19
19
|
# @param glyphs [Array<Fontisan::Ufo::Glyph>] (unused here;
|
|
20
20
|
# bbox is computed by the caller for performance)
|
|
21
21
|
# @return [Fontisan::Tables::Head]
|
|
22
|
-
def self.build(font, glyphs:, units_per_em: nil,
|
|
22
|
+
def self.build(font, glyphs:, units_per_em: nil,
|
|
23
|
+
loca_format: LOCA_FORMAT_LONG)
|
|
23
24
|
bbox = font_bbox(font, glyphs)
|
|
24
25
|
Fontisan::Tables::Head.new(
|
|
25
26
|
version_raw: 0x00010000,
|
|
@@ -62,7 +62,8 @@ module Fontisan
|
|
|
62
62
|
io << [regions.size].pack("n") # regionCount
|
|
63
63
|
regions.each do |region|
|
|
64
64
|
region.each do |coords|
|
|
65
|
-
io << [f2dot14(coords[:start]), f2dot14(coords[:peak]),
|
|
65
|
+
io << [f2dot14(coords[:start]), f2dot14(coords[:peak]),
|
|
66
|
+
f2dot14(coords[:end])].pack("nnn")
|
|
66
67
|
end
|
|
67
68
|
end
|
|
68
69
|
io
|
|
@@ -51,7 +51,9 @@ module Fontisan
|
|
|
51
51
|
|
|
52
52
|
def self.format0_bytes(records)
|
|
53
53
|
# Strings are stored UTF-16BE on disk (Windows Unicode).
|
|
54
|
-
encoded = records.map
|
|
54
|
+
encoded = records.map do |r|
|
|
55
|
+
r[:value].encode("UTF-16BE").force_encoding("BINARY")
|
|
56
|
+
end
|
|
55
57
|
storage = encoded.join
|
|
56
58
|
storage_offset = 6 + (records.size * 12)
|
|
57
59
|
|
|
@@ -75,7 +75,8 @@ module Fontisan
|
|
|
75
75
|
if glyph && glyph[:data]
|
|
76
76
|
offsets << current_offset
|
|
77
77
|
data = glyph[:data].b
|
|
78
|
-
glyph_data << [glyph[:origin_x] || 0,
|
|
78
|
+
glyph_data << [glyph[:origin_x] || 0,
|
|
79
|
+
glyph[:origin_y] || 0].pack("nn")
|
|
79
80
|
glyph_data << (glyph[:graphic_type] || PNG_TYPE).ljust(4)[0, 4]
|
|
80
81
|
glyph_data << data
|
|
81
82
|
current_offset += 8 + data.bytesize
|
|
@@ -26,7 +26,8 @@ module Fontisan
|
|
|
26
26
|
axis_value_count = axis_value_list.size
|
|
27
27
|
|
|
28
28
|
design_axes = serialize_design_axes(axes)
|
|
29
|
-
value_tables, value_offsets = serialize_axis_values(axis_value_list,
|
|
29
|
+
value_tables, value_offsets = serialize_axis_values(axis_value_list,
|
|
30
|
+
design_axes.bytesize)
|
|
30
31
|
|
|
31
32
|
header = serialize_header(
|
|
32
33
|
design_axis_count: design_axis_count,
|
|
@@ -45,7 +46,8 @@ module Fontisan
|
|
|
45
46
|
io
|
|
46
47
|
end
|
|
47
48
|
|
|
48
|
-
def self.serialize_header(design_axis_count:, design_axes_size:,
|
|
49
|
+
def self.serialize_header(design_axis_count:, design_axes_size:,
|
|
50
|
+
axis_value_count:, elided_name_id:)
|
|
49
51
|
design_axes_offset = HEADER_SIZE
|
|
50
52
|
offset_to_axis_value_offsets = design_axes_offset + design_axes_size
|
|
51
53
|
|
|
@@ -86,7 +88,8 @@ module Fontisan
|
|
|
86
88
|
flags = av[:flags] || av["flags"] || 0
|
|
87
89
|
name_id = av[:name_id] || av["name_id"] || 0
|
|
88
90
|
value = f2dot14(av[:value] || av["value"] || 0)
|
|
89
|
-
value_tables << [format, axis_idx, flags, name_id,
|
|
91
|
+
value_tables << [format, axis_idx, flags, name_id,
|
|
92
|
+
value].pack("nnnnn")
|
|
90
93
|
end
|
|
91
94
|
|
|
92
95
|
[value_tables, value_offsets]
|
|
@@ -82,7 +82,8 @@ module Fontisan
|
|
|
82
82
|
|
|
83
83
|
if raw && raw.encoding == Encoding::UTF_16BE
|
|
84
84
|
raw.encode("UTF-8")
|
|
85
|
-
elsif raw && raw.bytesize >= 2 && raw.getbyte(0).between?(0,
|
|
85
|
+
elsif raw && raw.bytesize >= 2 && raw.getbyte(0).between?(0,
|
|
86
|
+
127) && raw.getbyte(1).zero?
|
|
86
87
|
# Looks like UTF-16BE
|
|
87
88
|
raw.force_encoding("UTF-16BE").encode("UTF-8")
|
|
88
89
|
else
|
|
@@ -165,7 +166,10 @@ module Fontisan
|
|
|
165
166
|
return unless glyf && loca && head
|
|
166
167
|
|
|
167
168
|
# Tables need context-aware initialization before per-glyph access.
|
|
168
|
-
|
|
169
|
+
if loca.respond_to?(:parse_with_context)
|
|
170
|
+
loca.parse_with_context(head.index_to_loc_format,
|
|
171
|
+
num_glyphs)
|
|
172
|
+
end
|
|
169
173
|
|
|
170
174
|
num_glyphs.times do |gid|
|
|
171
175
|
glyph_name = glyph_name_for(font, gid) || "glyph#{gid}"
|
|
@@ -17,10 +17,14 @@ module Fontisan
|
|
|
17
17
|
# @return [String] the output_path
|
|
18
18
|
def self.convert(ufo, output_path:, compiler: :ttf, **_opts)
|
|
19
19
|
compiler_class = Convert::COMPILER_FOR_FORMAT[compiler.to_sym]
|
|
20
|
-
|
|
20
|
+
unless compiler_class
|
|
21
|
+
raise ArgumentError,
|
|
22
|
+
"unknown intermediate compiler: #{compiler.inspect}"
|
|
23
|
+
end
|
|
21
24
|
|
|
22
25
|
Dir.mktmpdir do |dir|
|
|
23
|
-
intermediate_path = File.join(dir,
|
|
26
|
+
intermediate_path = File.join(dir,
|
|
27
|
+
"intermediate#{ext_for(compiler)}")
|
|
24
28
|
compiler_class.new(ufo).compile(output_path: intermediate_path)
|
|
25
29
|
|
|
26
30
|
loaded = Fontisan::FontLoader.load(intermediate_path)
|
|
@@ -18,7 +18,10 @@ module Fontisan
|
|
|
18
18
|
# @return [String] the output_path
|
|
19
19
|
def self.convert(ufo, output_path:, compiler: :otf, **_opts)
|
|
20
20
|
compiler_class = Convert::COMPILER_FOR_FORMAT[compiler.to_sym]
|
|
21
|
-
|
|
21
|
+
unless compiler_class
|
|
22
|
+
raise ArgumentError,
|
|
23
|
+
"unknown intermediate compiler: #{compiler.inspect}"
|
|
24
|
+
end
|
|
22
25
|
|
|
23
26
|
Dir.mktmpdir do |dir|
|
|
24
27
|
intermediate_path = File.join(dir, "intermediate.otf")
|
|
@@ -17,12 +17,17 @@ module Fontisan
|
|
|
17
17
|
# @param ps_options [Hash] forwarded to the generator
|
|
18
18
|
# (encoding, upm_scale, convert_curves)
|
|
19
19
|
# @return [String] the output_path
|
|
20
|
-
def self.convert(ufo, output_path:, generator_class:, compiler: :ttf,
|
|
20
|
+
def self.convert(ufo, output_path:, generator_class:, compiler: :ttf,
|
|
21
|
+
**ps_options)
|
|
21
22
|
compiler_class = Convert::COMPILER_FOR_FORMAT[compiler.to_sym]
|
|
22
|
-
|
|
23
|
+
unless compiler_class
|
|
24
|
+
raise ArgumentError,
|
|
25
|
+
"unknown intermediate compiler: #{compiler.inspect}"
|
|
26
|
+
end
|
|
23
27
|
|
|
24
28
|
Dir.mktmpdir do |dir|
|
|
25
|
-
intermediate_path = File.join(dir,
|
|
29
|
+
intermediate_path = File.join(dir,
|
|
30
|
+
"intermediate#{ext_for(compiler)}")
|
|
26
31
|
compiler_class.new(ufo).compile(output_path: intermediate_path)
|
|
27
32
|
|
|
28
33
|
loaded = Fontisan::FontLoader.load(intermediate_path)
|
|
@@ -32,14 +32,19 @@ module Fontisan
|
|
|
32
32
|
# @return [String] the output_path
|
|
33
33
|
def self.convert(ufo, output_path:, compiler: :ttf, **woff_options)
|
|
34
34
|
compiler_class = Convert::COMPILER_FOR_FORMAT[compiler.to_sym]
|
|
35
|
-
|
|
35
|
+
unless compiler_class
|
|
36
|
+
raise ArgumentError,
|
|
37
|
+
"unknown intermediate compiler: #{compiler.inspect}"
|
|
38
|
+
end
|
|
36
39
|
|
|
37
40
|
Dir.mktmpdir do |dir|
|
|
38
|
-
intermediate_path = File.join(dir,
|
|
41
|
+
intermediate_path = File.join(dir,
|
|
42
|
+
"intermediate#{format_ext(compiler)}")
|
|
39
43
|
compiler_class.new(ufo).compile(output_path: intermediate_path)
|
|
40
44
|
|
|
41
45
|
loaded = Fontisan::FontLoader.load(intermediate_path)
|
|
42
|
-
woff_bytes = Fontisan::Converters::WoffWriter.new.convert(loaded,
|
|
46
|
+
woff_bytes = Fontisan::Converters::WoffWriter.new.convert(loaded,
|
|
47
|
+
woff_options)
|
|
43
48
|
File.binwrite(output_path, woff_bytes)
|
|
44
49
|
end
|
|
45
50
|
|
|
@@ -21,14 +21,19 @@ module Fontisan
|
|
|
21
21
|
# @return [String] the output_path
|
|
22
22
|
def self.convert(ufo, output_path:, compiler: :ttf, **woff2_options)
|
|
23
23
|
compiler_class = Convert::COMPILER_FOR_FORMAT[compiler.to_sym]
|
|
24
|
-
|
|
24
|
+
unless compiler_class
|
|
25
|
+
raise ArgumentError,
|
|
26
|
+
"unknown intermediate compiler: #{compiler.inspect}"
|
|
27
|
+
end
|
|
25
28
|
|
|
26
29
|
Dir.mktmpdir do |dir|
|
|
27
|
-
intermediate_path = File.join(dir,
|
|
30
|
+
intermediate_path = File.join(dir,
|
|
31
|
+
"intermediate#{format_ext(compiler)}")
|
|
28
32
|
compiler_class.new(ufo).compile(output_path: intermediate_path)
|
|
29
33
|
|
|
30
34
|
loaded = Fontisan::FontLoader.load(intermediate_path)
|
|
31
|
-
result = Fontisan::Converters::Woff2Encoder.new.convert(loaded,
|
|
35
|
+
result = Fontisan::Converters::Woff2Encoder.new.convert(loaded,
|
|
36
|
+
woff2_options)
|
|
32
37
|
File.binwrite(output_path, result[:woff2_binary])
|
|
33
38
|
end
|
|
34
39
|
|
data/lib/fontisan/ufo/convert.rb
CHANGED
|
@@ -71,7 +71,10 @@ module Fontisan
|
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
compiler = COMPILER_FOR_FORMAT[format_key]
|
|
74
|
-
|
|
74
|
+
unless compiler
|
|
75
|
+
raise ArgumentError,
|
|
76
|
+
"unknown UFO output format: #{to.inspect}"
|
|
77
|
+
end
|
|
75
78
|
|
|
76
79
|
compiler.new(ufo).compile(output_path: output_path)
|
|
77
80
|
output_path
|
data/lib/fontisan/ufo/info.rb
CHANGED
|
@@ -81,8 +81,10 @@ module Fontisan
|
|
|
81
81
|
# @return [Array(Integer, Integer)] [major, minor]
|
|
82
82
|
def self.parse_version(version)
|
|
83
83
|
case version.to_s
|
|
84
|
-
when /\A(\d+)\.(\d+)\.\d+\z/ then [Regexp.last_match(1).to_i,
|
|
85
|
-
|
|
84
|
+
when /\A(\d+)\.(\d+)\.\d+\z/ then [Regexp.last_match(1).to_i,
|
|
85
|
+
Regexp.last_match(2).to_i]
|
|
86
|
+
when /\A(\d+)\.(\d+)\z/ then [Regexp.last_match(1).to_i,
|
|
87
|
+
Regexp.last_match(2).to_i]
|
|
86
88
|
when /\A(\d+)\z/ then [Regexp.last_match(1).to_i, 0]
|
|
87
89
|
else [0, 0]
|
|
88
90
|
end
|
data/lib/fontisan/ufo/plist.rb
CHANGED
|
@@ -25,7 +25,9 @@ module Fontisan
|
|
|
25
25
|
root = doc.root
|
|
26
26
|
raise ParseError, "no <plist> root element" unless root&.name == "plist"
|
|
27
27
|
|
|
28
|
-
parse_value(root.children.find
|
|
28
|
+
parse_value(root.children.find do |c|
|
|
29
|
+
c.element? || c.cdata? || c.text? && !c.text.strip.empty?
|
|
30
|
+
end)
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
# @param value [Object] the value to serialize
|
|
@@ -73,7 +75,10 @@ module Fontisan
|
|
|
73
75
|
# dict children are key/value pairs (key first, then value)
|
|
74
76
|
while children.any?
|
|
75
77
|
key_node = children.shift
|
|
76
|
-
|
|
78
|
+
unless key_node.name == "key"
|
|
79
|
+
raise ParseError,
|
|
80
|
+
"dict key is not <key>"
|
|
81
|
+
end
|
|
77
82
|
|
|
78
83
|
value_node = children.shift
|
|
79
84
|
result[key_node.text] = parse_value(value_node)
|
data/lib/fontisan/ufo/reader.rb
CHANGED
|
@@ -94,7 +94,9 @@ module Fontisan
|
|
|
94
94
|
if order.first.is_a?(Hash)
|
|
95
95
|
order.reduce({}) { |h, pair| h.merge(pair) }
|
|
96
96
|
else
|
|
97
|
-
order.to_h
|
|
97
|
+
order.to_h do |filename|
|
|
98
|
+
[File.basename(filename, ".glif"), filename]
|
|
99
|
+
end
|
|
98
100
|
end
|
|
99
101
|
else
|
|
100
102
|
raise "unsupported contents.plist format: #{order.class}"
|
data/lib/fontisan/ufo/writer.rb
CHANGED
|
@@ -50,7 +50,8 @@ module Fontisan
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def write_fontinfo(path)
|
|
53
|
-
File.write(File.join(path, "fontinfo.plist"),
|
|
53
|
+
File.write(File.join(path, "fontinfo.plist"),
|
|
54
|
+
Plist.emit(@font.info.to_plist))
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
def write_layercontents(path)
|
|
@@ -75,11 +76,17 @@ module Fontisan
|
|
|
75
76
|
FileUtils.mkpath(layer_dir)
|
|
76
77
|
|
|
77
78
|
contents =
|
|
78
|
-
layer.glyphs.transform_values
|
|
79
|
-
|
|
79
|
+
layer.glyphs.transform_values do |g|
|
|
80
|
+
"#{safe_filename(g.name)}.glif"
|
|
81
|
+
end
|
|
82
|
+
File.write(File.join(layer_dir, "contents.plist"),
|
|
83
|
+
Plist.emit(contents))
|
|
80
84
|
|
|
81
85
|
layer.glyphs.each_value do |glyph|
|
|
82
|
-
File.write(
|
|
86
|
+
File.write(
|
|
87
|
+
File.join(layer_dir,
|
|
88
|
+
"#{safe_filename(glyph.name)}.glif"), glyph.to_glif
|
|
89
|
+
)
|
|
83
90
|
end
|
|
84
91
|
end
|
|
85
92
|
end
|
|
@@ -96,7 +103,8 @@ module Fontisan
|
|
|
96
103
|
def write_kerning(path)
|
|
97
104
|
return if @font.kerning.empty?
|
|
98
105
|
|
|
99
|
-
File.write(File.join(path, "kerning.plist"),
|
|
106
|
+
File.write(File.join(path, "kerning.plist"),
|
|
107
|
+
Plist.emit(@font.kerning.to_plist))
|
|
100
108
|
end
|
|
101
109
|
|
|
102
110
|
def write_features(path)
|
data/lib/fontisan/version.rb
CHANGED
|
@@ -131,7 +131,10 @@ module Fontisan
|
|
|
131
131
|
def read_collection_directory(pos)
|
|
132
132
|
version = @data[pos, 4].unpack1("N")
|
|
133
133
|
pos += 4
|
|
134
|
-
|
|
134
|
+
unless version == 0x00010000
|
|
135
|
+
raise InvalidFontError,
|
|
136
|
+
"Unsupported CollectionHeader version 0x#{version.to_s(16)}"
|
|
137
|
+
end
|
|
135
138
|
|
|
136
139
|
num_fonts, pos = UInt255.decode_at(@data, pos)
|
|
137
140
|
font_entries = Array.new(num_fonts) do
|
|
@@ -32,7 +32,10 @@ module Fontisan
|
|
|
32
32
|
# @param fonts [Array<TrueTypeFont, OpenTypeFont>] Source fonts
|
|
33
33
|
# @return [String] WOFF2 collection binary
|
|
34
34
|
def encode_fonts(fonts)
|
|
35
|
-
|
|
35
|
+
if fonts.nil? || fonts.empty?
|
|
36
|
+
raise ArgumentError,
|
|
37
|
+
"fonts cannot be empty"
|
|
38
|
+
end
|
|
36
39
|
|
|
37
40
|
prepared = fonts.map.with_index { |font, i| prepare_font(font, i) }
|
|
38
41
|
_, font_to_ckeys = deduplicate(prepared)
|
|
@@ -50,7 +50,8 @@ module Fontisan
|
|
|
50
50
|
start_offset = source_offsets[i]
|
|
51
51
|
end_offset = source_offsets[i + 1]
|
|
52
52
|
if start_offset < end_offset
|
|
53
|
-
glyf << @glyf_data.byteslice(start_offset,
|
|
53
|
+
glyf << @glyf_data.byteslice(start_offset,
|
|
54
|
+
end_offset - start_offset)
|
|
54
55
|
end
|
|
55
56
|
remainder = glyf.bytesize % 4
|
|
56
57
|
glyf << ("\x00" * (4 - remainder)) if remainder.positive?
|
|
@@ -131,7 +131,8 @@ module Fontisan
|
|
|
131
131
|
streams[:bbox_entries] = StringIO.new(bbox_blob[bitmap_size..] || "")
|
|
132
132
|
|
|
133
133
|
# overlapSimpleBitmap follows all streams when optionFlags bit 0 set.
|
|
134
|
-
streams[:overlap_bitmap] =
|
|
134
|
+
streams[:overlap_bitmap] =
|
|
135
|
+
header[:has_overlap_bitmap] ? @data[pos, overlap_size] : ""
|
|
135
136
|
|
|
136
137
|
# nContour is read positionally (int16 per glyph).
|
|
137
138
|
nc_data = streams[:n_contour].string
|
|
@@ -178,7 +179,9 @@ module Fontisan
|
|
|
178
179
|
x = 0
|
|
179
180
|
y = 0
|
|
180
181
|
triplet_flags.each do |flag|
|
|
181
|
-
dx, dy, oc = TripletCodec.decode(flag,
|
|
182
|
+
dx, dy, oc = TripletCodec.decode(flag,
|
|
183
|
+
read_triplet_payload(flag,
|
|
184
|
+
streams[:glyph]))
|
|
182
185
|
x += dx
|
|
183
186
|
y += dy
|
|
184
187
|
xs << x
|
|
@@ -97,7 +97,8 @@ module Fontisan
|
|
|
97
97
|
def per_table_checksum(table)
|
|
98
98
|
data = table.bytes
|
|
99
99
|
if table.tag == "head"
|
|
100
|
-
data = "#{data.byteslice(0,
|
|
100
|
+
data = "#{data.byteslice(0,
|
|
101
|
+
8)}#{['00000000'].pack('H8')}#{data.byteslice(12..)}"
|
|
101
102
|
end
|
|
102
103
|
Utilities::ChecksumCalculator.calculate_table_checksum(data)
|
|
103
104
|
end
|
data/lib/fontisan.rb
CHANGED
|
@@ -119,7 +119,6 @@ module Fontisan
|
|
|
119
119
|
autoload :SfntBuilder, "fontisan/sfnt_builder"
|
|
120
120
|
autoload :Stitcher, "fontisan/stitcher"
|
|
121
121
|
autoload :StitcherCli, "fontisan/stitcher_cli"
|
|
122
|
-
autoload :Tasks, "fontisan/tasks"
|
|
123
122
|
autoload :TrueTypeCollection, "fontisan/true_type_collection"
|
|
124
123
|
autoload :TrueTypeFont, "fontisan/true_type_font"
|
|
125
124
|
autoload :TrueTypeFontExtensions, "fontisan/true_type_font_extensions"
|
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.
|
|
4
|
+
version: 0.4.24
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -168,6 +168,7 @@ files:
|
|
|
168
168
|
- docs/api/font-loader.md
|
|
169
169
|
- docs/api/font-writer.md
|
|
170
170
|
- docs/api/index.md
|
|
171
|
+
- docs/api/layer.md
|
|
171
172
|
- docs/api/models/glyph-accessor.md
|
|
172
173
|
- docs/api/models/glyph.md
|
|
173
174
|
- docs/api/models/table-analyzer.md
|
|
@@ -588,8 +589,6 @@ files:
|
|
|
588
589
|
- lib/fontisan/tables/svg.rb
|
|
589
590
|
- lib/fontisan/tables/variation_common.rb
|
|
590
591
|
- lib/fontisan/tables/vvar.rb
|
|
591
|
-
- lib/fontisan/tasks.rb
|
|
592
|
-
- lib/fontisan/tasks/fixture_downloader.rb
|
|
593
592
|
- lib/fontisan/true_type_collection.rb
|
|
594
593
|
- lib/fontisan/true_type_font.rb
|
|
595
594
|
- lib/fontisan/true_type_font_extensions.rb
|