ttfunk 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +114 -0
  5. data/{README.rdoc → README.md} +12 -8
  6. data/lib/ttfunk.rb +27 -21
  7. data/lib/ttfunk/collection.rb +41 -0
  8. data/lib/ttfunk/directory.rb +10 -4
  9. data/lib/ttfunk/encoding/mac_roman.rb +50 -40
  10. data/lib/ttfunk/encoding/windows_1252.rb +28 -22
  11. data/lib/ttfunk/reader.rb +11 -10
  12. data/lib/ttfunk/resource_file.rb +28 -21
  13. data/lib/ttfunk/subset.rb +5 -5
  14. data/lib/ttfunk/subset/base.rb +66 -38
  15. data/lib/ttfunk/subset/mac_roman.rb +11 -10
  16. data/lib/ttfunk/subset/unicode.rb +10 -8
  17. data/lib/ttfunk/subset/unicode_8bit.rb +16 -16
  18. data/lib/ttfunk/subset/windows_1252.rb +16 -15
  19. data/lib/ttfunk/subset_collection.rb +7 -8
  20. data/lib/ttfunk/table.rb +3 -5
  21. data/lib/ttfunk/table/cmap.rb +13 -13
  22. data/lib/ttfunk/table/cmap/format00.rb +8 -10
  23. data/lib/ttfunk/table/cmap/format04.rb +37 -32
  24. data/lib/ttfunk/table/cmap/format06.rb +16 -16
  25. data/lib/ttfunk/table/cmap/format10.rb +22 -17
  26. data/lib/ttfunk/table/cmap/format12.rb +28 -22
  27. data/lib/ttfunk/table/cmap/subtable.rb +30 -23
  28. data/lib/ttfunk/table/glyf.rb +13 -11
  29. data/lib/ttfunk/table/glyf/compound.rb +13 -10
  30. data/lib/ttfunk/table/glyf/simple.rb +5 -4
  31. data/lib/ttfunk/table/head.rb +13 -13
  32. data/lib/ttfunk/table/hhea.rb +13 -13
  33. data/lib/ttfunk/table/hmtx.rb +23 -18
  34. data/lib/ttfunk/table/kern.rb +57 -48
  35. data/lib/ttfunk/table/kern/format0.rb +18 -10
  36. data/lib/ttfunk/table/loca.rb +9 -9
  37. data/lib/ttfunk/table/maxp.rb +9 -9
  38. data/lib/ttfunk/table/name.rb +65 -56
  39. data/lib/ttfunk/table/os2.rb +21 -21
  40. data/lib/ttfunk/table/post.rb +32 -32
  41. data/lib/ttfunk/table/post/format10.rb +33 -27
  42. data/lib/ttfunk/table/post/format20.rb +10 -10
  43. data/lib/ttfunk/table/post/format30.rb +5 -5
  44. data/lib/ttfunk/table/post/format40.rb +3 -3
  45. data/lib/ttfunk/table/sbix.rb +19 -10
  46. metadata +55 -32
  47. metadata.gz.sig +0 -0
  48. data/CHANGELOG +0 -5
  49. data/data/fonts/DejaVuSans.ttf +0 -0
  50. data/examples/metrics.rb +0 -45
@@ -20,7 +20,7 @@ module TTFunk
20
20
  end
21
21
 
22
22
  def use(character)
23
- if !@unicodes.key?(character)
23
+ unless @unicodes.key?(character)
24
24
  @subset[@next] = character
25
25
  @unicodes[character] = @next
26
26
  @next += 1
@@ -41,23 +41,23 @@ module TTFunk
41
41
 
42
42
  protected
43
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)
44
+ def new_cmap_table(_options)
45
+ mapping = @subset.each_with_object({}) do |(code, unicode), map|
46
+ map[code] = unicode_cmap[unicode]
47
+ map
56
48
  end
57
49
 
58
- def original_glyph_ids
59
- ([0] + @unicodes.keys.map { |unicode| unicode_cmap[unicode] }).uniq.sort
60
- end
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
61
  end
62
62
  end
63
63
  end
@@ -34,23 +34,24 @@ module TTFunk
34
34
 
35
35
  protected
36
36
 
