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
@@ -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