ttfunk 1.4.0 → 1.5.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.
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
@@ -1,7 +1,6 @@
1
1
  module TTFunk
2
2
  class Table
3
3
  class Cmap
4
-
5
4
  module Format12
6
5
  attr_reader :language
7
6
  attr_reader :code_map
@@ -9,14 +8,16 @@ module TTFunk
9
8
  def self.encode(charmap)
10
9
  next_id = 0
11
10
  glyph_map = { 0 => 0 }
12
- range_firstglyphs, range_firstcodes, range_lengths = [], [], []
11
+ range_firstglyphs = []
12
+ range_firstcodes = []
13
+ range_lengths = []
13
14
  last_glyph = last_code = -999
14
15
 
15
- new_map = charmap.keys.sort.inject({}) do |map, code|
16
+ new_map = charmap.keys.sort.each_with_object({}) do |code, map|
16
17
  glyph_map[charmap[code]] ||= next_id += 1
17
- map[code] = { :old => charmap[code], :new => glyph_map[charmap[code]] }
18
+ map[code] = { old: charmap[code], new: glyph_map[charmap[code]] }
18
19
 
19
- if code > last_code+1 || glyph_map[charmap[code]] > last_glyph+1
20
+ if code > last_code + 1 || glyph_map[charmap[code]] > last_glyph + 1
20
21
  range_firstcodes << code
21
22
  range_firstglyphs << glyph_map[charmap[code]]
22
23
  range_lengths << 1
@@ -25,17 +26,20 @@ module TTFunk
25
26
  end
26
27
  last_code = code
27
28
  last_glyph = glyph_map[charmap[code]]
28
-
29
- map
30
29
  end
31
30
 
32
- subtable = [12, 0, 16+12*range_lengths.size, 0, range_lengths.size].pack('nnNNN')
31
+ subtable = [
32
+ 12, 0, 16 + 12 * range_lengths.size, 0, range_lengths.size
33
+ ].pack('nnNNN')
33
34
  range_lengths.each_with_index do |length, i|
34
- firstglyph, firstcode = range_firstglyphs[i], range_firstcodes[i]
35
- subtable << [firstcode, firstcode+length-1, firstglyph].pack('NNN')
35
+ firstglyph = range_firstglyphs[i]
36
+ firstcode = range_firstcodes[i]
37
+ subtable << [
38
+ firstcode, firstcode + length - 1, firstglyph
39
+ ].pack('NNN')
36
40
  end
37
41
 
38
- { :charmap => new_map, :subtable => subtable, :max_glyph_id => next_id+1 }
42
+ { charmap: new_map, subtable: subtable, max_glyph_id: next_id + 1 }
39
43
  end
40
44
 
41
45
  def [](code)
@@ -47,20 +51,22 @@ module TTFunk
47
51
  end
48
52
 
49
53
  private
50
- def parse_cmap!
51
- fractional_version, @language, groupcount = read(14, 'nx4NN')
52
- raise NotImplementedError, "cmap version 12.#{fractional_version} is not supported" if fractional_version != 0
53
- @code_map = {}
54
- (1..groupcount).each do
55
- startchar, endchar, startglyph = read(12, 'NNN')
56
- (0..(endchar-startchar)).each do |offset|
57
- @code_map[startchar+offset] = startglyph+offset
58
- end
54
+
55
+ def parse_cmap!
56
+ fractional_version, @language, groupcount = read(14, 'nx4NN')
57
+ if fractional_version != 0
58
+ raise NotImplementedError,
59
+ "cmap version 12.#{fractional_version} is not supported"
60
+ end
61
+ @code_map = {}
62
+ (1..groupcount).each do
63
+ startchar, endchar, startglyph = read(12, 'NNN')
64
+ (0..(endchar - startchar)).each do |offset|
65
+ @code_map[startchar + offset] = startglyph + offset
59
66
  end
60
67
  end
61
-
68
+ end
62
69
  end
63
-
64
70
  end
65
71
  end
66
72
  end