37
- def new_cmap_table(options)
38
- mapping = {}
39
- @subset.each_with_index do |unicode, cp1252|
40
- mapping[cp1252] = unicode_cmap[unicode] if cp1252
41
- end
42
-
43
- # yes, I really mean "mac roman". TTF has no cp1252 encoding, and the
44
- # alternative would be to encode it using a format 4 unicode table, which
45
- # is overkill. for our purposes, mac-roman suffices. (If we were building
46
- # a _real_ font, instead of a PDF-embeddable subset, things would probably
47
- # be different.)
48
- TTFunk::Table::Cmap.encode(mapping, :mac_roman)
37
+ def new_cmap_table(_options)
38
+ mapping = {}
39
+ @subset.each_with_index do |unicode, cp1252|
40
+ mapping[cp1252] = unicode_cmap[unicode] if cp1252
49
41
  end
50
42
 
51
- def original_glyph_ids
52
- ([0] + @subset.map { |unicode| unicode && unicode_cmap[unicode] }).compact.uniq.sort
53
- end
43
+ # yes, I really mean "mac roman". TTF has no cp1252 encoding, and the
44
+ # alternative would be to encode it using a format 4 unicode table,
45
+ # which is overkill. for our purposes, mac-roman suffices. (If we were
46
+ # building a _real_ font, instead of a PDF-embeddable subset, things
47
+ # would probably be different.)
48
+ TTFunk::Table::Cmap.encode(mapping, :mac_roman)
49
+ end
50
+
51
+ def original_glyph_ids
52
+ ([0] + @subset.map { |unicode| unicode && unicode_cmap[unicode] })
53
+ .compact.uniq.sort
54
+ end
54
55
  end
55
56
  end
56
57
  end
@@ -15,15 +15,14 @@ module TTFunk
15
15
  def use(characters)
16
16
  characters.each do |char|
17
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
18
+ @subsets.each_with_index do |subset, _i|
19
+ next unless subset.covers?(char)
20
+ subset.use(char)
21
+ covered = true
22
+ break
24
23
  end
25
24
 
26
- if !covered
25
+ unless covered
27
26
  @subsets << Subset.for(@original, :unicode_8bit)
28
27
  @subsets.last.use(char)
29
28
  end
@@ -57,7 +56,7 @@ module TTFunk
57
56
  if parts.empty? || parts.last[0] != current_subset
58
57
  encoded_char = char.chr
59
58
  if encoded_char.respond_to?(:force_encoding)
60
- encoded_char.force_encoding("ASCII-8BIT")
59
+ encoded_char.force_encoding('ASCII-8BIT')
61
60
  end
62
61
  parts << [current_subset, encoded_char]
63
62
  else
data/lib/ttfunk/table.rb CHANGED
@@ -28,8 +28,6 @@ module TTFunk
28
28
  def raw
29
29
  if exists?
30
30
  parse_from(offset) { io.read(length) }
31
- else
32
- nil
33
31
  end
34
32
  end
35
33
 
@@ -39,8 +37,8 @@ module TTFunk
39
37
 
40
38
  private
41
39
 
42
- def parse!
43
- # do nothing, by default
44
- end
40
+ def parse!
41
+ # do nothing, by default
42
+ end
45
43
  end
46
44
  end
@@ -1,4 +1,4 @@
1
- module TTFunk
1
+ module TTFunk
2
2
  class Table
3
3
  class Cmap < Table
4
4
  attr_reader :version
@@ -8,29 +8,29 @@ module TTFunk
8
8
  result = Cmap::Subtable.encode(charmap, encoding)
9
9
 
10
10
  # pack 'version' and 'table-count'
11
- result[:table] = [0, 1, result.delete(:subtable)].pack("nnA*")
12
- return result
11
+ result[:table] = [0, 1, result.delete(:subtable)].pack('nnA*')
12
+ result
13
13
  end
14
14
 
15
15
  def unicode
16
- # Because most callers just call .first on the result, put tables with
16
+ # Because most callers just call .first on the result, put tables with
17
17
  # highest-number format first. Unsupported formats will be ignored.
18
- @unicode ||= @tables.select { |table| table.unicode? && table.supported? }
19
- .sort{|a,b| b.format <=> a.format }
18
+ @unicode ||= @tables
19
+ .select { |table| table.unicode? && table.supported? }
20
+ .sort { |a, b| b.format <=> a.format }
20
21
  end
21
22
 
22
23
  private
23
24
 
