fontisan 0.2.13 → 0.2.16
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 +56 -196
- data/Gemfile +1 -1
- data/docs/.gitignore +17 -0
- data/docs/.vitepress/config.ts +317 -0
- data/docs/.vitepress/theme/components/ApiMethod.vue +127 -0
- data/docs/.vitepress/theme/components/Badge.vue +51 -0
- data/docs/.vitepress/theme/components/FeatureComparison.vue +87 -0
- data/docs/.vitepress/theme/components/HeroCodeBlock.vue +98 -0
- data/docs/.vitepress/theme/components/WithinHero.vue +30 -0
- data/docs/.vitepress/theme/index.ts +22 -0
- data/docs/.vitepress/theme/style.css +330 -0
- data/docs/api/conversion-options.md +141 -0
- data/docs/api/converters/curve-converter.md +34 -0
- data/docs/api/converters/hint-converter.md +34 -0
- data/docs/api/converters/outline-converter.md +27 -0
- data/docs/api/font-loader.md +111 -0
- data/docs/api/font-writer.md +103 -0
- data/docs/api/index.md +79 -0
- data/docs/api/models/glyph-accessor.md +43 -0
- data/docs/api/models/glyph.md +40 -0
- data/docs/api/models/table-analyzer.md +35 -0
- data/docs/api/sfnt-font.md +53 -0
- data/docs/api/type1-font.md +43 -0
- data/docs/api/validators/font-validator.md +31 -0
- data/docs/api/validators/helper.md +36 -0
- data/docs/api/validators/profile.md +39 -0
- data/docs/cli/convert.md +87 -0
- data/docs/cli/dump-table.md +110 -0
- data/docs/cli/export.md +176 -0
- data/docs/cli/features.md +124 -0
- data/docs/cli/glyphs.md +90 -0
- data/docs/cli/index.md +208 -0
- data/docs/cli/info.md +254 -0
- data/docs/cli/instance.md +122 -0
- data/docs/cli/ls.md +95 -0
- data/docs/cli/optical-size.md +94 -0
- data/docs/cli/pack.md +125 -0
- data/docs/cli/scripts.md +105 -0
- data/docs/cli/subset.md +39 -0
- data/docs/cli/tables.md +84 -0
- data/docs/cli/unicode.md +101 -0
- data/docs/cli/validate.md +48 -0
- data/docs/cli/variable.md +126 -0
- data/docs/cli/version.md +46 -0
- data/docs/guide/cli/convert.md +108 -0
- data/docs/guide/cli/export.md +138 -0
- data/docs/guide/cli/index.md +99 -0
- data/docs/guide/cli/info.md +144 -0
- data/docs/guide/cli/pack.md +155 -0
- data/docs/guide/cli/subset.md +118 -0
- data/docs/guide/cli/validate.md +139 -0
- data/docs/guide/color-fonts/bitmaps.md +177 -0
- data/docs/guide/color-fonts/colr-cpal.md +175 -0
- data/docs/guide/color-fonts/index.md +140 -0
- data/docs/guide/color-fonts/svg.md +154 -0
- data/docs/guide/color.md +51 -0
- data/docs/guide/comparisons/font-validator.md +222 -0
- data/docs/guide/comparisons/fonttools.md +200 -0
- data/docs/guide/comparisons/index.md +83 -0
- data/docs/guide/comparisons/lcdf-typetools.md +205 -0
- data/docs/guide/contributing.md +279 -0
- data/docs/guide/conversion/collections.md +251 -0
- data/docs/guide/conversion/curves.md +246 -0
- data/docs/guide/conversion/index.md +157 -0
- data/docs/guide/conversion/options.md +251 -0
- data/docs/guide/conversion/ttf-otf.md +184 -0
- data/docs/guide/conversion/type1.md +208 -0
- data/docs/guide/conversion/web.md +240 -0
- data/docs/guide/conversion.md +39 -0
- data/docs/guide/formats/collections.md +147 -0
- data/docs/guide/formats/dfont.md +99 -0
- data/docs/guide/formats/index.md +65 -0
- data/docs/guide/formats/otf.md +103 -0
- data/docs/guide/formats/svg.md +97 -0
- data/docs/guide/formats/ttf.md +105 -0
- data/docs/guide/formats/type1.md +118 -0
- data/docs/guide/formats/woff.md +115 -0
- data/docs/guide/hinting/autohint.md +141 -0
- data/docs/guide/hinting/conversion.md +161 -0
- data/docs/guide/hinting/index.md +86 -0
- data/docs/guide/hinting/postscript.md +149 -0
- data/docs/guide/hinting/truetype.md +135 -0
- data/docs/guide/hinting.md +44 -0
- data/docs/guide/index.md +152 -0
- data/docs/guide/installation.md +116 -0
- data/docs/guide/migrations/extract-ttc.md +549 -0
- data/docs/guide/migrations/font-validator.md +260 -0
- data/docs/guide/migrations/fonttools.md +208 -0
- data/docs/guide/migrations/index.md +64 -0
- data/docs/guide/migrations/otfinfo.md +197 -0
- data/docs/guide/quick-start.md +204 -0
- data/docs/guide/type1.md +58 -0
- data/docs/guide/universal-outline.md +151 -0
- data/docs/guide/validation/custom.md +195 -0
- data/docs/guide/validation/helpers.md +188 -0
- data/docs/guide/validation/index.md +132 -0
- data/docs/guide/validation/profiles.md +156 -0
- data/docs/guide/validation.md +47 -0
- data/docs/guide/variable-fonts/advanced.md +231 -0
- data/docs/guide/variable-fonts/axes.md +209 -0
- data/docs/guide/variable-fonts/conversion.md +197 -0
- data/docs/guide/variable-fonts/index.md +84 -0
- data/docs/guide/variable-fonts/instances.md +187 -0
- data/docs/guide/variable-fonts/named-instances.md +194 -0
- data/docs/guide/variable-fonts/static.md +168 -0
- data/docs/guide/variable.md +58 -0
- data/docs/guide/woff.md +59 -0
- data/docs/index.md +136 -0
- data/docs/lychee.toml +37 -0
- data/docs/package-lock.json +2560 -0
- data/docs/package.json +15 -0
- data/docs/public/apple-touch-icon.png +0 -0
- data/docs/public/favicon-96x96.png +0 -0
- data/docs/public/favicon.ico +0 -0
- data/docs/public/favicon.svg +1 -0
- data/docs/public/logo-full.svg +1 -0
- data/docs/public/logo.svg +1 -0
- data/docs/public/site.webmanifest +21 -0
- data/docs/public/web-app-manifest-192x192.png +0 -0
- data/docs/public/web-app-manifest-512x512.png +0 -0
- data/fontisan.gemspec +1 -1
- data/lib/fontisan/commands/features_command.rb +0 -1
- data/lib/fontisan/commands/info_command.rb +5 -5
- data/lib/fontisan/commands/scripts_command.rb +0 -1
- data/lib/fontisan/converters/format_converter.rb +2 -1
- data/lib/fontisan/converters/type1_converter.rb +65 -60
- data/lib/fontisan/hints/hint_converter.rb +2 -1
- data/lib/fontisan/loading_modes.rb +0 -2
- data/lib/fontisan/open_type_font.rb +0 -40
- data/lib/fontisan/sfnt_font.rb +41 -22
- data/lib/fontisan/tables/glyf/compound_glyph.rb +0 -1
- data/lib/fontisan/true_type_collection.rb +8 -8
- data/lib/fontisan/true_type_font.rb +1 -59
- data/lib/fontisan/type1/afm_parser.rb +2 -1
- data/lib/fontisan/type1/cff_to_type1_converter.rb +24 -19
- data/lib/fontisan/type1/private_dict.rb +28 -7
- data/lib/fontisan/type1/seac_expander.rb +22 -17
- data/lib/fontisan/variable/delta_applicator.rb +3 -3
- data/lib/fontisan/variation/optimizer.rb +0 -1
- data/lib/fontisan/version.rb +1 -1
- data/lib/fontisan/woff2_font.rb +2 -2
- data/lib/fontisan/woff_font.rb +3 -3
- data/lib/fontisan.rb +3 -2
- metadata +122 -4
|
@@ -32,7 +32,7 @@ module Fontisan
|
|
|
32
32
|
|
|
33
33
|
# Get a single font from the collection
|
|
34
34
|
#
|
|
35
|
-
#
|
|
35
|
+
# Uses the base class from_collection method.
|
|
36
36
|
#
|
|
37
37
|
# @param index [Integer] Index of the font (0-based)
|
|
38
38
|
# @param io [IO] Open file handle
|
|
@@ -42,13 +42,13 @@ module Fontisan
|
|
|
42
42
|
return nil if index >= num_fonts
|
|
43
43
|
|
|
44
44
|
require_relative "true_type_font"
|
|
45
|
-
TrueTypeFont.
|
|
45
|
+
TrueTypeFont.from_collection(io, font_offsets[index], mode: mode)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
# Extract fonts as TrueTypeFont objects
|
|
49
49
|
#
|
|
50
50
|
# Reads each font from the TTC file and returns them as TrueTypeFont objects.
|
|
51
|
-
#
|
|
51
|
+
# Uses the base class from_collection method.
|
|
52
52
|
#
|
|
53
53
|
# @param io [IO] Open file handle to read fonts from
|
|
54
54
|
# @return [Array<TrueTypeFont>] Array of font objects
|
|
@@ -56,13 +56,13 @@ module Fontisan
|
|
|
56
56
|
require_relative "true_type_font"
|
|
57
57
|
|
|
58
58
|
font_offsets.map do |offset|
|
|
59
|
-
TrueTypeFont.
|
|
59
|
+
TrueTypeFont.from_collection(io, offset)
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
# List all fonts in the collection with basic metadata
|
|
64
64
|
#
|
|
65
|
-
#
|
|
65
|
+
# Uses the base class from_collection method.
|
|
66
66
|
#
|
|
67
67
|
# @param io [IO] Open file handle to read fonts from
|
|
68
68
|
# @return [CollectionListInfo] List of fonts with metadata
|
|
@@ -73,7 +73,7 @@ module Fontisan
|
|
|
73
73
|
require_relative "tables/name"
|
|
74
74
|
|
|
75
75
|
fonts = font_offsets.map.with_index do |offset, index|
|
|
76
|
-
font = TrueTypeFont.
|
|
76
|
+
font = TrueTypeFont.from_collection(io, offset)
|
|
77
77
|
|
|
78
78
|
# Extract basic font info
|
|
79
79
|
name_table = font.table("name")
|
|
@@ -119,7 +119,7 @@ module Fontisan
|
|
|
119
119
|
|
|
120
120
|
# Calculate table sharing statistics
|
|
121
121
|
#
|
|
122
|
-
#
|
|
122
|
+
# Uses the base class from_collection method.
|
|
123
123
|
#
|
|
124
124
|
# @param io [IO] Open file handle
|
|
125
125
|
# @return [TableSharingInfo] Sharing statistics
|
|
@@ -129,7 +129,7 @@ module Fontisan
|
|
|
129
129
|
|
|
130
130
|
# Extract all fonts
|
|
131
131
|
fonts = font_offsets.map do |offset|
|
|
132
|
-
TrueTypeFont.
|
|
132
|
+
TrueTypeFont.from_collection(io, offset)
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
# Build table hash map (checksum -> size)
|
|
@@ -23,66 +23,8 @@ module Fontisan
|
|
|
23
23
|
# ttf.to_file("output.ttf")
|
|
24
24
|
#
|
|
25
25
|
# @example Reading from TTC collection
|
|
26
|
-
# ttf = TrueTypeFont.
|
|
26
|
+
# ttf = TrueTypeFont.from_collection(io, offset)
|
|
27
27
|
class TrueTypeFont < SfntFont
|
|
28
|
-
# Read TrueType Font from a file
|
|
29
|
-
#
|
|
30
|
-
# @param path [String] Path to the TTF file
|
|
31
|
-
# @param mode [Symbol] Loading mode (:metadata or :full, default: :full)
|
|
32
|
-
# @param lazy [Boolean] If true, load tables on demand (default: false)
|
|
33
|
-
# @return [TrueTypeFont] A new instance
|
|
34
|
-
# @raise [ArgumentError] if path is nil or empty, or if mode is invalid
|
|
35
|
-
# @raise [Errno::ENOENT] if file does not exist
|
|
36
|
-
# @raise [RuntimeError] if file format is invalid
|
|
37
|
-
def self.from_file(path, mode: LoadingModes::FULL, lazy: false)
|
|
38
|
-
if path.nil? || path.to_s.empty?
|
|
39
|
-
raise ArgumentError,
|
|
40
|
-
"path cannot be nil or empty"
|
|
41
|
-
end
|
|
42
|
-
raise Errno::ENOENT, "File not found: #{path}" unless File.exist?(path)
|
|
43
|
-
|
|
44
|
-
# Validate mode
|
|
45
|
-
LoadingModes.validate_mode!(mode)
|
|
46
|
-
|
|
47
|
-
File.open(path, "rb") do |io|
|
|
48
|
-
font = read(io)
|
|
49
|
-
font.initialize_storage
|
|
50
|
-
font.loading_mode = mode
|
|
51
|
-
font.lazy_load_enabled = lazy
|
|
52
|
-
|
|
53
|
-
if lazy
|
|
54
|
-
# Reuse existing IO handle by duplicating it to prevent double file open
|
|
55
|
-
# The dup ensures the handle stays open after this block closes
|
|
56
|
-
font.io_source = io.dup
|
|
57
|
-
font.setup_finalizer
|
|
58
|
-
else
|
|
59
|
-
# Read tables upfront
|
|
60
|
-
font.read_table_data(io)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
font
|
|
64
|
-
end
|
|
65
|
-
rescue BinData::ValidityError, EOFError => e
|
|
66
|
-
raise "Invalid TTF file: #{e.message}"
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Read TrueType Font from TTC at specific offset
|
|
70
|
-
#
|
|
71
|
-
# @param io [IO] Open file handle
|
|
72
|
-
# @param offset [Integer] Byte offset to the font
|
|
73
|
-
# @param mode [Symbol] Loading mode (:metadata or :full, default: :full)
|
|
74
|
-
# @return [TrueTypeFont] A new instance
|
|
75
|
-
def self.from_ttc(io, offset, mode: LoadingModes::FULL)
|
|
76
|
-
LoadingModes.validate_mode!(mode)
|
|
77
|
-
|
|
78
|
-
io.seek(offset)
|
|
79
|
-
font = read(io)
|
|
80
|
-
font.initialize_storage
|
|
81
|
-
font.loading_mode = mode
|
|
82
|
-
font.read_table_data(io)
|
|
83
|
-
font
|
|
84
|
-
end
|
|
85
|
-
|
|
86
28
|
# Check if font is TrueType flavored
|
|
87
29
|
#
|
|
88
30
|
# @return [Boolean] true for TrueType fonts
|
|
@@ -153,7 +153,8 @@ module Fontisan
|
|
|
153
153
|
elsif @copyright.nil? && (match = line.match(/^Notice\s+(\S.*)/i))
|
|
154
154
|
@copyright = match[1].strip
|
|
155
155
|
elsif @font_bbox.nil? && (match = line.match(/^FontBBox\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)$/i))
|
|
156
|
-
@font_bbox = [match[1].to_i, match[2].to_i, match[3].to_i,
|
|
156
|
+
@font_bbox = [match[1].to_i, match[2].to_i, match[3].to_i,
|
|
157
|
+
match[4].to_i]
|
|
157
158
|
end
|
|
158
159
|
|
|
159
160
|
# Break early if all metrics found
|
|
@@ -49,7 +49,7 @@ module Fontisan
|
|
|
49
49
|
cntrmask: 20, # CFF: 20, Type 1: Not supported (skip)
|
|
50
50
|
|
|
51
51
|
# End char
|
|
52
|
-
endchar: 14,
|
|
52
|
+
endchar: 14, # CFF: 14, Type 1: 14
|
|
53
53
|
|
|
54
54
|
# Miscellaneous
|
|
55
55
|
callsubr: 10, # CFF: 10, Type 1: 10
|
|
@@ -83,7 +83,7 @@ module Fontisan
|
|
|
83
83
|
def convert(cff_charstring, private_dict: {})
|
|
84
84
|
# Parse CFF CharString into operations
|
|
85
85
|
parser = Tables::Cff::CharStringParser.new(cff_charstring,
|
|
86
|
-
|
|
86
|
+
stem_count: private_dict[:stem_count].to_i)
|
|
87
87
|
operations = parser.parse
|
|
88
88
|
|
|
89
89
|
# Extract width from operations (CFF spec: odd stack before first move = width)
|
|
@@ -106,13 +106,14 @@ module Fontisan
|
|
|
106
106
|
|
|
107
107
|
# Find first move operator
|
|
108
108
|
first_move_idx = operations.index do |op|
|
|
109
|
-
%i[rmoveto hmoveto vmoveto rcurveline rrcurveline vvcurveto
|
|
109
|
+
%i[rmoveto hmoveto vmoveto rcurveline rrcurveline vvcurveto
|
|
110
|
+
hhcurveto].include?(op[:name])
|
|
110
111
|
end
|
|
111
112
|
|
|
112
113
|
return @default_width unless first_move_idx
|
|
113
114
|
|
|
114
115
|
# Count operands before first move
|
|
115
|
-
operand_count = operations[0...first_move_idx].sum
|
|
116
|
+
operand_count = operations[0...first_move_idx].sum do |op|
|
|
116
117
|
op[:operands]&.length || 0
|
|
117
118
|
end
|
|
118
119
|
|
|
@@ -140,10 +141,10 @@ module Fontisan
|
|
|
140
141
|
|
|
141
142
|
# Add hsbw (horizontal sidebearing and width) at start
|
|
142
143
|
# This is the standard width operator for horizontal fonts
|
|
143
|
-
result << encode_number(0)
|
|
144
|
+
result << encode_number(0) # left sidebearing (usually 0 for CFF)
|
|
144
145
|
result << encode_number(glyph_width)
|
|
145
146
|
result << ESCAPE_BYTE
|
|
146
|
-
result << 34
|
|
147
|
+
result << 34 # hsbw operator (two-byte: 12 34)
|
|
147
148
|
|
|
148
149
|
x = 0
|
|
149
150
|
y = 0
|
|
@@ -154,11 +155,12 @@ module Fontisan
|
|
|
154
155
|
if width && operations.any?
|
|
155
156
|
# Count operands before first move to determine if width was in stack
|
|
156
157
|
first_move_idx = operations.index do |op|
|
|
157
|
-
%i[rmoveto hmoveto vmoveto rcurveline rrcurveline vvcurveto
|
|
158
|
+
%i[rmoveto hmoveto vmoveto rcurveline rrcurveline vvcurveto
|
|
159
|
+
hhcurveto].include?(op[:name])
|
|
158
160
|
end
|
|
159
161
|
|
|
160
162
|
if first_move_idx
|
|
161
|
-
operand_count = operations[0...first_move_idx].sum
|
|
163
|
+
operand_count = operations[0...first_move_idx].sum do |op|
|
|
162
164
|
op[:operands]&.length || 0
|
|
163
165
|
end
|
|
164
166
|
|
|
@@ -182,16 +184,17 @@ module Fontisan
|
|
|
182
184
|
end
|
|
183
185
|
|
|
184
186
|
if operands.length >= 2
|
|
185
|
-
dx
|
|
187
|
+
dx = operands[0]
|
|
188
|
+
dy = operands[1]
|
|
186
189
|
x += dx
|
|
187
190
|
y += dy
|
|
188
191
|
result << encode_number(dx)
|
|
189
192
|
result << encode_number(dy)
|
|
190
|
-
result << 21
|
|
193
|
+
result << 21 # rmoveto
|
|
191
194
|
elsif operands.length == 1
|
|
192
195
|
# Only dy (hmoveto/vmoveto style)
|
|
193
196
|
result << encode_number(operands.first)
|
|
194
|
-
result << 4
|
|
197
|
+
result << 4 # vmoveto (closest approximation)
|
|
195
198
|
end
|
|
196
199
|
first_move = false
|
|
197
200
|
when :hmoveto
|
|
@@ -205,14 +208,14 @@ module Fontisan
|
|
|
205
208
|
dx = operands.first
|
|
206
209
|
x += dx if dx
|
|
207
210
|
result << encode_number(dx)
|
|
208
|
-
result << 22
|
|
211
|
+
result << 22 # hmoveto
|
|
209
212
|
first_move = false
|
|
210
213
|
when :vmoveto
|
|
211
214
|
# vmoveto dy
|
|
212
215
|
dy = op[:operands].first
|
|
213
216
|
y += dy if dy
|
|
214
217
|
result << encode_number(dy)
|
|
215
|
-
result << 4
|
|
218
|
+
result << 4 # vmoveto
|
|
216
219
|
first_move = false
|
|
217
220
|
when :rlineto
|
|
218
221
|
# rlineto dx dy
|
|
@@ -221,29 +224,31 @@ module Fontisan
|
|
|
221
224
|
y += dy
|
|
222
225
|
result << encode_number(dx)
|
|
223
226
|
result << encode_number(dy)
|
|
224
|
-
result << 5
|
|
227
|
+
result << 5 # rlineto
|
|
225
228
|
first_move = false
|
|
226
229
|
when :hlineto
|
|
227
230
|
# hlineto dx
|
|
228
231
|
dx = op[:operands].first
|
|
229
232
|
x += dx
|
|
230
233
|
result << encode_number(dx)
|
|
231
|
-
result << 6
|
|
234
|
+
result << 6 # hlineto
|
|
232
235
|
first_move = false
|
|
233
236
|
when :vlineto
|
|
234
237
|
# vlineto dy
|
|
235
238
|
dy = op[:operands].first
|
|
236
239
|
y += dy
|
|
237
240
|
result << encode_number(dy)
|
|
238
|
-
result << 7
|
|
241
|
+
result << 7 # vlineto
|
|
239
242
|
first_move = false
|
|
240
243
|
when :rrcurveto
|
|
241
244
|
# rrcurveto dx1 dy1 dx2 dy2 dx3 dy3
|
|
242
245
|
dx1, dy1, dx2, dy2, dx3, dy3 = op[:operands]
|
|
243
246
|
x += dx1 + dx2 + dx3
|
|
244
247
|
y += dy1 + dy2 + dy3
|
|
245
|
-
[dx1, dy1, dx2, dy2, dx3, dy3].each
|
|
246
|
-
|
|
248
|
+
[dx1, dy1, dx2, dy2, dx3, dy3].each do |val|
|
|
249
|
+
result << encode_number(val)
|
|
250
|
+
end
|
|
251
|
+
result << 8 # rrcurveto
|
|
247
252
|
first_move = false
|
|
248
253
|
when :hhcurveto, :hvcurveto, :vhcurveto
|
|
249
254
|
# Flexible curve operators
|
|
@@ -293,7 +298,7 @@ module Fontisan
|
|
|
293
298
|
[num + 139].pack("C")
|
|
294
299
|
else
|
|
295
300
|
# Use escape sequence (255) followed by 2-byte signed integer
|
|
296
|
-
num += 32768 if num
|
|
301
|
+
num += 32768 if num.negative?
|
|
297
302
|
[255, num % 256, num >> 8].pack("C*")
|
|
298
303
|
end
|
|
299
304
|
end
|
|
@@ -176,18 +176,39 @@ module Fontisan
|
|
|
176
176
|
# puts priv.to_type1_format
|
|
177
177
|
def to_type1_format
|
|
178
178
|
result = []
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
unless @blue_values.empty?
|
|
180
|
+
result << array_to_type1(:BlueValues,
|
|
181
|
+
@blue_values)
|
|
182
|
+
end
|
|
183
|
+
unless @other_blues.empty?
|
|
184
|
+
result << array_to_type1(:OtherBlues,
|
|
185
|
+
@other_blues)
|
|
186
|
+
end
|
|
187
|
+
unless @family_blues.empty?
|
|
188
|
+
result << array_to_type1(:FamilyBlues,
|
|
189
|
+
@family_blues)
|
|
190
|
+
end
|
|
191
|
+
unless @family_other_blues.empty?
|
|
192
|
+
result << array_to_type1(:FamilyOtherBlues,
|
|
193
|
+
@family_other_blues)
|
|
194
|
+
end
|
|
183
195
|
result << scalar_to_type1(:BlueScale, @blue_scale)
|
|
184
196
|
result << scalar_to_type1(:BlueShift, @blue_shift)
|
|
185
197
|
result << scalar_to_type1(:BlueFuzz, @blue_fuzz)
|
|
186
198
|
result << array_to_type1(:StdHW, @std_hw) unless @std_hw.empty?
|
|
187
199
|
result << array_to_type1(:StdVW, @std_vw) unless @std_vw.empty?
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
200
|
+
unless @stem_snap_h.empty?
|
|
201
|
+
result << array_to_type1(:StemSnapH,
|
|
202
|
+
@stem_snap_h)
|
|
203
|
+
end
|
|
204
|
+
unless @stem_snap_v.empty?
|
|
205
|
+
result << array_to_type1(:StemSnapV,
|
|
206
|
+
@stem_snap_v)
|
|
207
|
+
end
|
|
208
|
+
unless @force_bold == false
|
|
209
|
+
result << boolean_to_type1(:ForceBold,
|
|
210
|
+
@force_bold)
|
|
211
|
+
end
|
|
191
212
|
result << scalar_to_type1(:lenIV, @len_iv)
|
|
192
213
|
|
|
193
214
|
result.join("\n")
|
|
@@ -68,11 +68,13 @@ module Fontisan
|
|
|
68
68
|
accent_glyph_name = @charstrings.encoding[components[:accent]]
|
|
69
69
|
|
|
70
70
|
if base_glyph_name.nil?
|
|
71
|
-
raise Fontisan::Error,
|
|
71
|
+
raise Fontisan::Error,
|
|
72
|
+
"Base glyph for char code #{components[:base]} not found"
|
|
72
73
|
end
|
|
73
74
|
|
|
74
75
|
if accent_glyph_name.nil?
|
|
75
|
-
raise Fontisan::Error,
|
|
76
|
+
raise Fontisan::Error,
|
|
77
|
+
"Accent glyph for char code #{components[:accent]} not found"
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
# Get CharStrings for base and accent
|
|
@@ -80,11 +82,13 @@ module Fontisan
|
|
|
80
82
|
accent_charstring = @charstrings[accent_glyph_name]
|
|
81
83
|
|
|
82
84
|
if base_charstring.nil?
|
|
83
|
-
raise Fontisan::Error,
|
|
85
|
+
raise Fontisan::Error,
|
|
86
|
+
"CharString not found for base glyph #{base_glyph_name}"
|
|
84
87
|
end
|
|
85
88
|
|
|
86
89
|
if accent_charstring.nil?
|
|
87
|
-
raise Fontisan::Error,
|
|
90
|
+
raise Fontisan::Error,
|
|
91
|
+
"CharString not found for accent glyph #{accent_glyph_name}"
|
|
88
92
|
end
|
|
89
93
|
|
|
90
94
|
# Parse both CharStrings into command sequences
|
|
@@ -92,7 +96,8 @@ module Fontisan
|
|
|
92
96
|
accent_commands = parse_charstring_to_commands(accent_charstring)
|
|
93
97
|
|
|
94
98
|
# Transform accent by (adx, ady) offset
|
|
95
|
-
accent_commands = transform_commands(accent_commands, components[:adx],
|
|
99
|
+
accent_commands = transform_commands(accent_commands, components[:adx],
|
|
100
|
+
components[:ady])
|
|
96
101
|
|
|
97
102
|
# Merge base and accent commands
|
|
98
103
|
merged_commands = merge_outline_commands(base_commands, accent_commands)
|
|
@@ -278,8 +283,8 @@ module Fontisan
|
|
|
278
283
|
dx1, dy1, dx2, dy2, dx3, dy3 = cmd[1..6]
|
|
279
284
|
control_x = x + dx1
|
|
280
285
|
control_y = y + dy1
|
|
281
|
-
|
|
282
|
-
|
|
286
|
+
x + dx1 + dx2
|
|
287
|
+
y + dy1 + dy2
|
|
283
288
|
end_x = x + dx1 + dx2 + dx3
|
|
284
289
|
end_y = y + dy1 + dy2 + dy3
|
|
285
290
|
|
|
@@ -288,7 +293,7 @@ module Fontisan
|
|
|
288
293
|
cx: control_x,
|
|
289
294
|
cy: control_y,
|
|
290
295
|
x: end_x,
|
|
291
|
-
y: end_y
|
|
296
|
+
y: end_y,
|
|
292
297
|
}
|
|
293
298
|
x = end_x
|
|
294
299
|
y = end_y
|
|
@@ -326,13 +331,13 @@ module Fontisan
|
|
|
326
331
|
# vhcurveto: dy1 dx2 dy2 dx3
|
|
327
332
|
# hvcurveto: dx1 dy2 dx3 dy3
|
|
328
333
|
if cmd[0] == :vhcurveto
|
|
329
|
-
dy1, dx2, dy2,
|
|
334
|
+
dy1, dx2, dy2, = cmd[1..4]
|
|
330
335
|
control_x = x
|
|
331
336
|
control_y = y + dy1
|
|
332
337
|
end_x = x + dx2
|
|
333
338
|
end_y = y + dy1 + dy2
|
|
334
339
|
else
|
|
335
|
-
dx1, dy2,
|
|
340
|
+
dx1, dy2, _, dy3 = cmd[1..4]
|
|
336
341
|
control_x = x + dx1
|
|
337
342
|
control_y = y
|
|
338
343
|
end_x = x + dx1 + dx2
|
|
@@ -344,7 +349,7 @@ module Fontisan
|
|
|
344
349
|
cx: control_x,
|
|
345
350
|
cy: control_y,
|
|
346
351
|
x: end_x,
|
|
347
|
-
y: end_y
|
|
352
|
+
y: end_y,
|
|
348
353
|
}
|
|
349
354
|
end
|
|
350
355
|
|
|
@@ -355,7 +360,7 @@ module Fontisan
|
|
|
355
360
|
# @param dy [Integer] Y offset
|
|
356
361
|
# @return [Array<Hash>] Transformed commands
|
|
357
362
|
def transform_commands(commands, dx, dy)
|
|
358
|
-
return commands if dx
|
|
363
|
+
return commands if dx.zero? && dy.zero?
|
|
359
364
|
|
|
360
365
|
commands.map do |cmd|
|
|
361
366
|
case cmd[:type]
|
|
@@ -367,7 +372,7 @@ module Fontisan
|
|
|
367
372
|
cx: cmd[:cx] + dx,
|
|
368
373
|
cy: cmd[:cy] + dy,
|
|
369
374
|
x: cmd[:x] + dx,
|
|
370
|
-
y: cmd[:y] + dy
|
|
375
|
+
y: cmd[:y] + dy,
|
|
371
376
|
}
|
|
372
377
|
when :curve_to
|
|
373
378
|
{
|
|
@@ -377,7 +382,7 @@ module Fontisan
|
|
|
377
382
|
cx2: cmd[:cx2] + dx,
|
|
378
383
|
cy2: cmd[:cy2] + dy,
|
|
379
384
|
x: cmd[:x] + dx,
|
|
380
|
-
y: cmd[:y] + dy
|
|
385
|
+
y: cmd[:y] + dy,
|
|
381
386
|
}
|
|
382
387
|
else
|
|
383
388
|
cmd # close_path, etc. pass through
|
|
@@ -428,8 +433,8 @@ module Fontisan
|
|
|
428
433
|
# Use hsbw to set initial position
|
|
429
434
|
dx = cmd[:x] - x
|
|
430
435
|
# hsbw is a two-byte operator: 12 34
|
|
431
|
-
charstring << encode_number(dx)
|
|
432
|
-
charstring << encode_number(0)
|
|
436
|
+
charstring << encode_number(dx) # sbw value
|
|
437
|
+
charstring << encode_number(0) # width (always 0 for decomposed glyphs)
|
|
433
438
|
charstring << 12 # First byte of two-byte operator
|
|
434
439
|
charstring << 34 # Second byte - hsbw
|
|
435
440
|
x = cmd[:x]
|
|
@@ -492,7 +497,7 @@ module Fontisan
|
|
|
492
497
|
[num + 139].pack("C*")
|
|
493
498
|
else
|
|
494
499
|
# Use escape sequence (255) followed by 2-byte signed integer
|
|
495
|
-
num += 32768 if num
|
|
500
|
+
num += 32768 if num.negative?
|
|
496
501
|
[255, num % 256, num >> 8].pack("C*")
|
|
497
502
|
end
|
|
498
503
|
end
|
|
@@ -136,13 +136,13 @@ module Fontisan
|
|
|
136
136
|
normalized_coords = @axis_normalizer.normalize(user_coords)
|
|
137
137
|
region_scalars = @region_matcher.match(normalized_coords)
|
|
138
138
|
|
|
139
|
-
glyph_ids.
|
|
140
|
-
|
|
139
|
+
glyph_ids.to_h do |glyph_id|
|
|
140
|
+
[glyph_id, {
|
|
141
141
|
outline_deltas: @glyph_delta_processor&.apply_deltas(glyph_id,
|
|
142
142
|
region_scalars),
|
|
143
143
|
metric_deltas: @metric_delta_processor.apply_deltas(glyph_id,
|
|
144
144
|
region_scalars),
|
|
145
|
-
}
|
|
145
|
+
}]
|
|
146
146
|
end
|
|
147
147
|
end
|
|
148
148
|
|
data/lib/fontisan/version.rb
CHANGED
data/lib/fontisan/woff2_font.rb
CHANGED
|
@@ -373,11 +373,11 @@ module Fontisan
|
|
|
373
373
|
raise InvalidFontError,
|
|
374
374
|
"Unknown WOFF2 flavor: 0x#{woff2.header.flavor.to_s(16)}"
|
|
375
375
|
end
|
|
376
|
+
rescue BinData::ValidityError, EOFError => e
|
|
377
|
+
raise InvalidFontError, "Invalid WOFF2 file: #{e.message}"
|
|
376
378
|
end
|
|
377
379
|
|
|
378
380
|
woff2
|
|
379
|
-
rescue BinData::ValidityError, EOFError => e
|
|
380
|
-
raise InvalidFontError, "Invalid WOFF2 file: #{e.message}"
|
|
381
381
|
end
|
|
382
382
|
|
|
383
383
|
# Read table directory from IO
|
data/lib/fontisan/woff_font.rb
CHANGED
|
@@ -98,10 +98,10 @@ module Fontisan
|
|
|
98
98
|
font.io_source = io
|
|
99
99
|
font.read_compressed_table_data(io)
|
|
100
100
|
font
|
|
101
|
+
rescue BinData::ValidityError, EOFError => e
|
|
102
|
+
Kernel.raise(::Fontisan::InvalidFontError,
|
|
103
|
+
"Invalid WOFF file: #{e.message}")
|
|
101
104
|
end
|
|
102
|
-
rescue BinData::ValidityError, EOFError => e
|
|
103
|
-
Kernel.raise(::Fontisan::InvalidFontError,
|
|
104
|
-
"Invalid WOFF file: #{e.message}")
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
# Initialize storage hashes
|
data/lib/fontisan.rb
CHANGED
|
@@ -33,10 +33,11 @@ require "bindata"
|
|
|
33
33
|
require "zlib"
|
|
34
34
|
require "stringio"
|
|
35
35
|
require "lutaml/model"
|
|
36
|
-
require "lutaml/model/xml_adapter/nokogiri_adapter"
|
|
37
36
|
|
|
38
37
|
# Configure lutaml-model to use Nokogiri adapter for XML serialization
|
|
39
|
-
Lutaml::Model::Config.
|
|
38
|
+
Lutaml::Model::Config.configure do |config|
|
|
39
|
+
config.xml_adapter_type = :nokogiri
|
|
40
|
+
end
|
|
40
41
|
|
|
41
42
|
# Core
|
|
42
43
|
require_relative "fontisan/version"
|