ttfunk 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|