ttfunk 1.5.1 → 1.6.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +60 -0
- data/README.md +2 -1
- data/lib/ttfunk.rb +45 -0
- data/lib/ttfunk/aggregate.rb +15 -0
- data/lib/ttfunk/bin_utils.rb +47 -0
- data/lib/ttfunk/bit_field.rb +31 -0
- data/lib/ttfunk/collection.rb +3 -1
- data/lib/ttfunk/directory.rb +6 -0
- data/lib/ttfunk/encoded_string.rb +97 -0
- data/lib/ttfunk/max.rb +25 -0
- data/lib/ttfunk/min.rb +25 -0
- data/lib/ttfunk/one_based_array.rb +36 -0
- data/lib/ttfunk/otf_encoder.rb +61 -0
- data/lib/ttfunk/placeholder.rb +13 -0
- data/lib/ttfunk/reader.rb +34 -32
- data/lib/ttfunk/resource_file.rb +7 -5
- data/lib/ttfunk/sci_form.rb +29 -0
- data/lib/ttfunk/sub_table.rb +38 -0
- data/lib/ttfunk/subset.rb +2 -0
- data/lib/ttfunk/subset/base.rb +61 -120
- data/lib/ttfunk/subset/code_page.rb +89 -0
- data/lib/ttfunk/subset/mac_roman.rb +5 -42
- data/lib/ttfunk/subset/unicode.rb +12 -6
- data/lib/ttfunk/subset/unicode_8bit.rb +14 -12
- data/lib/ttfunk/subset/windows_1252.rb +5 -47
- data/lib/ttfunk/subset_collection.rb +4 -0
- data/lib/ttfunk/sum.rb +20 -0
- data/lib/ttfunk/table.rb +4 -0
- data/lib/ttfunk/table/cff.rb +69 -0
- data/lib/ttfunk/table/cff/charset.rb +212 -0
- data/lib/ttfunk/table/cff/charsets.rb +14 -0
- data/lib/ttfunk/table/cff/charsets/expert.rb +189 -0
- data/lib/ttfunk/table/cff/charsets/expert_subset.rb +119 -0
- data/lib/ttfunk/table/cff/charsets/iso_adobe.rb +241 -0
- data/lib/ttfunk/table/cff/charsets/standard_strings.rb +404 -0
- data/lib/ttfunk/table/cff/charstring.rb +487 -0
- data/lib/ttfunk/table/cff/charstrings_index.rb +39 -0
- data/lib/ttfunk/table/cff/dict.rb +266 -0
- data/lib/ttfunk/table/cff/encoding.rb +220 -0
- data/lib/ttfunk/table/cff/encodings.rb +12 -0
- data/lib/ttfunk/table/cff/encodings/expert.rb +206 -0
- data/lib/ttfunk/table/cff/encodings/standard.rb +181 -0
- data/lib/ttfunk/table/cff/fd_selector.rb +150 -0
- data/lib/ttfunk/table/cff/font_dict.rb +79 -0
- data/lib/ttfunk/table/cff/font_index.rb +29 -0
- data/lib/ttfunk/table/cff/header.rb +33 -0
- data/lib/ttfunk/table/cff/index.rb +125 -0
- data/lib/ttfunk/table/cff/one_based_index.rb +31 -0
- data/lib/ttfunk/table/cff/path.rb +66 -0
- data/lib/ttfunk/table/cff/private_dict.rb +84 -0
- data/lib/ttfunk/table/cff/subr_index.rb +19 -0
- data/lib/ttfunk/table/cff/top_dict.rb +230 -0
- data/lib/ttfunk/table/cff/top_index.rb +16 -0
- data/lib/ttfunk/table/cmap.rb +4 -4
- data/lib/ttfunk/table/cmap/format00.rb +1 -2
- data/lib/ttfunk/table/cmap/format04.rb +11 -3
- data/lib/ttfunk/table/cmap/format06.rb +2 -0
- data/lib/ttfunk/table/cmap/format10.rb +2 -0
- data/lib/ttfunk/table/cmap/format12.rb +2 -0
- data/lib/ttfunk/table/cmap/subtable.rb +12 -8
- data/lib/ttfunk/table/dsig.rb +50 -0
- data/lib/ttfunk/table/glyf.rb +11 -9
- data/lib/ttfunk/table/glyf/compound.rb +14 -7
- data/lib/ttfunk/table/glyf/path_based.rb +47 -0
- data/lib/ttfunk/table/glyf/simple.rb +21 -15
- data/lib/ttfunk/table/head.rb +43 -5
- data/lib/ttfunk/table/hhea.rb +47 -4
- data/lib/ttfunk/table/hmtx.rb +11 -4
- data/lib/ttfunk/table/kern.rb +3 -0
- data/lib/ttfunk/table/kern/format0.rb +3 -0
- data/lib/ttfunk/table/loca.rb +2 -0
- data/lib/ttfunk/table/maxp.rb +144 -10
- data/lib/ttfunk/table/name.rb +75 -37
- data/lib/ttfunk/table/os2.rb +327 -4
- data/lib/ttfunk/table/post.rb +8 -1
- data/lib/ttfunk/table/post/format10.rb +2 -0
- data/lib/ttfunk/table/post/format20.rb +5 -1
- data/lib/ttfunk/table/post/format30.rb +2 -0
- data/lib/ttfunk/table/post/format40.rb +2 -0
- data/lib/ttfunk/table/sbix.rb +2 -0
- data/lib/ttfunk/table/simple.rb +2 -0
- data/lib/ttfunk/table/vorg.rb +54 -0
- data/lib/ttfunk/ttf_encoder.rb +220 -0
- metadata +88 -20
- metadata.gz.sig +0 -0
- data/lib/ttfunk/encoding/mac_roman.rb +0 -100
- data/lib/ttfunk/encoding/windows_1252.rb +0 -76
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class OneBasedArray
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(size = 0)
|
8
|
+
@entries = Array.new(size)
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](idx)
|
12
|
+
if idx == 0
|
13
|
+
raise IndexError,
|
14
|
+
"index #{idx} was outside the bounds of the array"
|
15
|
+
end
|
16
|
+
|
17
|
+
entries[idx - 1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def size
|
21
|
+
entries.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_ary
|
25
|
+
entries
|
26
|
+
end
|
27
|
+
|
28
|
+
def each(&block)
|
29
|
+
entries.each(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :entries
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class OTFEncoder < TTFEncoder
|
5
|
+
OPTIMAL_TABLE_ORDER = [
|
6
|
+
'head', 'hhea', 'maxp', 'OS/2', 'name', 'cmap', 'post', 'CFF '
|
7
|
+
].freeze
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# CFF fonts don't maintain a glyf table, all glyph information is stored
|
12
|
+
# in the charstrings index. Return an empty hash here to indicate a glyf
|
13
|
+
# table should not be encoded.
|
14
|
+
def glyf_table
|
15
|
+
@glyf_table ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Since CFF fonts don't maintain a glyf table, they also don't maintain
|
19
|
+
# a loca table. Return an empty hash here to indicate a loca table
|
20
|
+
# shouldn't be encoded.
|
21
|
+
def loca_table
|
22
|
+
@loca_table ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def base_table
|
26
|
+
@base_table ||= TTFunk::Table::Simple.new(original, 'BASE').raw
|
27
|
+
end
|
28
|
+
|
29
|
+
def cff_table
|
30
|
+
@cff_table ||= original.cff.encode(new_to_old_glyph, old_to_new_glyph)
|
31
|
+
end
|
32
|
+
|
33
|
+
def vorg_table
|
34
|
+
@vorg_table ||= TTFunk::Table::Vorg.encode(original.vertical_origins)
|
35
|
+
end
|
36
|
+
|
37
|
+
def tables
|
38
|
+
@tables ||= super.merge(
|
39
|
+
'BASE' => base_table,
|
40
|
+
'VORG' => vorg_table,
|
41
|
+
'CFF ' => cff_table
|
42
|
+
).reject { |_tag, table| table.nil? }
|
43
|
+
end
|
44
|
+
|
45
|
+
def optimal_table_order
|
46
|
+
# DSIG is always last
|
47
|
+
OPTIMAL_TABLE_ORDER +
|
48
|
+
(tables.keys - ['DSIG'] - OPTIMAL_TABLE_ORDER) +
|
49
|
+
['DSIG']
|
50
|
+
end
|
51
|
+
|
52
|
+
def collect_glyphs(glyph_ids)
|
53
|
+
# CFF top indexes are supposed to contain only one font, although they're
|
54
|
+
# capable of supporting many (no idea why this is true, maybe for CFF
|
55
|
+
# v2??). Anyway it's cool to do top_index[0], don't worry about it.
|
56
|
+
glyph_ids.each_with_object({}) do |id, h|
|
57
|
+
h[id] = original.cff.top_index[0].charstrings_index[id]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/ttfunk/reader.rb
CHANGED
@@ -1,45 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TTFunk
|
2
4
|
module Reader
|
3
5
|
private
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
def io
|
8
|
+
@file.contents
|
9
|
+
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
11
|
+
def read(bytes, format)
|
12
|
+
io.read(bytes).unpack(format)
|
13
|
+
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
def read_signed(count)
|
16
|
+
read(count * 2, 'n*').map { |i| to_signed(i) }
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
def to_signed(number)
|
20
|
+
number >= 0x8000 ? -((number ^ 0xFFFF) + 1) : number
|
21
|
+
end
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
def parse_from(position)
|
24
|
+
saved = io.pos
|
25
|
+
io.pos = position
|
26
|
+
result = yield position
|
27
|
+
io.pos = saved
|
28
|
+
result
|
29
|
+
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
31
|
+
# For debugging purposes
|
32
|
+
def hexdump(string)
|
33
|
+
bytes = string.unpack('C*')
|
34
|
+
bytes.each_with_index do |c, i|
|
35
|
+
printf('%02X', c)
|
36
|
+
if (i + 1) % 16 == 0
|
37
|
+
puts
|
38
|
+
elsif (i + 1) % 8 == 0
|
39
|
+
print ' '
|
40
|
+
else
|
41
|
+
print ' '
|
41
42
|
end
|
42
|
-
puts unless bytes.length % 16 == 0
|
43
43
|
end
|
44
|
+
puts unless bytes.length % 16 == 0
|
45
|
+
end
|
44
46
|
end
|
45
47
|
end
|
data/lib/ttfunk/resource_file.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module TTFunk
|
2
4
|
class ResourceFile
|
3
5
|
attr_reader :map
|
@@ -23,7 +25,7 @@ module TTFunk
|
|
23
25
|
name_list_offset += map_offset
|
24
26
|
|
25
27
|
@io.pos = type_list_offset
|
26
|
-
max_index = @io.read(2).
|
28
|
+
max_index = @io.read(2).unpack1('n')
|
27
29
|
0.upto(max_index) do
|
28
30
|
type, max_type_index, ref_list_offset = @io.read(8).unpack('A4nn')
|
29
31
|
@map[type] = { list: [], named: {} }
|
@@ -32,8 +34,8 @@ module TTFunk
|
|
32
34
|
0.upto(max_type_index) do
|
33
35
|
id, name_ofs, attr = @io.read(5).unpack('nnC')
|
34
36
|
data_ofs = @io.read(3)
|
35
|
-
data_ofs = data_offset + [0, data_ofs].pack('CA*').
|
36
|
-
handle = @io.read(4).
|
37
|
+
data_ofs = data_offset + [0, data_ofs].pack('CA*').unpack1('N')
|
38
|
+
handle = @io.read(4).unpack1('N')
|
37
39
|
|
38
40
|
entry = {
|
39
41
|
id: id,
|
@@ -44,7 +46,7 @@ module TTFunk
|
|
44
46
|
|
45
47
|
if name_list_offset + name_ofs < map_offset + map_length
|
46
48
|
parse_from(name_ofs + name_list_offset) do
|
47
|
-
len = @io.read(1).
|
49
|
+
len = @io.read(1).unpack1('C')
|
48
50
|
entry[:name] = @io.read(len)
|
49
51
|
end
|
50
52
|
end
|
@@ -61,7 +63,7 @@ module TTFunk
|
|
61
63
|
collection = index.is_a?(Integer) ? :list : :named
|
62
64
|
if @map[type][collection][index]
|
63
65
|
parse_from(@map[type][collection][index][:offset]) do
|
64
|
-
length = @io.read(4).
|
66
|
+
length = @io.read(4).unpack1('N')
|
65
67
|
return @io.read(length)
|
66
68
|
end
|
67
69
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class SciForm
|
5
|
+
attr_reader :significand, :exponent
|
6
|
+
alias eql? ==
|
7
|
+
|
8
|
+
def initialize(significand, exponent = 0)
|
9
|
+
@significand = significand
|
10
|
+
@exponent = exponent
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_f
|
14
|
+
significand * 10**exponent
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
case other
|
19
|
+
when Float
|
20
|
+
other == to_f
|
21
|
+
when self.class
|
22
|
+
other.significand == significand &&
|
23
|
+
other.exponent == exponent
|
24
|
+
else
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './reader'
|
4
|
+
|
5
|
+
module TTFunk
|
6
|
+
class SubTable
|
7
|
+
class EOTError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
include Reader
|
11
|
+
|
12
|
+
attr_reader :file, :table_offset, :length
|
13
|
+
|
14
|
+
def initialize(file, offset, length = nil)
|
15
|
+
@file = file
|
16
|
+
@table_offset = offset
|
17
|
+
@length = length
|
18
|
+
parse_from(@table_offset) { parse! }
|
19
|
+
end
|
20
|
+
|
21
|
+
# end of table
|
22
|
+
def eot?
|
23
|
+
# if length isn't set yet there's no way to know if we're at the end of
|
24
|
+
# the table or not
|
25
|
+
return false unless length
|
26
|
+
|
27
|
+
io.pos > table_offset + length
|
28
|
+
end
|
29
|
+
|
30
|
+
def read(*args)
|
31
|
+
if eot?
|
32
|
+
raise EOTError, 'attempted to read past the end of the table'
|
33
|
+
end
|
34
|
+
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/ttfunk/subset.rb
CHANGED
data/lib/ttfunk/subset/base.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../table/cmap'
|
2
4
|
require_relative '../table/glyf'
|
3
5
|
require_relative '../table/head'
|
@@ -13,6 +15,9 @@ require_relative '../table/simple'
|
|
13
15
|
module TTFunk
|
14
16
|
module Subset
|
15
17
|
class Base
|
18
|
+
MICROSOFT_PLATFORM_ID = 3
|
19
|
+
MS_SYMBOL_ENCODING_ID = 0
|
20
|
+
|
16
21
|
attr_reader :original
|
17
22
|
|
18
23
|
def initialize(original)
|
@@ -23,146 +28,82 @@ module TTFunk
|
|
23
28
|
false
|
24
29
|
end
|
25
30
|
|
31
|
+
def microsoft_symbol?
|
32
|
+
new_cmap_table[:platform_id] == MICROSOFT_PLATFORM_ID &&
|
33
|
+
new_cmap_table[:encoding_id] == MS_SYMBOL_ENCODING_ID
|
34
|
+
end
|
35
|
+
|
26
36
|
def to_unicode_map
|
27
37
|
{}
|
28
38
|
end
|
29
39
|
|
30
40
|
def encode(options = {})
|
31
|
-
|
32
|
-
|
41
|
+
encoder_klass.new(original, self, options).encode
|
42
|
+
end
|
33
43
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
next_glyph_id = cmap_table[:max_glyph_id]
|
44
|
+
def encoder_klass
|
45
|
+
original.cff.exists? ? OTFEncoder : TTFEncoder
|
46
|
+
end
|
39
47
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
next_glyph_id += 1
|
44
|
-
end
|
45
|
-
end
|
48
|
+
def unicode_cmap
|
49
|
+
@unicode_cmap ||= @original.cmap.unicode.first
|
50
|
+
end
|
46
51
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
# the cmap table (encoded above).
|
51
|
-
glyf_table = TTFunk::Table::Glyf.encode(
|
52
|
-
glyphs, new2old_glyph, old2new_glyph
|
53
|
-
)
|
54
|
-
loca_table = TTFunk::Table::Loca.encode(glyf_table[:offsets])
|
55
|
-
hmtx_table = TTFunk::Table::Hmtx.encode(
|
56
|
-
original.horizontal_metrics, new2old_glyph
|
57
|
-
)
|
58
|
-
hhea_table = TTFunk::Table::Hhea.encode(
|
59
|
-
original.horizontal_header, hmtx_table
|
60
|
-
)
|
61
|
-
maxp_table = TTFunk::Table::Maxp.encode(
|
62
|
-
original.maximum_profile, old2new_glyph
|
63
|
-
)
|
64
|
-
post_table = TTFunk::Table::Post.encode(
|
65
|
-
original.postscript, new2old_glyph
|
66
|
-
)
|
67
|
-
name_table = TTFunk::Table::Name.encode(
|
68
|
-
original.name, glyf_table[:table]
|
69
|
-
)
|
70
|
-
head_table = TTFunk::Table::Head.encode(
|
71
|
-
original.header, loca_table
|
72
|
-
)
|
73
|
-
|
74
|
-
# "optional" tables. Fonts may omit these if they do not need them.
|
75
|
-
# Because they apply globally, we can simply copy them over, without
|
76
|
-
# modification, if they exist.
|
77
|
-
os2_table = original.os2.raw
|
78
|
-
cvt_table = TTFunk::Table::Simple.new(original, 'cvt ').raw
|
79
|
-
fpgm_table = TTFunk::Table::Simple.new(original, 'fpgm').raw
|
80
|
-
prep_table = TTFunk::Table::Simple.new(original, 'prep').raw
|
81
|
-
|
82
|
-
# for PDF's, the kerning info is all included in the PDF as the text is
|
83
|
-
# drawn. Thus, the PDF readers do not actually use the kerning info in
|
84
|
-
# embedded fonts. If the library is used for something else, the
|
85
|
-
# generated subfont may need a kerning table... in that case, you need
|
86
|
-
# to opt into it.
|
87
|
-
if options[:kerning]
|
88
|
-
kern_table =
|
89
|
-
TTFunk::Table::Kern.encode(original.kerning, old2new_glyph)
|
90
|
-
end
|
52
|
+
def glyphs
|
53
|
+
@glyphs ||= collect_glyphs(original_glyph_ids)
|
54
|
+
end
|
91
55
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
'kern' => kern_table,
|
96
|
-
'hmtx' => hmtx_table[:table],
|
97
|
-
'hhea' => hhea_table,
|
98
|
-
'maxp' => maxp_table,
|
99
|
-
'OS/2' => os2_table,
|
100
|
-
'post' => post_table,
|
101
|
-
'name' => name_table,
|
102
|
-
'head' => head_table,
|
103
|
-
'prep' => prep_table,
|
104
|
-
'fpgm' => fpgm_table,
|
105
|
-
'cvt ' => cvt_table }
|
106
|
-
|
107
|
-
tables.delete_if { |_tag, table| table.nil? }
|
108
|
-
|
109
|
-
search_range = (Math.log(tables.length) / Math.log(2)).to_i * 16
|
110
|
-
entry_selector = (Math.log(search_range) / Math.log(2)).to_i
|
111
|
-
range_shift = tables.length * 16 - search_range
|
112
|
-
|
113
|
-
newfont = [
|
114
|
-
original.directory.scaler_type,
|
115
|
-
tables.length,
|
116
|
-
search_range,
|
117
|
-
entry_selector,
|
118
|
-
range_shift
|
119
|
-
].pack('Nn*')
|
120
|
-
|
121
|
-
directory_size = tables.length * 16
|
122
|
-
offset = newfont.length + directory_size
|
123
|
-
|
124
|
-
table_data = ''
|
125
|
-
head_offset = nil
|
126
|
-
tables.each do |tag, data|
|
127
|
-
newfont << [tag, checksum(data), offset, data.length].pack('A4N*')
|
128
|
-
table_data << data
|
129
|
-
head_offset = offset if tag == 'head'
|
130
|
-
offset += data.length
|
131
|
-
while offset % 4 != 0
|
132
|
-
offset += 1
|
133
|
-
table_data << "\0"
|
134
|
-
end
|
56
|
+
def collect_glyphs(glyph_ids)
|
57
|
+
collected = glyph_ids.each_with_object({}) do |id, h|
|
58
|
+
h[id] = glyph_for(id)
|
135
59
|
end
|
136
60
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
61
|
+
additional_ids = collected.values
|
62
|
+
.select { |g| g && g.compound? }
|
63
|
+
.map(&:glyph_ids)
|
64
|
+
.flatten
|
65
|
+
|
66
|
+
collected.update(collect_glyphs(additional_ids)) if additional_ids.any?
|
141
67
|
|
142
|
-
|
68
|
+
collected
|
143
69
|
end
|
144
70
|
|
145
|
-
|
71
|
+
def old_to_new_glyph
|
72
|
+
@old_to_new_glyph ||= begin
|
73
|
+
charmap = new_cmap_table[:charmap]
|
74
|
+
old_to_new = charmap.each_with_object(0 => 0) do |(_, ids), map|
|
75
|
+
map[ids[:old]] = ids[:new]
|
76
|
+
end
|
146
77
|
|
147
|
-
|
148
|
-
@unicode_cmap ||= @original.cmap.unicode.first
|
149
|
-
end
|
78
|
+
next_glyph_id = new_cmap_table[:max_glyph_id]
|
150
79
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
80
|
+
glyphs.keys.each do |old_id|
|
81
|
+
unless old_to_new.key?(old_id)
|
82
|
+
old_to_new[old_id] = next_glyph_id
|
83
|
+
next_glyph_id += 1
|
84
|
+
end
|
85
|
+
end
|
155
86
|
|
156
|
-
|
157
|
-
glyphs = glyph_ids.each_with_object({}) do |id, h|
|
158
|
-
h[id] = original.glyph_outlines.for(id)
|
87
|
+
old_to_new
|
159
88
|
end
|
160
|
-
|
161
|
-
.map(&:glyph_ids).flatten
|
89
|
+
end
|
162
90
|
|
163
|
-
|
91
|
+
def new_to_old_glyph
|
92
|
+
@new_to_old_glyph ||= old_to_new_glyph.invert
|
93
|
+
end
|
164
94
|
|
165
|
-
|
95
|
+
private
|
96
|
+
|
97
|
+
def glyph_for(glyph_id)
|
98
|
+
if original.cff.exists?
|
99
|
+
original
|
100
|
+
.cff
|
101
|
+
.top_index[0]
|
102
|
+
.charstrings_index[glyph_id]
|
103
|
+
.glyph
|
104
|
+
else
|
105
|
+
original.glyph_outlines.for(glyph_id)
|
106
|
+
end
|
166
107
|
end
|
167
108
|
end
|
168
109
|
end
|