fontisan 0.4.30 → 0.4.31
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2a84b3003bce58bcd29e1ada7dd175235d6a57f90c062e70e8c6696f818b4a4a
|
|
4
|
+
data.tar.gz: e9890a2151d339ec8aee34bab3da816615afa1a19aade3739fb1928e2711158e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ee92245d86e3b325daa81caf2657c2dcad21c796a775ceea579482a85b5ba59e751f2f993e3ab9a5ac7af12ec8d6b1b698c4b8625fb968f44d5ebfc86434a7e
|
|
7
|
+
data.tar.gz: 5120ad6b71b8263c33fc0d22bf9a80d61377417aa95916ca2a205801aa1788fb8be958fa36c5e0cb428fa2ac4b7f155c83181c0a251d829a29fbfd6b9779d9c2
|
|
@@ -213,6 +213,7 @@ module Fontisan
|
|
|
213
213
|
|
|
214
214
|
hhea = @font.table("hhea")
|
|
215
215
|
maxp = @font.table("maxp")
|
|
216
|
+
head = @font.table("head")
|
|
216
217
|
num_h_metrics = hhea&.number_of_h_metrics || 1
|
|
217
218
|
num_glyphs = maxp&.num_glyphs || 0
|
|
218
219
|
|
|
@@ -220,11 +221,22 @@ module Fontisan
|
|
|
220
221
|
hmtx.parse_with_context(num_h_metrics, num_glyphs)
|
|
221
222
|
end
|
|
222
223
|
|
|
224
|
+
# Fallback advance width when hmtx lookup fails for a GID.
|
|
225
|
+
# Per the OpenType spec, glyphs at GID >= numberOfHMetrics
|
|
226
|
+
# inherit the last LongHorMetric's advanceWidth. If the table
|
|
227
|
+
# is empty or corrupt, fall back to the font's unitsPerEm.
|
|
228
|
+
fallback_width = if hmtx.respond_to?(:h_metrics) && hmtx.h_metrics&.any?
|
|
229
|
+
hmtx.h_metrics.last[:advance_width]
|
|
230
|
+
else
|
|
231
|
+
head&.units_per_em || 1000
|
|
232
|
+
end
|
|
233
|
+
|
|
223
234
|
num_glyphs.times do |gid|
|
|
224
|
-
metric = hmtx
|
|
225
|
-
|
|
235
|
+
metric = hmtx&.metric_for(gid)
|
|
236
|
+
aw = metric ? metric[:advance_width] : nil
|
|
237
|
+
widths[gid] = aw&.positive? ? aw : fallback_width
|
|
226
238
|
rescue StandardError
|
|
227
|
-
widths[gid] =
|
|
239
|
+
widths[gid] = fallback_width
|
|
228
240
|
end
|
|
229
241
|
widths
|
|
230
242
|
end
|
|
@@ -278,12 +290,45 @@ module Fontisan
|
|
|
278
290
|
flatten_compound_into(raw, glyph, cache, Set.new)
|
|
279
291
|
end
|
|
280
292
|
|
|
293
|
+
normalize_glyph_metrics!(glyph, cache[:head])
|
|
281
294
|
add_cmap_unicodes(gid, glyph)
|
|
282
295
|
glyph
|
|
283
296
|
rescue StandardError
|
|
284
297
|
nil
|
|
285
298
|
end
|
|
286
299
|
|
|
300
|
+
# Fix glyphs whose contours extend far left of the origin
|
|
301
|
+
# (massively negative LSB) or whose advance width was lost
|
|
302
|
+
# during donor hmtx extraction. Without this, Egyptian
|
|
303
|
+
# Hieroglyphs and similar donor glyphs overflow into the
|
|
304
|
+
# preceding character cell.
|
|
305
|
+
#
|
|
306
|
+
# Shifts all x-coordinates so xMin >= 0, then ensures the
|
|
307
|
+
# advance width covers the glyph's full visual extent.
|
|
308
|
+
def normalize_glyph_metrics!(glyph, head)
|
|
309
|
+
return if glyph.contours.empty?
|
|
310
|
+
|
|
311
|
+
bbox = glyph.bbox
|
|
312
|
+
return unless bbox
|
|
313
|
+
|
|
314
|
+
upm = head&.units_per_em || 1000
|
|
315
|
+
threshold = -(upm * 0.1).to_i
|
|
316
|
+
|
|
317
|
+
# Shift contours right if the glyph extends past the origin
|
|
318
|
+
if bbox.x_min < threshold
|
|
319
|
+
shift = -bbox.x_min.to_i
|
|
320
|
+
glyph.contours.each do |contour|
|
|
321
|
+
contour.points.each { |pt| pt.x = pt.x + shift }
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Ensure advance width is positive and covers the glyph
|
|
326
|
+
visual_width = glyph.bbox&.x_max&.to_i || upm
|
|
327
|
+
if glyph.width.to_i <= 0 || glyph.width.to_i < visual_width
|
|
328
|
+
glyph.width = [visual_width, upm].max
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
287
332
|
# Copy a SimpleGlyph's contours + points into a Ufo::Glyph.
|
|
288
333
|
def copy_simple_contours(simple, ufo_glyph)
|
|
289
334
|
num_contours = simple.end_pts_of_contours&.size || 0
|
|
@@ -14,15 +14,36 @@ module Fontisan
|
|
|
14
14
|
def self.build(font, glyphs:)
|
|
15
15
|
info = font.info
|
|
16
16
|
widths = glyphs.map { |g| g.width.to_i }
|
|
17
|
+
|
|
18
|
+
# Calculate real LSB/RSB/extent from glyph bounding boxes
|
|
19
|
+
# instead of hardcoding 0. These values help layout engines
|
|
20
|
+
# optimize text rendering and detect clipping.
|
|
21
|
+
min_lsb = nil
|
|
22
|
+
min_rsb = nil
|
|
23
|
+
max_extent = nil
|
|
24
|
+
|
|
25
|
+
glyphs.each do |glyph|
|
|
26
|
+
w = glyph.width.to_i
|
|
27
|
+
bbox = glyph.bbox
|
|
28
|
+
next unless bbox
|
|
29
|
+
|
|
30
|
+
xmin = bbox.x_min.to_i
|
|
31
|
+
xmax = bbox.x_max.to_i
|
|
32
|
+
min_lsb = xmin if min_lsb.nil? || xmin < min_lsb
|
|
33
|
+
rsb = w - xmax
|
|
34
|
+
min_rsb = rsb if min_rsb.nil? || rsb < min_rsb
|
|
35
|
+
max_extent = xmax if max_extent.nil? || xmax > max_extent
|
|
36
|
+
end
|
|
37
|
+
|
|
17
38
|
Fontisan::Tables::Hhea.new(
|
|
18
39
|
version_raw: VERSION_1_0,
|
|
19
40
|
ascent: info.ascender || 800,
|
|
20
41
|
descent: info.descender || -200,
|
|
21
42
|
line_gap: info.open_type_hhea_line_gap || 0,
|
|
22
43
|
advance_width_max: widths.max || 0,
|
|
23
|
-
min_left_side_bearing: 0,
|
|
24
|
-
min_right_side_bearing: 0,
|
|
25
|
-
x_max_extent:
|
|
44
|
+
min_left_side_bearing: min_lsb || 0,
|
|
45
|
+
min_right_side_bearing: min_rsb || 0,
|
|
46
|
+
x_max_extent: max_extent || 0,
|
|
26
47
|
caret_slope_rise: 1,
|
|
27
48
|
caret_slope_run: 0,
|
|
28
49
|
caret_offset: 0,
|
|
@@ -9,15 +9,26 @@ module Fontisan
|
|
|
9
9
|
# No trailing "leftSideBearing" array (use numberOfHMetrics = numGlyphs).
|
|
10
10
|
# @see https://learn.microsoft.com/en-us/typography/opentype/spec/hmtx
|
|
11
11
|
module Hmtx
|
|
12
|
-
# @param
|
|
12
|
+
# @param font [Fontisan::Ufo::Font]
|
|
13
13
|
# @param glyphs [Array<Fontisan::Ufo::Glyph>] in gid order
|
|
14
14
|
# @return [String] hmtx table bytes
|
|
15
|
-
def self.build(
|
|
15
|
+
def self.build(font, glyphs:)
|
|
16
|
+
upm = font.info.units_per_em&.to_i || 1000
|
|
16
17
|
data = +""
|
|
17
18
|
glyphs.each do |glyph|
|
|
18
19
|
bbox = glyph.bbox
|
|
19
20
|
lsb = bbox ? bbox.x_min.to_i : 0
|
|
20
|
-
|
|
21
|
+
width = glyph.width.to_i
|
|
22
|
+
|
|
23
|
+
# Safety net: never emit advance_width=0 for a non-empty
|
|
24
|
+
# glyph. This happens when donor hmtx parsing fails during
|
|
25
|
+
# stitching and the width wasn't caught upstream.
|
|
26
|
+
if width <= 0
|
|
27
|
+
width = bbox ? (bbox.x_max.to_i - bbox.x_min.to_i) : upm
|
|
28
|
+
width = [width, upm].max
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
data << [width, lsb].pack("nn")
|
|
21
32
|
end
|
|
22
33
|
data
|
|
23
34
|
end
|
|
@@ -5,11 +5,8 @@ module Fontisan
|
|
|
5
5
|
module Compile
|
|
6
6
|
# UFO → OTF. Uses CFF outlines (instead of TrueType glyf/loca).
|
|
7
7
|
# Maxp version 0.5 (no TrueType metrics); sfnt version OTTO.
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# that satisfies the OTTO signature but does NOT yet encode
|
|
11
|
-
# real charstrings. Full CFF construction lands when TODO 10
|
|
12
|
-
# ships.
|
|
8
|
+
# The CFF table is built by Compile::Cff which encodes real
|
|
9
|
+
# Type 2 charstrings from UFO contours via CharStringBuilder.
|
|
13
10
|
class OtfCompiler < BaseCompiler
|
|
14
11
|
SFNT_VERSION = SFNT_VERSION_OPEN_TYPE
|
|
15
12
|
|
data/lib/fontisan/ufo/point.rb
CHANGED
|
@@ -10,7 +10,8 @@ module Fontisan
|
|
|
10
10
|
# "offcurve" is the UFO 1/2 name; UFO 3 uses "qcurve". Both are
|
|
11
11
|
# accepted on read.
|
|
12
12
|
class Point
|
|
13
|
-
|
|
13
|
+
attr_accessor :x, :y
|
|
14
|
+
attr_reader :type, :smooth
|
|
14
15
|
|
|
15
16
|
def initialize(x:, y:, type:, smooth: false)
|
|
16
17
|
@x = x
|
data/lib/fontisan/version.rb
CHANGED