# -*- 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-2024 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 'hexapdf/font/true_type/table' require 'set' module HexaPDF module Font module TrueType # Represents a font in the TrueType font file format. class Font # The default configuration: # # font.ttf.table_mapping:: # The default mapping from table tag as symbol to table class name. # # font.ttf.unknown_format:: # Action to take when encountering unknown subtables. Can either be :ignore # which ignores them or :raise which raises an error. DEFAULT_CONFIG = { 'font.true_type.table_mapping' => { head: 'HexaPDF::Font::TrueType::Table::Head', cmap: 'HexaPDF::Font::TrueType::Table::Cmap', hhea: 'HexaPDF::Font::TrueType::Table::Hhea', hmtx: 'HexaPDF::Font::TrueType::Table::Hmtx', loca: 'HexaPDF::Font::TrueType::Table::Loca', maxp: 'HexaPDF::Font::TrueType::Table::Maxp', name: 'HexaPDF::Font::TrueType::Table::Name', post: 'HexaPDF::Font::TrueType::Table::Post', glyf: 'HexaPDF::Font::TrueType::Table::Glyf', 'OS/2': 'HexaPDF::Font::TrueType::Table::OS2', kern: 'HexaPDF::Font::TrueType::Table::Kern', }, 'font.true_type.unknown_format' => :ignore, } # The IO stream associated with this file. attr_reader :io # The configuration for the TrueType font. attr_reader :config # Creates a new TrueType font file object for the given IO object. # # The +config+ hash can contain configuration options. def initialize(io, config: {}) @io = io @config = DEFAULT_CONFIG.merge(config) @tables = {} end # Returns the table instance for the given tag (a symbol), or +nil+ if no such table exists. def [](tag) return @tables[tag] if @tables.key?(tag) entry = directory.entry(tag.to_s.b) entry ? @tables[tag] = table_class(tag).new(self, entry) : nil end # Returns the font directory. def directory @directory ||= Table::Directory.new(self, io ? Table::Directory::SELF_ENTRY : nil) end # Returns a set of features this font supports. # # Features that may be available are for example :kern or :liga. def features @features ||= Set.new.tap do |set| set << :kern if self[:kern]&.horizontal_kerning_subtable end end # Returns the PostScript font name. def font_name self[:name][:postscript_name].preferred_record end # Returns the full name of the font. def full_name self[:name][:font_name].preferred_record end # Returns the family name of the font. def family_name self[:name][:font_family].preferred_record end # Returns the weight of the font. def weight self[:'OS/2'].weight_class || 0 end # Returns the bounding of the font. def bounding_box self[:head].bbox end # Returns the cap height of the font. def cap_height self[:'OS/2'].cap_height end # Returns the x-height of the font. def x_height self[:'OS/2'].x_height end # Returns the ascender of the font. def ascender self[:'OS/2'].typo_ascender || self[:hhea].ascent end # Returns the descender of the font. def descender self[:'OS/2'].typo_descender || self[:hhea].descent end # Returns the italic angle of the font, in degrees counter-clockwise from the vertical. def italic_angle self[:post].italic_angle.to_f end # Returns the dominant width of vertical stems. # # Note: This attribute does not actually exist in TrueType fonts, so it is estimated based # on the #weight. def dominant_vertical_stem_width weight / 5 end # Returns the distance from the baseline to the top of the underline. def underline_position self[:post].underline_position end # Returns the stroke width for the underline. def underline_thickness self[:post].underline_thickness end # Returns the distance from the baseline to the top of the strikeout line. def strikeout_position self[:'OS/2'].strikeout_position end # Returns the stroke width for the strikeout line. def strikeout_thickness self[:'OS/2'].strikeout_size end # Returns th glyph ID of the missing glyph, i.e. 0. def missing_glyph_id 0 end private # Returns the class that is used for handling tables of the given tag. def table_class(tag) k = config['font.true_type.table_mapping'].fetch(tag, 'HexaPDF::Font::TrueType::Table') ::Object.const_get(k) end end end end end