prawn 0.3.0 → 0.4.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.
- data/Rakefile +3 -1
- data/data/fonts/Action Man.dfont +0 -0
- data/examples/general/measurement_units.rb +2 -2
- data/examples/graphics/image_flow.rb +2 -2
- data/examples/graphics/stroke_bounds.rb +1 -1
- data/examples/m17n/win_ansi_charset.rb +3 -3
- data/examples/text/dfont.rb +49 -0
- data/examples/text/flowing_text_with_header_and_footer.rb +2 -48
- data/examples/text/font_calculations.rb +7 -6
- data/examples/text/font_size.rb +4 -4
- data/examples/text/text_flow.rb +1 -1
- data/lib/prawn.rb +6 -3
- data/lib/prawn/compatibility.rb +12 -17
- data/lib/prawn/document.rb +10 -10
- data/lib/prawn/document/internals.rb +8 -3
- data/lib/prawn/document/text.rb +39 -57
- data/lib/prawn/document/text/box.rb +1 -2
- data/lib/prawn/document/text/wrapping.rb +59 -0
- data/lib/prawn/errors.rb +0 -8
- data/lib/prawn/font.rb +192 -277
- data/lib/prawn/font/afm.rb +199 -0
- data/lib/prawn/font/dfont.rb +31 -0
- data/lib/prawn/font/ttf.rb +318 -0
- data/lib/prawn/graphics.rb +7 -2
- data/lib/prawn/images/png.rb +1 -1
- data/lib/prawn/reference.rb +7 -4
- data/spec/font_spec.rb +154 -61
- data/spec/text_spec.rb +47 -6
- data/vendor/pdf-inspector/lib/pdf/inspector.rb +1 -1
- data/vendor/ttfunk/example.rb +42 -2
- data/vendor/ttfunk/lib/ttfunk.rb +96 -42
- data/vendor/ttfunk/lib/ttfunk/directory.rb +17 -0
- data/vendor/ttfunk/lib/ttfunk/encoding/mac_roman.rb +88 -0
- data/vendor/ttfunk/lib/ttfunk/encoding/windows_1252.rb +69 -0
- data/vendor/ttfunk/lib/ttfunk/reader.rb +44 -0
- data/vendor/ttfunk/lib/ttfunk/resource_file.rb +78 -0
- data/vendor/ttfunk/lib/ttfunk/subset.rb +18 -0
- data/vendor/ttfunk/lib/ttfunk/subset/base.rb +141 -0
- data/vendor/ttfunk/lib/ttfunk/subset/mac_roman.rb +46 -0
- data/vendor/ttfunk/lib/ttfunk/subset/unicode.rb +48 -0
- data/vendor/ttfunk/lib/ttfunk/subset/unicode_8bit.rb +63 -0
- data/vendor/ttfunk/lib/ttfunk/subset/windows_1252.rb +51 -0
- data/vendor/ttfunk/lib/ttfunk/subset_collection.rb +72 -0
- data/vendor/ttfunk/lib/ttfunk/table.rb +37 -18
- data/vendor/ttfunk/lib/ttfunk/table/cmap.rb +24 -84
- data/vendor/ttfunk/lib/ttfunk/table/cmap/format00.rb +54 -0
- data/vendor/ttfunk/lib/ttfunk/table/cmap/format04.rb +126 -0
- data/vendor/ttfunk/lib/ttfunk/table/cmap/subtable.rb +79 -0
- data/vendor/ttfunk/lib/ttfunk/table/glyf.rb +64 -0
- data/vendor/ttfunk/lib/ttfunk/table/glyf/compound.rb +81 -0
- data/vendor/ttfunk/lib/ttfunk/table/glyf/simple.rb +37 -0
- data/vendor/ttfunk/lib/ttfunk/table/head.rb +38 -19
- data/vendor/ttfunk/lib/ttfunk/table/hhea.rb +35 -21
- data/vendor/ttfunk/lib/ttfunk/table/hmtx.rb +40 -13
- data/vendor/ttfunk/lib/ttfunk/table/kern.rb +69 -38
- data/vendor/ttfunk/lib/ttfunk/table/kern/format0.rb +62 -0
- data/vendor/ttfunk/lib/ttfunk/table/loca.rb +43 -0
- data/vendor/ttfunk/lib/ttfunk/table/maxp.rb +34 -11
- data/vendor/ttfunk/lib/ttfunk/table/name.rb +109 -42
- data/vendor/ttfunk/lib/ttfunk/table/os2.rb +78 -0
- data/vendor/ttfunk/lib/ttfunk/table/post.rb +91 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format10.rb +43 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format20.rb +35 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format25.rb +23 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format30.rb +17 -0
- data/vendor/ttfunk/lib/ttfunk/table/post/format40.rb +17 -0
- data/vendor/ttfunk/lib/ttfunk/table/simple.rb +14 -0
- metadata +54 -25
- data/examples/table/addressbook.csv +0 -6
- data/examples/table/cell.rb +0 -40
- data/examples/table/currency.csv +0 -1834
- data/examples/table/fancy_table.rb +0 -62
- data/examples/table/ruport_formatter.rb +0 -53
- data/examples/table/table.rb +0 -51
- data/examples/table/table_alignment.rb +0 -18
- data/examples/table/table_border_color.rb +0 -17
- data/examples/table/table_colspan.rb +0 -19
- data/examples/table/table_header_color.rb +0 -19
- data/examples/table/table_header_underline.rb +0 -15
- data/lib/prawn/document/table.rb +0 -338
- data/lib/prawn/font/cmap.rb +0 -59
- data/lib/prawn/font/metrics.rb +0 -378
- data/lib/prawn/font/wrapping.rb +0 -47
- data/lib/prawn/graphics/cell.rb +0 -264
- data/spec/metrics_spec.rb +0 -62
- data/spec/table_spec.rb +0 -179
- data/vendor/ttfunk/lib/ttfunk/table/directory.rb +0 -25
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'ttfunk/subset/unicode'
|
2
|
+
require 'ttfunk/subset/unicode_8bit'
|
3
|
+
require 'ttfunk/subset/mac_roman'
|
4
|
+
require 'ttfunk/subset/windows_1252'
|
5
|
+
|
6
|
+
module TTFunk
|
7
|
+
module Subset
|
8
|
+
def self.for(original, encoding)
|
9
|
+
case encoding.to_sym
|
10
|
+
when :unicode then Unicode.new(original)
|
11
|
+
when :unicode_8bit then Unicode8Bit.new(original)
|
12
|
+
when :mac_roman then MacRoman.new(original)
|
13
|
+
when :windows_1252 then Windows1252.new(original)
|
14
|
+
else raise NotImplementedError, "encoding #{encoding} is not supported"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'ttfunk/table/cmap'
|
2
|
+
require 'ttfunk/table/glyf'
|
3
|
+
require 'ttfunk/table/head'
|
4
|
+
require 'ttfunk/table/hhea'
|
5
|
+
require 'ttfunk/table/hmtx'
|
6
|
+
require 'ttfunk/table/kern'
|
7
|
+
require 'ttfunk/table/loca'
|
8
|
+
require 'ttfunk/table/maxp'
|
9
|
+
require 'ttfunk/table/name'
|
10
|
+
require 'ttfunk/table/post'
|
11
|
+
require 'ttfunk/table/simple'
|
12
|
+
|
13
|
+
module TTFunk
|
14
|
+
module Subset
|
15
|
+
class Base
|
16
|
+
attr_reader :original
|
17
|
+
|
18
|
+
def initialize(original)
|
19
|
+
@original = original
|
20
|
+
end
|
21
|
+
|
22
|
+
def unicode?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_unicode_map
|
27
|
+
{}
|
28
|
+
end
|
29
|
+
|
30
|
+
def encode(options={})
|
31
|
+
cmap_table = new_cmap_table(options)
|
32
|
+
glyphs = collect_glyphs(original_glyph_ids)
|
33
|
+
|
34
|
+
old2new_glyph = cmap_table[:charmap].inject({ 0 => 0 }) { |map, (code, ids)| map[ids[:old]] = ids[:new]; map }
|
35
|
+
next_glyph_id = cmap_table[:max_glyph_id]
|
36
|
+
|
37
|
+
glyphs.keys.each do |old_id|
|
38
|
+
unless old2new_glyph.key?(old_id)
|
39
|
+
old2new_glyph[old_id] = next_glyph_id
|
40
|
+
next_glyph_id += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
new2old_glyph = old2new_glyph.invert
|
45
|
+
|
46
|
+
# "mandatory" tables. Every font should ("should") have these, including
|
47
|
+
# the cmap table (encoded above).
|
48
|
+
glyf_table = TTFunk::Table::Glyf.encode(glyphs, new2old_glyph, old2new_glyph)
|
49
|
+
loca_table = TTFunk::Table::Loca.encode(glyf_table[:offsets])
|
50
|
+
hmtx_table = TTFunk::Table::Hmtx.encode(original.horizontal_metrics, new2old_glyph)
|
51
|
+
hhea_table = TTFunk::Table::Hhea.encode(original.horizontal_header, hmtx_table)
|
52
|
+
maxp_table = TTFunk::Table::Maxp.encode(original.maximum_profile, old2new_glyph)
|
53
|
+
post_table = TTFunk::Table::Post.encode(original.postscript, new2old_glyph)
|
54
|
+
name_table = TTFunk::Table::Name.encode(original.name)
|
55
|
+
head_table = TTFunk::Table::Head.encode(original.header, loca_table)
|
56
|
+
|
57
|
+
# "optional" tables. Fonts may omit these if they do not need them. Because they
|
58
|
+
# apply globally, we can simply copy them over, without modification, if they
|
59
|
+
# exist.
|
60
|
+
os2_table = original.os2.raw
|
61
|
+
cvt_table = TTFunk::Table::Simple.new(original, "cvt ").raw
|
62
|
+
fpgm_table = TTFunk::Table::Simple.new(original, "fpgm").raw
|
63
|
+
prep_table = TTFunk::Table::Simple.new(original, "prep").raw
|
64
|
+
|
65
|
+
# for PDF's, the kerning info is all included in the PDF as the text is
|
66
|
+
# drawn. Thus, the PDF readers do not actually use the kerning info in
|
67
|
+
# embedded fonts. If the library is used for something else, the generated
|
68
|
+
# subfont may need a kerning table... in that case, you need to opt into it.
|
69
|
+
if options[:kerning]
|
70
|
+
kern_table = TTFunk::Table::Kern.encode(original.kerning, old2new_glyph)
|
71
|
+
end
|
72
|
+
|
73
|
+
tables = { 'cmap' => cmap_table[:table],
|
74
|
+
'glyf' => glyf_table[:table],
|
75
|
+
'loca' => loca_table[:table],
|
76
|
+
'kern' => kern_table,
|
77
|
+
'hmtx' => hmtx_table[:table],
|
78
|
+
'hhea' => hhea_table,
|
79
|
+
'maxp' => maxp_table,
|
80
|
+
'OS/2' => os2_table,
|
81
|
+
'post' => post_table,
|
82
|
+
'name' => name_table,
|
83
|
+
'head' => head_table,
|
84
|
+
'prep' => prep_table,
|
85
|
+
'fpgm' => fpgm_table,
|
86
|
+
'cvt ' => cvt_table }
|
87
|
+
|
88
|
+
tables.delete_if { |tag, table| table.nil? }
|
89
|
+
|
90
|
+
search_range = (Math.log(tables.length) / Math.log(2)).to_i * 16
|
91
|
+
entry_selector = (Math.log(search_range) / Math.log(2)).to_i
|
92
|
+
range_shift = tables.length * 16 - search_range
|
93
|
+
|
94
|
+
newfont = [original.directory.scaler_type, tables.length, search_range, entry_selector, range_shift].pack("Nn*")
|
95
|
+
|
96
|
+
directory_size = tables.length * 16
|
97
|
+
offset = newfont.length + directory_size
|
98
|
+
|
99
|
+
table_data = ""
|
100
|
+
head_offset = nil
|
101
|
+
tables.each do |tag, data|
|
102
|
+
newfont << [tag, checksum(data), offset, data.length].pack("A4N*")
|
103
|
+
table_data << data
|
104
|
+
head_offset = offset if tag == 'head'
|
105
|
+
offset += data.length
|
106
|
+
while offset % 4 != 0
|
107
|
+
offset += 1
|
108
|
+
table_data << "\0"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
newfont << table_data
|
113
|
+
sum = checksum(newfont)
|
114
|
+
adjustment = 0xB1B0AFBA - sum
|
115
|
+
newfont[head_offset+8,4] = [adjustment].pack("N")
|
116
|
+
|
117
|
+
return newfont
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def unicode_cmap
|
123
|
+
@unicode_cmap ||= @original.cmap.unicode.first
|
124
|
+
end
|
125
|
+
|
126
|
+
def checksum(data)
|
127
|
+
data += "\0" * (4 - data.length % 4) unless data.length % 4 == 0
|
128
|
+
data.unpack("N*").inject(0) { |sum, dword| sum + dword } & 0xFFFF_FFFF
|
129
|
+
end
|
130
|
+
|
131
|
+
def collect_glyphs(glyph_ids)
|
132
|
+
glyphs = glyph_ids.inject({}) { |h, id| h[id] = original.glyph_outlines.for(id); h }
|
133
|
+
additional_ids = glyphs.values.select { |g| g && g.compound? }.map { |g| g.glyph_ids }.flatten
|
134
|
+
|
135
|
+
glyphs.update(collect_glyphs(additional_ids)) if additional_ids.any?
|
136
|
+
|
137
|
+
return glyphs
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'ttfunk/subset/base'
|
3
|
+
require 'ttfunk/encoding/mac_roman'
|
4
|
+
|
5
|
+
module TTFunk
|
6
|
+
module Subset
|
7
|
+
class MacRoman < Base
|
8
|
+
def initialize(original)
|
9
|
+
super
|
10
|
+
@subset = Array.new(256)
|
11
|
+
end
|
12
|
+
|
13
|
+
def use(character)
|
14
|
+
@subset[Encoding::MacRoman::FROM_UNICODE[character]] = character
|
15
|
+
end
|
16
|
+
|
17
|
+
def covers?(character)
|
18
|
+
Encoding::MacRoman.covers?(character)
|
19
|
+
end
|
20
|
+
|
21
|
+
def includes?(character)
|
22
|
+
code = Encoding::MacRoman::FROM_UNICODE[character]
|
23
|
+
code && @subset[code]
|
24
|
+
end
|
25
|
+
|
26
|
+
def from_unicode(character)
|
27
|
+
Encoding::MacRoman::FROM_UNICODE[character]
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def new_cmap_table(options)
|
33
|
+
mapping = {}
|
34
|
+
@subset.each_with_index do |unicode, roman|
|
35
|
+
mapping[roman] = unicode_cmap[unicode] if roman
|
36
|
+
end
|
37
|
+
|
38
|
+
TTFunk::Table::Cmap.encode(mapping, :mac_roman)
|
39
|
+
end
|
40
|
+
|
41
|
+
def original_glyph_ids
|
42
|
+
([0] + @subset.map { |unicode| unicode && unicode_cmap[unicode] }).compact.uniq.sort
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'ttfunk/subset/base'
|
3
|
+
|
4
|
+
module TTFunk
|
5
|
+
module Subset
|
6
|
+
class Unicode < Base
|
7
|
+
def initialize(original)
|
8
|
+
super
|
9
|
+
@subset = Set.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def unicode?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_unicode_map
|
17
|
+
@subset.inject({}) { |map, code| map[code] = code; map }
|
18
|
+
end
|
19
|
+
|
20
|
+
def use(character)
|
21
|
+
@subset << character
|
22
|
+
end
|
23
|
+
|
24
|
+
def covers?(character)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def includes?(character)
|
29
|
+
@subset.includes(character)
|
30
|
+
end
|
31
|
+
|
32
|
+
def from_unicode(character)
|
33
|
+
character
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def new_cmap_table(options)
|
39
|
+
mapping = @subset.inject({}) { |map, code| map[code] = unicode_cmap[code]; map }
|
40
|
+
TTFunk::Table::Cmap.encode(mapping, :unicode)
|
41
|
+
end
|
42
|
+
|
43
|
+
def original_glyph_ids
|
44
|
+
([0] + @subset.map { |code| unicode_cmap[code] }).uniq.sort
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'ttfunk/subset/base'
|
3
|
+
|
4
|
+
module TTFunk
|
5
|
+
module Subset
|
6
|
+
class Unicode8Bit < Base
|
7
|
+
def initialize(original)
|
8
|
+
super
|
9
|
+
@subset = { 0x20 => 0x20 }
|
10
|
+
@unicodes = { 0x20 => 0x20 }
|
11
|
+
@next = 0x21 # apparently, PDF's don't like to use chars between 0-31
|
12
|
+
end
|
13
|
+
|
14
|
+
def unicode?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_unicode_map
|
19
|
+
@subset.dup
|
20
|
+
end
|
21
|
+
|
22
|
+
def use(character)
|
23
|
+
if !@unicodes.key?(character)
|
24
|
+
@subset[@next] = character
|
25
|
+
@unicodes[character] = @next
|
26
|
+
@next += 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def covers?(character)
|
31
|
+
@unicodes.key?(character) || @next < 256
|
32
|
+
end
|
33
|
+
|
34
|
+
def includes?(character)
|
35
|
+
@unicodes.key?(character)
|
36
|
+
end
|
37
|
+
|
38
|
+
def from_unicode(character)
|
39
|
+
@unicodes[character]
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def new_cmap_table(options)
|
45
|
+
mapping = @subset.inject({}) do |map, (code,unicode)|
|
46
|
+
map[code] = unicode_cmap[unicode]
|
47
|
+
map
|
48
|
+
end
|
49
|
+
|
50
|
+
# since we're mapping a subset of the unicode glyphs into an
|
51
|
+
# arbitrary 256-character space, the actual encoding we're
|
52
|
+
# using is irrelevant. We choose MacRoman because it's a 256-character
|
53
|
+
# encoding that happens to be well-supported in both TTF and
|
54
|
+
# PDF formats.
|
55
|
+
TTFunk::Table::Cmap.encode(mapping, :mac_roman)
|
56
|
+
end
|
57
|
+
|
58
|
+
def original_glyph_ids
|
59
|
+
([0] + @unicodes.keys.map { |unicode| unicode_cmap[unicode] }).uniq.sort
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'ttfunk/subset/base'
|
3
|
+
require 'ttfunk/encoding/windows_1252'
|
4
|
+
|
5
|
+
module TTFunk
|
6
|
+
module Subset
|
7
|
+
class Windows1252 < Base
|
8
|
+
def initialize(original)
|
9
|
+
super
|
10
|
+
@subset = Array.new(256)
|
11
|
+
end
|
12
|
+
|
13
|
+
def use(character)
|
14
|
+
@subset[Encoding::Windows1252::FROM_UNICODE[character]] = character
|
15
|
+
end
|
16
|
+
|
17
|
+
def covers?(character)
|
18
|
+
Encoding::Windows1252.covers?(character)
|
19
|
+
end
|
20
|
+
|
21
|
+
def includes?(character)
|
22
|
+
code = Encoding::Windows1252::FROM_UNICODE[character]
|
23
|
+
code && @subset[code]
|
24
|
+
end
|
25
|
+
|
26
|
+
def from_unicode(character)
|
27
|
+
Encoding::Windows1252::FROM_UNICODE[character]
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def new_cmap_table(options)
|
33
|
+
mapping = {}
|
34
|
+
@subset.each_with_index do |unicode, cp1252|
|
35
|
+
mapping[cp1252] = unicode_cmap[unicode] if cp1252
|
36
|
+
end
|
37
|
+
|
38
|
+
# yes, I really mean "mac roman". TTF has no cp1252 encoding, and the
|
39
|
+
# alternative would be to encode it using a format 4 unicode table, which
|
40
|
+
# is overkill. for our purposes, mac-roman suffices. (If we were building
|
41
|
+
# a _real_ font, instead of a PDF-embeddable subset, things would probably
|
42
|
+
# be different.)
|
43
|
+
TTFunk::Table::Cmap.encode(mapping, :mac_roman)
|
44
|
+
end
|
45
|
+
|
46
|
+
def original_glyph_ids
|
47
|
+
([0] + @subset.map { |unicode| unicode && unicode_cmap[unicode] }).compact.uniq.sort
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'ttfunk/subset'
|
2
|
+
|
3
|
+
module TTFunk
|
4
|
+
class SubsetCollection
|
5
|
+
def initialize(original)
|
6
|
+
@original = original
|
7
|
+
@subsets = [Subset.for(@original, :mac_roman)]
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](subset)
|
11
|
+
@subsets[subset]
|
12
|
+
end
|
13
|
+
|
14
|
+
# +characters+ should be an array of UTF-16 characters
|
15
|
+
def use(characters)
|
16
|
+
characters.each do |char|
|
17
|
+
covered = false
|
18
|
+
@subsets.each_with_index do |subset, i|
|
19
|
+
if subset.covers?(char)
|
20
|
+
subset.use(char)
|
21
|
+
covered = true
|
22
|
+
break
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if !covered
|
27
|
+
@subsets << Subset.for(@original, :unicode_8bit)
|
28
|
+
@subsets.last.use(char)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# +characters+ should be an array of UTF-16 characters. Returns
|
34
|
+
# an array of subset chunks, where each chunk is another array of
|
35
|
+
# two elements. The first element is the subset number, and the
|
36
|
+
# second element is the string of characters to render with that
|
37
|
+
# font subset. The strings will be encoded for their subset font,
|
38
|
+
# and so may not look (in the raw) like what was passed in, but
|
39
|
+
# they will render correctly with the indicated subset font.
|
40
|
+
def encode(characters)
|
41
|
+
return [] if characters.empty?
|
42
|
+
|
43
|
+
# TODO: probably would be more optimal to nix the #use method,
|
44
|
+
# and merge it into this one, so it can be done in a single
|
45
|
+
# pass instead of two passes.
|
46
|
+
use(characters)
|
47
|
+
|
48
|
+
parts = []
|
49
|
+
current_subset = 0
|
50
|
+
current_char = 0
|
51
|
+
char = characters[current_char]
|
52
|
+
|
53
|
+
loop do
|
54
|
+
while @subsets[current_subset].includes?(char)
|
55
|
+
char = @subsets[current_subset].from_unicode(char)
|
56
|
+
|
57
|
+
if parts.empty? || parts.last[0] != current_subset
|
58
|
+
parts << [current_subset, char.chr]
|
59
|
+
else
|
60
|
+
parts.last[1] << char
|
61
|
+
end
|
62
|
+
|
63
|
+
current_char += 1
|
64
|
+
return parts if current_char >= characters.length
|
65
|
+
char = characters[current_char]
|
66
|
+
end
|
67
|
+
|
68
|
+
current_subset = (current_subset + 1) % @subsets.length
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|