24
- def parse!
25
- @version, table_count = read(4, "nn")
26
- @tables = []
25
+ def parse!
26
+ @version, table_count = read(4, 'nn')
27
+ @tables = []
27
28
 
28
- table_count.times do
29
- @tables << Cmap::Subtable.new(file, offset)
30
- end
29
+ table_count.times do
30
+ @tables << Cmap::Subtable.new(file, offset)
31
31
  end
32
+ end
32
33
  end
33
-
34
34
  end
35
35
  end
36
36
 
@@ -4,7 +4,6 @@ require_relative '../../encoding/windows_1252'
4
4
  module TTFunk
5
5
  class Table
6
6
  class Cmap
7
-
8
7
  module Format00
9
8
  attr_reader :language
10
9
  attr_reader :code_map
@@ -20,17 +19,17 @@ module TTFunk
20
19
  glyph_indexes = Array.new(256, 0)
21
20
  glyph_map = { 0 => 0 }
22
21
 
23
- new_map = charmap.keys.sort.inject({}) do |map, code|
22
+ new_map = charmap.keys.sort.each_with_object({}) do |code, map|
24
23
  glyph_map[charmap[code]] ||= next_id += 1
25
- map[code] = { :old => charmap[code], :new => glyph_map[charmap[code]] }
24
+ map[code] = { old: charmap[code], new: glyph_map[charmap[code]] }
26
25
  glyph_indexes[code] = glyph_map[charmap[code]]
27
26
  map
28
27
  end
29
28
 
30
29
  # format, length, language, indices
31
- subtable = [0, 262, 0, *glyph_indexes].pack("nnnC*")
30
+ subtable = [0, 262, 0, *glyph_indexes].pack('nnnC*')
32
31
 
