File: base.rb

package info (click to toggle)
ruby-ttfunk 1.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 18,472 kB
  • sloc: ruby: 7,954; makefile: 7
file content (161 lines) | stat: -rw-r--r-- 4,321 bytes parent folder | download
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# frozen_string_literal: true

require_relative '../table/cmap'
require_relative '../table/glyf'
require_relative '../table/head'
require_relative '../table/hhea'
require_relative '../table/hmtx'
require_relative '../table/kern'
require_relative '../table/loca'
require_relative '../table/maxp'
require_relative '../table/name'
require_relative '../table/post'
require_relative '../table/simple'

module TTFunk
  module Subset
    # Base subset.
    #
    # @api private
    class Base
      # Microsoft Platform ID
      MICROSOFT_PLATFORM_ID = 3

      # Symbol Encoding ID for Microsoft Platform
      MS_SYMBOL_ENCODING_ID = 0

      # Original font
      #
      # @return [TTFunk::File]
      attr_reader :original

      # @param original [TTFunk::File]
      def initialize(original)
        @original = original
      end

      # Is this Unicode-based subset?
      #
      # @return [Boolean]
      def unicode?
        false
      end

      # Does this subset use Microsoft Symbolic encoding?
      #
      # @return [Boolean]
      def microsoft_symbol?
        new_cmap_table[:platform_id] == MICROSOFT_PLATFORM_ID &&
          new_cmap_table[:encoding_id] == MS_SYMBOL_ENCODING_ID
      end

      # Get a mapping from this subset to Unicode.
      #
      # @return [Hash{Integer => Integer}]
      def to_unicode_map
        {}
      end

      # Encode this subset into a binary font representation.
      #
      # @param options [Hash]
      # @return [String]
      def encode(options = {})
        encoder_klass.new(original, self, options).encode
      end

      # Encoder class for this subset.
      #
      # @return [TTFunk::TTFEncoder, TTFunk::OTFEncoder]
      def encoder_klass
        original.cff.exists? ? OTFEncoder : TTFEncoder
      end

      # Get the first Unicode cmap from the original font.
      #
      # @return [TTFunk::Table::Cmap::Subtable]
      def unicode_cmap
        @unicode_cmap ||= @original.cmap.unicode.first
      end

      # Get glyphs in this subset.
      #
      # @return [Hash{Integer => TTFunk::Table::Glyf::Simple,
      #   TTFunk::Table::Glyf::Compound}] if original is a TrueType font
      # @return [Hash{Integer => TTFunk::Table::Cff::Charstring] if original is
      #   a CFF-based OpenType font
      def glyphs
        @glyphs ||= collect_glyphs(original_glyph_ids)
      end

      # Get glyphs by their IDs in the original font.
      #
      # @param glyph_ids [Array<Integer>]
      # @return [Hash{Integer => TTFunk::Table::Glyf::Simple,
      #   TTFunk::Table::Glyf::Compound>] if original is a TrueType font
      # @return [Hash{Integer => TTFunk::Table::Cff::Charstring}] if original is
      #   a CFF-based OpenType font
      def collect_glyphs(glyph_ids)
        collected =
          glyph_ids.each_with_object({}) do |id, h|
            h[id] = glyph_for(id)
          end

        additional_ids = collected.values
          .select { |g| g && g.compound? }
          .map(&:glyph_ids)
          .flatten

        collected.update(collect_glyphs(additional_ids)) if additional_ids.any?

        collected
      end

      # Glyph ID mapping from the original font to this subset.
      #
      # @return [Hash{Integer => Integer}]
      def old_to_new_glyph
        @old_to_new_glyph ||=
          begin
            charmap = new_cmap_table[:charmap]
            old_to_new =
              charmap.each_with_object(0 => 0) do |(_, ids), map|
                map[ids[:old]] = ids[:new]
              end

            next_glyph_id = new_cmap_table[:max_glyph_id]

            glyphs.each_key do |old_id|
              unless old_to_new.key?(old_id)
                old_to_new[old_id] = next_glyph_id
                next_glyph_id += 1
              end
            end

            old_to_new
          end
      end

      # Glyph ID mapping from this subset to the original font.
      #
      # @return [Hash{Integer => Integer}]
      def new_to_old_glyph
        @new_to_old_glyph ||= old_to_new_glyph.invert
      end

      private

      def glyph_for(glyph_id)
        if original.cff.exists?
          original
            .cff
            .top_index[0]
            .charstrings_index[glyph_id]
            .glyph
        else
          original.glyph_outlines.for(glyph_id)
        end
      end
    end
  end
end