ttfunk 1.7.0 → 1.8.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
- 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
|