fontisan 0.2.7 → 0.2.9
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.yml +103 -0
- data/.rubocop_todo.yml +65 -361
- data/CHANGELOG.md +116 -0
- data/Gemfile +1 -1
- data/README.adoc +106 -27
- data/Rakefile +12 -7
- data/benchmark/variation_quick_bench.rb +1 -1
- data/docs/APPLE_LEGACY_FONTS.adoc +173 -0
- data/docs/COLLECTION_VALIDATION.adoc +143 -0
- data/docs/COLOR_FONTS.adoc +127 -0
- data/docs/DOCUMENTATION_SUMMARY.md +141 -0
- data/docs/FONT_HINTING.adoc +9 -1
- data/docs/VALIDATION.adoc +254 -0
- data/docs/WOFF_WOFF2_FORMATS.adoc +94 -0
- data/lib/fontisan/cli.rb +45 -13
- data/lib/fontisan/collection/dfont_builder.rb +2 -1
- data/lib/fontisan/commands/convert_command.rb +2 -4
- data/lib/fontisan/commands/info_command.rb +3 -3
- data/lib/fontisan/commands/pack_command.rb +2 -1
- data/lib/fontisan/commands/validate_command.rb +157 -6
- data/lib/fontisan/converters/collection_converter.rb +22 -13
- data/lib/fontisan/converters/svg_generator.rb +2 -1
- data/lib/fontisan/converters/woff2_encoder.rb +6 -6
- data/lib/fontisan/converters/woff_writer.rb +3 -1
- data/lib/fontisan/font_loader.rb +7 -6
- data/lib/fontisan/formatters/text_formatter.rb +18 -14
- data/lib/fontisan/hints/hint_converter.rb +1 -1
- data/lib/fontisan/hints/hint_validator.rb +13 -10
- data/lib/fontisan/hints/truetype_instruction_analyzer.rb +15 -8
- data/lib/fontisan/hints/truetype_instruction_generator.rb +1 -1
- data/lib/fontisan/models/collection_validation_report.rb +104 -0
- data/lib/fontisan/models/font_report.rb +24 -0
- data/lib/fontisan/models/validation_report.rb +7 -2
- data/lib/fontisan/open_type_font.rb +18 -425
- data/lib/fontisan/optimizers/charstring_rewriter.rb +1 -1
- data/lib/fontisan/optimizers/subroutine_optimizer.rb +6 -2
- data/lib/fontisan/sfnt_font.rb +699 -0
- data/lib/fontisan/sfnt_table.rb +264 -0
- data/lib/fontisan/subset/glyph_mapping.rb +2 -0
- data/lib/fontisan/subset/table_subsetter.rb +2 -2
- data/lib/fontisan/tables/cblc.rb +8 -4
- data/lib/fontisan/tables/cff/index.rb +2 -0
- data/lib/fontisan/tables/cff.rb +6 -3
- data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +1 -1
- data/lib/fontisan/tables/cff2.rb +1 -1
- data/lib/fontisan/tables/cmap.rb +5 -5
- data/lib/fontisan/tables/cmap_table.rb +231 -0
- data/lib/fontisan/tables/glyf.rb +8 -10
- data/lib/fontisan/tables/glyf_table.rb +255 -0
- data/lib/fontisan/tables/head.rb +3 -3
- data/lib/fontisan/tables/head_table.rb +111 -0
- data/lib/fontisan/tables/hhea.rb +4 -4
- data/lib/fontisan/tables/hhea_table.rb +255 -0
- data/lib/fontisan/tables/hmtx_table.rb +191 -0
- data/lib/fontisan/tables/loca_table.rb +212 -0
- data/lib/fontisan/tables/maxp.rb +2 -2
- data/lib/fontisan/tables/maxp_table.rb +258 -0
- data/lib/fontisan/tables/name.rb +1 -1
- data/lib/fontisan/tables/name_table.rb +176 -0
- data/lib/fontisan/tables/os2.rb +8 -8
- data/lib/fontisan/tables/os2_table.rb +329 -0
- data/lib/fontisan/tables/post.rb +2 -2
- data/lib/fontisan/tables/post_table.rb +183 -0
- data/lib/fontisan/tables/sbix.rb +5 -4
- data/lib/fontisan/true_type_font.rb +12 -464
- data/lib/fontisan/utilities/checksum_calculator.rb +0 -44
- data/lib/fontisan/validation/collection_validator.rb +4 -2
- data/lib/fontisan/validators/basic_validator.rb +11 -21
- data/lib/fontisan/validators/font_book_validator.rb +29 -50
- data/lib/fontisan/validators/opentype_validator.rb +24 -28
- data/lib/fontisan/validators/validator.rb +87 -66
- data/lib/fontisan/validators/web_font_validator.rb +16 -21
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/glyf_transformer.rb +31 -8
- data/lib/fontisan/woff2/hmtx_transformer.rb +2 -1
- data/lib/fontisan/woff2/table_transformer.rb +4 -2
- data/lib/fontisan/woff2_font.rb +4 -2
- data/lib/fontisan/woff_font.rb +46 -30
- data/lib/fontisan.rb +2 -2
- data/scripts/compare_stack_aware.rb +1 -1
- data/scripts/measure_optimization.rb +1 -2
- metadata +23 -2
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../sfnt_table"
|
|
4
|
+
require_relative "post"
|
|
5
|
+
|
|
6
|
+
module Fontisan
|
|
7
|
+
module Tables
|
|
8
|
+
# OOP representation of the 'post' (PostScript) table
|
|
9
|
+
#
|
|
10
|
+
# The post table contains PostScript information, primarily glyph names.
|
|
11
|
+
# Different versions exist (1.0, 2.0, 2.5, 3.0, 4.0) with varying
|
|
12
|
+
# glyph name storage strategies.
|
|
13
|
+
#
|
|
14
|
+
# This class extends SfntTable to provide post-specific validation and
|
|
15
|
+
# convenience methods for accessing PostScript metrics and glyph names.
|
|
16
|
+
#
|
|
17
|
+
# @example Accessing post table data
|
|
18
|
+
# post = font.sfnt_table("post")
|
|
19
|
+
# post.italic_angle # => 0.0
|
|
20
|
+
# post.underline_position # => -100
|
|
21
|
+
# post.underline_thickness # => 50
|
|
22
|
+
# post.glyph_name_for(42) # => "A"
|
|
23
|
+
class PostTable < SfntTable
|
|
24
|
+
# Get post table version
|
|
25
|
+
#
|
|
26
|
+
# @return [Float, nil] Version number (1.0, 2.0, 2.5, 3.0, or 4.0)
|
|
27
|
+
def version
|
|
28
|
+
return nil unless parsed
|
|
29
|
+
|
|
30
|
+
parsed.version
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Get italic angle in degrees
|
|
34
|
+
#
|
|
35
|
+
# Positive value means counter-clockwise tilt
|
|
36
|
+
#
|
|
37
|
+
# @return [Float, nil] Italic angle in degrees, or nil if not parsed
|
|
38
|
+
def italic_angle
|
|
39
|
+
return nil unless parsed
|
|
40
|
+
|
|
41
|
+
parsed.italic_angle
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Check if font is italic
|
|
45
|
+
#
|
|
46
|
+
# @return [Boolean] true if italic_angle != 0
|
|
47
|
+
def italic?
|
|
48
|
+
angle = italic_angle
|
|
49
|
+
!angle.nil? && angle != 0
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Get underline position
|
|
53
|
+
#
|
|
54
|
+
# Distance from baseline to top of underline (negative for under baseline)
|
|
55
|
+
#
|
|
56
|
+
# @return [Integer, nil] Underline position in FUnits, or nil if not parsed
|
|
57
|
+
def underline_position
|
|
58
|
+
parsed&.underline_position
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get underline thickness
|
|
62
|
+
#
|
|
63
|
+
# @return [Integer, nil] Underline thickness in FUnits, or nil if not parsed
|
|
64
|
+
def underline_thickness
|
|
65
|
+
parsed&.underline_thickness
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Check if font is fixed pitch (monospaced)
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean] true if font is monospaced
|
|
71
|
+
def fixed_pitch?
|
|
72
|
+
return false unless parsed
|
|
73
|
+
|
|
74
|
+
parsed.is_fixed_pitch == 1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Get minimum memory for Type 42 fonts
|
|
78
|
+
#
|
|
79
|
+
# @return [Integer, nil] Minimum memory in bytes, or nil if not parsed
|
|
80
|
+
def min_mem_type42
|
|
81
|
+
parsed&.min_mem_type42
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Get maximum memory for Type 42 fonts
|
|
85
|
+
#
|
|
86
|
+
# @return [Integer, nil] Maximum memory in bytes, or nil if not parsed
|
|
87
|
+
def max_mem_type42
|
|
88
|
+
parsed&.max_mem_type42
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Get minimum memory for Type 1 fonts
|
|
92
|
+
#
|
|
93
|
+
# @return [Integer, nil] Minimum memory in bytes, or nil if not parsed
|
|
94
|
+
def min_mem_type1
|
|
95
|
+
parsed&.min_mem_type1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Get maximum memory for Type 1 fonts
|
|
99
|
+
#
|
|
100
|
+
# @return [Integer, nil] Maximum memory in bytes, or nil if not parsed
|
|
101
|
+
def max_mem_type1
|
|
102
|
+
parsed&.max_mem_type1
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Get all glyph names
|
|
106
|
+
#
|
|
107
|
+
# Only available for version 1.0 and 2.0
|
|
108
|
+
#
|
|
109
|
+
# @return [Array<String>] Array of glyph names
|
|
110
|
+
def glyph_names
|
|
111
|
+
return [] unless parsed
|
|
112
|
+
|
|
113
|
+
parsed.glyph_names || []
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Get glyph name by ID
|
|
117
|
+
#
|
|
118
|
+
# @param glyph_id [Integer] Glyph ID
|
|
119
|
+
# @return [String, nil] Glyph name, or nil if not found
|
|
120
|
+
def glyph_name_for(glyph_id)
|
|
121
|
+
names = glyph_names
|
|
122
|
+
return nil if glyph_id.negative? || glyph_id >= names.length
|
|
123
|
+
|
|
124
|
+
names[glyph_id]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Check if glyph names are available
|
|
128
|
+
#
|
|
129
|
+
# @return [Boolean] true if glyph names can be retrieved
|
|
130
|
+
def has_glyph_names?
|
|
131
|
+
return false unless parsed
|
|
132
|
+
|
|
133
|
+
parsed.has_glyph_names?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Get the number of glyphs with names
|
|
137
|
+
#
|
|
138
|
+
# @return [Integer] Number of named glyphs
|
|
139
|
+
def named_glyph_count
|
|
140
|
+
glyph_names.length
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
protected
|
|
144
|
+
|
|
145
|
+
# Validate the parsed post table
|
|
146
|
+
#
|
|
147
|
+
# @return [Boolean] true if valid
|
|
148
|
+
# @raise [InvalidFontError] if post table is invalid
|
|
149
|
+
def validate_parsed_table?
|
|
150
|
+
return true unless parsed
|
|
151
|
+
|
|
152
|
+
# Validate version
|
|
153
|
+
unless parsed.valid_version?
|
|
154
|
+
raise InvalidFontError,
|
|
155
|
+
"Invalid post table version: #{parsed.version} " \
|
|
156
|
+
"(must be 1.0, 2.0, 2.5, 3.0, or 4.0)"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Validate italic angle
|
|
160
|
+
unless parsed.valid_italic_angle?
|
|
161
|
+
raise InvalidFontError,
|
|
162
|
+
"Invalid post italic angle: #{parsed.italic_angle} " \
|
|
163
|
+
"(must be between -60 and 60 degrees)"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Validate fixed pitch flag
|
|
167
|
+
unless parsed.valid_fixed_pitch_flag?
|
|
168
|
+
raise InvalidFontError,
|
|
169
|
+
"Invalid post is_fixed_pitch: #{parsed.is_fixed_pitch} " \
|
|
170
|
+
"(must be 0 or 1)"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Validate version 2.0 data completeness
|
|
174
|
+
unless parsed.complete_version_2_data?
|
|
175
|
+
raise InvalidFontError,
|
|
176
|
+
"Invalid post version 2.0 table: incomplete data"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
true
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
data/lib/fontisan/tables/sbix.rb
CHANGED
|
@@ -166,7 +166,8 @@ module Fontisan
|
|
|
166
166
|
# Sample first few glyphs to detect formats
|
|
167
167
|
strike[:graphic_types]&.each do |type|
|
|
168
168
|
format_name = GRAPHIC_TYPE_NAMES[type]
|
|
169
|
-
formats << format_name if format_name && !["dupe",
|
|
169
|
+
formats << format_name if format_name && !["dupe",
|
|
170
|
+
"mask"].include?(format_name)
|
|
170
171
|
end
|
|
171
172
|
end
|
|
172
173
|
formats.uniq.compact
|
|
@@ -312,12 +313,12 @@ module Fontisan
|
|
|
312
313
|
|
|
313
314
|
# Check if offsets are valid
|
|
314
315
|
next if glyph_offset >= strike_size || next_glyph_offset > strike_size
|
|
315
|
-
next if next_glyph_offset <= glyph_offset
|
|
316
|
+
next if next_glyph_offset <= glyph_offset # Empty glyph
|
|
316
317
|
|
|
317
318
|
# Calculate absolute offset in table
|
|
318
319
|
# glyph_offset is relative to strike start, so add strike_offset
|
|
319
320
|
absolute_offset = strike_offset + glyph_offset
|
|
320
|
-
next if absolute_offset + 8 > raw_data.length
|
|
321
|
+
next if absolute_offset + 8 > raw_data.length # Need at least header
|
|
321
322
|
|
|
322
323
|
# Read graphic type (skip originOffsetX and originOffsetY = 4 bytes)
|
|
323
324
|
io = StringIO.new(raw_data)
|
|
@@ -345,7 +346,7 @@ module Fontisan
|
|
|
345
346
|
next_offset = strike[:glyph_offsets][glyph_id + 1]
|
|
346
347
|
|
|
347
348
|
return nil unless offset && next_offset
|
|
348
|
-
return nil if next_offset <= offset
|
|
349
|
+
return nil if next_offset <= offset # Empty glyph
|
|
349
350
|
|
|
350
351
|
# Calculate absolute position in table
|
|
351
352
|
absolute_offset = strike[:base_offset] + offset
|