@@ -11,11 +11,12 @@ module TTFunk
11
11
  attr_reader :format
12
12
 
13
13
  ENCODING_MAPPINGS = {
14
- :mac_roman => { :platform_id => 1, :encoding_id => 0 },
15
- # use microsoft unicode, instead of generic unicode, for optimal windows support
16
- :unicode => { :platform_id => 3, :encoding_id => 1 },
17
- :unicode_ucs4 => { :platform_id => 3, :encoding_id => 10 }
18
- }
14
+ mac_roman: { platform_id: 1, encoding_id: 0 }.freeze,
15
+ # Use microsoft unicode, instead of generic unicode, for optimal
16
+ # Windows support
17
+ unicode: { platform_id: 3, encoding_id: 1 }.freeze,
18
+ unicode_ucs4: { platform_id: 3, encoding_id: 10 }.freeze
19
+ }.freeze
19
20
 
20
21
  def self.encode(charmap, encoding)
21
22
  case encoding
@@ -26,32 +27,37 @@ module TTFunk
26
27
  when :unicode_ucs4
27
28
  result = Format12.encode(charmap)
28
29
  else
29
- raise NotImplementedError, "encoding #{encoding.inspect} is not supported"
30
+ raise NotImplementedError,
31
+ "encoding #{encoding.inspect} is not supported"
30
32
  end
31
33
 
32
34
  mapping = ENCODING_MAPPINGS[encoding]
33
35
 
34
36
  # platform-id, encoding-id, offset
35
- result[:subtable] = [mapping[:platform_id], mapping[:encoding_id],
36
- 12, result[:subtable]].pack("nnNA*")
37
-
38
- return result
37
+ result[:subtable] = [
38
+ mapping[:platform_id],
39
+ mapping[:encoding_id],
40
+ 12,
41
+ result[:subtable]
42
+ ].pack('nnNA*')
43
+
44
+ result
39
45
  end
40
46
 
41
47
  def initialize(file, table_start)
42
48
  @file = file
43
- @platform_id, @encoding_id, @offset = read(8, "nnN")
49
+ @platform_id, @encoding_id, @offset = read(8, 'nnN')
44
50
  @offset += table_start
45
51
 
46
52
  parse_from(@offset) do
47
- @format = read(2, "n").first
53
+ @format = read(2, 'n').first
48
54
 
49
55
  case @format
50
- when 0 then extend(TTFunk::Table::Cmap::Format00)
51
- when 4 then extend(TTFunk::Table::Cmap::Format04)
52
- when 6 then extend(TTFunk::Table::Cmap::Format06)
53
- when 10 then extend(TTFunk::Table::Cmap::Format10)
54
- when 12 then extend(TTFunk::Table::Cmap::Format12)
56
+ when 0 then extend(TTFunk::Table::Cmap::Format00)
57
+ when 4 then extend(TTFunk::Table::Cmap::Format04)
58
+ when 6 then extend(TTFunk::Table::Cmap::Format06)
59
+ when 10 then extend(TTFunk::Table::Cmap::Format10)
60
+ when 12 then extend(TTFunk::Table::Cmap::Format12)
55
61
  end
56
62
 
57
63
  parse_cmap!
@@ -59,23 +65,24 @@ module TTFunk
59
65
  end
60
66
 
61
67
  def unicode?
62
- platform_id == 3 && (encoding_id == 1 || encoding_id == 10) && format != 0 ||
63
- platform_id == 0 && format != 0
68
+ platform_id == 3 && (encoding_id == 1 || encoding_id == 10) &&
69
+ format != 0 ||
70
+ platform_id == 0 && format != 0
64
71
  end
65
72
 
66
73
  def supported?
67
74
  false
68
75
  end
69
76
 
70
- def [](code)
77
+ def [](_code)
71
78
  raise NotImplementedError, "cmap format #{@format} is not supported"
72
79
  end
73
80
 
74
81
  private
75
82
 
