# -*- encoding: utf-8; frozen_string_literal: true -*-
#
#--
# This file is part of HexaPDF.
#
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
# Copyright (C) 2014-2020 Thomas Leitner
#
# HexaPDF is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License version 3 as
# published by the Free Software Foundation with the addition of the
# following permission added to Section 15 as permitted in Section 7(a):
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
# INFRINGEMENT OF THIRD PARTY RIGHTS.
#
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with HexaPDF. If not, see .
#
# The interactive user interfaces in modified source and object code
# versions of HexaPDF must display Appropriate Legal Notices, as required
# under Section 5 of the GNU Affero General Public License version 3.
#
# In accordance with Section 7(b) of the GNU Affero General Public
# License, a covered work must retain the producer line in every PDF that
# is created or manipulated using HexaPDF.
#
# If the GNU Affero General Public License doesn't fit your need,
# commercial licenses are available at .
#++
require 'forwardable'
require 'set'
require 'hexapdf/font/type1'
require 'hexapdf/font/encoding'
module HexaPDF
module Font
module Type1
# Represents a Type1 font.
#
# This class abstracts from the specifics of the Type1 font and allows working with it in a
# standardized way.
#
# The following method calls are forwarded to the contained FontMetrics object:
#
# * font_name
# * full_name
# * family_name
# * weight
# * weight_class
# * font_bbox
# * italic_angle
# * ascender
# * descender
# * cap_height
# * x_height
# * horizontal_dominant_width
# * vertical_dominant_width
class Font
extend Forwardable
# Creates a Type1 font object from an AFM source.
def self.from_afm(source)
new(AFMParser.parse(source))
end
# The associated FontMetrics object.
attr_reader :metrics
def_delegators :@metrics, :font_name, :full_name, :family_name
def_delegators :@metrics, :weight, :weight_class, :bounding_box, :italic_angle
def_delegators :@metrics, :ascender, :descender, :cap_height, :x_height
def_delegators :@metrics, :dominant_horizontal_stem_width, :dominant_vertical_stem_width
def_delegators :@metrics, :underline_thickness
# Creates a new Type1 font object with the given font metrics.
def initialize(metrics)
@metrics = metrics
end
# Returns the built-in encoding of the font.
def encoding
@encoding ||=
begin
if @metrics.encoding_scheme == 'AdobeStandardEncoding'
Encoding.for_name(:StandardEncoding)
elsif font_name == 'ZapfDingbats' || font_name == 'Symbol'
Encoding.for_name((font_name + "Encoding").to_sym)
else
encoding = Encoding::Base.new
@metrics.character_metrics.each do |key, char_metric|
next unless key.kind_of?(Integer) && key >= 0
encoding.code_to_name[key] = char_metric.name
end
encoding
end
end
end
# :call-seq:
# font.width(glyph_name) -> width or nil
# font.width(glyph_code) -> width or nil
#
# Returns the width of the glyph which can either be specified by glyph name or by an
# integer that is interpreted according to the built-in encoding.
#
# If there is no glyph found for the name or code, +nil+ is returned.
def width(glyph)
(metric = @metrics.character_metrics[glyph]) && metric.width
end
# Returns the name/id of the missing glyph, i.e. .notdef.
def missing_glyph_id
:'.notdef'
end
# Returns a set of features this font supports.
#
# For Type1 fonts, the features that may be available :kern and :liga.
def features
@features ||= Set.new.tap do |set|
set << :kern unless metrics.kerning_pairs.empty?
set << :liga unless metrics.ligature_pairs.empty?
end
end
# Returns the distance from the baseline to the top of the underline.
def underline_position
@metrics.underline_position + @metrics.underline_thickness / 2.0
end
# Returns the distance from the baseline to the top of the strikeout line.
def strikeout_position
# We use the suggested value from the OpenType spec.
225
end
# Returns the thickness of the strikeout line.
def strikeout_thickness
# The OpenType spec suggests the width of an em-dash or 50, so we use that as
# approximation since the AFM files don't contain this information.
if (bbox = @metrics.character_metrics[:emdash]&.bbox)
bbox[3] - bbox[1]
else
50
end
end
end
end
end
end