File: format04.rb

package info (click to toggle)
ruby-ttfunk 1.0.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 916 kB
  • sloc: ruby: 1,719; makefile: 16
file content (126 lines) | stat: -rw-r--r-- 4,029 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
module TTFunk
  class Table
    class Cmap

      module Format04
        attr_reader :language
        attr_reader :code_map

        # Expects a hash mapping character codes to glyph ids (where the
        # glyph ids are from the original font). Returns a hash including
        # a new map (:charmap) that maps the characters in charmap to a
        # another hash containing both the old (:old) and new (:new) glyph
        # ids. The returned hash also includes a :subtable key, which contains
        # the encoded subtable for the given charmap.
        def self.encode(charmap)
          end_codes = []
          start_codes = []
          next_id = 0
          last = difference = nil

          glyph_map = { 0 => 0 }
          new_map = charmap.keys.sort.inject({}) do |map, code|
            old = charmap[code]
            glyph_map[old] ||= next_id += 1
            map[code] = { :old => old, :new => glyph_map[old] }

            delta = glyph_map[old] - code
            if last.nil? || delta != difference
              end_codes << last if last
              start_codes << code
              difference = delta
            end
            last = code

            map
          end

          end_codes << last if last
          end_codes << 0xFFFF
          start_codes << 0xFFFF
          segcount = start_codes.length

          # build the conversion tables
          deltas = []
          range_offsets = []
          glyph_indices = []

          offset = 0
          start_codes.zip(end_codes).each_with_index do |(a, b), segment|
            if a == 0xFFFF
              deltas << 0
              range_offsets << 0
              break
            end

            start_glyph_id = new_map[a][:new]
            if a - start_glyph_id >= 0x8000
              deltas << 0
              range_offsets << 2 * (glyph_indices.length + segcount - segment)
              a.upto(b) { |code| glyph_indices << new_map[code][:new] }
            else
              deltas << -a + start_glyph_id
              range_offsets << 0
            end
            offset += 2
          end

          # format, length, language
          subtable = [4, 16 + 8 * segcount + 2 * glyph_indices.length, 0].pack("nnn")

          search_range = 2 * 2 ** (Math.log(segcount) / Math.log(2)).to_i
          entry_selector = (Math.log(search_range / 2) / Math.log(2)).to_i
          range_shift = (2 * segcount) - search_range
          subtable << [segcount * 2, search_range, entry_selector, range_shift].pack("nnnn")

          subtable << end_codes.pack("n*") << "\0\0" << start_codes.pack("n*")
          subtable << deltas.pack("n*") << range_offsets.pack("n*") << glyph_indices.pack("n*")

          { :charmap => new_map, :subtable => subtable, :max_glyph_id => next_id+1 }
        end

        def [](code)
          @code_map[code] || 0
        end

        def supported?
          true
        end

        private

          def parse_cmap!
            length, @language, segcount_x2 = read(6, "nnn")
            segcount = segcount_x2 / 2

            io.read(6) # skip searching hints

            end_code = read(segcount_x2, "n*")
            io.read(2) # skip reserved value
            start_code = read(segcount_x2, "n*")
            id_delta = read_signed(segcount)
            id_range_offset = read(segcount_x2, "n*")

            glyph_ids = read(length - io.pos + @offset, "n*")

            @code_map = {}

            end_code.each_with_index do |tail, i|
              start_code[i].upto(tail) do |code|
                if id_range_offset[i].zero?
                  glyph_id = code + id_delta[i]
                else
                  index = id_range_offset[i] / 2 + (code - start_code[i]) - (segcount - i)
                  glyph_id = glyph_ids[index] || 0 # because some TTF fonts are broken
                  glyph_id += id_delta[i] if glyph_id != 0
                end

                @code_map[code] = glyph_id & 0xFFFF
              end
            end
          end
      end

    end
  end
end