fontisan 0.4.33 → 0.4.35
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 +19 -217
- data/TODO.improvements/README.md +4 -4
- data/lib/fontisan/audit/check.rb +47 -0
- data/lib/fontisan/audit/check_registry.rb +47 -0
- data/lib/fontisan/audit/checks/cmap_check.rb +249 -0
- data/lib/fontisan/audit/checks/collection_integrity_check.rb +102 -0
- data/lib/fontisan/audit/checks/glyph_name_check.rb +107 -0
- data/lib/fontisan/audit/checks/ots_compatibility_check.rb +191 -0
- data/lib/fontisan/audit/checks/table_directory_check.rb +157 -0
- data/lib/fontisan/audit/checks.rb +22 -0
- data/lib/fontisan/audit.rb +20 -0
- data/lib/fontisan/cli.rb +78 -0
- data/lib/fontisan/commands/audit_command.rb +291 -0
- data/lib/fontisan/commands.rb +1 -0
- data/lib/fontisan/models/audit_report.rb +148 -0
- data/lib/fontisan/models.rb +2 -0
- data/lib/fontisan/type1/charstrings.rb +34 -1
- data/lib/fontisan/type1/seac_expander.rb +68 -57
- data/lib/fontisan/ufo/compile/feature_compiler.rb +161 -0
- data/lib/fontisan/ufo/compile/gsub.rb +267 -0
- data/lib/fontisan/ufo/compile.rb +3 -1
- data/lib/fontisan/ufo/font.rb +3 -2
- data/lib/fontisan/ufo/image.rb +15 -2
- data/lib/fontisan/ufo/image_set.rb +82 -2
- data/lib/fontisan/ufo/reader.rb +8 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan.rb +1 -0
- metadata +14 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Ufo
|
|
7
|
+
module Compile
|
|
8
|
+
# Builds the OpenType `GSUB` (Glyph Substitution) table from
|
|
9
|
+
# ligature substitution rules extracted from features.fea via
|
|
10
|
+
# {FeatureCompiler}.
|
|
11
|
+
#
|
|
12
|
+
# Emits a minimal but valid GSUB with:
|
|
13
|
+
#
|
|
14
|
+
# - ScriptList: DFLT script, default language system
|
|
15
|
+
# - FeatureList: one feature record per ligature feature tag
|
|
16
|
+
# (e.g. "liga", "dlig", "clig")
|
|
17
|
+
# - LookupList: one LigatureSubst lookup per feature tag
|
|
18
|
+
#
|
|
19
|
+
# Each `sub A B C by ABC;` rule becomes a LigatureSet on the
|
|
20
|
+
# first glyph (A), with a Ligature record pointing at ABC and
|
|
21
|
+
# component count 2 (B, C).
|
|
22
|
+
#
|
|
23
|
+
# @see https://learn.microsoft.com/en-us/typography/opentype/spec/gsub
|
|
24
|
+
module Gsub
|
|
25
|
+
SCRIPT_DFLT = "DFLT"
|
|
26
|
+
LANGSYS_DEFAULT = 0
|
|
27
|
+
|
|
28
|
+
# Build GSUB table bytes from parsed feature rules.
|
|
29
|
+
#
|
|
30
|
+
# @param parsed [FeatureCompiler::ParsedFeatures]
|
|
31
|
+
# @param name_to_gid [Hash{String=>Integer}]
|
|
32
|
+
# @return [String, nil] GSUB bytes, or nil if no ligature rules
|
|
33
|
+
def self.build(parsed:, name_to_gid:)
|
|
34
|
+
features = ligature_features(parsed, name_to_gid)
|
|
35
|
+
return nil if features.empty?
|
|
36
|
+
|
|
37
|
+
lookups = features.values
|
|
38
|
+
build_gsub_table(features.keys, lookups)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Resolve ligature rules to per-feature lookup data.
|
|
42
|
+
#
|
|
43
|
+
# @param parsed [FeatureCompiler::ParsedFeatures]
|
|
44
|
+
# @param name_to_gid [Hash{String=>Integer}]
|
|
45
|
+
# @return [Hash{String=>Array}] feature_tag → lookup data
|
|
46
|
+
def self.ligature_features(parsed, name_to_gid)
|
|
47
|
+
parsed.ligatures.keys.each_with_object({}) do |tag, h|
|
|
48
|
+
rules = parsed.ligatures_for(tag)
|
|
49
|
+
sets = build_ligature_sets(rules, name_to_gid)
|
|
50
|
+
h[tag] = sets unless sets.empty?
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Build ligature sets keyed by the first glyph in each sequence.
|
|
55
|
+
#
|
|
56
|
+
# @param rules [Array<Hash>] {sequence:, result:}
|
|
57
|
+
# @param name_to_gid [Hash]
|
|
58
|
+
# @return [Hash{Integer=>Array}] gid → [{result_gid, components: [gids]}]
|
|
59
|
+
def self.build_ligature_sets(rules, name_to_gid)
|
|
60
|
+
sets = Hash.new { |h, k| h[k] = [] }
|
|
61
|
+
rules.each do |rule|
|
|
62
|
+
seq_gids = rule[:sequence].map { |n| name_to_gid[n] }
|
|
63
|
+
result_gid = name_to_gid[rule[:result]]
|
|
64
|
+
next unless seq_gids.all? && result_gid
|
|
65
|
+
|
|
66
|
+
first = seq_gids.first
|
|
67
|
+
components = seq_gids[1..]
|
|
68
|
+
sets[first] << { result_gid: result_gid, components: components }
|
|
69
|
+
end
|
|
70
|
+
sets
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# ---------- GSUB binary assembly ----------
|
|
74
|
+
|
|
75
|
+
# Build the complete GSUB table.
|
|
76
|
+
#
|
|
77
|
+
# @param tags [Array<String>] feature tags in lookup order
|
|
78
|
+
# @param lookups [Array<Hash>] one lookup per tag
|
|
79
|
+
# @return [String]
|
|
80
|
+
def self.build_gsub_table(tags, lookups)
|
|
81
|
+
io = StringIO.new(+"")
|
|
82
|
+
# Header: version (uint32 0x00010000), scriptListOffset,
|
|
83
|
+
# featureListOffset, lookupListOffset (each uint16)
|
|
84
|
+
header_size = 2 + 2 + 2 + 4 # version(4) + 3×uint16
|
|
85
|
+
|
|
86
|
+
# Serialize each list independently, then compute offsets.
|
|
87
|
+
script_list = build_script_list(tags.size)
|
|
88
|
+
feature_list = build_feature_list(tags)
|
|
89
|
+
lookup_list = build_lookup_list(lookups)
|
|
90
|
+
|
|
91
|
+
script_list_offset = header_size
|
|
92
|
+
feature_list_offset = script_list_offset + script_list.bytesize
|
|
93
|
+
lookup_list_offset = feature_list_offset + feature_list.bytesize
|
|
94
|
+
|
|
95
|
+
io << [0x00010000].pack("N")
|
|
96
|
+
io << [script_list_offset].pack("n")
|
|
97
|
+
io << [feature_list_offset].pack("n")
|
|
98
|
+
io << [lookup_list_offset].pack("n")
|
|
99
|
+
io << script_list
|
|
100
|
+
io << feature_list
|
|
101
|
+
io << lookup_list
|
|
102
|
+
io.string
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# ScriptList: one script (DFLT) with a default LangSys that
|
|
106
|
+
# references all features (featureIndex 0..count-1).
|
|
107
|
+
def self.build_script_list(feature_count)
|
|
108
|
+
io = +""
|
|
109
|
+
io << [1].pack("n") # scriptCount = 1
|
|
110
|
+
|
|
111
|
+
# ScriptRecord: tag(4) + scriptOffset(2)
|
|
112
|
+
# Script table starts right after the ScriptList header:
|
|
113
|
+
# scriptCount(2) + ScriptRecord(6) = 8
|
|
114
|
+
script_offset = 8
|
|
115
|
+
io << SCRIPT_DFLT
|
|
116
|
+
io << [script_offset].pack("n")
|
|
117
|
+
|
|
118
|
+
# Script table: defaultLangSysOffset(2) + langSysCount(2) = 4
|
|
119
|
+
# LangSys follows immediately
|
|
120
|
+
default_langsys_offset = script_offset + 4
|
|
121
|
+
io << [default_langsys_offset].pack("n")
|
|
122
|
+
io << [0].pack("n") # langSysCount
|
|
123
|
+
|
|
124
|
+
# LangSys: lookupOrder(2)=0, requiredFeatureIndex(2)=0xFFFF,
|
|
125
|
+
# featureIndexCount(2), featureIndices[]
|
|
126
|
+
io << [0].pack("n")
|
|
127
|
+
io << [0xFFFF].pack("n")
|
|
128
|
+
io << [feature_count].pack("n")
|
|
129
|
+
feature_count.times { |i| io << [i].pack("n") }
|
|
130
|
+
|
|
131
|
+
io
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# FeatureList: one FeatureRecord per tag. Each points to a
|
|
135
|
+
# Feature table with one lookup index (the lookup for that tag).
|
|
136
|
+
def self.build_feature_list(tags)
|
|
137
|
+
io = +""
|
|
138
|
+
io << [tags.size].pack("n") # featureCount
|
|
139
|
+
|
|
140
|
+
# FeatureRecords: tag(4) + featureOffset(2)
|
|
141
|
+
records_size = tags.size * 6
|
|
142
|
+
header_size = 2 + records_size
|
|
143
|
+
|
|
144
|
+
feature_records = +""
|
|
145
|
+
feature_tables = +""
|
|
146
|
+
tags.each_with_index do |tag, idx|
|
|
147
|
+
feature_records << tag.ljust(4, " ")[0, 4]
|
|
148
|
+
feature_records << [header_size + (idx * 4)].pack("n")
|
|
149
|
+
|
|
150
|
+
feature_tables << [0].pack("n") # featureParams (null)
|
|
151
|
+
feature_tables << [1].pack("n") # lookupIndexCount
|
|
152
|
+
feature_tables << [idx].pack("n") # lookupIndex (1:1 feature→lookup)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
io << feature_records
|
|
156
|
+
io << feature_tables
|
|
157
|
+
io
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# LookupList: one lookup per feature tag. Each lookup is a
|
|
161
|
+
# LigatureSubst (type 4) lookup.
|
|
162
|
+
def self.build_lookup_list(lookups)
|
|
163
|
+
io = +""
|
|
164
|
+
count = lookups.size
|
|
165
|
+
io << [count].pack("n") # lookupCount
|
|
166
|
+
|
|
167
|
+
# Serialize each lookup first, then compute offsets relative
|
|
168
|
+
# to the start of the LookupList.
|
|
169
|
+
serialized = lookups.map { |sets| build_ligature_lookup(sets) }
|
|
170
|
+
offset_table_size = 2 + (count * 2)
|
|
171
|
+
pos = offset_table_size
|
|
172
|
+
offsets = serialized.map do |s|
|
|
173
|
+
o = pos
|
|
174
|
+
pos += s.bytesize
|
|
175
|
+
o
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
offsets.each { |o| io << [o].pack("n") }
|
|
179
|
+
serialized.each { |s| io << s }
|
|
180
|
+
|
|
181
|
+
io
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Build a single LigatureSubst lookup (type 4, format 1).
|
|
185
|
+
def self.build_ligature_lookup(sets)
|
|
186
|
+
io = +""
|
|
187
|
+
# Lookup table: lookupType(2)=4, lookupFlag(2)=0, subTableCount(2)=1
|
|
188
|
+
io << [4].pack("n")
|
|
189
|
+
io << [0].pack("n")
|
|
190
|
+
io << [1].pack("n")
|
|
191
|
+
|
|
192
|
+
# Subtable offset (relative to start of Lookup table)
|
|
193
|
+
lookup_header_size = 6
|
|
194
|
+
io << [lookup_header_size].pack("n")
|
|
195
|
+
|
|
196
|
+
# LigatureSubstFormat1: format(2)=1, coverageOffset(2),
|
|
197
|
+
# ligatureSetCount(2), ligatureSetOffsets[]
|
|
198
|
+
first_glyphs = sets.keys.sort
|
|
199
|
+
set_count = first_glyphs.size
|
|
200
|
+
subst_header_size = 2 + 2 + 2 + (set_count * 2)
|
|
201
|
+
|
|
202
|
+
# Serialize each LigatureSet to compute offsets
|
|
203
|
+
serialized_sets = first_glyphs.map { |gid| sets[gid] }.map { |s| build_ligature_set(s) }
|
|
204
|
+
coverage = build_coverage(first_glyphs)
|
|
205
|
+
|
|
206
|
+
pos = subst_header_size
|
|
207
|
+
set_offsets = serialized_sets.map do |s|
|
|
208
|
+
o = pos
|
|
209
|
+
pos += s.bytesize
|
|
210
|
+
o
|
|
211
|
+
end
|
|
212
|
+
coverage_offset = pos
|
|
213
|
+
|
|
214
|
+
io << [1].pack("n") # substFormat = 1
|
|
215
|
+
io << [coverage_offset].pack("n")
|
|
216
|
+
io << [set_count].pack("n")
|
|
217
|
+
set_offsets.each { |o| io << [o].pack("n") }
|
|
218
|
+
serialized_sets.each { |s| io << s }
|
|
219
|
+
io << coverage
|
|
220
|
+
|
|
221
|
+
io
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Build a LigatureSet for one first glyph.
|
|
225
|
+
# @param ligatures [Array<Hash>] {result_gid:, components:}
|
|
226
|
+
def self.build_ligature_set(ligatures)
|
|
227
|
+
io = +""
|
|
228
|
+
count = ligatures.size
|
|
229
|
+
io << [count].pack("n")
|
|
230
|
+
|
|
231
|
+
# Serialize each Ligature record
|
|
232
|
+
serialized = ligatures.map { |lig| build_ligature(lig) }
|
|
233
|
+
pos = 2 + (count * 2)
|
|
234
|
+
offsets = serialized.map do |s|
|
|
235
|
+
o = pos
|
|
236
|
+
pos += s.bytesize
|
|
237
|
+
o
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
offsets.each { |o| io << [o].pack("n") }
|
|
241
|
+
serialized.each { |s| io << s }
|
|
242
|
+
io
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Build a single Ligature record.
|
|
246
|
+
# @param lig [Hash] {result_gid:, components: [gids]}
|
|
247
|
+
def self.build_ligature(lig)
|
|
248
|
+
io = +""
|
|
249
|
+
io << [lig[:result_gid]].pack("n")
|
|
250
|
+
io << [lig[:components].size + 1].pack("n") # componentCount includes first
|
|
251
|
+
lig[:components].each { |gid| io << [gid].pack("n") }
|
|
252
|
+
io
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Build a Coverage table (format 1 = glyph list).
|
|
256
|
+
# @param glyphs [Array<Integer>] sorted glyph IDs
|
|
257
|
+
def self.build_coverage(glyphs)
|
|
258
|
+
io = +""
|
|
259
|
+
io << [1].pack("n") # coverageFormat = 1
|
|
260
|
+
io << [glyphs.size].pack("n")
|
|
261
|
+
glyphs.each { |gid| io << [gid].pack("n") }
|
|
262
|
+
io
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
data/lib/fontisan/ufo/compile.rb
CHANGED
|
@@ -34,8 +34,10 @@ module Fontisan
|
|
|
34
34
|
autoload :OtfCompiler, "fontisan/ufo/compile/otf_compiler"
|
|
35
35
|
autoload :Filters, "fontisan/ufo/compile/filters"
|
|
36
36
|
autoload :FeatureWriters, "fontisan/ufo/compile/feature_writers"
|
|
37
|
-
autoload :Fvar,
|
|
37
|
+
autoload :Fvar, "fontisan/ufo/compile/fvar"
|
|
38
|
+
autoload :FeatureCompiler, "fontisan/ufo/compile/feature_compiler"
|
|
38
39
|
autoload :Gpos, "fontisan/ufo/compile/gpos"
|
|
40
|
+
autoload :Gsub, "fontisan/ufo/compile/gsub"
|
|
39
41
|
autoload :Gvar, "fontisan/ufo/compile/gvar"
|
|
40
42
|
autoload :Head, "fontisan/ufo/compile/head"
|
|
41
43
|
autoload :Hhea, "fontisan/ufo/compile/hhea"
|
data/lib/fontisan/ufo/font.rb
CHANGED
|
@@ -11,8 +11,9 @@ module Fontisan
|
|
|
11
11
|
# The class is the read/write API; serialization is handled by
|
|
12
12
|
# Fontisan::Ufo::Reader and Fontisan::Ufo::Writer.
|
|
13
13
|
class Font
|
|
14
|
-
attr_accessor :path, :info, :features, :kerning, :groups, :lib,
|
|
15
|
-
|
|
14
|
+
attr_accessor :path, :info, :features, :kerning, :groups, :lib,
|
|
15
|
+
:ufo_version, :images
|
|
16
|
+
attr_reader :layers, :data
|
|
16
17
|
|
|
17
18
|
def initialize
|
|
18
19
|
@path = nil
|
data/lib/fontisan/ufo/image.rb
CHANGED
|
@@ -3,13 +3,26 @@
|
|
|
3
3
|
module Fontisan
|
|
4
4
|
module Ufo
|
|
5
5
|
# A background image anchored to a glyph (common in color fonts).
|
|
6
|
+
#
|
|
7
|
+
# In UFO 3, images live in `UFO/images/` as binary blobs (PNG in
|
|
8
|
+
# practice). Each image is referenced from a glyph's GLIF via
|
|
9
|
+
# `<image fileName="..."/>`. The image set stores the actual bytes
|
|
10
|
+
# and deduplicates by sha256 content hash.
|
|
6
11
|
class Image
|
|
7
|
-
attr_reader :file_name, :transformation, :color
|
|
12
|
+
attr_reader :file_name, :transformation, :color, :sha, :bytes
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
# @param file_name [String] filename within the UFO images/ directory
|
|
15
|
+
# @param transformation [Hash, nil] UFO image transformation matrix
|
|
16
|
+
# @param color [String, nil] optional color hint
|
|
17
|
+
# @param sha [String, nil] content sha256 hex digest
|
|
18
|
+
# @param bytes [String, nil] raw image bytes (set by ImageSet)
|
|
19
|
+
def initialize(file_name:, transformation: nil, color: nil, sha: nil,
|
|
20
|
+
bytes: nil)
|
|
10
21
|
@file_name = file_name.to_s
|
|
11
22
|
@transformation = transformation
|
|
12
23
|
@color = color
|
|
24
|
+
@sha = sha
|
|
25
|
+
@bytes = bytes
|
|
13
26
|
end
|
|
14
27
|
end
|
|
15
28
|
end
|
|
@@ -1,19 +1,99 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
3
5
|
module Fontisan
|
|
4
6
|
module Ufo
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
+
# UFO 3 background image set. Lives at `UFO/images/` in a UFO
|
|
8
|
+
# source directory. Each image is a PNG file referenced by glyph
|
|
9
|
+
# GLIF `<image fileName="..."/>` elements.
|
|
10
|
+
#
|
|
11
|
+
# Per the UFO 3 spec, image files are arbitrary binary blobs (in
|
|
12
|
+
# practice always PNG). The image set deduplicates by content hash
|
|
13
|
+
# (sha256) so the same image referenced from multiple glyphs takes
|
|
14
|
+
# one slot.
|
|
15
|
+
#
|
|
16
|
+
# @example Read images from a UFO directory
|
|
17
|
+
# imageset = ImageSet.load_from_dir("MyFont.ufo/images")
|
|
18
|
+
# imageset.each { |img| puts img.file_name }
|
|
7
19
|
class ImageSet
|
|
20
|
+
include Enumerable
|
|
21
|
+
|
|
22
|
+
# @return [Hash{String=>Image}] filename → Image
|
|
8
23
|
attr_reader :images
|
|
9
24
|
|
|
10
25
|
def initialize
|
|
11
26
|
@images = {}
|
|
12
27
|
end
|
|
13
28
|
|
|
29
|
+
# Load all PNG files from a UFO `images/` directory.
|
|
30
|
+
#
|
|
31
|
+
# @param dir [String] path to the `images` directory
|
|
32
|
+
# @return [ImageSet]
|
|
33
|
+
def self.load_from_dir(dir)
|
|
34
|
+
is = new
|
|
35
|
+
return is unless File.directory?(dir)
|
|
36
|
+
|
|
37
|
+
Dir.each_child(dir).sort.each do |name|
|
|
38
|
+
path = File.join(dir, name)
|
|
39
|
+
next unless File.file?(path)
|
|
40
|
+
|
|
41
|
+
is.register_file(name, File.binread(path))
|
|
42
|
+
end
|
|
43
|
+
is
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Register an image from its filename and raw bytes. Computes the
|
|
47
|
+
# sha256 content hash automatically.
|
|
48
|
+
#
|
|
49
|
+
# @param file_name [String]
|
|
50
|
+
# @param bytes [String] binary image data
|
|
51
|
+
# @return [Image]
|
|
52
|
+
def register_file(file_name, bytes)
|
|
53
|
+
image = Image.new(
|
|
54
|
+
file_name: file_name,
|
|
55
|
+
sha: Digest::SHA256.hexdigest(bytes),
|
|
56
|
+
bytes: bytes,
|
|
57
|
+
)
|
|
58
|
+
@images[file_name.to_s] = image
|
|
59
|
+
image
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Look up an image by its filename.
|
|
63
|
+
#
|
|
64
|
+
# @param file_name [String]
|
|
65
|
+
# @return [Image, nil]
|
|
66
|
+
def find(file_name)
|
|
67
|
+
@images[file_name.to_s]
|
|
68
|
+
end
|
|
69
|
+
alias [] find
|
|
70
|
+
|
|
71
|
+
# Iterate over images in filename order.
|
|
72
|
+
def each(&)
|
|
73
|
+
return enum_for(:each) unless block_given?
|
|
74
|
+
|
|
75
|
+
@images.each_value(&)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Number of registered images.
|
|
79
|
+
def count
|
|
80
|
+
@images.size
|
|
81
|
+
end
|
|
82
|
+
|
|
14
83
|
def empty?
|
|
15
84
|
@images.empty?
|
|
16
85
|
end
|
|
86
|
+
|
|
87
|
+
# Write all images to a target `images/` directory. Useful for
|
|
88
|
+
# UFO round-trip serialization.
|
|
89
|
+
#
|
|
90
|
+
# @param dir [String] target `images` directory path
|
|
91
|
+
def write_to_dir(dir)
|
|
92
|
+
FileUtils.mkpath(dir) unless File.directory?(dir)
|
|
93
|
+
@images.each do |name, img|
|
|
94
|
+
File.binwrite(File.join(dir, name), img.bytes)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
17
97
|
end
|
|
18
98
|
end
|
|
19
99
|
end
|
data/lib/fontisan/ufo/reader.rb
CHANGED
|
@@ -35,6 +35,7 @@ module Fontisan
|
|
|
35
35
|
read_groups
|
|
36
36
|
read_features
|
|
37
37
|
read_lib
|
|
38
|
+
read_images
|
|
38
39
|
@font
|
|
39
40
|
end
|
|
40
41
|
|
|
@@ -147,6 +148,13 @@ module Fontisan
|
|
|
147
148
|
@font.lib = Lib.new(data)
|
|
148
149
|
end
|
|
149
150
|
|
|
151
|
+
def read_images
|
|
152
|
+
dir = join(@font.path, "images")
|
|
153
|
+
return unless File.directory?(dir)
|
|
154
|
+
|
|
155
|
+
@font.images = ImageSet.load_from_dir(dir)
|
|
156
|
+
end
|
|
157
|
+
|
|
150
158
|
def join(*parts)
|
|
151
159
|
File.join(*parts)
|
|
152
160
|
end
|
data/lib/fontisan/version.rb
CHANGED
data/lib/fontisan.rb
CHANGED
|
@@ -77,6 +77,7 @@ module Fontisan
|
|
|
77
77
|
|
|
78
78
|
# Namespace hubs (each hub declares its own child autoloads)
|
|
79
79
|
autoload :Binary, "fontisan/binary"
|
|
80
|
+
autoload :Audit, "fontisan/audit"
|
|
80
81
|
autoload :Collection, "fontisan/collection"
|
|
81
82
|
autoload :Commands, "fontisan/commands"
|
|
82
83
|
autoload :Converters, "fontisan/converters"
|
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.35
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -293,6 +293,15 @@ files:
|
|
|
293
293
|
- exe/fontisan
|
|
294
294
|
- fontisan.gemspec
|
|
295
295
|
- lib/fontisan.rb
|
|
296
|
+
- lib/fontisan/audit.rb
|
|
297
|
+
- lib/fontisan/audit/check.rb
|
|
298
|
+
- lib/fontisan/audit/check_registry.rb
|
|
299
|
+
- lib/fontisan/audit/checks.rb
|
|
300
|
+
- lib/fontisan/audit/checks/cmap_check.rb
|
|
301
|
+
- lib/fontisan/audit/checks/collection_integrity_check.rb
|
|
302
|
+
- lib/fontisan/audit/checks/glyph_name_check.rb
|
|
303
|
+
- lib/fontisan/audit/checks/ots_compatibility_check.rb
|
|
304
|
+
- lib/fontisan/audit/checks/table_directory_check.rb
|
|
296
305
|
- lib/fontisan/base_collection.rb
|
|
297
306
|
- lib/fontisan/binary.rb
|
|
298
307
|
- lib/fontisan/binary/base_record.rb
|
|
@@ -309,6 +318,7 @@ files:
|
|
|
309
318
|
- lib/fontisan/collection/table_deduplicator.rb
|
|
310
319
|
- lib/fontisan/collection/writer.rb
|
|
311
320
|
- lib/fontisan/commands.rb
|
|
321
|
+
- lib/fontisan/commands/audit_command.rb
|
|
312
322
|
- lib/fontisan/commands/base_command.rb
|
|
313
323
|
- lib/fontisan/commands/convert_command.rb
|
|
314
324
|
- lib/fontisan/commands/dump_table_command.rb
|
|
@@ -387,6 +397,7 @@ files:
|
|
|
387
397
|
- lib/fontisan/metrics_calculator.rb
|
|
388
398
|
- lib/fontisan/models.rb
|
|
389
399
|
- lib/fontisan/models/all_scripts_features_info.rb
|
|
400
|
+
- lib/fontisan/models/audit_report.rb
|
|
390
401
|
- lib/fontisan/models/bitmap_glyph.rb
|
|
391
402
|
- lib/fontisan/models/bitmap_strike.rb
|
|
392
403
|
- lib/fontisan/models/collection_brief_info.rb
|
|
@@ -649,6 +660,7 @@ files:
|
|
|
649
660
|
- lib/fontisan/ufo/compile/cmap.rb
|
|
650
661
|
- lib/fontisan/ufo/compile/colr.rb
|
|
651
662
|
- lib/fontisan/ufo/compile/cpal.rb
|
|
663
|
+
- lib/fontisan/ufo/compile/feature_compiler.rb
|
|
652
664
|
- lib/fontisan/ufo/compile/feature_writers.rb
|
|
653
665
|
- lib/fontisan/ufo/compile/feature_writers/base.rb
|
|
654
666
|
- lib/fontisan/ufo/compile/feature_writers/curs.rb
|
|
@@ -670,6 +682,7 @@ files:
|
|
|
670
682
|
- lib/fontisan/ufo/compile/fvar.rb
|
|
671
683
|
- lib/fontisan/ufo/compile/glyf_loca.rb
|
|
672
684
|
- lib/fontisan/ufo/compile/gpos.rb
|
|
685
|
+
- lib/fontisan/ufo/compile/gsub.rb
|
|
673
686
|
- lib/fontisan/ufo/compile/gvar.rb
|
|
674
687
|
- lib/fontisan/ufo/compile/head.rb
|
|
675
688
|
- lib/fontisan/ufo/compile/hhea.rb
|