76
- def parse_cmap!
77
- # do nothing...
78
- end
83
+ def parse_cmap!
84
+ # do nothing...
85
+ end
79
86
  end
80
87
  end
81
88
  end
@@ -7,12 +7,12 @@ module TTFunk
7
7
  # mapping old glyph-ids to new glyph-ids.
8
8
  #
9
9
  # Returns a hash containing:
10
- #
10
+ #
11
11
  # * :table - a string representing the encoded 'glyf' table containing
12
12
  # the given glyphs.
13
13
  # * :offsets - an array of offsets for each glyph
14
14
  def self.encode(glyphs, new2old, old2new)
15
- result = { :table => "", :offsets => [] }
15
+ result = { table: '', offsets: [] }
16
16
 
17
17
  new2old.keys.sort.each do |new_id|
18
18
  glyph = glyphs[new2old[new_id]]
@@ -23,7 +23,7 @@ module TTFunk
23
23
  # include an offset at the end of the table, for use in computing the
24
24
  # size of the last glyph
25
25
  result[:offsets] << result[:table].length
26
- return result
26
+ result
27
27
  end
28
28
 
29
29
  def for(glyph_id)
@@ -32,16 +32,18 @@ module TTFunk
32
32
  index = file.glyph_locations.index_of(glyph_id)
33
33
  size = file.glyph_locations.size_of(glyph_id)
34
34
 
35
- if size.zero? # blank glyph, e.g. space character
35
+ if size == 0 # blank glyph, e.g. space character
36
36
  @cache[glyph_id] = nil
37
37
  return nil
38
38
  end
39
39
 
40
40
  parse_from(offset + index) do
41
41
  raw = io.read(size)
42
- number_of_contours, x_min, y_min, x_max, y_max = raw.unpack("n5").map { |i| to_signed(i) }
42
+ number_of_contours, x_min, y_min, x_max, y_max =
43
+ raw.unpack('n5').map { |i| to_signed(i) }
43
44
 
44
- @cache[glyph_id] = if number_of_contours == -1
45
+ @cache[glyph_id] =
46
+ if number_of_contours == -1
45
47
  Compound.new(raw, x_min, y_min, x_max, y_max)
46
48
  else
47
49
  Simple.new(raw, number_of_contours, x_min, y_min, x_max, y_max)
@@ -51,11 +53,11 @@ module TTFunk
51
53
 
52
54
  private
53
55
 
54
- def parse!
55
- # because the glyf table is rather complex to parse, we defer
56
- # the parse until we need a specific glyf, and then cache it.
57
- @cache = {}
58
- end
56
+ def parse!
57
+ # because the glyf table is rather complex to parse, we defer
58
+ # the parse until we need a specific glyf, and then cache it.
59
+ @cache = {}
60
+ end
59
61
  end
60
62
  end
61
63
  end
@@ -21,7 +21,10 @@ module TTFunk
21
21
 
22
22
  def initialize(raw, x_min, y_min, x_max, y_max)
23
23
  @raw = raw
24
- @x_min, @y_min, @x_max, @y_max = x_min, y_min, x_max, y_max
24
+ @x_min = x_min
25
+ @y_min = y_min
26
+ @x_max = x_max
27
+ @y_max = y_max
25
28
 
26
29
  # Because TTFunk only cares about glyphs insofar as they (1) provide
27
30
  # a bounding box for each glyph, and (2) can be rewritten into a
@@ -37,18 +40,19 @@ module TTFunk
37
40
  offset = 10 # 2 bytes for each of num-contours, min x/y, max x/y
38
41
 
39
42
  loop do
40
- flags, glyph_id = @raw[offset, 4].unpack("n*")
43
+ flags, glyph_id = @raw[offset, 4].unpack('n*')
41
44
  @glyph_ids << glyph_id
42
45
  @glyph_id_offsets << offset + 2
43
46
 
44
47
  break unless flags & MORE_COMPONENTS != 0
45
48
  offset += 4
46
49
 
