# -*- encoding: utf-8 -*- # #-- # This file is part of HexaPDF. # # HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby # Copyright (C) 2014-2017 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. #++ require 'hexapdf/font/true_type/table' module HexaPDF module Font module TrueType class Table # The 'post' table contains information for using a font on a PostScript printer. # # post format 2.5 is currently not implemented because use of the format is deprecated since # 2000 in the specification and no font with a format 2.5 post subtable was available for # testing. # # See: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html class Post < Table # The format of the table (a Rational). attr_accessor :format # The italic angle (a Rational). attr_accessor :italic_angle # The suggested distance of the top of the underline from the baseline (negative values # indicate underlines below the baseline). attr_accessor :underline_position # The suggested thickness for underlines. attr_accessor :underline_thickness # Specifies whether the font is proportional (value is 0) or monospaced (value is not 0). attr_accessor :is_fixed_pitch # Returns +true+ if the font is monospaced. # # See: #is_fixed_pitch def is_fixed_pitch? @is_fixed_pitch != 0 end # Minimum memory usage when a font is downloaded. attr_accessor :min_mem_type42 # Maximum memory usage when a font is downloaded. attr_accessor :max_mem_type42 # Minimum memory usage when a Type1 font is downloaded. attr_accessor :min_mem_type1 # Maximum memory usage when a Type1 font is downloaded. attr_accessor :max_mem_type1 # Returns the name for the given glpyh id or ".notdef" if the given glyph id has no name. def [](glyph_id) @glyph_names[glyph_id] || '.notdef'.freeze end private def parse_table #:nodoc: @format = read_fixed @italic_angle = read_fixed @underline_position, @underline_thickness, @is_fixed_pitch, @min_mem_type42, @max_mem_type42, @min_mem_type1, @max_mem_type1 = read_formatted(24, 's>2N5') sub_table_length = directory_entry.length - 32 @glyph_names = case @format when 1 then Format1.parse(io, sub_table_length) when 2 then Format2.parse(io, sub_table_length) when 3 then Format3.parse(io, sub_table_length) when 4 then Format4.parse(io, sub_table_length) else if font.config['font.true_type.unknown_format'] == :raise raise HexaPDF::Error, "Unsupported post table format: #{@format}" else [] end end end # 'post' table format 1 module Format1 # The 258 predefined glyph names in the standard Macintosh ordering. GLYPH_NAMES = %w[ .notdef .null nonmarkingreturn space exclam quotedbl numbersign dollar percent ampersand quotesingle parenleft parenright asterisk plus comma hyphen period slash zero one two three four five six seven eight nine colon semicolon less equal greater question at A B C D E F G H I J K L M N O P Q R S T U V W X Y Z bracketleft backslash bracketright asciicircum underscore grave a b c d e f g h i j k l m n o p q r s t u v w x y z braceleft bar braceright asciitilde Adieresis Aring Ccedilla Eacute Ntilde Odieresis Udieresis aacute agrave acircumflex adieresis atilde aring ccedilla eacute egrave ecircumflex edieresis iacute igrave icircumflex idieresis ntilde oacute ograve ocircumflex odieresis otilde uacute ugrave ucircumflex udieresis dagger degree cent sterling section bullet paragraph germandbls registered copyright trademark acute dieresis notequal AE Oslash infinity plusminus lessequal greaterequal yen mu partialdiff summation product pi integral ordfeminine ordmasculine Omega ae oslash questiondown exclamdown logicalnot radical florin approxequal Delta guillemotleft guillemotright ellipsis nonbreakingspace Agrave Atilde Otilde OE oe endash emdash quotedblleft quotedblright quoteleft quoteright divide lozenge ydieresis Ydieresis fraction currency guilsinglleft guilsinglright fi fl daggerdbl periodcentered quotesinglbase quotedblbase perthousand Acircumflex Ecircumflex Aacute Edieresis Egrave Iacute Icircumflex Idieresis Igrave Oacute Ocircumflex apple Ograve Uacute Ucircumflex Ugrave dotlessi circumflex tilde macron breve dotaccent ring cedilla hungarumlaut ogonek caron Lslash lslash Scaron scaron Zcaron zcaron brokenbar Eth eth Yacute yacute Thorn thorn minus multiply onesuperior twosuperior threesuperior onehalf onequarter threequarters franc Gbreve gbreve Idotaccent Scedilla scedilla Cacute cacute Ccaron ccaron dcroat ].freeze # :call-seq: # Format1.parse(io, length) -> glyph_names # # Returns the array containing the 258 predefined glpyh names. def self.parse(_io, _length) GLYPH_NAMES end end # 'post' table format 2 module Format2 # :call-seq: # Format2.parse(io, length) -> glyph_names # # Parses the format 2 post subtable from the given IO at the current position and # returns the contained glyph name map. def self.parse(io, length) end_pos = io.pos + length num_glyphs = io.read(2).unpack('n').first glyph_name_index = io.read(2 * num_glyphs).unpack('n*') names = [] names << io.read(io.getbyte).force_encoding(::Encoding::UTF_8) while io.pos < end_pos mapper(glyph_name_index, names) end def self.mapper(glyph_name_index, names) #:nodoc: lambda do |glyph_id| name_index = glyph_name_index[glyph_id] if !name_index nil elsif name_index <= 257 Format1::GLYPH_NAMES[name_index] else names[name_index - 258] end end end end # 'post' table format 3 module Format3 # :call-seq: # Format3.parse(io, length) -> glyph_names # # Since the post table format 3 does not contain any valid glyph names, an empty array # is returned. def self.parse(_io, _length) [].freeze end end # 'post' table format 4 module Format4 # :call-seq: # Format4.parse(io, length) -> glyph_names # # Parses the format 4 post subtable from the given IO at the current position and # returns a lambda mapping the glyph id to a character code. def self.parse(io, length) mapper(io.read(length).unpack('n*')) end def self.mapper(char_codes) #:nodoc: lambda {|glyph_id| char_codes[glyph_id] || 0xFFFF } end end end end end end end