Class: Prawn::SVG::FontRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/prawn/svg/font_registry.rb

Constant Summary collapse

GENERIC_CSS_FONT_MAPPING =
{
  'serif'      => 'Times-Roman',
  'sans-serif' => 'Helvetica',
  'cursive'    => 'Times-Roman',
  'fantasy'    => 'Times-Roman',
  'monospace'  => 'Courier'
}.freeze
FONT_WEIGHT_FALLBACKS =
{
  thin:       :extralight,
  extralight: :light,
  light:      :normal,
  normal:     nil,
  medium:     :normal,
  semibold:   :bold,
  bold:       :normal,
  extrabold:  :bold,
  black:      :extrabold
}.freeze
FONT_WEIGHTS =
FONT_WEIGHT_FALLBACKS.keys.freeze
FONT_WEIGHT_VALUES =
{
  thin: 100, extralight: 200, light: 300, normal: 400, medium: 500,
  semibold: 600, bold: 700, extrabold: 800, black: 900
}.freeze
FONT_WEIGHT_ALIASES =

Font files use various names for the same weight. Map common alternatives to the canonical weight names used in FONT_WEIGHTS.

{
  thin:       %i[hairline],
  extralight: %i[ultralight ultra_light extra_light],
  semibold:   %i[semi_bold demi_bold demibold],
  extrabold:  %i[extra_bold ultra_bold ultrabold],
  black:      %i[heavy]
}.freeze
FONT_STRETCH_MAPPING =
{
  'ultra-condensed' => :ultra_condensed,
  'extra-condensed' => :extra_condensed,
  'condensed'       => :condensed,
  'semi-condensed'  => :semi_condensed,
  'normal'          => :normal,
  'semi-expanded'   => :semi_expanded,
  'expanded'        => :expanded,
  'extra-expanded'  => :extra_expanded,
  'ultra-expanded'  => :ultra_expanded
}.freeze
FONT_STRETCHES =
%i[
  ultra_condensed extra_condensed condensed semi_condensed normal
  semi_expanded expanded extra_expanded ultra_expanded
].freeze
DEFAULT_FONT_PATHS =
[
  '/Library/Fonts',
  '/System/Library/Fonts',
  "#{Dir.home}/Library/Fonts",
  '/usr/share/fonts/truetype',
  '/mnt/c/Windows/Fonts' # Bash on Ubuntu on Windows
].freeze

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(font_families) ⇒ FontRegistry

Returns a new instance of FontRegistry.



67
68
69
# File 'lib/prawn/svg/font_registry.rb', line 67

def initialize(font_families)
  @font_families = font_families
end

Class Attribute Details

.external_font_familiesObject (readonly)

Returns the value of attribute external_font_families.



259
260
261
# File 'lib/prawn/svg/font_registry.rb', line 259

def external_font_families
  @external_font_families
end

.font_pathObject (readonly)

Returns the value of attribute font_path.



259
260
261
# File 'lib/prawn/svg/font_registry.rb', line 259

def font_path
  @font_path
end

Class Method Details

.load_external_fontsObject



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/prawn/svg/font_registry.rb', line 261

def load_external_fonts
  @external_font_families = {}

  external_font_paths.each do |filename|
    ttc = TTC.new(filename)
    if ttc.fonts.any?
      ttc.fonts.each do |font|
        subfamily = (font[:subfamily] || 'normal').gsub(/\s+/, '_').downcase.to_sym
        subfamily = :normal if subfamily == :regular
        family_hash = (external_font_families[font[:family]] ||= {})
        font_data = { file: filename, font: font[:index] }
        family_hash[subfamily] ||= font_data
        register_by_weight_class(family_hash, font[:weight_class], subfamily, font_data)
      end
    else
      ttf = TTF.new(filename)
      next unless ttf.family

      subfamily = (ttf.subfamily || 'normal').gsub(/\s+/, '_').downcase.to_sym
      subfamily = :normal if subfamily == :regular
      family_hash = (external_font_families[ttf.family] ||= {})
      family_hash[subfamily] ||= filename
      register_by_weight_class(family_hash, ttf.weight_class, subfamily, family_hash[subfamily])
    end
  end
end

Instance Method Details

#correctly_cased_font_name(name) ⇒ Object



76
77
78
79
# File 'lib/prawn/svg/font_registry.rb', line 76

def correctly_cased_font_name(name)
  merge_external_fonts
  @font_case_mapping[name.downcase]
end

#find_local_font_data(local_name, css_weight, css_style, css_stretch) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/prawn/svg/font_registry.rb', line 107

def find_local_font_data(local_name, css_weight, css_style, css_stretch)
  merge_external_fonts

  name = correctly_cased_font_name(local_name) || local_name
  name = GENERIC_CSS_FONT_MAPPING[name] if GENERIC_CSS_FONT_MAPPING.key?(name) && !@font_families.key?(name)

  subfamilies = @font_families[name]
  return unless subfamilies&.any?

  weight = css_weight ? weight_for_css_font_weight(css_weight) : :normal
  style = %w[italic oblique].include?(css_style) ? :italic : nil
  stretch = stretch_for_css_font_stretch(css_stretch)
  effective_stretch = stretch == :normal ? nil : stretch

  font = find_suitable_font(name, weight, style, effective_stretch)
  return unless font

  subfamilies[font.subfamily]
end

#installed_fontsObject



71
72
73
74
# File 'lib/prawn/svg/font_registry.rb', line 71

def installed_fonts
  merge_external_fonts
  @font_families
end

#load(family, weight = nil, style = nil, stretch = nil) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
# File 'lib/prawn/svg/font_registry.rb', line 81

def load(family, weight = nil, style = nil, stretch = nil)
  weight = weight_for_css_font_weight(weight) unless FONT_WEIGHTS.include?(weight)
  stretch = stretch_for_css_font_stretch(stretch)

  CSS::FontFamilyParser.parse(family).detect do |name|
    name = name.gsub(/\s{2,}/, ' ')

    font = find_suitable_font(name, weight, style, stretch)
    break font if font
  end
end

#register_font_face(family_name, css_weight, css_style, css_stretch, font_data) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/prawn/svg/font_registry.rb', line 93

def register_font_face(family_name, css_weight, css_style, css_stretch, font_data)
  merge_external_fonts

  weight = css_weight ? weight_for_css_font_weight(css_weight) : :normal
  style = %w[italic oblique].include?(css_style) ? :italic : nil
  stretch = stretch_for_css_font_stretch(css_stretch)
  effective_stretch = stretch == :normal ? nil : stretch

  font = Font.new(family_name, weight, style, effective_stretch)
  family = (@font_families[family_name] ||= {})
  family[font.subfamily] = font_data
  @font_case_mapping[family_name.downcase] = family_name
end