fontisan 0.2.0 → 0.2.1
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 +270 -131
- data/README.adoc +158 -4
- data/Rakefile +44 -47
- data/lib/fontisan/cli.rb +84 -33
- data/lib/fontisan/collection/builder.rb +81 -0
- data/lib/fontisan/collection/table_deduplicator.rb +76 -0
- data/lib/fontisan/commands/base_command.rb +16 -0
- data/lib/fontisan/commands/convert_command.rb +97 -170
- data/lib/fontisan/commands/instance_command.rb +71 -80
- data/lib/fontisan/commands/validate_command.rb +25 -0
- data/lib/fontisan/config/validation_rules.yml +1 -1
- data/lib/fontisan/constants.rb +10 -0
- data/lib/fontisan/converters/format_converter.rb +150 -1
- data/lib/fontisan/converters/outline_converter.rb +80 -18
- data/lib/fontisan/converters/woff_writer.rb +1 -1
- data/lib/fontisan/font_loader.rb +3 -5
- data/lib/fontisan/font_writer.rb +7 -6
- data/lib/fontisan/hints/hint_converter.rb +133 -0
- data/lib/fontisan/hints/postscript_hint_applier.rb +221 -140
- data/lib/fontisan/hints/postscript_hint_extractor.rb +100 -0
- data/lib/fontisan/hints/truetype_hint_applier.rb +90 -44
- data/lib/fontisan/hints/truetype_hint_extractor.rb +127 -0
- data/lib/fontisan/loading_modes.rb +2 -0
- data/lib/fontisan/models/font_export.rb +2 -2
- data/lib/fontisan/models/hint.rb +173 -1
- data/lib/fontisan/models/validation_report.rb +1 -1
- data/lib/fontisan/open_type_font.rb +25 -9
- data/lib/fontisan/open_type_font_extensions.rb +54 -0
- data/lib/fontisan/pipeline/format_detector.rb +249 -0
- data/lib/fontisan/pipeline/output_writer.rb +154 -0
- data/lib/fontisan/pipeline/strategies/base_strategy.rb +75 -0
- data/lib/fontisan/pipeline/strategies/instance_strategy.rb +93 -0
- data/lib/fontisan/pipeline/strategies/named_strategy.rb +118 -0
- data/lib/fontisan/pipeline/strategies/preserve_strategy.rb +56 -0
- data/lib/fontisan/pipeline/transformation_pipeline.rb +411 -0
- data/lib/fontisan/pipeline/variation_resolver.rb +165 -0
- data/lib/fontisan/tables/cff/charstring.rb +33 -4
- data/lib/fontisan/tables/cff/charstring_builder.rb +34 -0
- data/lib/fontisan/tables/cff/charstring_parser.rb +237 -0
- data/lib/fontisan/tables/cff/charstring_rebuilder.rb +172 -0
- data/lib/fontisan/tables/cff/dict_builder.rb +15 -0
- data/lib/fontisan/tables/cff/hint_operation_injector.rb +207 -0
- data/lib/fontisan/tables/cff/offset_recalculator.rb +70 -0
- data/lib/fontisan/tables/cff/private_dict_writer.rb +125 -0
- data/lib/fontisan/tables/cff/table_builder.rb +221 -0
- data/lib/fontisan/tables/cff.rb +2 -0
- data/lib/fontisan/tables/cff2/private_dict_blend_handler.rb +246 -0
- data/lib/fontisan/tables/cff2/region_matcher.rb +200 -0
- data/lib/fontisan/tables/cff2/table_builder.rb +574 -0
- data/lib/fontisan/tables/cff2/table_reader.rb +419 -0
- data/lib/fontisan/tables/cff2/variation_data_extractor.rb +212 -0
- data/lib/fontisan/tables/cff2.rb +9 -4
- data/lib/fontisan/tables/cvar.rb +2 -41
- data/lib/fontisan/tables/gvar.rb +2 -41
- data/lib/fontisan/true_type_font.rb +24 -9
- data/lib/fontisan/true_type_font_extensions.rb +54 -0
- data/lib/fontisan/utilities/checksum_calculator.rb +42 -0
- data/lib/fontisan/validation/checksum_validator.rb +2 -2
- data/lib/fontisan/validation/table_validator.rb +1 -1
- data/lib/fontisan/validation/variable_font_validator.rb +218 -0
- data/lib/fontisan/variation/converter.rb +120 -13
- data/lib/fontisan/variation/instance_writer.rb +341 -0
- data/lib/fontisan/variation/tuple_variation_header.rb +51 -0
- data/lib/fontisan/variation/variable_svg_generator.rb +268 -0
- data/lib/fontisan/variation/variation_preserver.rb +288 -0
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/version.rb.orig +9 -0
- data/lib/fontisan/woff2/glyf_transformer.rb +666 -0
- data/lib/fontisan/woff2/hmtx_transformer.rb +164 -0
- data/lib/fontisan/woff2_font.rb +475 -470
- data/lib/fontisan/woff_font.rb +16 -11
- data/lib/fontisan.rb +12 -0
- metadata +31 -2
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "stringio"
|
|
4
|
+
|
|
5
|
+
module Fontisan
|
|
6
|
+
module Woff2
|
|
7
|
+
# Reconstructs hmtx table from WOFF2 transformed format
|
|
8
|
+
#
|
|
9
|
+
# WOFF2 hmtx transformation optimizes horizontal metrics by:
|
|
10
|
+
# - Using variable-length encoding for advance widths
|
|
11
|
+
# - Optionally deriving LSB from glyf bounding boxes
|
|
12
|
+
# - Omitting redundant trailing advance widths
|
|
13
|
+
#
|
|
14
|
+
# See: https://www.w3.org/TR/WOFF2/#hmtx_table_format
|
|
15
|
+
#
|
|
16
|
+
# @example Reconstructing hmtx table
|
|
17
|
+
# hmtx_data = HmtxTransformer.reconstruct(
|
|
18
|
+
# transformed_data,
|
|
19
|
+
# num_glyphs,
|
|
20
|
+
# number_of_h_metrics
|
|
21
|
+
# )
|
|
22
|
+
class HmtxTransformer
|
|
23
|
+
# Flags for hmtx transformation
|
|
24
|
+
HMTX_FLAG_EXPLICIT_ADVANCE_WIDTHS = 0x01
|
|
25
|
+
HMTX_FLAG_EXPLICIT_LSB_VALUES = 0x02
|
|
26
|
+
HMTX_FLAG_SYMMETRIC = 0x04
|
|
27
|
+
|
|
28
|
+
# Reconstruct hmtx table from transformed data
|
|
29
|
+
#
|
|
30
|
+
# @param transformed_data [String] The transformed hmtx table data
|
|
31
|
+
# @param num_glyphs [Integer] Number of glyphs
|
|
32
|
+
# @param num_h_metrics [Integer] From hhea.numberOfHMetrics
|
|
33
|
+
# @param glyf_lsbs [Array<Integer>, nil] LSB values from glyf bboxes (optional)
|
|
34
|
+
# @return [String] Standard hmtx table data
|
|
35
|
+
# @raise [InvalidFontError] If data is corrupted or invalid
|
|
36
|
+
def self.reconstruct(transformed_data, num_glyphs, num_h_metrics, glyf_lsbs = nil)
|
|
37
|
+
io = StringIO.new(transformed_data)
|
|
38
|
+
|
|
39
|
+
# Read transformation flags
|
|
40
|
+
flags = read_uint8(io)
|
|
41
|
+
|
|
42
|
+
# Read advance widths
|
|
43
|
+
advance_widths = []
|
|
44
|
+
|
|
45
|
+
if (flags & HMTX_FLAG_EXPLICIT_ADVANCE_WIDTHS).zero?
|
|
46
|
+
# Proportional encoding - read deltas
|
|
47
|
+
# First advance width is explicit
|
|
48
|
+
first_advance = read_255_uint16(io)
|
|
49
|
+
advance_widths << first_advance
|
|
50
|
+
|
|
51
|
+
# Remaining are deltas from previous
|
|
52
|
+
(num_h_metrics - 1).times do
|
|
53
|
+
delta = read_int16(io)
|
|
54
|
+
advance_widths << (advance_widths.last + delta)
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
# Explicit advance widths in transformed format
|
|
58
|
+
num_h_metrics.times do
|
|
59
|
+
advance_widths << read_255_uint16(io)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Read LSB values
|
|
64
|
+
lsbs = []
|
|
65
|
+
|
|
66
|
+
if (flags & HMTX_FLAG_EXPLICIT_LSB_VALUES) != 0
|
|
67
|
+
# Explicit LSB values
|
|
68
|
+
num_glyphs.times do
|
|
69
|
+
lsbs << read_int16(io)
|
|
70
|
+
end
|
|
71
|
+
elsif glyf_lsbs
|
|
72
|
+
# Use LSB values from glyf bounding boxes
|
|
73
|
+
lsbs = glyf_lsbs
|
|
74
|
+
else
|
|
75
|
+
# Need to read LSB values for long metrics
|
|
76
|
+
num_h_metrics.times do
|
|
77
|
+
lsbs << read_int16(io)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Remaining LSBs for glyphs that share the last advance width
|
|
81
|
+
(num_glyphs - num_h_metrics).times do
|
|
82
|
+
lsbs << read_int16(io)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Build standard hmtx table
|
|
87
|
+
build_hmtx_table(advance_widths, lsbs, num_h_metrics, num_glyphs)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Read variable-length 255UInt16 integer
|
|
91
|
+
#
|
|
92
|
+
# Format from WOFF2 spec:
|
|
93
|
+
# - value < 253: one byte
|
|
94
|
+
# - value == 253: 253 + next uint16
|
|
95
|
+
# - value == 254: 253 * 2 + next uint16
|
|
96
|
+
# - value == 255: 253 * 3 + next uint16
|
|
97
|
+
#
|
|
98
|
+
# @param io [StringIO] Input stream
|
|
99
|
+
# @return [Integer] Decoded value
|
|
100
|
+
def self.read_255_uint16(io)
|
|
101
|
+
code = read_uint8(io)
|
|
102
|
+
|
|
103
|
+
case code
|
|
104
|
+
when 255
|
|
105
|
+
759 + read_uint16(io) # 253 * 3 + value
|
|
106
|
+
when 254
|
|
107
|
+
506 + read_uint16(io) # 253 * 2 + value
|
|
108
|
+
when 253
|
|
109
|
+
253 + read_uint16(io)
|
|
110
|
+
else
|
|
111
|
+
code
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Build standard hmtx table format
|
|
116
|
+
#
|
|
117
|
+
# Standard hmtx format:
|
|
118
|
+
# - longHorMetric[numberOfHMetrics] (advanceWidth, lsb pairs)
|
|
119
|
+
# - int16[numGlyphs - numberOfHMetrics] (additional LSBs)
|
|
120
|
+
#
|
|
121
|
+
# @param advance_widths [Array<Integer>] Advance widths
|
|
122
|
+
# @param lsbs [Array<Integer>] Left side bearings
|
|
123
|
+
# @param num_h_metrics [Integer] Number of entries with full hMetrics
|
|
124
|
+
# @param num_glyphs [Integer] Total number of glyphs
|
|
125
|
+
# @return [String] Standard hmtx table data
|
|
126
|
+
def self.build_hmtx_table(advance_widths, lsbs, num_h_metrics, num_glyphs)
|
|
127
|
+
data = +""
|
|
128
|
+
|
|
129
|
+
# Write longHorMetric array (advanceWidth + lsb pairs)
|
|
130
|
+
num_h_metrics.times do |i|
|
|
131
|
+
advance_width = advance_widths[i] || advance_widths.last
|
|
132
|
+
lsb = lsbs[i] || 0
|
|
133
|
+
|
|
134
|
+
data << [advance_width].pack("n") # uint16 advanceWidth
|
|
135
|
+
data << [lsb].pack("n") # int16 lsb
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Write remaining LSB values
|
|
139
|
+
# These glyphs all share the last advance width from the array
|
|
140
|
+
(num_h_metrics...num_glyphs).each do |i|
|
|
141
|
+
lsb = lsbs[i] || 0
|
|
142
|
+
data << [lsb].pack("n") # int16 lsb
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
data
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Helper methods for reading binary data
|
|
149
|
+
|
|
150
|
+
def self.read_uint8(io)
|
|
151
|
+
io.read(1)&.unpack1("C") || raise(EOFError, "Unexpected end of stream")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.read_uint16(io)
|
|
155
|
+
io.read(2)&.unpack1("n") || raise(EOFError, "Unexpected end of stream")
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def self.read_int16(io)
|
|
159
|
+
value = read_uint16(io)
|
|
160
|
+
value > 0x7FFF ? value - 0x10000 : value
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|