33
- { :charmap => new_map, :subtable => subtable, :max_glyph_id => next_id+1 }
32
+ { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
34
33
  end
35
34
 
36
35
  def [](code)
@@ -43,12 +42,11 @@ module TTFunk
43
42
 
44
43
  private
45
44
 
46
- def parse_cmap!
47
- @language = read(4, "x2n")
48
- @code_map = read(256, "C*")
49
- end
45
+ def parse_cmap!
46
+ @language = read(4, 'x2n')
47
+ @code_map = read(256, 'C*')
48
+ end
50
49
  end
51
-
52
50
  end
53
51
  end
54
52
  end
@@ -1,7 +1,6 @@
1
1
  module TTFunk
2
2
  class Table
3
3
  class Cmap
4
-
5
4
  module Format04
6
5
  attr_reader :language
7
6
  attr_reader :code_map
@@ -19,10 +18,10 @@ module TTFunk
19
18
  last = difference = nil
20
19
 
21
20
  glyph_map = { 0 => 0 }
22
- new_map = charmap.keys.sort.inject({}) do |map, code|
21
+ new_map = charmap.keys.sort.each_with_object({}) do |code, map|
23
22
  old = charmap[code]
24
23
  glyph_map[old] ||= next_id += 1
25
- map[code] = { :old => old, :new => glyph_map[old] }
24
+ map[code] = { old: old, new: glyph_map[old] }
26
25
 
27
26
  delta = glyph_map[old] - code
28
27
  if last.nil? || delta != difference
@@ -66,17 +65,22 @@ module TTFunk
66
65
  end
67
66
 
68
67
  # format, length, language
69
- subtable = [4, 16 + 8 * segcount + 2 * glyph_indices.length, 0].pack("nnn")
68
+ subtable = [
69
+ 4, 16 + 8 * segcount + 2 * glyph_indices.length, 0
70
+ ].pack('nnn')
70
71
 
71
- search_range = 2 * 2 ** (Math.log(segcount) / Math.log(2)).to_i
72
+ search_range = 2 * 2**(Math.log(segcount) / Math.log(2)).to_i
72
73
  entry_selector = (Math.log(search_range / 2) / Math.log(2)).to_i
73
74
  range_shift = (2 * segcount) - search_range
74
- subtable << [segcount * 2, search_range, entry_selector, range_shift].pack("nnnn")
75
+ subtable << [
76
+ segcount * 2, search_range, entry_selector, range_shift
77
+ ].pack('nnnn')
75
78
 
76
- subtable << end_codes.pack("n*") << "\0\0" << start_codes.pack("n*")
77
- subtable << deltas.pack("n*") << range_offsets.pack("n*") << glyph_indices.pack("n*")
79
+ subtable << end_codes.pack('n*') << "\0\0" << start_codes.pack('n*')
80
+ subtable << deltas.pack('n*') << range_offsets.pack('n*')
81
+ subtable << glyph_indices.pack('n*')
78
82
 
79
- { :charmap => new_map, :subtable => subtable, :max_glyph_id => next_id+1 }
83
+ { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
80
84
  end
81
85
 
82
86
  def [](code)
@@ -89,38 +93,39 @@ module TTFunk
89
93
 
90
94
  private
91
95
 
92
- def parse_cmap!
93
- length, @language, segcount_x2 = read(6, "nnn")
94
- segcount = segcount_x2 / 2
95
-
96
- io.read(6) # skip searching hints
96
+ def parse_cmap!
97
+ length, @language, segcount_x2 = read(6, 'nnn')
98
+ segcount = segcount_x2 / 2
97
99
 
98
- end_code = read(segcount_x2, "n*")
99
- io.read(2) # skip reserved value
100
- start_code = read(segcount_x2, "n*")
101
- id_delta = read_signed(segcount)
102
- id_range_offset = read(segcount_x2, "n*")
100
+ io.read(6) # skip searching hints
103
101
 
104
- glyph_ids = read(length - io.pos + @offset, "n*")
102
+ end_code = read(segcount_x2, 'n*')
103
+ io.read(2) # skip reserved value
104
+ start_code = read(segcount_x2, 'n*')
105
+ id_delta = read_signed(segcount)
106
+ id_range_offset = read(segcount_x2, 'n*')
105
107
 
106
- @code_map = {}
108
+ glyph_ids = read(length - io.pos + @offset, 'n*')
107
109
 
108
- end_code.each_with_index do |tail, i|
109
- start_code[i].upto(tail) do |code|
110
- if id_range_offset[i].zero?
111
- glyph_id = code + id_delta[i]
112
- else
113
- index = id_range_offset[i] / 2 + (code - start_code[i]) - (segcount - i)
114
- glyph_id = glyph_ids[index] || 0 # because some TTF fonts are broken
115
- glyph_id += id_delta[i] if glyph_id != 0
116
- end
110
+ @code_map = {}
117
111
 
118
- @code_map[code] = glyph_id & 0xFFFF
112
+ end_code.each_with_index do |tail, i|
113
+ start_code[i].upto(tail) do |code|
114
+ if id_range_offset[i] == 0
115
+ glyph_id = code + id_delta[i]
116
+ else
117
+ index = id_range_offset[i] / 2 +
118
+ (code - start_code[i]) - (segcount - i)
119
+ # Decause some TTF fonts are broken
120
+ glyph_id = glyph_ids[index] || 0
121
+ glyph_id += id_delta[i] if glyph_id != 0
119
122
  end
123
+
124
+ @code_map[code] = glyph_id & 0xFFFF
120
125
  end
121
126
  end
127
+ end
122
128
  end
123
-
124
129
  end
125
130
  end
126
131
  end
@@ -1,7 +1,6 @@
1
1
  module TTFunk
2
2
  class Table
3
3
  class Cmap
4
-
5
4
  module Format06
6
5
  attr_reader :language
7
6
  attr_reader :code_map
@@ -11,20 +10,22 @@ module TTFunk
11
10
  glyph_map = { 0 => 0 }
12
11
 
13
12
  sorted_chars = charmap.keys.sort
14
- low_char, high_char = sorted_chars.first, sorted_chars.last
15
- entry_count = (1+high_char-low_char)
13
+ low_char = sorted_chars.first
14
+ high_char = sorted_chars.last
15
+ entry_count = 1 + high_char - low_char
16
16
  glyph_indexes = Array.new(entry_count, 0)
17
17
 
18
- new_map = charmap.keys.sort.inject({}) do |map, code|
18
+ new_map = charmap.keys.sort.each_with_object({}) do |code, map|
19
19
  glyph_map[charmap[code]] ||= next_id += 1
20
- map[code] = { :old => charmap[code], :new => glyph_map[charmap[code]] }
20
+ map[code] = { old: charmap[code], new: glyph_map[charmap[code]] }
21
21
  glyph_indexes[code - low_char] = glyph_map[charmap[code]]
22
- map
23
22
  end
24
23
 
25
- subtable = [6, 10+entry_count*2, 0, low_char, entry_count, *glyph_indexes].pack('n*')
24
+ subtable = [
25
+ 6, 10 + entry_count * 2, 0, low_char, entry_count, *glyph_indexes
26
+ ].pack('n*')
26
27
 
27
- { :charmap => new_map, :subtable => subtable, :max_glyph_id => next_id+1 }
28
+ { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
28
29
  end
29
30
 
30
31
  def [](code)
@@ -36,16 +37,15 @@ module TTFunk
36
37
  end
37
38
 
38
39
  private
39
- def parse_cmap!
40
- @language, firstcode, entrycount = read(8, 'x2nnn')
41
- @code_map = {}
42
- (firstcode...(firstcode+entrycount)).each do |code|
43
- @code_map[code] = read(2, 'n').first & 0xFFFF
44
- end
45
- end
46
40
 
41
+ def parse_cmap!
42
+ @language, firstcode, entrycount = read(8, 'x2nnn')
43
+ @code_map = {}
44
+ (firstcode...(firstcode + entrycount)).each do |code|
45
+ @code_map[code] = read(2, 'n').first & 0xFFFF
46
+ end
47
+ end
47
48
  end
48
-
49
49
  end
50
50
  end
51
51
  end
@@ -1,7 +1,6 @@
1
1
  module TTFunk
2
2
  class Table
3
3
  class Cmap
4
-
5
4
  module Format10
6
5
  attr_reader :language
7
6
  attr_reader :code_map
@@ -11,20 +10,23 @@ module TTFunk
11
10
  glyph_map = { 0 => 0 }
12
11
 
13
12
  sorted_chars = charmap.keys.sort
14
- low_char, high_char = sorted_chars.first, sorted_chars.last
15
- entry_count = (1+high_char-low_char)
13
+ low_char = sorted_chars.first
14
+ high_char = sorted_chars.last
15
+ entry_count = 1 + high_char - low_char
16
16
  glyph_indexes = Array.new(entry_count, 0)
17
17
 
18
- new_map = charmap.keys.sort.inject({}) do |map, code|
18
+ new_map = charmap.keys.sort.each_with_object({}) do |code, map|
19
19
  glyph_map[charmap[code]] ||= next_id += 1
20
- map[code] = { :old => charmap[code], :new => glyph_map[charmap[code]] }
20
+ map[code] = { old: charmap[code], new: glyph_map[charmap[code]] }
21
21
  glyph_indexes[code - low_char] = glyph_map[charmap[code]]
22
- map
23
22
  end
24
23
 
25
- subtable = [10, 0, 20+entry_count*4, 0, low_char, entry_count, *glyph_indexes].pack('nnN*')
24
+ subtable = [
25
+ 10, 0, 20 + entry_count * 4, 0, low_char, entry_count,
26
+ *glyph_indexes
27
+ ].pack('nnN*')
26
28
 
27
- { :charmap => new_map, :subtable => subtable, :max_glyph_id => next_id+1 }
29
+ { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
28
30
  end
29
31
 
30
32
  def [](code)
@@ -36,17 +38,20 @@ module TTFunk
36
38
  end
37
39
 
38
40
  private
39
- def parse_cmap!
40
- fractional_version, @language, firstcode, entrycount = read(18, 'nx4NNN')
41
- raise NotImplementedError, "cmap version 10.#{fractional_version} is not supported" if fractional_version != 0
42
- @code_map = {}
43
- (firstcode...(firstcode+entrycount)).each do |code|
44
- @code_map[code] = read(2, 'n').first & 0xFFFF
45
- end
46
- end
47
41
 
42
+ def parse_cmap!
43
+ fractional_version, @language, firstcode, entrycount =
44
+ read(18, 'nx4NNN')
45
+ if fractional_version != 0
46
+ raise NotImplementedError,
47
+ "cmap version 10.#{fractional_version} is not supported"
48
+ end
49
+ @code_map = {}
50
+ (firstcode...(firstcode + entrycount)).each do |code|
51
+ @code_map[code] = read(2, 'n').first & 0xFFFF
52
+ end
53
+ end
48
54
  end
49
-
50
55
  end
51
56
  end
52
57
  end