File: hhea.rb

package info (click to toggle)
ruby-ttfunk 1.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 18,472 kB
  • sloc: ruby: 7,954; makefile: 7
file content (145 lines) | stat: -rw-r--r-- 4,071 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
# frozen_string_literal: true

require_relative '../table'

module TTFunk
  class Table
    # Horizontal Header (`hhea`) table.
    class Hhea < Table
      # Table version
      # @return [Integer]
      attr_reader :version

      # Typographic ascent.
      # @return [Integer]
      attr_reader :ascent

      # Typographic descent.
      # @return [Integer]
      attr_reader :descent

      # Typographic line gap.
      # @return [Integer]
      attr_reader :line_gap

      # Maximum advance width value in `hmtx` table.
      # @return [Integer]
      attr_reader :advance_width_max

      # Minimum left sidebearing value in `hmtx` table for glyphs with contours
      # (empty glyphs should be ignored).
      # @return [Integer]
      attr_reader :min_left_side_bearing

      # Minimum right sidebearing value.
      # @return [Integer]
      attr_reader :min_right_side_bearing

      # Maximum extent.
      # @return [Integer]
      attr_reader :x_max_extent

      # Caret slope rise.
      # @return [Integer]
      attr_reader :caret_slope_rise

      # @deprecated Use {caret_slope_rise} instead.
      # @!parse attr_reader :carot_slope_rise
      # @return [Integer]
      def carot_slope_rise
        @caret_slope_rise
      end

      # Caret slope run.
      # @return [Integer]
      attr_reader :caret_slope_run

      # @deprecated Use {caret_slope_run} instead.
      # @!parse attr_reader :carot_slope_run
      # @return [Integer]
      def carot_slope_run
        @caret_slope_run
      end

      # Caret offset.
      # @return [Integer]
      attr_reader :caret_offset

      # Metric data format. `0` for current format.
      # @return [Integer]
      attr_reader :metric_data_format

      # Number of hMetric entries in `hmtx` table.
      # @return [Integer]
      attr_reader :number_of_metrics

      class << self
        # Encode table.
        #
        # @param hhea [TTFunk::Table::Hhea] table to encode.
        # @param hmtx [TTFunk::Table::Hmtx]
        # @param original [TTFunk::File] original font file.
        # @param mapping [Hash{Integer => Integer}] keys are new glyph IDs, values
        #   are old glyph IDs
        # @return [String]
        def encode(hhea, hmtx, original, mapping)
          ''.b.tap do |table|
            table << [hhea.version].pack('N')
            table << [
              hhea.ascent, hhea.descent, hhea.line_gap,
              *min_max_values_for(original, mapping),
              hhea.caret_slope_rise, hhea.caret_slope_run, hhea.caret_offset,
              0, 0, 0, 0, hhea.metric_data_format, hmtx[:number_of_metrics],
            ].pack('n*')
          end
        end

        private

        def min_max_values_for(original, mapping)
          min_lsb = Min.new
          min_rsb = Min.new
          max_aw = Max.new
          max_extent = Max.new

          mapping.each_value do |old_glyph_id|
            horiz_metrics = original.horizontal_metrics.for(old_glyph_id)
            next unless horiz_metrics

            min_lsb << horiz_metrics.left_side_bearing
            max_aw << horiz_metrics.advance_width

            glyph = original.find_glyph(old_glyph_id)
            next unless glyph

            x_delta = glyph.x_max - glyph.x_min

            min_rsb << (horiz_metrics.advance_width - horiz_metrics.left_side_bearing - x_delta)

            max_extent << (horiz_metrics.left_side_bearing + x_delta)
          end

          [
            max_aw.value_or(0), min_lsb.value_or(0),
            min_rsb.value_or(0), max_extent.value_or(0),
          ]
        end
      end

      private

      def parse!
        @version = read(4, 'N').first
        @ascent, @descent, @line_gap = read_signed(3)
        @advance_width_max = read(2, 'n').first

        @min_left_side_bearing, @min_right_side_bearing, @x_max_extent,
          @caret_slope_rise, @caret_slope_run, @caret_offset,
          _reserved, _reserved, _reserved, _reserved,
          @metric_data_format = read_signed(11)

        @number_of_metrics = read(2, 'n').first
      end
    end
  end
end