Module: Pdfrb::Font::MetricsHelper

Defined in:
lib/pdfrb/font/metrics_helper.rb

Class Method Summary collapse

Class Method Details

.afm_for(name) ⇒ Object



102
103
104
105
106
107
108
109
110
# File 'lib/pdfrb/font/metrics_helper.rb', line 102

def afm_for(name)
  @afm_cache ||= {}
  return @afm_cache[name] if @afm_cache.key?(name)

  path = standard14_afm_path(name)
  return @afm_cache[name] = nil unless path && File.exist?(path)

  @afm_cache[name] = Pdfrb::Font::AFMParser.parse(File.read(path))
end

.metrics_for(font_name_or_data) ⇒ Object

Convenience: dispatch on font_name vs raw bytes.



85
86
87
88
89
90
91
92
93
94
95
# File 'lib/pdfrb/font/metrics_helper.rb', line 85

def metrics_for(font_name_or_data)
  case font_name_or_data
  when String
    if font_name_or_data.bytesize >= 4 && valid_magic?(font_name_or_data)
      metrics_for_ttf(font_name_or_data)
    else
      metrics_for_standard14(font_name_or_data)
    end
  when Symbol then metrics_for_standard14(font_name_or_data)
  end
end

.metrics_for_standard14(font_name) ⇒ Pdfrb::Font::Metrics?

Look up metrics for a Standard 14 font name.

Parameters:

  • font_name (String, Symbol)

    e.g., "Helvetica", "Times-Roman".

Returns:



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/pdfrb/font/metrics_helper.rb', line 32

def metrics_for_standard14(font_name)
  afm = afm_for(font_name.to_s)
  return nil unless afm

  Metrics.new(
    font_name: font_name.to_s,
    units_per_em: 1000,
    ascent: afm.ascender,
    descent: afm.descender,
    cap_height: afm.cap_height,
    x_height: afm.x_height,
    line_gap: 0,
    avg_width: afm.average_width,
    max_width: afm.max_width,
    stem_v: 80,
    italic_angle: afm.italic_angle || 0,
    bbox: afm.bbox
  )
rescue StandardError
  nil
end

.metrics_for_ttf(font_data) ⇒ Pdfrb::Font::Metrics?

Look up metrics from raw TTF/OTF bytes.

Parameters:

  • font_data (String)

    raw TTF or OTF bytes.

Returns:



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
# File 'lib/pdfrb/font/metrics_helper.rb', line 57

def metrics_for_ttf(font_data)
  return nil unless font_data && font_data.bytesize >= 4

  ttf = Pdfrb::Font::TrueType::File.new(font_data)
  head = ttf.head_table_parsed
  hhea = ttf.hhea_table_parsed
  os2 = ttf.os2_table_parsed
  return nil unless head && hhea

  Metrics.new(
    font_name: ttf.name_table_parsed&.ps_name || "EmbeddedFont",
    units_per_em: head.units_per_em || 1000,
    ascent: hhea.ascender || os2&.s_typo_ascender || 800,
    descent: hhea.descender || os2&.s_typo_descender || -200,
    cap_height: os2&.s_cap_height || 700,
    x_height: 500,
    line_gap: hhea.line_gap || 0,
    avg_width: 500,
    max_width: hhea.advance_width_max || 1000,
    stem_v: 80,
    italic_angle: (head.italic? ? -12 : 0),
    bbox: head.bbox
  )
rescue StandardError
  nil
end

.standard14_afm_path(name) ⇒ Object



112
113
114
115
116
117
118
# File 'lib/pdfrb/font/metrics_helper.rb', line 112

def standard14_afm_path(name)
  # __dir__ is lib/pdfrb/font/. Three .. gets to project root
  # (lib/pdfrb/font → lib/pdfrb → lib → project root). Data
  # files live at <root>/data/pdfrb/afm/.
  base = File.expand_path("../../../data/pdfrb/afm", __dir__)
  File.join(base, "#{name}.afm")
end

.valid_magic?(bytes) ⇒ Boolean

Returns:

  • (Boolean)


97
98
99
100
# File 'lib/pdfrb/font/metrics_helper.rb', line 97

def valid_magic?(bytes)
  magic = bytes.byteslice(0, 4)
  ["\x00\x01\x00\x00".b, "OTTO".b, "true".b, "ttcf".b].include?(magic)
end