47
- if flags & ARG_1_AND_2_ARE_WORDS != 0
48
- offset += 4
49
- else
50
- offset += 2
51
- end
50
+ offset +=
51
+ if flags & ARG_1_AND_2_ARE_WORDS != 0
52
+ 4
53
+ else
54
+ 2
55
+ end
52
56
 
53
57
  if flags & WE_HAVE_A_TWO_BY_TWO != 0
54
58
  offset += 8
@@ -69,13 +73,12 @@ module TTFunk
69
73
  new_ids = glyph_ids.map { |id| mapping[id] }
70
74
 
71
75
  new_ids.zip(@glyph_id_offsets).each do |new_id, offset|
72
- result[offset, 2] = [new_id].pack("n")
76
+ result[offset, 2] = [new_id].pack('n')
73
77
  end
74
78
 
75
- return result
79
+ result
76
80
  end
77
81
  end
78
82
  end
79
83
  end
80
84
  end
81
-
@@ -11,8 +11,10 @@ module TTFunk
11
11
  def initialize(raw, number_of_contours, x_min, y_min, x_max, y_max)
12
12
  @raw = raw
13
13
  @number_of_contours = number_of_contours
14
- @x_min, @y_min = x_min, y_min
15
- @x_max, @y_max = x_max, y_max
14
+ @x_min = x_min
15
+ @y_min = y_min
16
+ @x_max = x_max
17
+ @y_max = y_max
16
18
 
17
19
  # Because TTFunk is, at this time, a library for simply pulling
18
20
  # metrics out of font files, or for writing font subsets, we don't
@@ -27,11 +29,10 @@ module TTFunk
27
29
  false
28
30
  end
29
31
 
30
- def recode(mapping)
32
+ def recode(_mapping)
31
33
  raw
32
34
  end
33
35
  end
34
36
  end
35
37
  end
36
38
  end
37
-
@@ -1,6 +1,6 @@
1
1
  require_relative '../table'
2
2
 
3
- module TTFunk
3
+ module TTFunk
4
4
  class Table
5
5
  class Head < TTFunk::Table
6
6
  attr_reader :version
@@ -23,22 +23,22 @@ module TTFunk
23
23
 
24
24
  def self.encode(head, loca)
25
25
  table = head.raw
26
- table[8,4] = "\0\0\0\0" # set checksum adjustment to 0 initially
27
- table[-4,2] = [loca[:type]].pack("n") # set index_to_loc_format
28
- return table
26
+ table[8, 4] = "\0\0\0\0" # set checksum adjustment to 0 initially
27
+ table[-4, 2] = [loca[:type]].pack('n') # set index_to_loc_format
28
+ table
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- def parse!
34
- @version, @font_revision, @check_sum_adjustment, @magic_number,
35
- @flags, @units_per_em, @created, @modified = read(36, "N4n2q2")
36
-
37
- @x_min, @y_min, @x_max, @y_max = read_signed(4)
38
-
39
- @mac_style, @lowest_rec_ppem, @font_direction_hint,
40
- @index_to_loc_format, @glyph_data_format = read(10, "n*")
41
- end
33
+ def parse!
34
+ @version, @font_revision, @check_sum_adjustment, @magic_number,
35
+ @flags, @units_per_em, @created, @modified = read(36, 'N4n2q2')
36
+
37
+ @x_min, @y_min, @x_max, @y_max = read_signed(4)
38
+
39
+ @mac_style, @lowest_rec_ppem, @font_direction_hint,
40
+ @index_to_loc_format, @glyph_data_format = read(10, 'n*')
41
+ end
42
42
  end
43
43
  end
44
44
  end
@@ -1,6 +1,6 @@
1
1
  require_relative '../table'
2
2
 
3
- module TTFunk
3
+ module TTFunk
4
4
  class Table
5
5
  class Hhea < Table
6
6
  attr_reader :version
@@ -18,24 +18,24 @@ module TTFunk
18
18
 
19
19
  def self.encode(hhea, hmtx)
20
20
  raw = hhea.raw
