fontisan 0.4.43 → 0.4.44
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/TODO.bug-fixes/12-type1-generators-duck-typing.md +80 -0
- data/TODO.bug-fixes/13-hint-extractor-duck-typing.md +30 -0
- data/TODO.bug-fixes/14-font-interface-type-checks.md +82 -0
- data/TODO.bug-fixes/15-glyph-type-checks-and-simple-glyph-points-bug.md +49 -0
- data/TODO.bug-fixes/16-variation-table-checks.md +31 -0
- data/TODO.bug-fixes/17-ufo-and-stitcher-cleanup.md +32 -0
- data/TODO.bug-fixes/18-generic-value-and-io-checks.md +56 -0
- data/TODO.bug-fixes/19-serialization-migration.md +52 -0
- data/TODO.bug-fixes/20-variation-validator-type-checks.md +29 -0
- data/TODO.bug-fixes/21-xml-builder-send.md +24 -0
- data/TODO.bug-fixes/22-type1-converter-font-info.md +25 -0
- data/TODO.bug-fixes/23-boundary-type-checks.md +31 -0
- data/TODO.bug-fixes/24-validator-dynamic-field-key.md +25 -0
- data/TODO.bug-fixes/25-serialization-migration.md +49 -0
- data/TODO.bug-fixes/26-variation-preserver-spec-migration.md +25 -0
- data/TODO.bug-fixes/27-table-class-registry.md +50 -0
- data/TODO.bug-fixes/28-spec-doubles-cleanup.md +40 -0
- data/TODO.bug-fixes/29-strategy-registries.md +30 -0
- data/TODO.bug-fixes/README.md +24 -0
- data/lib/fontisan/binary/base_record.rb +13 -12
- data/lib/fontisan/collection/builder.rb +3 -5
- data/lib/fontisan/collection/dfont_builder.rb +4 -11
- data/lib/fontisan/collection/writer.rb +1 -1
- data/lib/fontisan/commands/pack_command.rb +4 -4
- data/lib/fontisan/commands/unpack_command.rb +1 -1
- data/lib/fontisan/commands/validate_command.rb +1 -1
- data/lib/fontisan/converters/format_converter.rb +2 -2
- data/lib/fontisan/converters/glyf_table_builder.rb +1 -1
- data/lib/fontisan/converters/outline_converter.rb +2 -6
- data/lib/fontisan/converters/table_copier.rb +4 -12
- data/lib/fontisan/converters/type1_converter.rb +12 -37
- data/lib/fontisan/converters/woff2_encoder.rb +8 -10
- data/lib/fontisan/converters/woff_writer.rb +3 -3
- data/lib/fontisan/export/exporter.rb +8 -9
- data/lib/fontisan/export/table_serializer.rb +71 -186
- data/lib/fontisan/export/transformers/font_to_ttx.rb +18 -18
- data/lib/fontisan/export/transformers/head_transformer.rb +1 -1
- data/lib/fontisan/export/transformers/hhea_transformer.rb +1 -1
- data/lib/fontisan/export/transformers/maxp_transformer.rb +1 -1
- data/lib/fontisan/export/transformers/name_transformer.rb +1 -1
- data/lib/fontisan/export/transformers/os2_transformer.rb +1 -1
- data/lib/fontisan/export/transformers/post_transformer.rb +1 -1
- data/lib/fontisan/export/ttx_generator.rb +36 -37
- data/lib/fontisan/font_writer.rb +1 -1
- data/lib/fontisan/glyph_accessor.rb +7 -9
- data/lib/fontisan/hints/postscript_hint_extractor.rb +24 -64
- data/lib/fontisan/hints/truetype_hint_extractor.rb +1 -1
- data/lib/fontisan/models/ttx/tables/binary_table.rb +1 -1
- data/lib/fontisan/optimizers/charstring_rewriter.rb +1 -1
- data/lib/fontisan/sfnt_font.rb +2 -0
- data/lib/fontisan/sfnt_source.rb +28 -0
- data/lib/fontisan/stitcher/source.rb +8 -10
- data/lib/fontisan/subset/builder.rb +2 -2
- data/lib/fontisan/subset/table_strategy/cff2.rb +2 -3
- data/lib/fontisan/svg_to_glyf/assembler.rb +59 -20
- data/lib/fontisan/svg_to_glyf/document.rb +28 -9
- data/lib/fontisan/svg_to_glyf/geometry/normalizer.rb +34 -17
- data/lib/fontisan/tables/cff/charstring.rb +3 -0
- data/lib/fontisan/tables/cff/dict.rb +6 -3
- data/lib/fontisan/tables/cff/index.rb +1 -1
- data/lib/fontisan/tables/cff/private_dict_writer.rb +2 -2
- data/lib/fontisan/tables/cff/table_builder.rb +1 -1
- data/lib/fontisan/tables/cff.rb +3 -0
- data/lib/fontisan/tables/cff2/table_builder.rb +2 -4
- data/lib/fontisan/tables/cff2.rb +3 -0
- data/lib/fontisan/tables/cmap.rb +3 -0
- data/lib/fontisan/tables/cvar.rb +3 -0
- data/lib/fontisan/tables/fvar.rb +3 -0
- data/lib/fontisan/tables/glyf/simple_glyph.rb +9 -0
- data/lib/fontisan/tables/glyf.rb +12 -8
- data/lib/fontisan/tables/glyf_table.rb +1 -1
- data/lib/fontisan/tables/gvar.rb +3 -0
- data/lib/fontisan/tables/head.rb +3 -0
- data/lib/fontisan/tables/hhea.rb +3 -0
- data/lib/fontisan/tables/hmtx.rb +3 -7
- data/lib/fontisan/tables/hvar.rb +4 -0
- data/lib/fontisan/tables/loca.rb +3 -0
- data/lib/fontisan/tables/maxp.rb +3 -0
- data/lib/fontisan/tables/mvar.rb +3 -0
- data/lib/fontisan/tables/name.rb +3 -0
- data/lib/fontisan/tables/os2.rb +3 -0
- data/lib/fontisan/tables/post.rb +3 -0
- data/lib/fontisan/tables/registry.rb +71 -0
- data/lib/fontisan/tables/vvar.rb +3 -0
- data/lib/fontisan/tables.rb +2 -0
- data/lib/fontisan/type1/afm_generator.rb +12 -65
- data/lib/fontisan/type1/charstring_converter.rb +2 -2
- data/lib/fontisan/type1/conversion_options.rb +3 -2
- data/lib/fontisan/type1/decryptor.rb +1 -1
- data/lib/fontisan/type1/generator.rb +14 -9
- data/lib/fontisan/type1/inf_generator.rb +10 -35
- data/lib/fontisan/type1/pfa_generator.rb +36 -26
- data/lib/fontisan/type1/pfb_generator.rb +24 -12
- data/lib/fontisan/type1/pfm_generator.rb +14 -31
- data/lib/fontisan/type1/ttf_to_type1_converter.rb +30 -24
- data/lib/fontisan/ufo/bounds.rb +91 -0
- data/lib/fontisan/ufo/compile/fvar.rb +2 -2
- data/lib/fontisan/ufo/contour.rb +5 -0
- data/lib/fontisan/ufo/convert/from_bin_data.rb +15 -29
- data/lib/fontisan/ufo/info.rb +4 -0
- data/lib/fontisan/ufo/point.rb +0 -7
- data/lib/fontisan/ufo.rb +1 -0
- data/lib/fontisan/utilities/brotli_wrapper.rb +3 -5
- data/lib/fontisan/utilities/padding.rb +9 -8
- data/lib/fontisan/validation/collection_validator.rb +2 -2
- data/lib/fontisan/validators/validator.rb +4 -4
- data/lib/fontisan/variable/delta_applicator.rb +4 -11
- data/lib/fontisan/variable/metric_delta_processor.rb +3 -3
- data/lib/fontisan/variable/static_font_builder.rb +19 -23
- data/lib/fontisan/variation/instance_font_wrapper.rb +41 -0
- data/lib/fontisan/variation/instance_writer.rb +19 -17
- data/lib/fontisan/variation/metrics_adjuster.rb +17 -20
- data/lib/fontisan/variation/validator.rb +26 -38
- data/lib/fontisan/variation/variable_svg_generator.rb +0 -48
- data/lib/fontisan/variation/variation_preserver.rb +2 -3
- data/lib/fontisan/variation.rb +1 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/sfnt_checksum.rb +1 -1
- data/lib/fontisan/woff2/table_transformer.rb +22 -29
- data/lib/fontisan/woff2_font.rb +9 -8
- data/lib/fontisan/woff_font.rb +2 -0
- data/lib/fontisan.rb +1 -0
- metadata +24 -2
data/lib/fontisan/tables/fvar.rb
CHANGED
|
@@ -155,6 +155,15 @@ module Fontisan
|
|
|
155
155
|
end
|
|
156
156
|
end
|
|
157
157
|
|
|
158
|
+
# All points across every contour, in contour order.
|
|
159
|
+
#
|
|
160
|
+
# @return [Array<Hash>] Array of {x:, y:, on_curve:} hashes
|
|
161
|
+
def points
|
|
162
|
+
return [] if empty?
|
|
163
|
+
|
|
164
|
+
num_contours.times.flat_map { |c| points_for_contour(c) }
|
|
165
|
+
end
|
|
166
|
+
|
|
158
167
|
private
|
|
159
168
|
|
|
160
169
|
# Parse glyph header (10 bytes)
|
data/lib/fontisan/tables/glyf.rb
CHANGED
|
@@ -38,6 +38,9 @@ module Fontisan
|
|
|
38
38
|
# puts glyph.simple? ? "Simple glyph" : "Compound glyph"
|
|
39
39
|
# puts glyph.bounding_box # => [xMin, yMin, xMax, yMax]
|
|
40
40
|
class Glyf < Binary::BaseRecord
|
|
41
|
+
extend Registered
|
|
42
|
+
|
|
43
|
+
register_tag "glyf"
|
|
41
44
|
# Store the raw data for deferred parsing
|
|
42
45
|
attr_accessor :raw_data
|
|
43
46
|
|
|
@@ -215,13 +218,13 @@ module Fontisan
|
|
|
215
218
|
next true if glyph.nil? # Empty glyphs are OK
|
|
216
219
|
|
|
217
220
|
# Simple glyphs have instructions
|
|
218
|
-
|
|
221
|
+
if glyph.nil?
|
|
222
|
+
# Compound glyphs may have instructions too
|
|
223
|
+
true
|
|
224
|
+
else
|
|
219
225
|
inst_len = glyph.instruction_length
|
|
220
226
|
# If instructions present, length should be reasonable
|
|
221
227
|
inst_len.nil? || inst_len >= 0
|
|
222
|
-
else
|
|
223
|
-
# Compound glyphs may have instructions too
|
|
224
|
-
true
|
|
225
228
|
end
|
|
226
229
|
end
|
|
227
230
|
rescue StandardError
|
|
@@ -240,7 +243,8 @@ module Fontisan
|
|
|
240
243
|
|
|
241
244
|
# Simple glyphs: contours should be >= 0
|
|
242
245
|
# Compound glyphs: numberOfContours = -1
|
|
243
|
-
|
|
246
|
+
case glyph
|
|
247
|
+
when SimpleGlyph, CompoundGlyph
|
|
244
248
|
glyph.num_contours >= -1
|
|
245
249
|
else
|
|
246
250
|
true
|
|
@@ -277,9 +281,9 @@ module Fontisan
|
|
|
277
281
|
# @param glyph_id [Integer] Glyph ID
|
|
278
282
|
# @raise [ArgumentError] If validation fails
|
|
279
283
|
def validate_context!(loca, head, glyph_id)
|
|
280
|
-
unless loca.
|
|
284
|
+
unless loca.is_a?(Loca)
|
|
281
285
|
raise ArgumentError,
|
|
282
|
-
"loca must be a parsed Loca table
|
|
286
|
+
"loca must be a parsed Loca table"
|
|
283
287
|
end
|
|
284
288
|
|
|
285
289
|
unless loca.parsed?
|
|
@@ -287,7 +291,7 @@ module Fontisan
|
|
|
287
291
|
"loca table must be parsed with parse_with_context before use"
|
|
288
292
|
end
|
|
289
293
|
|
|
290
|
-
unless head.
|
|
294
|
+
unless head.is_a?(Head)
|
|
291
295
|
raise ArgumentError,
|
|
292
296
|
"head must be a parsed Head table"
|
|
293
297
|
end
|
data/lib/fontisan/tables/gvar.rb
CHANGED
|
@@ -20,6 +20,9 @@ module Fontisan
|
|
|
20
20
|
# gvar = Fontisan::Tables::Gvar.read(data)
|
|
21
21
|
# deltas = gvar.glyph_variations(glyph_id)
|
|
22
22
|
class Gvar < Binary::BaseRecord
|
|
23
|
+
extend Registered
|
|
24
|
+
|
|
25
|
+
register_tag "gvar"
|
|
23
26
|
uint16 :major_version
|
|
24
27
|
uint16 :minor_version
|
|
25
28
|
uint16 :axis_count
|
data/lib/fontisan/tables/head.rb
CHANGED
|
@@ -15,6 +15,9 @@ module Fontisan
|
|
|
15
15
|
# puts head.units_per_em # => 2048
|
|
16
16
|
# puts head.version_number # => 1.0
|
|
17
17
|
class Head < Binary::BaseRecord
|
|
18
|
+
extend Registered
|
|
19
|
+
|
|
20
|
+
register_tag "head"
|
|
18
21
|
# Magic number that must be present in the head table
|
|
19
22
|
MAGIC_NUMBER = 0x5F0F3CF5
|
|
20
23
|
|
data/lib/fontisan/tables/hhea.rb
CHANGED
data/lib/fontisan/tables/hmtx.rb
CHANGED
|
@@ -38,6 +38,9 @@ module Fontisan
|
|
|
38
38
|
# puts "Advance width: #{metric[:advance_width]}"
|
|
39
39
|
# puts "LSB: #{metric[:lsb]}"
|
|
40
40
|
class Hmtx < Binary::BaseRecord
|
|
41
|
+
extend Registered
|
|
42
|
+
|
|
43
|
+
register_tag "hmtx"
|
|
41
44
|
# LongHorMetric record structure
|
|
42
45
|
#
|
|
43
46
|
# @!attribute advance_width
|
|
@@ -47,13 +50,6 @@ module Fontisan
|
|
|
47
50
|
class LongHorMetric < Binary::BaseRecord
|
|
48
51
|
uint16 :advance_width
|
|
49
52
|
int16 :lsb
|
|
50
|
-
|
|
51
|
-
# Convert to hash for convenience
|
|
52
|
-
#
|
|
53
|
-
# @return [Hash] Hash with :advance_width and :lsb keys
|
|
54
|
-
def to_h
|
|
55
|
-
{ advance_width: advance_width, lsb: lsb }
|
|
56
|
-
end
|
|
57
53
|
end
|
|
58
54
|
|
|
59
55
|
# Store the raw data for deferred parsing
|
data/lib/fontisan/tables/hvar.rb
CHANGED
|
@@ -18,6 +18,10 @@ module Fontisan
|
|
|
18
18
|
# hvar = Fontisan::Tables::Hvar.read(data)
|
|
19
19
|
# advance_deltas = hvar.advance_width_deltas(glyph_id, coordinates)
|
|
20
20
|
class Hvar < Binary::BaseRecord
|
|
21
|
+
extend Registered
|
|
22
|
+
|
|
23
|
+
register_tag "HVAR"
|
|
24
|
+
|
|
21
25
|
uint16 :major_version
|
|
22
26
|
uint16 :minor_version
|
|
23
27
|
uint32 :item_variation_store_offset
|
data/lib/fontisan/tables/loca.rb
CHANGED
data/lib/fontisan/tables/maxp.rb
CHANGED
data/lib/fontisan/tables/mvar.rb
CHANGED
|
@@ -21,6 +21,9 @@ module Fontisan
|
|
|
21
21
|
# mvar = Fontisan::Tables::Mvar.read(data)
|
|
22
22
|
# hasc_deltas = mvar.metric_deltas("hasc")
|
|
23
23
|
class Mvar < Binary::BaseRecord
|
|
24
|
+
extend Registered
|
|
25
|
+
|
|
26
|
+
register_tag "MVAR"
|
|
24
27
|
uint16 :major_version
|
|
25
28
|
uint16 :minor_version
|
|
26
29
|
uint16 :reserved
|
data/lib/fontisan/tables/name.rb
CHANGED
|
@@ -64,6 +64,9 @@ module Fontisan
|
|
|
64
64
|
# name = Fontisan::Tables::Name.read(data)
|
|
65
65
|
# puts name.english_name(Fontisan::Tables::Name::FAMILY)
|
|
66
66
|
class Name < Binary::BaseRecord
|
|
67
|
+
extend Registered
|
|
68
|
+
|
|
69
|
+
register_tag "name"
|
|
67
70
|
# Name ID constants for common name records
|
|
68
71
|
COPYRIGHT = 0
|
|
69
72
|
FAMILY = 1
|
data/lib/fontisan/tables/os2.rb
CHANGED
data/lib/fontisan/tables/post.rb
CHANGED
|
@@ -10,6 +10,9 @@ module Fontisan
|
|
|
10
10
|
#
|
|
11
11
|
# Reference: OpenType specification, post table
|
|
12
12
|
class Post < Binary::BaseRecord
|
|
13
|
+
extend Registered
|
|
14
|
+
|
|
15
|
+
register_tag "post"
|
|
13
16
|
# Standard Mac glyph names for version 1.0 (258 glyphs)
|
|
14
17
|
# rubocop:disable Metrics/CollectionLiteralLength
|
|
15
18
|
STANDARD_NAMES = %w[
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fontisan
|
|
4
|
+
module Tables
|
|
5
|
+
# Single source of truth mapping OpenType table tags to Ruby classes.
|
|
6
|
+
#
|
|
7
|
+
# Each table class registers itself via `register_tag`:
|
|
8
|
+
#
|
|
9
|
+
# module Fontisan::Tables
|
|
10
|
+
# class Hvar < Binary::BaseRecord
|
|
11
|
+
# register_tag "HVAR"
|
|
12
|
+
# ...
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# Dispatch sites use `Tables::Registry.for(tag)` instead of a
|
|
17
|
+
# `case tag when "HVAR"` switch. This is OCP-compliant: adding a
|
|
18
|
+
# new table means adding one `register_tag` line in the table's
|
|
19
|
+
# file, not editing every dispatch site.
|
|
20
|
+
module Registry
|
|
21
|
+
@by_tag = {}
|
|
22
|
+
@by_class = {}
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
# @api internal Called by table classes at load time.
|
|
26
|
+
# @param tag [String] Four-byte OpenType table tag (e.g., "HVAR").
|
|
27
|
+
# @param klass [Class] The Ruby class implementing the table.
|
|
28
|
+
def register(tag, klass)
|
|
29
|
+
@by_tag[tag] = klass
|
|
30
|
+
@by_class[klass] = tag
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Look up the table class for a tag.
|
|
34
|
+
#
|
|
35
|
+
# @param tag [String] Four-byte OpenType table tag.
|
|
36
|
+
# @return [Class, nil] The table class, or nil if unknown.
|
|
37
|
+
def for(tag)
|
|
38
|
+
@by_tag[tag]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Reverse lookup: tag for a given class.
|
|
42
|
+
#
|
|
43
|
+
# @param klass [Class] A table class.
|
|
44
|
+
# @return [String, nil] The tag, or nil if not registered.
|
|
45
|
+
def tag_for(klass)
|
|
46
|
+
@by_class[klass]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [Array<String>] All registered tags.
|
|
50
|
+
def tags
|
|
51
|
+
@by_tag.keys
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Clear all registrations (testing only).
|
|
55
|
+
def __clear__
|
|
56
|
+
@by_tag.clear
|
|
57
|
+
@by_class.clear
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Mixin applied to table classes that register themselves with the
|
|
63
|
+
# Registry at load time. Used via `register_tag "HVAR"` in the
|
|
64
|
+
# class body.
|
|
65
|
+
module Registered
|
|
66
|
+
def register_tag(tag)
|
|
67
|
+
Tables::Registry.register(tag, self)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/fontisan/tables/vvar.rb
CHANGED
|
@@ -18,6 +18,9 @@ module Fontisan
|
|
|
18
18
|
# vvar = Fontisan::Tables::Vvar.read(data)
|
|
19
19
|
# advance_deltas = vvar.advance_height_deltas(glyph_id, coordinates)
|
|
20
20
|
class Vvar < Binary::BaseRecord
|
|
21
|
+
extend Registered
|
|
22
|
+
|
|
23
|
+
register_tag "VVAR"
|
|
21
24
|
uint16 :major_version
|
|
22
25
|
uint16 :minor_version
|
|
23
26
|
uint32 :item_variation_store_offset
|
data/lib/fontisan/tables.rb
CHANGED
|
@@ -57,6 +57,8 @@ module Fontisan
|
|
|
57
57
|
autoload :Os2Table, "fontisan/tables/os2_table"
|
|
58
58
|
autoload :Post, "fontisan/tables/post"
|
|
59
59
|
autoload :PostTable, "fontisan/tables/post_table"
|
|
60
|
+
autoload :Registered, "fontisan/tables/registry"
|
|
61
|
+
autoload :Registry, "fontisan/tables/registry"
|
|
60
62
|
autoload :Sbix, "fontisan/tables/sbix"
|
|
61
63
|
autoload :SimpleGlyph, "fontisan/tables/glyf/simple_glyph"
|
|
62
64
|
autoload :Svg, "fontisan/tables/svg"
|
|
@@ -167,10 +167,10 @@ module Fontisan
|
|
|
167
167
|
|
|
168
168
|
# Underline properties
|
|
169
169
|
post = @font.table(Constants::POST_TAG)
|
|
170
|
-
underline_position = post&.underline_position
|
|
170
|
+
underline_position = post&.underline_position
|
|
171
171
|
afm_lines << "UnderlinePosition #{underline_position}" if underline_position
|
|
172
172
|
|
|
173
|
-
underline_thickness = post&.underline_thickness
|
|
173
|
+
underline_thickness = post&.underline_thickness
|
|
174
174
|
afm_lines << "UnderlineThickness #{underline_thickness}" if underline_thickness
|
|
175
175
|
end
|
|
176
176
|
|
|
@@ -252,11 +252,7 @@ module Fontisan
|
|
|
252
252
|
os2 = @font.table(Constants::OS2_TAG)
|
|
253
253
|
return "Regular" unless os2
|
|
254
254
|
|
|
255
|
-
weight_class =
|
|
256
|
-
os2.us_weight_class
|
|
257
|
-
elsif os2.respond_to?(:weight_class)
|
|
258
|
-
os2.weight_class
|
|
259
|
-
end
|
|
255
|
+
weight_class = os2.us_weight_class
|
|
260
256
|
return "Regular" unless weight_class
|
|
261
257
|
|
|
262
258
|
case weight_class
|
|
@@ -280,11 +276,7 @@ module Fontisan
|
|
|
280
276
|
post = @font.table(Constants::POST_TAG)
|
|
281
277
|
return 0.0 unless post
|
|
282
278
|
|
|
283
|
-
|
|
284
|
-
post.italic_angle
|
|
285
|
-
else
|
|
286
|
-
0.0
|
|
287
|
-
end
|
|
279
|
+
post.italic_angle
|
|
288
280
|
end
|
|
289
281
|
|
|
290
282
|
# Check if font is monospace
|
|
@@ -294,11 +286,7 @@ module Fontisan
|
|
|
294
286
|
post = @font.table(Constants::POST_TAG)
|
|
295
287
|
return false unless post
|
|
296
288
|
|
|
297
|
-
|
|
298
|
-
post.is_fixed_pitch
|
|
299
|
-
else
|
|
300
|
-
false
|
|
301
|
-
end
|
|
289
|
+
post.is_fixed_pitch
|
|
302
290
|
end
|
|
303
291
|
|
|
304
292
|
# Extract version from name table
|
|
@@ -308,9 +296,7 @@ module Fontisan
|
|
|
308
296
|
name_table = @font.table(Constants::NAME_TAG)
|
|
309
297
|
return nil unless name_table
|
|
310
298
|
|
|
311
|
-
|
|
312
|
-
name_table.version_string(1) || name_table.version_string(3)
|
|
313
|
-
end
|
|
299
|
+
name_table.english_name(Tables::Name::VERSION)
|
|
314
300
|
end
|
|
315
301
|
|
|
316
302
|
# Extract copyright from name table
|
|
@@ -320,9 +306,7 @@ module Fontisan
|
|
|
320
306
|
name_table = @font.table(Constants::NAME_TAG)
|
|
321
307
|
return nil unless name_table
|
|
322
308
|
|
|
323
|
-
|
|
324
|
-
name_table.copyright(1) || name_table.copyright(3)
|
|
325
|
-
end
|
|
309
|
+
name_table.english_name(Tables::Name::COPYRIGHT)
|
|
326
310
|
end
|
|
327
311
|
|
|
328
312
|
# Extract character mappings from cmap table
|
|
@@ -332,30 +316,7 @@ module Fontisan
|
|
|
332
316
|
cmap = @font.table(Constants::CMAP_TAG)
|
|
333
317
|
return {} unless cmap
|
|
334
318
|
|
|
335
|
-
@extract_character_mappings ||=
|
|
336
|
-
mappings = {}
|
|
337
|
-
|
|
338
|
-
# Try to get Unicode mappings (most reliable method)
|
|
339
|
-
if cmap.respond_to?(:unicode_mappings)
|
|
340
|
-
mappings = cmap.unicode_mappings || {}
|
|
341
|
-
elsif cmap.respond_to?(:unicode_bmp_mapping)
|
|
342
|
-
mappings = cmap.unicode_bmp_mapping || {}
|
|
343
|
-
elsif cmap.respond_to?(:subtables)
|
|
344
|
-
# Look for Unicode BMP subtable
|
|
345
|
-
unicode_subtable = cmap.subtables.find do |subtable|
|
|
346
|
-
subtable.respond_to?(:platform_id) &&
|
|
347
|
-
subtable.platform_id == 3 &&
|
|
348
|
-
subtable.respond_to?(:encoding_id) &&
|
|
349
|
-
subtable.encoding_id == 1
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
if unicode_subtable.respond_to?(:glyph_index_map)
|
|
353
|
-
mappings = unicode_subtable.glyph_index_map
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
mappings
|
|
358
|
-
end
|
|
319
|
+
@extract_character_mappings ||= cmap.unicode_mappings || {}
|
|
359
320
|
end
|
|
360
321
|
|
|
361
322
|
# Extract font bounding box
|
|
@@ -365,12 +326,7 @@ module Fontisan
|
|
|
365
326
|
head = @font.table(Constants::HEAD_TAG)
|
|
366
327
|
return nil unless head
|
|
367
328
|
|
|
368
|
-
|
|
369
|
-
head.font_bounding_box
|
|
370
|
-
elsif head.respond_to?(:x_min) && head.respond_to?(:y_min) &&
|
|
371
|
-
head.respond_to?(:x_max) && head.respond_to?(:y_max)
|
|
372
|
-
[head.x_min, head.y_min, head.x_max, head.y_max]
|
|
373
|
-
end
|
|
329
|
+
[head.x_min, head.y_min, head.x_max, head.y_max]
|
|
374
330
|
end
|
|
375
331
|
|
|
376
332
|
# Extract glyph bounding box
|
|
@@ -390,7 +346,7 @@ module Fontisan
|
|
|
390
346
|
return nil unless head_table
|
|
391
347
|
|
|
392
348
|
# Ensure loca is parsed with context
|
|
393
|
-
if loca_table.
|
|
349
|
+
if loca_table.is_a?(Tables::Loca) && !loca_table.parsed?
|
|
394
350
|
maxp = @font.table(Constants::MAXP_TAG)
|
|
395
351
|
if maxp
|
|
396
352
|
loca_table.parse_with_context(head_table.index_to_loc_format,
|
|
@@ -398,17 +354,8 @@ module Fontisan
|
|
|
398
354
|
end
|
|
399
355
|
end
|
|
400
356
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
return nil unless glyph
|
|
404
|
-
|
|
405
|
-
if glyph.respond_to?(:bounding_box)
|
|
406
|
-
glyph.bounding_box
|
|
407
|
-
elsif glyph.respond_to?(:x_min) && glyph.respond_to?(:y_min) &&
|
|
408
|
-
glyph.respond_to?(:x_max) && glyph.respond_to?(:y_max)
|
|
409
|
-
[glyph.x_min, glyph.y_min, glyph.x_max, glyph.y_max]
|
|
410
|
-
end
|
|
411
|
-
end
|
|
357
|
+
glyph = glyf_table.glyph_for(glyph_id, loca_table, head_table)
|
|
358
|
+
glyph&.bounding_box
|
|
412
359
|
end
|
|
413
360
|
|
|
414
361
|
# Extract kerning pairs from GPOS table
|
|
@@ -151,10 +151,10 @@ module Fontisan
|
|
|
151
151
|
return base_cff unless accent_cff
|
|
152
152
|
|
|
153
153
|
io = String.new(encoding: Encoding::ASCII_8BIT)
|
|
154
|
-
io << base_cff.
|
|
154
|
+
io << base_cff.delete_suffix("\u000E")
|
|
155
155
|
io << encode_cff_number(seac_data[:adx].to_i) if seac_data[:adx].to_i != 0
|
|
156
156
|
io << encode_cff_number(seac_data[:ady].to_i) if seac_data[:ady].to_i != 0
|
|
157
|
-
io << accent_cff.
|
|
157
|
+
io << accent_cff.delete_suffix("\u000E")
|
|
158
158
|
io << encode_cff_operator(TYPE1_TO_CFF[:endchar])
|
|
159
159
|
rescue StandardError
|
|
160
160
|
encode_cff_operator(TYPE1_TO_CFF[:endchar])
|
|
@@ -99,10 +99,11 @@ module Fontisan
|
|
|
99
99
|
@autohint
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
-
#
|
|
102
|
+
# Expose the options as a Hash. Internal accessor for code that
|
|
103
|
+
# passes options as a plain Hash (e.g., to legacy generators).
|
|
103
104
|
#
|
|
104
105
|
# @return [Hash] Options as hash
|
|
105
|
-
def
|
|
106
|
+
def options_hash
|
|
106
107
|
{
|
|
107
108
|
upm_scale: @upm_scale,
|
|
108
109
|
encoding: @encoding,
|
|
@@ -139,32 +139,37 @@ module Fontisan
|
|
|
139
139
|
def generate
|
|
140
140
|
result = {}
|
|
141
141
|
|
|
142
|
+
opts = options_hash
|
|
143
|
+
|
|
142
144
|
# Always generate AFM
|
|
143
|
-
result[:afm] = AFMGenerator.generate(@font,
|
|
145
|
+
result[:afm] = AFMGenerator.generate(@font, opts)
|
|
144
146
|
|
|
145
147
|
# Always generate PFM (for Windows compatibility)
|
|
146
|
-
result[:pfm] = PFMGenerator.generate(@font,
|
|
148
|
+
result[:pfm] = PFMGenerator.generate(@font, opts)
|
|
147
149
|
|
|
148
150
|
# Generate PFB or PFA based on format option
|
|
149
151
|
format = @options.format || :pfb
|
|
150
152
|
if format == :pfa
|
|
151
|
-
result[:pfa] = PFAGenerator.generate(@font,
|
|
153
|
+
result[:pfa] = PFAGenerator.generate(@font, opts)
|
|
152
154
|
else
|
|
153
|
-
result[:pfb] = PFBGenerator.generate(@font,
|
|
155
|
+
result[:pfb] = PFBGenerator.generate(@font, opts)
|
|
154
156
|
end
|
|
155
157
|
|
|
156
158
|
# Generate INF for Windows installation
|
|
157
|
-
result[:inf] = INFGenerator.generate(@font,
|
|
159
|
+
result[:inf] = INFGenerator.generate(@font, opts)
|
|
158
160
|
|
|
159
161
|
result
|
|
160
162
|
end
|
|
161
163
|
|
|
162
164
|
private
|
|
163
165
|
|
|
164
|
-
#
|
|
166
|
+
# Build the options hash forwarded to the per-format generators.
|
|
167
|
+
# This is an internal adapter — ConversionOptions exposes typed
|
|
168
|
+
# accessors; the per-format generators accept a Hash for historical
|
|
169
|
+
# reasons.
|
|
165
170
|
#
|
|
166
171
|
# @return [Hash] Options as hash
|
|
167
|
-
def
|
|
172
|
+
def options_hash
|
|
168
173
|
{
|
|
169
174
|
upm_scale: @options.upm_scale || 1000,
|
|
170
175
|
encoding: @options.encoding || Encodings::AdobeStandard,
|
|
@@ -200,8 +205,8 @@ module Fontisan
|
|
|
200
205
|
# @return [String] Base filename
|
|
201
206
|
def self.extract_base_name(font)
|
|
202
207
|
name_table = font.table(Constants::NAME_TAG)
|
|
203
|
-
if name_table
|
|
204
|
-
name = name_table.
|
|
208
|
+
if name_table
|
|
209
|
+
name = name_table.english_name(Tables::Name::POSTSCRIPT_NAME)
|
|
205
210
|
return name if name
|
|
206
211
|
end
|
|
207
212
|
|
|
@@ -181,15 +181,10 @@ module Fontisan
|
|
|
181
181
|
# @return [String] Font name
|
|
182
182
|
def extract_font_name
|
|
183
183
|
name_table = @font.table(Constants::NAME_TAG)
|
|
184
|
-
return
|
|
184
|
+
return extract_postscript_name unless name_table
|
|
185
185
|
|
|
186
|
-
|
|
187
|
-
if name_table.respond_to?(:full_font_name)
|
|
188
|
-
name_table.full_font_name(1) || name_table.full_font_name(3) ||
|
|
189
|
-
extract_postscript_name
|
|
190
|
-
else
|
|
186
|
+
name_table.english_name(Tables::Name::FULL_NAME) ||
|
|
191
187
|
extract_postscript_name
|
|
192
|
-
end
|
|
193
188
|
end
|
|
194
189
|
|
|
195
190
|
# Extract PostScript name
|
|
@@ -199,12 +194,8 @@ module Fontisan
|
|
|
199
194
|
name_table = @font.table(Constants::NAME_TAG)
|
|
200
195
|
return @font.post_script_name || "Unknown" unless name_table
|
|
201
196
|
|
|
202
|
-
|
|
203
|
-
name_table.postscript_name(1) || name_table.postscript_name(3) ||
|
|
204
|
-
@font.post_script_name || "Unknown"
|
|
205
|
-
else
|
|
197
|
+
name_table.english_name(Tables::Name::POSTSCRIPT_NAME) ||
|
|
206
198
|
@font.post_script_name || "Unknown"
|
|
207
|
-
end
|
|
208
199
|
end
|
|
209
200
|
|
|
210
201
|
# Extract family name
|
|
@@ -214,9 +205,7 @@ module Fontisan
|
|
|
214
205
|
name_table = @font.table(Constants::NAME_TAG)
|
|
215
206
|
return nil unless name_table
|
|
216
207
|
|
|
217
|
-
|
|
218
|
-
name_table.font_family(1) || name_table.font_family(3)
|
|
219
|
-
end
|
|
208
|
+
name_table.english_name(Tables::Name::FAMILY)
|
|
220
209
|
end
|
|
221
210
|
|
|
222
211
|
# Extract weight
|
|
@@ -226,11 +215,7 @@ module Fontisan
|
|
|
226
215
|
os2 = @font.table(Constants::OS2_TAG)
|
|
227
216
|
return nil unless os2
|
|
228
217
|
|
|
229
|
-
weight_class =
|
|
230
|
-
os2.us_weight_class
|
|
231
|
-
elsif os2.respond_to?(:weight_class)
|
|
232
|
-
os2.weight_class
|
|
233
|
-
end
|
|
218
|
+
weight_class = os2.us_weight_class
|
|
234
219
|
return nil unless weight_class
|
|
235
220
|
|
|
236
221
|
case weight_class
|
|
@@ -254,9 +239,7 @@ module Fontisan
|
|
|
254
239
|
post = @font.table(Constants::POST_TAG)
|
|
255
240
|
return nil unless post
|
|
256
241
|
|
|
257
|
-
|
|
258
|
-
post.italic_angle
|
|
259
|
-
end
|
|
242
|
+
post.italic_angle
|
|
260
243
|
end
|
|
261
244
|
|
|
262
245
|
# Extract version
|
|
@@ -266,9 +249,7 @@ module Fontisan
|
|
|
266
249
|
name_table = @font.table(Constants::NAME_TAG)
|
|
267
250
|
return nil unless name_table
|
|
268
251
|
|
|
269
|
-
|
|
270
|
-
name_table.version_string(1) || name_table.version_string(3)
|
|
271
|
-
end
|
|
252
|
+
name_table.english_name(Tables::Name::VERSION)
|
|
272
253
|
end
|
|
273
254
|
|
|
274
255
|
# Extract copyright
|
|
@@ -278,9 +259,7 @@ module Fontisan
|
|
|
278
259
|
name_table = @font.table(Constants::NAME_TAG)
|
|
279
260
|
return nil unless name_table
|
|
280
261
|
|
|
281
|
-
|
|
282
|
-
name_table.copyright(1) || name_table.copyright(3)
|
|
283
|
-
end
|
|
262
|
+
name_table.english_name(Tables::Name::COPYRIGHT)
|
|
284
263
|
end
|
|
285
264
|
|
|
286
265
|
# Extract vendor/manufacturer
|
|
@@ -290,9 +269,7 @@ module Fontisan
|
|
|
290
269
|
name_table = @font.table(Constants::NAME_TAG)
|
|
291
270
|
return nil unless name_table
|
|
292
271
|
|
|
293
|
-
|
|
294
|
-
name_table.manufacturer(1) || name_table.manufacturer(3)
|
|
295
|
-
end
|
|
272
|
+
name_table.english_name(Tables::Name::MANUFACTURER)
|
|
296
273
|
end
|
|
297
274
|
|
|
298
275
|
# Extract license information
|
|
@@ -302,9 +279,7 @@ module Fontisan
|
|
|
302
279
|
name_table = @font.table(Constants::NAME_TAG)
|
|
303
280
|
return nil unless name_table
|
|
304
281
|
|
|
305
|
-
|
|
306
|
-
name_table.license(1) || name_table.license(3)
|
|
307
|
-
end
|
|
282
|
+
name_table.english_name(Tables::Name::LICENSE_DESCRIPTION)
|
|
308
283
|
end
|
|
309
284
|
|
|
310
285
|
# Get default PFB filename
|