File: font_descriptor.rb

package info (click to toggle)
ruby-pdf-reader 2.15.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 33,512 kB
  • sloc: ruby: 11,959; sh: 46; makefile: 11
file content (137 lines) | stat: -rw-r--r-- 4,354 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
# coding: utf-8
# typed: strict
# frozen_string_literal: true

require 'ttfunk'

class PDF::Reader

  # Font descriptors are outlined in Section 9.8, PDF 32000-1:2008, pp 281-288
  class FontDescriptor

    #: String
    attr_reader :font_name

    #: String?
    attr_reader :font_family

    #: Symbol
    attr_reader :font_stretch

    #: Numeric
    attr_reader :font_weight

    #: Array[Numeric]
    attr_reader :font_bounding_box

    #: Numeric
    attr_reader :cap_height

    #: Numeric
    attr_reader :ascent

    #: Numeric
    attr_reader :descent

    #: Numeric
    attr_reader :leading

    #: Numeric
    attr_reader :avg_width

    #: Numeric
    attr_reader :max_width

    #: Numeric
    attr_reader :missing_width

    #: Numeric?
    attr_reader :italic_angle

    #: Numeric?
    attr_reader :stem_v

    #: Numeric?
    attr_reader :x_height

    #: Integer
    attr_reader :font_flags

    #: (PDF::Reader::ObjectHash, Hash[untyped, untyped]) -> void
    def initialize(ohash, fd_hash)
      # TODO change these to typed derefs
      @ascent                = ohash.deref_number(fd_hash[:Ascent])    || 0 #: Numeric
      @descent               = ohash.deref_number(fd_hash[:Descent])   || 0 #: Numeric
      @missing_width         = ohash.deref_number(fd_hash[:MissingWidth]) || 0 #: Numeric
      @font_bounding_box     = ohash.deref_array_of_numbers(
        fd_hash[:FontBBox]
      ) || [0,0,0,0] #: Array[Numeric]
      @avg_width             = ohash.deref_number(fd_hash[:AvgWidth])  || 0 #: Numeric
      @cap_height            = ohash.deref_number(fd_hash[:CapHeight]) || 0 #: Numeric
      @font_flags            = ohash.deref_integer(fd_hash[:Flags])     || 0 #: Integer
      @italic_angle          = ohash.deref_number(fd_hash[:ItalicAngle]) #: Numeric?
      @font_name             = ohash.deref_name(fd_hash[:FontName]).to_s #: String
      @leading               = ohash.deref_number(fd_hash[:Leading])   || 0 #: Numeric
      @max_width             = ohash.deref_number(fd_hash[:MaxWidth])  || 0 #: Numeric
      @stem_v                = ohash.deref_number(fd_hash[:StemV]) #: Numeric?
      @x_height              = ohash.deref_number(fd_hash[:XHeight]) #: Numeric?
      @font_stretch          = ohash.deref_name(fd_hash[:FontStretch]) || :Normal #: Symbol
      @font_weight           = ohash.deref_number(fd_hash[:FontWeight])  || 400 #: Numeric
      @font_family           = ohash.deref_string(fd_hash[:FontFamily]) #: String?

      # A FontDescriptor may have an embedded font program in FontFile
      # (Type 1 Font Program), FontFile2 (TrueType font program), or
      # FontFile3 (Other font program as defined by Subtype entry)
      # Subtype entries:
      # 1) Type1C:        Type 1 Font Program in Compact Font Format
      # 2) CIDFontType0C: Type 0 Font Program in Compact Font Format
      # 3) OpenType:      OpenType Font Program
      # see Section 9.9, PDF 32000-1:2008, pp 288-292
      @font_program_stream = ohash.deref_stream(fd_hash[:FontFile2]) #: PDF::Reader::Stream?
      #TODO handle FontFile and FontFile3
      @ttf_program_stream = nil #: TTFunk::File?

      @is_ttf = @font_program_stream ? true : false #: bool
      @glyph_to_pdf_sf = nil #: Numeric?
    end

    #: (Integer) -> Numeric
    def glyph_width(char_code)
      if @is_ttf
        if ttf_program_stream.cmap.unicode.length > 0
          glyph_id = ttf_program_stream.cmap.unicode.first[char_code]
        else
          glyph_id = char_code
        end
        char_metric = ttf_program_stream.horizontal_metrics.metrics[glyph_id]
        if char_metric
          char_metric.advance_width
        else
          0
        end
      end
    end

    # PDF states that a glyph is 1000 units wide, true type doesn't enforce
    # any behavior, but uses units/em to define how wide the 'M' is (the widest letter)
    #: () -> Numeric
    def glyph_to_pdf_scale_factor
      if @is_ttf
        @glyph_to_pdf_sf ||= (1.0 / ttf_program_stream.header.units_per_em) * 1000.0
      else
        @glyph_to_pdf_sf ||= 1.0
      end
      @glyph_to_pdf_sf
    end

    private

    #: () -> TTFunk::File
    def ttf_program_stream
      raise MalformedPDFError, "No font_program_stream" unless @font_program_stream

      @ttf_program_stream ||= TTFunk::File.new(@font_program_stream.unfiltered_data)
    end
  end

end