21
- raw[-2,2] = [hmtx[:number_of_metrics]].pack("n")
22
- return raw
21
+ raw[-2, 2] = [hmtx[:number_of_metrics]].pack('n')
22
+ raw
23
23
  end
24
24
 
25
25
  private
26
26
 
27
- def parse!
28
- @version = read(4, "N").first
29
- @ascent, @descent, @line_gap = read_signed(3)
30
- @advance_width_max = read(2, "n").first
27
+ def parse!
28
+ @version = read(4, 'N').first
29
+ @ascent, @descent, @line_gap = read_signed(3)
30
+ @advance_width_max = read(2, 'n').first
31
31
 
32
- @min_left_side_bearing, @min_right_side_bearing, @x_max_extent,
33
- @carot_slope_rise, @carot_slope_run, @caret_offset,
34
- reserved, reserved, reserved, reserved,
35
- @metric_data_format = read_signed(11)
32
+ @min_left_side_bearing, @min_right_side_bearing, @x_max_extent,
33
+ @carot_slope_rise, @carot_slope_run, @caret_offset,
34
+ _reserved, _reserved, _reserved, _reserved,
35
+ @metric_data_format = read_signed(11)
36
36
 
37
- @number_of_metrics = read(2, "n").first
38
- end
37
+ @number_of_metrics = read(2, 'n').first
38
+ end
39
39
  end
40
40
  end
41
41
  end
@@ -1,7 +1,7 @@
1
1
  require_relative '../table'
2
2
 
3
- module TTFunk
4
- class Table
3
+ module TTFunk
4
+ class Table
5
5
  class Hmtx < Table
6
6
  attr_reader :metrics
7
7
  attr_reader :left_side_bearings
@@ -13,35 +13,40 @@ module TTFunk
13
13
  [metric.advance_width, metric.left_side_bearing]
14
14
  end
15
15
 
16
- { :number_of_metrics => metrics.length,
17
- :table => metrics.flatten.pack("n*") }
16
+ {
17
+ number_of_metrics: metrics.length,
18
+ table: metrics.flatten.pack('n*')
19
+ }
18
20
  end
19
21
 
20
22
  HorizontalMetric = Struct.new(:advance_width, :left_side_bearing)
21
23
 
22
24
  def for(glyph_id)
23
25
  @metrics[glyph_id] ||
24
- HorizontalMetric.new(@metrics.last.advance_width,
25
- @left_side_bearings[glyph_id - @metrics.length])
26
+ HorizontalMetric.new(
27
+ @metrics.last.advance_width,
28
+ @left_side_bearings[glyph_id - @metrics.length]
29
+ )
26
30
  end
27
31
 
28
32
  private
29
33
 
30
- def parse!
31
- @metrics = []
34
+ def parse!
35
+ @metrics = []
32
36
 
33
- file.horizontal_header.number_of_metrics.times do
34
- advance = read(2, "n").first
35
- lsb = read_signed(1).first
36
- @metrics.push HorizontalMetric.new(advance, lsb)
37
- end
37
+ file.horizontal_header.number_of_metrics.times do
38
+ advance = read(2, 'n').first
39
+ lsb = read_signed(1).first
40
+ @metrics.push HorizontalMetric.new(advance, lsb)
41
+ end
38
42
 
39
- lsb_count = file.maximum_profile.num_glyphs - file.horizontal_header.number_of_metrics
40
- @left_side_bearings = read_signed(lsb_count)
43
+ lsb_count = file.maximum_profile.num_glyphs -
44
+ file.horizontal_header.number_of_metrics
45
+ @left_side_bearings = read_signed(lsb_count)
41
46
 
42
- @widths = @metrics.map { |metric| metric.advance_width }
43
- @widths += [@widths.last] * @left_side_bearings.length
44
- end
47
+ @widths = @metrics.map(&:advance_width)
48
+ @widths += [@widths.last] * @left_side_bearings.length
49
+ end
45
50
  end
46
51
  end
47
52
  end