fontisan 0.1.0 → 0.2.0
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 +529 -65
- data/Gemfile +1 -0
- data/LICENSE +5 -1
- data/README.adoc +1301 -275
- data/Rakefile +27 -2
- data/benchmark/variation_quick_bench.rb +47 -0
- data/docs/EXTRACT_TTC_MIGRATION.md +549 -0
- data/fontisan.gemspec +4 -1
- data/lib/fontisan/binary/base_record.rb +22 -1
- data/lib/fontisan/cli.rb +309 -0
- data/lib/fontisan/collection/builder.rb +260 -0
- data/lib/fontisan/collection/offset_calculator.rb +227 -0
- data/lib/fontisan/collection/table_analyzer.rb +204 -0
- data/lib/fontisan/collection/table_deduplicator.rb +241 -0
- data/lib/fontisan/collection/writer.rb +306 -0
- data/lib/fontisan/commands/base_command.rb +8 -1
- data/lib/fontisan/commands/convert_command.rb +291 -0
- data/lib/fontisan/commands/export_command.rb +161 -0
- data/lib/fontisan/commands/info_command.rb +40 -6
- data/lib/fontisan/commands/instance_command.rb +295 -0
- data/lib/fontisan/commands/ls_command.rb +113 -0
- data/lib/fontisan/commands/pack_command.rb +241 -0
- data/lib/fontisan/commands/subset_command.rb +245 -0
- data/lib/fontisan/commands/unpack_command.rb +338 -0
- data/lib/fontisan/commands/validate_command.rb +178 -0
- data/lib/fontisan/commands/variable_command.rb +30 -1
- data/lib/fontisan/config/collection_settings.yml +56 -0
- data/lib/fontisan/config/conversion_matrix.yml +212 -0
- data/lib/fontisan/config/export_settings.yml +66 -0
- data/lib/fontisan/config/subset_profiles.yml +100 -0
- data/lib/fontisan/config/svg_settings.yml +60 -0
- data/lib/fontisan/config/validation_rules.yml +149 -0
- data/lib/fontisan/config/variable_settings.yml +99 -0
- data/lib/fontisan/config/woff2_settings.yml +77 -0
- data/lib/fontisan/constants.rb +69 -0
- data/lib/fontisan/converters/conversion_strategy.rb +96 -0
- data/lib/fontisan/converters/format_converter.rb +259 -0
- data/lib/fontisan/converters/outline_converter.rb +936 -0
- data/lib/fontisan/converters/svg_generator.rb +244 -0
- data/lib/fontisan/converters/table_copier.rb +117 -0
- data/lib/fontisan/converters/woff2_encoder.rb +416 -0
- data/lib/fontisan/converters/woff_writer.rb +391 -0
- data/lib/fontisan/error.rb +203 -0
- data/lib/fontisan/export/exporter.rb +262 -0
- data/lib/fontisan/export/table_serializer.rb +255 -0
- data/lib/fontisan/export/transformers/font_to_ttx.rb +172 -0
- data/lib/fontisan/export/transformers/head_transformer.rb +96 -0
- data/lib/fontisan/export/transformers/hhea_transformer.rb +59 -0
- data/lib/fontisan/export/transformers/maxp_transformer.rb +63 -0
- data/lib/fontisan/export/transformers/name_transformer.rb +63 -0
- data/lib/fontisan/export/transformers/os2_transformer.rb +121 -0
- data/lib/fontisan/export/transformers/post_transformer.rb +51 -0
- data/lib/fontisan/export/ttx_generator.rb +527 -0
- data/lib/fontisan/export/ttx_parser.rb +300 -0
- data/lib/fontisan/font_loader.rb +121 -12
- data/lib/fontisan/font_writer.rb +301 -0
- data/lib/fontisan/formatters/text_formatter.rb +102 -0
- data/lib/fontisan/glyph_accessor.rb +503 -0
- data/lib/fontisan/hints/hint_converter.rb +177 -0
- data/lib/fontisan/hints/postscript_hint_applier.rb +185 -0
- data/lib/fontisan/hints/postscript_hint_extractor.rb +254 -0
- data/lib/fontisan/hints/truetype_hint_applier.rb +71 -0
- data/lib/fontisan/hints/truetype_hint_extractor.rb +162 -0
- data/lib/fontisan/loading_modes.rb +113 -0
- data/lib/fontisan/metrics_calculator.rb +277 -0
- data/lib/fontisan/models/collection_font_summary.rb +52 -0
- data/lib/fontisan/models/collection_info.rb +76 -0
- data/lib/fontisan/models/collection_list_info.rb +37 -0
- data/lib/fontisan/models/font_export.rb +158 -0
- data/lib/fontisan/models/font_summary.rb +48 -0
- data/lib/fontisan/models/glyph_outline.rb +343 -0
- data/lib/fontisan/models/hint.rb +233 -0
- data/lib/fontisan/models/outline.rb +664 -0
- data/lib/fontisan/models/table_sharing_info.rb +40 -0
- data/lib/fontisan/models/ttx/glyph_order.rb +31 -0
- data/lib/fontisan/models/ttx/tables/binary_table.rb +67 -0
- data/lib/fontisan/models/ttx/tables/head_table.rb +74 -0
- data/lib/fontisan/models/ttx/tables/hhea_table.rb +74 -0
- data/lib/fontisan/models/ttx/tables/maxp_table.rb +55 -0
- data/lib/fontisan/models/ttx/tables/name_table.rb +45 -0
- data/lib/fontisan/models/ttx/tables/os2_table.rb +157 -0
- data/lib/fontisan/models/ttx/tables/post_table.rb +50 -0
- data/lib/fontisan/models/ttx/ttfont.rb +49 -0
- data/lib/fontisan/models/validation_report.rb +203 -0
- data/lib/fontisan/open_type_collection.rb +156 -2
- data/lib/fontisan/open_type_font.rb +296 -10
- data/lib/fontisan/optimizers/charstring_rewriter.rb +161 -0
- data/lib/fontisan/optimizers/pattern_analyzer.rb +308 -0
- data/lib/fontisan/optimizers/stack_tracker.rb +246 -0
- data/lib/fontisan/optimizers/subroutine_builder.rb +134 -0
- data/lib/fontisan/optimizers/subroutine_generator.rb +207 -0
- data/lib/fontisan/optimizers/subroutine_optimizer.rb +107 -0
- data/lib/fontisan/outline_extractor.rb +423 -0
- data/lib/fontisan/subset/builder.rb +268 -0
- data/lib/fontisan/subset/glyph_mapping.rb +215 -0
- data/lib/fontisan/subset/options.rb +142 -0
- data/lib/fontisan/subset/profile.rb +152 -0
- data/lib/fontisan/subset/table_subsetter.rb +461 -0
- data/lib/fontisan/svg/font_face_generator.rb +278 -0
- data/lib/fontisan/svg/font_generator.rb +264 -0
- data/lib/fontisan/svg/glyph_generator.rb +168 -0
- data/lib/fontisan/svg/view_box_calculator.rb +137 -0
- data/lib/fontisan/tables/cff/cff_glyph.rb +176 -0
- data/lib/fontisan/tables/cff/charset.rb +282 -0
- data/lib/fontisan/tables/cff/charstring.rb +905 -0
- data/lib/fontisan/tables/cff/charstring_builder.rb +322 -0
- data/lib/fontisan/tables/cff/charstrings_index.rb +162 -0
- data/lib/fontisan/tables/cff/dict.rb +351 -0
- data/lib/fontisan/tables/cff/dict_builder.rb +242 -0
- data/lib/fontisan/tables/cff/encoding.rb +274 -0
- data/lib/fontisan/tables/cff/header.rb +102 -0
- data/lib/fontisan/tables/cff/index.rb +237 -0
- data/lib/fontisan/tables/cff/index_builder.rb +170 -0
- data/lib/fontisan/tables/cff/private_dict.rb +284 -0
- data/lib/fontisan/tables/cff/top_dict.rb +236 -0
- data/lib/fontisan/tables/cff.rb +487 -0
- data/lib/fontisan/tables/cff2/blend_operator.rb +240 -0
- data/lib/fontisan/tables/cff2/charstring_parser.rb +591 -0
- data/lib/fontisan/tables/cff2/operand_stack.rb +232 -0
- data/lib/fontisan/tables/cff2.rb +341 -0
- data/lib/fontisan/tables/cvar.rb +242 -0
- data/lib/fontisan/tables/fvar.rb +2 -2
- data/lib/fontisan/tables/glyf/compound_glyph.rb +483 -0
- data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +136 -0
- data/lib/fontisan/tables/glyf/curve_converter.rb +343 -0
- data/lib/fontisan/tables/glyf/glyph_builder.rb +450 -0
- data/lib/fontisan/tables/glyf/simple_glyph.rb +382 -0
- data/lib/fontisan/tables/glyf.rb +235 -0
- data/lib/fontisan/tables/gvar.rb +270 -0
- data/lib/fontisan/tables/hhea.rb +124 -0
- data/lib/fontisan/tables/hmtx.rb +287 -0
- data/lib/fontisan/tables/hvar.rb +191 -0
- data/lib/fontisan/tables/loca.rb +322 -0
- data/lib/fontisan/tables/maxp.rb +192 -0
- data/lib/fontisan/tables/mvar.rb +185 -0
- data/lib/fontisan/tables/name.rb +99 -30
- data/lib/fontisan/tables/variation_common.rb +346 -0
- data/lib/fontisan/tables/vvar.rb +234 -0
- data/lib/fontisan/true_type_collection.rb +156 -2
- data/lib/fontisan/true_type_font.rb +297 -11
- data/lib/fontisan/utilities/brotli_wrapper.rb +159 -0
- data/lib/fontisan/utilities/checksum_calculator.rb +18 -0
- data/lib/fontisan/utils/thread_pool.rb +134 -0
- data/lib/fontisan/validation/checksum_validator.rb +170 -0
- data/lib/fontisan/validation/consistency_validator.rb +197 -0
- data/lib/fontisan/validation/structure_validator.rb +198 -0
- data/lib/fontisan/validation/table_validator.rb +158 -0
- data/lib/fontisan/validation/validator.rb +152 -0
- data/lib/fontisan/variable/axis_normalizer.rb +215 -0
- data/lib/fontisan/variable/delta_applicator.rb +313 -0
- data/lib/fontisan/variable/glyph_delta_processor.rb +218 -0
- data/lib/fontisan/variable/instancer.rb +344 -0
- data/lib/fontisan/variable/metric_delta_processor.rb +282 -0
- data/lib/fontisan/variable/region_matcher.rb +208 -0
- data/lib/fontisan/variable/static_font_builder.rb +213 -0
- data/lib/fontisan/variable/table_updater.rb +219 -0
- data/lib/fontisan/variation/blend_applier.rb +199 -0
- data/lib/fontisan/variation/cache.rb +298 -0
- data/lib/fontisan/variation/cache_key_builder.rb +162 -0
- data/lib/fontisan/variation/converter.rb +268 -0
- data/lib/fontisan/variation/data_extractor.rb +86 -0
- data/lib/fontisan/variation/delta_applier.rb +266 -0
- data/lib/fontisan/variation/delta_parser.rb +228 -0
- data/lib/fontisan/variation/inspector.rb +275 -0
- data/lib/fontisan/variation/instance_generator.rb +273 -0
- data/lib/fontisan/variation/interpolator.rb +231 -0
- data/lib/fontisan/variation/metrics_adjuster.rb +318 -0
- data/lib/fontisan/variation/optimizer.rb +418 -0
- data/lib/fontisan/variation/parallel_generator.rb +150 -0
- data/lib/fontisan/variation/region_matcher.rb +221 -0
- data/lib/fontisan/variation/subsetter.rb +463 -0
- data/lib/fontisan/variation/table_accessor.rb +105 -0
- data/lib/fontisan/variation/validator.rb +345 -0
- data/lib/fontisan/variation/variation_context.rb +211 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2/directory.rb +257 -0
- data/lib/fontisan/woff2/header.rb +101 -0
- data/lib/fontisan/woff2/table_transformer.rb +163 -0
- data/lib/fontisan/woff2_font.rb +712 -0
- data/lib/fontisan/woff_font.rb +483 -0
- data/lib/fontisan.rb +120 -0
- data/scripts/compare_stack_aware.rb +187 -0
- data/scripts/measure_optimization.rb +141 -0
- metadata +205 -4
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Fontisan
|
|
4
|
+
module Models
|
|
5
|
+
# Universal hint representation supporting both TrueType and PostScript hints
|
|
6
|
+
#
|
|
7
|
+
# Hints are instructions that improve font rendering at small sizes by
|
|
8
|
+
# providing information about how to align features to the pixel grid.
|
|
9
|
+
# This model provides a format-agnostic representation that can be
|
|
10
|
+
# converted between TrueType instructions and PostScript hint operators.
|
|
11
|
+
#
|
|
12
|
+
# **Hint Types:**
|
|
13
|
+
#
|
|
14
|
+
# - `:stem` - Vertical or horizontal stem hints (PostScript hstem/vstem)
|
|
15
|
+
# - `:stem3` - Multiple stem hints (PostScript hstem3/vstem3)
|
|
16
|
+
# - `:flex` - Flex hints for smooth curves (PostScript flex)
|
|
17
|
+
# - `:counter` - Counter control hints (PostScript counter)
|
|
18
|
+
# - `:hint_replacement` - Hint replacement (PostScript hintmask)
|
|
19
|
+
# - `:delta` - Delta hints for pixel-level adjustments (TrueType DELTA)
|
|
20
|
+
# - `:interpolate` - Interpolation hints (TrueType IUP)
|
|
21
|
+
# - `:shift` - Shift hints (TrueType SHP)
|
|
22
|
+
# - `:align` - Alignment hints (TrueType ALIGNRP)
|
|
23
|
+
#
|
|
24
|
+
# @example Creating a stem hint
|
|
25
|
+
# hint = Fontisan::Models::Hint.new(
|
|
26
|
+
# type: :stem,
|
|
27
|
+
# data: { position: 100, width: 50, orientation: :vertical }
|
|
28
|
+
# )
|
|
29
|
+
#
|
|
30
|
+
# @example Converting to TrueType
|
|
31
|
+
# tt_instructions = hint.to_truetype
|
|
32
|
+
#
|
|
33
|
+
# @example Converting to PostScript
|
|
34
|
+
# ps_operators = hint.to_postscript
|
|
35
|
+
class Hint
|
|
36
|
+
# @return [Symbol] Hint type
|
|
37
|
+
attr_reader :type
|
|
38
|
+
|
|
39
|
+
# @return [Hash] Hint-specific data
|
|
40
|
+
attr_reader :data
|
|
41
|
+
|
|
42
|
+
# @return [Symbol] Source format (:truetype or :postscript)
|
|
43
|
+
attr_reader :source_format
|
|
44
|
+
|
|
45
|
+
# Initialize a new hint
|
|
46
|
+
#
|
|
47
|
+
# @param type [Symbol] Hint type
|
|
48
|
+
# @param data [Hash] Hint-specific data
|
|
49
|
+
# @param source_format [Symbol] Source format (optional)
|
|
50
|
+
def initialize(type:, data:, source_format: nil)
|
|
51
|
+
@type = type
|
|
52
|
+
@data = data
|
|
53
|
+
@source_format = source_format
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Convert hint to TrueType instruction format
|
|
57
|
+
#
|
|
58
|
+
# @return [Array<Integer>] TrueType instruction bytes
|
|
59
|
+
def to_truetype
|
|
60
|
+
case type
|
|
61
|
+
when :stem
|
|
62
|
+
convert_stem_to_truetype
|
|
63
|
+
when :flex
|
|
64
|
+
convert_flex_to_truetype
|
|
65
|
+
when :counter
|
|
66
|
+
convert_counter_to_truetype
|
|
67
|
+
when :delta
|
|
68
|
+
# Already in TrueType format
|
|
69
|
+
data[:instructions] || []
|
|
70
|
+
when :interpolate
|
|
71
|
+
# IUP instruction
|
|
72
|
+
[0x30] # IUP[y], or [0x31] for IUP[x]
|
|
73
|
+
when :shift
|
|
74
|
+
# SHP instruction
|
|
75
|
+
data[:instructions] || []
|
|
76
|
+
when :align
|
|
77
|
+
# ALIGNRP instruction
|
|
78
|
+
[0x3C]
|
|
79
|
+
else
|
|
80
|
+
# Unknown hint type - return empty
|
|
81
|
+
[]
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Convert hint to PostScript hint format
|
|
86
|
+
#
|
|
87
|
+
# @return [Hash] PostScript hint operators and arguments
|
|
88
|
+
def to_postscript
|
|
89
|
+
case type
|
|
90
|
+
when :stem
|
|
91
|
+
convert_stem_to_postscript
|
|
92
|
+
when :stem3
|
|
93
|
+
convert_stem3_to_postscript
|
|
94
|
+
when :flex
|
|
95
|
+
convert_flex_to_postscript
|
|
96
|
+
when :counter
|
|
97
|
+
convert_counter_to_postscript
|
|
98
|
+
when :hint_replacement
|
|
99
|
+
# Hintmask operator
|
|
100
|
+
{ operator: :hintmask, args: data[:mask] || [] }
|
|
101
|
+
when :delta, :interpolate, :shift, :align
|
|
102
|
+
# TrueType-specific hints don't have direct PS equivalents
|
|
103
|
+
# Return approximation using stem hints
|
|
104
|
+
approximate_as_postscript
|
|
105
|
+
else
|
|
106
|
+
# Unknown hint type
|
|
107
|
+
{}
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Check if hint is compatible with target format
|
|
112
|
+
#
|
|
113
|
+
# @param format [Symbol] Target format (:truetype or :postscript)
|
|
114
|
+
# @return [Boolean] True if compatible
|
|
115
|
+
def compatible_with?(format)
|
|
116
|
+
case format
|
|
117
|
+
when :truetype
|
|
118
|
+
# Most PostScript hints can be converted to TrueType
|
|
119
|
+
%i[stem flex counter delta interpolate shift align].include?(type)
|
|
120
|
+
when :postscript
|
|
121
|
+
# Most TrueType hints can be approximated in PostScript
|
|
122
|
+
%i[stem stem3 flex counter hint_replacement].include?(type)
|
|
123
|
+
else
|
|
124
|
+
false
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
# Convert stem hint to TrueType instructions
|
|
131
|
+
def convert_stem_to_truetype
|
|
132
|
+
position = data[:position] || 0
|
|
133
|
+
width = data[:width] || 0
|
|
134
|
+
orientation = data[:orientation] || :vertical
|
|
135
|
+
|
|
136
|
+
# TrueType uses MDAP (Move Direct Absolute Point) and MDRP (Move Direct Relative Point)
|
|
137
|
+
# to control stem positioning
|
|
138
|
+
instructions = []
|
|
139
|
+
|
|
140
|
+
if orientation == :vertical
|
|
141
|
+
# Vertical stem: use Y-axis instructions
|
|
142
|
+
instructions << 0x2E # MDAP[rnd] - mark reference point
|
|
143
|
+
instructions << 0xC0 # MDRP[min,rnd,black] - move relative point
|
|
144
|
+
else
|
|
145
|
+
# Horizontal stem: use X-axis instructions
|
|
146
|
+
instructions << 0x2F # MDAP[rnd]
|
|
147
|
+
instructions << 0xC0 # MDRP[min,rnd,black]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
instructions
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Convert flex hint to TrueType instructions
|
|
154
|
+
def convert_flex_to_truetype
|
|
155
|
+
# Flex hints ensure smooth curves
|
|
156
|
+
# TrueType approximates with smooth curve flags on contour points
|
|
157
|
+
# Return empty as this is handled at the contour level
|
|
158
|
+
[]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Convert counter hint to TrueType instructions
|
|
162
|
+
def convert_counter_to_truetype
|
|
163
|
+
# Counter hints control interior space
|
|
164
|
+
# TrueType uses control value program (CVT) for this
|
|
165
|
+
# Return empty as this requires CVT table modification
|
|
166
|
+
[]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Convert stem hint to PostScript operators
|
|
170
|
+
def convert_stem_to_postscript
|
|
171
|
+
position = data[:position] || 0
|
|
172
|
+
width = data[:width] || 0
|
|
173
|
+
orientation = data[:orientation] || :vertical
|
|
174
|
+
|
|
175
|
+
operator = orientation == :vertical ? :vstem : :hstem
|
|
176
|
+
|
|
177
|
+
{
|
|
178
|
+
operator: operator,
|
|
179
|
+
args: [position, width],
|
|
180
|
+
}
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Convert stem3 hint to PostScript operators
|
|
184
|
+
def convert_stem3_to_postscript
|
|
185
|
+
stems = data[:stems] || []
|
|
186
|
+
orientation = data[:orientation] || :vertical
|
|
187
|
+
|
|
188
|
+
operator = orientation == :vertical ? :vstem3 : :hstem3
|
|
189
|
+
|
|
190
|
+
# Flatten stem positions and widths
|
|
191
|
+
args = stems.flat_map { |s| [s[:position], s[:width]] }
|
|
192
|
+
|
|
193
|
+
{
|
|
194
|
+
operator: operator,
|
|
195
|
+
args: args,
|
|
196
|
+
}
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Convert flex hint to PostScript operators
|
|
200
|
+
def convert_flex_to_postscript
|
|
201
|
+
points = data[:points] || []
|
|
202
|
+
|
|
203
|
+
{
|
|
204
|
+
operator: :flex,
|
|
205
|
+
args: points.flat_map { |p| [p[:x], p[:y]] },
|
|
206
|
+
}
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Convert counter hint to PostScript operators
|
|
210
|
+
def convert_counter_to_postscript
|
|
211
|
+
zones = data[:zones] || []
|
|
212
|
+
|
|
213
|
+
{
|
|
214
|
+
operator: :counter,
|
|
215
|
+
args: zones,
|
|
216
|
+
}
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Approximate TrueType-specific hints as PostScript stem hints
|
|
220
|
+
def approximate_as_postscript
|
|
221
|
+
# Best effort: create a stem hint from available data
|
|
222
|
+
if data[:position] && data[:width]
|
|
223
|
+
{
|
|
224
|
+
operator: :vstem,
|
|
225
|
+
args: [data[:position], data[:width]],
|
|
226
|
+
}
|
|
227
|
+
else
|
|
228
|
+
{}
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|