fontisan 0.4.43 → 0.4.45
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/30-eliminate-send-in-specs.md +36 -0
- data/TODO.bug-fixes/README.md +27 -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/base_command.rb +1 -1
- data/lib/fontisan/commands/info_command.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/conversion_options.rb +1 -1
- data/lib/fontisan/converters/format_converter.rb +3 -3
- 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 +13 -38
- 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/hint_converter.rb +1 -1
- 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/optimizers/pattern_analyzer.rb +1 -1
- data/lib/fontisan/parsers/dfont_parser.rb +2 -2
- data/lib/fontisan/pipeline/transformation_pipeline.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 +3 -5
- 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 +3 -3
- 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/converter.rb +1 -1
- 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 +16 -21
- 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 +11 -10
- data/lib/fontisan/woff_font.rb +2 -0
- data/lib/fontisan.rb +1 -0
- metadata +25 -2
data/lib/fontisan/tables/cff2.rb
CHANGED
|
@@ -20,6 +20,9 @@ module Fontisan
|
|
|
20
20
|
# cff2 = Fontisan::Tables::Cff2.read(data)
|
|
21
21
|
# num_glyphs = cff2.glyph_count
|
|
22
22
|
class Cff2 < Binary::BaseRecord
|
|
23
|
+
extend Registered
|
|
24
|
+
|
|
25
|
+
register_tag "CFF2"
|
|
23
26
|
# Inner namespace autoloads — declared here so Cff2::* constants
|
|
24
27
|
# resolve on first reference without require_relative.
|
|
25
28
|
autoload :BlendOperator, "fontisan/tables/cff2/blend_operator"
|
data/lib/fontisan/tables/cmap.rb
CHANGED
data/lib/fontisan/tables/cvar.rb
CHANGED
|
@@ -20,6 +20,9 @@ module Fontisan
|
|
|
20
20
|
# cvar = Fontisan::Tables::Cvar.read(data)
|
|
21
21
|
# cvt_deltas = cvar.cvt_variations
|
|
22
22
|
class Cvar < Binary::BaseRecord
|
|
23
|
+
extend Registered
|
|
24
|
+
|
|
25
|
+
register_tag "cvar"
|
|
23
26
|
uint16 :major_version
|
|
24
27
|
uint16 :minor_version
|
|
25
28
|
uint16 :tuple_variation_count
|
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])
|
|
@@ -170,7 +170,7 @@ module Fontisan
|
|
|
170
170
|
!parser.seac_components.nil?
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
-
private
|
|
173
|
+
# Methods below were private; made public for testability (TODO 30)
|
|
174
174
|
|
|
175
175
|
# Encode number in CFF format
|
|
176
176
|
#
|
|
@@ -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
|
|