ttfunk 1.7.0 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +74 -0
- data/README.md +17 -15
- data/lib/ttfunk/aggregate.rb +5 -0
- data/lib/ttfunk/bin_utils.rb +27 -8
- data/lib/ttfunk/bit_field.rb +25 -2
- data/lib/ttfunk/collection.rb +27 -3
- data/lib/ttfunk/directory.rb +7 -1
- data/lib/ttfunk/encoded_string.rb +58 -4
- data/lib/ttfunk/max.rb +14 -0
- data/lib/ttfunk/min.rb +14 -0
- data/lib/ttfunk/one_based_array.rb +20 -0
- data/lib/ttfunk/otf_encoder.rb +5 -14
- data/lib/ttfunk/placeholder.rb +15 -1
- data/lib/ttfunk/reader.rb +6 -4
- data/lib/ttfunk/resource_file.rb +29 -5
- data/lib/ttfunk/sci_form.rb +20 -3
- data/lib/ttfunk/sub_table.rb +29 -4
- data/lib/ttfunk/subset/base.rb +48 -0
- data/lib/ttfunk/subset/code_page.rb +49 -2
- data/lib/ttfunk/subset/mac_roman.rb +2 -0
- data/lib/ttfunk/subset/unicode.rb +32 -0
- data/lib/ttfunk/subset/unicode_8bit.rb +32 -0
- data/lib/ttfunk/subset/windows_1252.rb +2 -0
- data/lib/ttfunk/subset.rb +8 -0
- data/lib/ttfunk/subset_collection.rb +39 -14
- data/lib/ttfunk/sum.rb +13 -0
- data/lib/ttfunk/table/cff/charset.rb +96 -18
- data/lib/ttfunk/table/cff/charsets/expert.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/expert_subset.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/iso_adobe.rb +3 -2
- data/lib/ttfunk/table/cff/charsets/standard_strings.rb +3 -2
- data/lib/ttfunk/table/cff/charsets.rb +1 -0
- data/lib/ttfunk/table/cff/charstring.rb +33 -12
- data/lib/ttfunk/table/cff/charstrings_index.rb +17 -11
- data/lib/ttfunk/table/cff/dict.rb +53 -23
- data/lib/ttfunk/table/cff/encoding.rb +82 -24
- data/lib/ttfunk/table/cff/encodings/expert.rb +3 -2
- data/lib/ttfunk/table/cff/encodings/standard.rb +3 -2
- data/lib/ttfunk/table/cff/encodings.rb +1 -0
- data/lib/ttfunk/table/cff/fd_selector.rb +61 -21
- data/lib/ttfunk/table/cff/font_dict.rb +30 -18
- data/lib/ttfunk/table/cff/font_index.rb +22 -10
- data/lib/ttfunk/table/cff/header.rb +16 -3
- data/lib/ttfunk/table/cff/index.rb +97 -65
- data/lib/ttfunk/table/cff/one_based_index.rb +11 -1
- data/lib/ttfunk/table/cff/path.rb +43 -4
- data/lib/ttfunk/table/cff/private_dict.rb +31 -11
- data/lib/ttfunk/table/cff/subr_index.rb +7 -2
- data/lib/ttfunk/table/cff/top_dict.rb +82 -59
- data/lib/ttfunk/table/cff/top_index.rb +10 -6
- data/lib/ttfunk/table/cff.rb +41 -21
- data/lib/ttfunk/table/cmap/format00.rb +27 -6
- data/lib/ttfunk/table/cmap/format04.rb +34 -14
- data/lib/ttfunk/table/cmap/format06.rb +28 -1
- data/lib/ttfunk/table/cmap/format10.rb +29 -2
- data/lib/ttfunk/table/cmap/format12.rb +29 -2
- data/lib/ttfunk/table/cmap/subtable.rb +50 -6
- data/lib/ttfunk/table/cmap.rb +21 -0
- data/lib/ttfunk/table/dsig.rb +47 -6
- data/lib/ttfunk/table/glyf/compound.rb +73 -6
- data/lib/ttfunk/table/glyf/path_based.rb +40 -3
- data/lib/ttfunk/table/glyf/simple.rb +50 -5
- data/lib/ttfunk/table/glyf.rb +15 -7
- data/lib/ttfunk/table/head.rb +84 -6
- data/lib/ttfunk/table/hhea.rb +71 -10
- data/lib/ttfunk/table/hmtx.rb +32 -5
- data/lib/ttfunk/table/kern/format0.rb +25 -7
- data/lib/ttfunk/table/kern.rb +16 -4
- data/lib/ttfunk/table/loca.rb +21 -8
- data/lib/ttfunk/table/maxp.rb +195 -10
- data/lib/ttfunk/table/name.rb +126 -9
- data/lib/ttfunk/table/os2.rb +150 -26
- data/lib/ttfunk/table/post/format10.rb +7 -0
- data/lib/ttfunk/table/post/format20.rb +9 -0
- data/lib/ttfunk/table/post/format30.rb +6 -0
- data/lib/ttfunk/table/post/format40.rb +5 -0
- data/lib/ttfunk/table/post.rb +63 -7
- data/lib/ttfunk/table/sbix.rb +50 -14
- data/lib/ttfunk/table/simple.rb +5 -0
- data/lib/ttfunk/table/vorg.rb +31 -3
- data/lib/ttfunk/table.rb +20 -1
- data/lib/ttfunk/ttf_encoder.rb +39 -41
- data/lib/ttfunk.rb +154 -1
- data.tar.gz.sig +0 -0
- metadata +50 -28
- metadata.gz.sig +0 -0
data/lib/ttfunk/table/head.rb
CHANGED
@@ -4,27 +4,91 @@ require_relative '../table'
|
|
4
4
|
|
5
5
|
module TTFunk
|
6
6
|
class Table
|
7
|
+
# Font Header (`head`) Table.
|
7
8
|
class Head < TTFunk::Table
|
9
|
+
# Table version.
|
10
|
+
# @return [Integer]
|
8
11
|
attr_reader :version
|
12
|
+
|
13
|
+
# Font revision.
|
14
|
+
# @return [Integer]
|
9
15
|
attr_reader :font_revision
|
16
|
+
|
17
|
+
# Checksum adjustment.
|
18
|
+
# @return [Integer]
|
10
19
|
attr_reader :checksum_adjustment
|
20
|
+
|
21
|
+
# Magic number.
|
22
|
+
# @return [Integer] must be `0x5F0F3CF5`
|
11
23
|
attr_reader :magic_number
|
24
|
+
|
25
|
+
# Flags.
|
26
|
+
# @return [Integer]
|
12
27
|
attr_reader :flags
|
28
|
+
|
29
|
+
# Units per Em.
|
30
|
+
# @return [Integer]
|
13
31
|
attr_reader :units_per_em
|
32
|
+
|
33
|
+
# Font creation time.
|
34
|
+
# @return [Integer] Long Date Time timestamp.
|
14
35
|
attr_reader :created
|
36
|
+
|
37
|
+
# Font modification time.
|
38
|
+
# @return [Integer] Long Date Time timestamp.
|
15
39
|
attr_reader :modified
|
40
|
+
|
41
|
+
# Minimum x coordinate across all glyph bounding boxes.
|
42
|
+
# @return [Integer]
|
16
43
|
attr_reader :x_min
|
44
|
+
|
45
|
+
# Minimum y coordinate across all glyph bounding boxes.
|
46
|
+
# @return [Integer]
|
17
47
|
attr_reader :y_min
|
48
|
+
|
49
|
+
# Maximum x coordinate across all glyph bounding boxes.
|
50
|
+
# @return [Integer]
|
18
51
|
attr_reader :x_max
|
52
|
+
|
53
|
+
# Maximum y coordinate across all glyph bounding boxes.
|
54
|
+
# @return [Integer]
|
19
55
|
attr_reader :y_max
|
56
|
+
|
57
|
+
# Mac font style.
|
58
|
+
# @return [Integer]
|
20
59
|
attr_reader :mac_style
|
60
|
+
|
61
|
+
# Smallest readable size in pixels.
|
62
|
+
# @return [Integer]
|
21
63
|
attr_reader :lowest_rec_ppem
|
64
|
+
|
65
|
+
# Font direction hint. Deprecated, set to 2.
|
66
|
+
# @return [Integer]
|
22
67
|
attr_reader :font_direction_hint
|
68
|
+
|
69
|
+
# Index to Location format.
|
70
|
+
# @return [Integer]
|
23
71
|
attr_reader :index_to_loc_format
|
72
|
+
|
73
|
+
# Glyph data format.
|
74
|
+
# @return [Integer]
|
24
75
|
attr_reader :glyph_data_format
|
25
76
|
|
26
77
|
class << self
|
27
|
-
#
|
78
|
+
# Long date time (used in TTF headers).
|
79
|
+
# January 1, 1904 00:00:00 UTC basis used by Long date time.
|
80
|
+
# @see https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html
|
81
|
+
# TrueType Font Tables
|
82
|
+
LONG_DATE_TIME_BASIS = Time.new(1904, 1, 1, 0, 0, 0, 0).to_i
|
83
|
+
private_constant :LONG_DATE_TIME_BASIS
|
84
|
+
|
85
|
+
# Encode table.
|
86
|
+
#
|
87
|
+
# @param head [TTFunk::Table::Head]
|
88
|
+
# @param loca [Hash] result of encoding Index to Location (`loca`) table
|
89
|
+
# @param mapping [Hash{Integer => Integer}] keys are new glyph IDs, values
|
90
|
+
# are old glyph IDs
|
91
|
+
# @return [EncodedString]
|
28
92
|
def encode(head, loca, mapping)
|
29
93
|
EncodedString.new do |table|
|
30
94
|
table <<
|
@@ -36,11 +100,25 @@ module TTFunk
|
|
36
100
|
head.created, head.modified,
|
37
101
|
*min_max_values_for(head, mapping),
|
38
102
|
head.mac_style, head.lowest_rec_ppem, head.font_direction_hint,
|
39
|
-
loca[:type] || 0, head.glyph_data_format
|
40
|
-
].pack('
|
103
|
+
loca[:type] || 0, head.glyph_data_format,
|
104
|
+
].pack('Nn2q>2n*')
|
41
105
|
end
|
42
106
|
end
|
43
107
|
|
108
|
+
# Convert Long Date Time timestamp to Time.
|
109
|
+
# @param ldt [Float, Integer]
|
110
|
+
# @return [Time]
|
111
|
+
def from_long_date_time(ldt)
|
112
|
+
Time.at(ldt + LONG_DATE_TIME_BASIS, in: 'UTC')
|
113
|
+
end
|
114
|
+
|
115
|
+
# Convert Time to Long Date Time timestamp
|
116
|
+
# @param time [Time]
|
117
|
+
# @return [Integer]
|
118
|
+
def to_long_date_time(time)
|
119
|
+
Integer(time) - LONG_DATE_TIME_BASIS
|
120
|
+
end
|
121
|
+
|
44
122
|
private
|
45
123
|
|
46
124
|
def min_max_values_for(head, mapping)
|
@@ -49,7 +127,7 @@ module TTFunk
|
|
49
127
|
y_min = Min.new
|
50
128
|
y_max = Max.new
|
51
129
|
|
52
|
-
mapping.
|
130
|
+
mapping.each_value do |old_glyph_id|
|
53
131
|
glyph = head.file.find_glyph(old_glyph_id)
|
54
132
|
next unless glyph
|
55
133
|
|
@@ -61,7 +139,7 @@ module TTFunk
|
|
61
139
|
|
62
140
|
[
|
63
141
|
x_min.value_or(0), y_min.value_or(0),
|
64
|
-
x_max.value_or(0), y_max.value_or(0)
|
142
|
+
x_max.value_or(0), y_max.value_or(0),
|
65
143
|
]
|
66
144
|
end
|
67
145
|
end
|
@@ -70,7 +148,7 @@ module TTFunk
|
|
70
148
|
|
71
149
|
def parse!
|
72
150
|
@version, @font_revision, @check_sum_adjustment, @magic_number,
|
73
|
-
@flags, @units_per_em, @created, @modified = read(36, '
|
151
|
+
@flags, @units_per_em, @created, @modified = read(36, 'N4n2q>2')
|
74
152
|
|
75
153
|
@x_min, @y_min, @x_max, @y_max = read_signed(4)
|
76
154
|
|
data/lib/ttfunk/table/hhea.rb
CHANGED
@@ -4,30 +4,92 @@ require_relative '../table'
|
|
4
4
|
|
5
5
|
module TTFunk
|
6
6
|
class Table
|
7
|
+
# Horizontal Header (`hhea`) table.
|
7
8
|
class Hhea < Table
|
9
|
+
# Table version
|
10
|
+
# @return [Integer]
|
8
11
|
attr_reader :version
|
12
|
+
|
13
|
+
# Typographic ascent.
|
14
|
+
# @return [Integer]
|
9
15
|
attr_reader :ascent
|
16
|
+
|
17
|
+
# Typographic descent.
|
18
|
+
# @return [Integer]
|
10
19
|
attr_reader :descent
|
20
|
+
|
21
|
+
# Typographic line gap.
|
22
|
+
# @return [Integer]
|
11
23
|
attr_reader :line_gap
|
24
|
+
|
25
|
+
# Maximum advance width value in `hmtx` table.
|
26
|
+
# @return [Integer]
|
12
27
|
attr_reader :advance_width_max
|
28
|
+
|
29
|
+
# Minimum left sidebearing value in `hmtx` table for glyphs with contours
|
30
|
+
# (empty glyphs should be ignored).
|
31
|
+
# @return [Integer]
|
13
32
|
attr_reader :min_left_side_bearing
|
33
|
+
|
34
|
+
# Minimum right sidebearing value.
|
35
|
+
# @return [Integer]
|
14
36
|
attr_reader :min_right_side_bearing
|
37
|
+
|
38
|
+
# Maximum extent.
|
39
|
+
# @return [Integer]
|
15
40
|
attr_reader :x_max_extent
|
16
|
-
|
17
|
-
|
41
|
+
|
42
|
+
# Caret slope rise.
|
43
|
+
# @return [Integer]
|
44
|
+
attr_reader :caret_slope_rise
|
45
|
+
|
46
|
+
# @deprecated Use {caret_slope_rise} instead.
|
47
|
+
# @!parse attr_reader :carot_slope_rise
|
48
|
+
# @return [Integer]
|
49
|
+
def carot_slope_rise
|
50
|
+
@caret_slope_rise
|
51
|
+
end
|
52
|
+
|
53
|
+
# Caret slope run.
|
54
|
+
# @return [Integer]
|
55
|
+
attr_reader :caret_slope_run
|
56
|
+
|
57
|
+
# @deprecated Use {caret_slope_run} instead.
|
58
|
+
# @!parse attr_reader :carot_slope_run
|
59
|
+
# @return [Integer]
|
60
|
+
def carot_slope_run
|
61
|
+
@caret_slope_run
|
62
|
+
end
|
63
|
+
|
64
|
+
# Caret offset.
|
65
|
+
# @return [Integer]
|
18
66
|
attr_reader :caret_offset
|
67
|
+
|
68
|
+
# Metric data format. `0` for current format.
|
69
|
+
# @return [Integer]
|
19
70
|
attr_reader :metric_data_format
|
71
|
+
|
72
|
+
# Number of hMetric entries in `hmtx` table.
|
73
|
+
# @return [Integer]
|
20
74
|
attr_reader :number_of_metrics
|
21
75
|
|
22
76
|
class << self
|
77
|
+
# Encode table.
|
78
|
+
#
|
79
|
+
# @param hhea [TTFunk::Table::Hhea] table to encode.
|
80
|
+
# @param hmtx [TTFunk::Table::Hmtx]
|
81
|
+
# @param original [TTFunk::File] original font file.
|
82
|
+
# @param mapping [Hash{Integer => Integer}] keys are new glyph IDs, values
|
83
|
+
# are old glyph IDs
|
84
|
+
# @return [String]
|
23
85
|
def encode(hhea, hmtx, original, mapping)
|
24
86
|
''.b.tap do |table|
|
25
87
|
table << [hhea.version].pack('N')
|
26
88
|
table << [
|
27
89
|
hhea.ascent, hhea.descent, hhea.line_gap,
|
28
90
|
*min_max_values_for(original, mapping),
|
29
|
-
hhea.
|
30
|
-
0, 0, 0, 0, hhea.metric_data_format, hmtx[:number_of_metrics]
|
91
|
+
hhea.caret_slope_rise, hhea.caret_slope_run, hhea.caret_offset,
|
92
|
+
0, 0, 0, 0, hhea.metric_data_format, hmtx[:number_of_metrics],
|
31
93
|
].pack('n*')
|
32
94
|
end
|
33
95
|
end
|
@@ -40,7 +102,7 @@ module TTFunk
|
|
40
102
|
max_aw = Max.new
|
41
103
|
max_extent = Max.new
|
42
104
|
|
43
|
-
mapping.
|
105
|
+
mapping.each_value do |old_glyph_id|
|
44
106
|
horiz_metrics = original.horizontal_metrics.for(old_glyph_id)
|
45
107
|
next unless horiz_metrics
|
46
108
|
|
@@ -52,15 +114,14 @@ module TTFunk
|
|
52
114
|
|
53
115
|
x_delta = glyph.x_max - glyph.x_min
|
54
116
|
|
55
|
-
min_rsb << horiz_metrics.advance_width -
|
56
|
-
horiz_metrics.left_side_bearing - x_delta
|
117
|
+
min_rsb << (horiz_metrics.advance_width - horiz_metrics.left_side_bearing - x_delta)
|
57
118
|
|
58
|
-
max_extent << horiz_metrics.left_side_bearing + x_delta
|
119
|
+
max_extent << (horiz_metrics.left_side_bearing + x_delta)
|
59
120
|
end
|
60
121
|
|
61
122
|
[
|
62
123
|
max_aw.value_or(0), min_lsb.value_or(0),
|
63
|
-
min_rsb.value_or(0), max_extent.value_or(0)
|
124
|
+
min_rsb.value_or(0), max_extent.value_or(0),
|
64
125
|
]
|
65
126
|
end
|
66
127
|
end
|
@@ -73,7 +134,7 @@ module TTFunk
|
|
73
134
|
@advance_width_max = read(2, 'n').first
|
74
135
|
|
75
136
|
@min_left_side_bearing, @min_right_side_bearing, @x_max_extent,
|
76
|
-
@
|
137
|
+
@caret_slope_rise, @caret_slope_run, @caret_offset,
|
77
138
|
_reserved, _reserved, _reserved, _reserved,
|
78
139
|
@metric_data_format = read_signed(11)
|
79
140
|
|
data/lib/ttfunk/table/hmtx.rb
CHANGED
@@ -4,32 +4,59 @@ require_relative '../table'
|
|
4
4
|
|
5
5
|
module TTFunk
|
6
6
|
class Table
|
7
|
+
# Horizontal Metrics (`hmtx`) table.
|
7
8
|
class Hmtx < Table
|
9
|
+
# Glyph horizontal metrics.
|
10
|
+
# @return [Array<HorizontalMetric>]
|
8
11
|
attr_reader :metrics
|
12
|
+
|
13
|
+
# Left side bearings.
|
14
|
+
# @return [Array<Ingteger>]
|
9
15
|
attr_reader :left_side_bearings
|
16
|
+
|
17
|
+
# Glyph widths.
|
18
|
+
# @return [Array<Integer>]
|
10
19
|
attr_reader :widths
|
11
20
|
|
21
|
+
# Encode table.
|
22
|
+
#
|
23
|
+
# @param hmtx [TTFunk::Table::Hmtx]
|
24
|
+
# @param mapping [Hash{Integer => Integer}] keys are new glyph IDs, values
|
25
|
+
# are old glyph IDs
|
26
|
+
# @return [Hash{:number_of_metrics => Integer, :table => String}]
|
27
|
+
# * `:number_of_metrics` - number of mertrics is the table.
|
28
|
+
# * `:table` - encoded table.
|
12
29
|
def self.encode(hmtx, mapping)
|
13
30
|
metrics =
|
14
|
-
mapping.keys.sort.map
|
31
|
+
mapping.keys.sort.map { |new_id|
|
15
32
|
metric = hmtx.for(mapping[new_id])
|
16
33
|
[metric.advance_width, metric.left_side_bearing]
|
17
|
-
|
34
|
+
}
|
18
35
|
|
19
36
|
{
|
20
37
|
number_of_metrics: metrics.length,
|
21
|
-
table: metrics.flatten.pack('n*')
|
38
|
+
table: metrics.flatten.pack('n*'),
|
22
39
|
}
|
23
40
|
end
|
24
41
|
|
42
|
+
# Horyzontal glyph metric.
|
43
|
+
#
|
44
|
+
# @!attribute [rw] advance_width
|
45
|
+
# @return [Integer] Advance width.
|
46
|
+
# @!attribute [rw] left_side_bearing
|
47
|
+
# @return [Integer] Left side bearing.
|
25
48
|
HorizontalMetric = Struct.new(:advance_width, :left_side_bearing)
|
26
49
|
|
50
|
+
# Get horizontal metric for glyph.
|
51
|
+
#
|
52
|
+
# @param glyph_id [Integer]
|
53
|
+
# @return [HorizontalMetric]
|
27
54
|
def for(glyph_id)
|
28
55
|
@metrics[glyph_id] ||
|
29
56
|
metrics_cache[glyph_id] ||=
|
30
57
|
HorizontalMetric.new(
|
31
58
|
@metrics.last.advance_width,
|
32
|
-
@left_side_bearings[glyph_id - @metrics.length]
|
59
|
+
@left_side_bearings[glyph_id - @metrics.length],
|
33
60
|
)
|
34
61
|
end
|
35
62
|
|
@@ -45,7 +72,7 @@ module TTFunk
|
|
45
72
|
file.horizontal_header.number_of_metrics.times do
|
46
73
|
advance = read(2, 'n').first
|
47
74
|
lsb = read_signed(1).first
|
48
|
-
@metrics.push
|
75
|
+
@metrics.push(HorizontalMetric.new(advance, lsb))
|
49
76
|
end
|
50
77
|
|
51
78
|
lsb_count = file.maximum_profile.num_glyphs -
|
@@ -5,12 +5,19 @@ require_relative '../../reader'
|
|
5
5
|
module TTFunk
|
6
6
|
class Table
|
7
7
|
class Kern
|
8
|
+
# Format 0 kerning subtable.
|
8
9
|
class Format0
|
9
10
|
include Reader
|
10
11
|
|
12
|
+
# Subtable attributes.
|
13
|
+
# @return [Hash{Symbol => any}]
|
11
14
|
attr_reader :attributes
|
15
|
+
|
16
|
+
# Kerning pairs.
|
17
|
+
# @return [Hash{Array(Integer, Integer) => Integer}]
|
12
18
|
attr_reader :pairs
|
13
19
|
|
20
|
+
# @param attributes [Hash{Symbol => any}]
|
14
21
|
def initialize(attributes = {})
|
15
22
|
@attributes = attributes
|
16
23
|
|
@@ -19,27 +26,38 @@ module TTFunk
|
|
19
26
|
@pairs = {}
|
20
27
|
num_pairs.times do |i|
|
21
28
|
# sanity check, in case there's a bad length somewhere
|
22
|
-
break if i * 3 + 2 > pairs.length
|
29
|
+
break if (i * 3) + 2 > pairs.length
|
23
30
|
|
24
31
|
left = pairs[i * 3]
|
25
|
-
right = pairs[i * 3 + 1]
|
26
|
-
value = to_signed(pairs[i * 3 + 2])
|
32
|
+
right = pairs[(i * 3) + 1]
|
33
|
+
value = to_signed(pairs[(i * 3) + 2])
|
27
34
|
@pairs[[left, right]] = value
|
28
35
|
end
|
29
36
|
end
|
30
37
|
|
38
|
+
# Is this vertical kerning?
|
39
|
+
# @return [Boolean]
|
31
40
|
def vertical?
|
32
41
|
@attributes[:vertical]
|
33
42
|
end
|
34
43
|
|
44
|
+
# Is this horizontal kerning?
|
45
|
+
# @return [Boolean]
|
35
46
|
def horizontal?
|
36
47
|
!vertical?
|
37
48
|
end
|
38
49
|
|
50
|
+
# Is this cross-stream kerning?
|
51
|
+
# @return [Boolean]
|
39
52
|
def cross_stream?
|
40
53
|
@attributes[:cross]
|
41
54
|
end
|
42
55
|
|
56
|
+
# Recode this subtable using the specified mapping.
|
57
|
+
#
|
58
|
+
# @param mapping [Hash{Integer => Integer}] keys are new glyph IDs,
|
59
|
+
# values are old glyph IDs
|
60
|
+
# @return [String]
|
43
61
|
def recode(mapping)
|
44
62
|
subset = []
|
45
63
|
pairs.each do |(left, right), value|
|
@@ -51,19 +69,19 @@ module TTFunk
|
|
51
69
|
return if subset.empty?
|
52
70
|
|
53
71
|
num_pairs = subset.length
|
54
|
-
search_range = 2 * 2**(Math.log(num_pairs) / Math.log(2))
|
55
|
-
entry_selector = (Math.log(search_range / 2) / Math.log(2))
|
72
|
+
search_range = 2 * (2**Integer(Math.log(num_pairs) / Math.log(2)))
|
73
|
+
entry_selector = Integer(Math.log(search_range / 2) / Math.log(2))
|
56
74
|
range_shift = (2 * num_pairs) - search_range
|
57
75
|
|
58
76
|
[
|
59
77
|
attributes[:version],
|
60
|
-
num_pairs * 6 + 14,
|
78
|
+
(num_pairs * 6) + 14,
|
61
79
|
attributes[:coverage],
|
62
80
|
num_pairs,
|
63
81
|
search_range,
|
64
82
|
entry_selector,
|
65
83
|
range_shift,
|
66
|
-
subset
|
84
|
+
subset,
|
67
85
|
].flatten.pack('n*')
|
68
86
|
end
|
69
87
|
end
|
data/lib/ttfunk/table/kern.rb
CHANGED
@@ -4,14 +4,26 @@ require_relative '../table'
|
|
4
4
|
|
5
5
|
module TTFunk
|
6
6
|
class Table
|
7
|
+
# Kerning (`kern`) table
|
7
8
|
class Kern < Table
|
9
|
+
# Table version
|
10
|
+
# @return [Integer]
|
8
11
|
attr_reader :version
|
12
|
+
|
13
|
+
# Subtables.
|
14
|
+
# @return [Array<TTFunk::Table::Kern::Format0>]
|
9
15
|
attr_reader :tables
|
10
16
|
|
17
|
+
# Encode table.
|
18
|
+
#
|
19
|
+
# @param kerning [TTFunk::Table::Kern]
|
20
|
+
# @param mapping [Hash{Integer => Integer}] keys are new glyph IDs, values
|
21
|
+
# are old glyph IDs
|
22
|
+
# @return [String, nil]
|
11
23
|
def self.encode(kerning, mapping)
|
12
24
|
return unless kerning.exists? && kerning.tables.any?
|
13
25
|
|
14
|
-
tables = kerning.tables.
|
26
|
+
tables = kerning.tables.filter_map { |table| table.recode(mapping) }
|
15
27
|
return if tables.empty?
|
16
28
|
|
17
29
|
[0, tables.length, tables.join].pack('nnA*')
|
@@ -52,11 +64,11 @@ module TTFunk
|
|
52
64
|
version: version,
|
53
65
|
length: length,
|
54
66
|
coverage: coverage,
|
55
|
-
data: raw[10
|
67
|
+
data: raw[10..],
|
56
68
|
vertical: (coverage & 0x1).zero?,
|
57
69
|
minimum: (coverage & 0x2 != 0),
|
58
70
|
cross: (coverage & 0x4 != 0),
|
59
|
-
override: (coverage & 0x8 != 0)
|
71
|
+
override: (coverage & 0x8 != 0),
|
60
72
|
)
|
61
73
|
end
|
62
74
|
|
@@ -73,7 +85,7 @@ module TTFunk
|
|
73
85
|
data: io.read(length - 8),
|
74
86
|
vertical: (coverage & 0x8000 != 0),
|
75
87
|
cross: (coverage & 0x4000 != 0),
|
76
|
-
variation: (coverage & 0x2000 != 0)
|
88
|
+
variation: (coverage & 0x2000 != 0),
|
77
89
|
)
|
78
90
|
end
|
79
91
|
end
|
data/lib/ttfunk/table/loca.rb
CHANGED
@@ -4,22 +4,27 @@ require_relative '../table'
|
|
4
4
|
|
5
5
|
module TTFunk
|
6
6
|
class Table
|
7
|
+
# Index to Location table.
|
7
8
|
class Loca < Table
|
9
|
+
# Glyph ofsets
|
10
|
+
# @return [Array<Integer>]
|
8
11
|
attr_reader :offsets
|
9
12
|
|
10
|
-
#
|
11
|
-
# glyph id with that index.
|
13
|
+
# Encode table.
|
12
14
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
15
|
+
# @param offsets [Array<Integer>] an array of offsets, with each index
|
16
|
+
# corresponding to the glyph id with that index.
|
17
|
+
# @return [Hash] result hash:
|
18
|
+
# * `:type` - the type of offset (to be encoded in the 'head' table):
|
19
|
+
# * `0` - short offsets
|
20
|
+
# * `1` - long offsets
|
21
|
+
# * `:table` - encoded bytes
|
17
22
|
def self.encode(offsets)
|
18
23
|
long_offsets =
|
19
|
-
offsets.any?
|
24
|
+
offsets.any? { |offset|
|
20
25
|
short_offset = offset / 2
|
21
26
|
short_offset * 2 != offset || short_offset > 0xffff
|
22
|
-
|
27
|
+
}
|
23
28
|
|
24
29
|
if long_offsets
|
25
30
|
{ type: 1, table: offsets.pack('N*') }
|
@@ -28,10 +33,18 @@ module TTFunk
|
|
28
33
|
end
|
29
34
|
end
|
30
35
|
|
36
|
+
# Glyph offset by ID.
|
37
|
+
#
|
38
|
+
# @param glyph_id [Integer]
|
39
|
+
# @return [Integer] - offset of the glyph in the `glyf` table
|
31
40
|
def index_of(glyph_id)
|
32
41
|
@offsets[glyph_id]
|
33
42
|
end
|
34
43
|
|
44
|
+
# Size of encoded glyph.
|
45
|
+
#
|
46
|
+
# @param glyph_id [Integer]
|
47
|
+
# @return [Integer]
|
35
48
|
def size_of(glyph_id)
|
36
49
|
@offsets[glyph_id + 1] - @offsets[glyph_id]
|
37
50
|
end
|