class HexaPDF::Font::Type1Wrapper
This class wraps a generic Type1
font object and provides the methods needed for working with the font in a PDF context.
Constants
- VALID_ENCODING_NAMES
Array of valid encoding names in PDF
Attributes
Returns the PDF object associated with the wrapper.
Returns the wrapped Type1
font object.
Public Class Methods
Creates a new Type1Wrapper
object wrapping the Type1
font.
The optional argument pdf_object
can be used to set the PDF font object that this wrapper should be associated with. If no object is set, a suitable one is automatically created.
If pdf_object
is provided, the PDF object's encoding is used. Otherwise, the WinAnsiEncoding or, for 'Special' fonts, the font's internal encoding is used. The optional argument custom_encoding
can be set to true
so that a custom encoding is used (only respected if pdf_object
is not provided).
# File lib/hexapdf/font/type1_wrapper.rb, line 118 def initialize(document, font, pdf_object: nil, custom_encoding: false) @wrapped_font = font @pdf_object = pdf_object || create_pdf_object(document) @missing_glyph_callable = document.config['font.on_missing_glyph'] if pdf_object @encoding = pdf_object.encoding @max_code = 255 # Encoding is not modified elsif custom_encoding @encoding = Encoding::Base.new @encoding.code_to_name[32] = :space @max_code = 32 # 32 = space elsif @wrapped_font.metrics.character_set == 'Special' @encoding = @wrapped_font.encoding @max_code = 255 # Encoding is not modified else @encoding = Encoding.for_name(:WinAnsiEncoding) @max_code = 255 # Encoding is not modified end @zapf_dingbats_opt = {zapf_dingbats: (@wrapped_font.font_name == 'ZapfDingbats')} @name_to_glyph = {} @codepoint_to_glyph = {} @encoded_glyphs = {} end
Public Instance Methods
Returns an array of glyph objects representing the characters in the UTF-8 encoded string.
If a Unicode codepoint is not available as glyph object, it is tried to map the codepoint using the font's internal encoding. This is useful, for example, for the ZapfDingbats font to use ASCII characters for accessing the glyphs.
# File lib/hexapdf/font/type1_wrapper.rb, line 172 def decode_utf8(str) str.codepoints.map! do |c| @codepoint_to_glyph[c] ||= begin name = Encoding::GlyphList.unicode_to_name(+'' << c, **@zapf_dingbats_opt) if @wrapped_font.metrics.character_set == 'Special' && (name == :'.notdef' || !@wrapped_font.metrics.character_metrics.key?(name)) name = @encoding.name(c) end name = +"u" << c.to_s(16).rjust(6, '0') if name == :'.notdef' glyph(name) end end end
Encodes the glyph and returns the code string.
# File lib/hexapdf/font/type1_wrapper.rb, line 188 def encode(glyph) @encoded_glyphs[glyph.name] ||= begin if glyph.name == @wrapped_font.missing_glyph_id raise HexaPDF::Error, "Glyph for #{glyph.str.inspect} missing" end code = @encoding.code(glyph.name) if code code.chr.freeze elsif @max_code < 255 @max_code += 1 @encoding.code_to_name[@max_code] = glyph.name @max_code.chr.freeze else raise HexaPDF::Error, "Type1 encoding has no codepoint for #{glyph.name}" end end end
Returns the type of the font, i.e. :Type1.
# File lib/hexapdf/font/type1_wrapper.rb, line 145 def font_type :Type1 end
Returns a Glyph
object for the given glyph name.
# File lib/hexapdf/font/type1_wrapper.rb, line 155 def glyph(name) @name_to_glyph[name] ||= begin str = Encoding::GlyphList.name_to_unicode(name, **@zapf_dingbats_opt) if @wrapped_font.metrics.character_metrics.key?(name) Glyph.new(@wrapped_font, name, str) else @missing_glyph_callable.call(str, font_type, @wrapped_font) end end end
Returns 1 since all Type1
fonts use 1000 units for the em-square.
# File lib/hexapdf/font/type1_wrapper.rb, line 150 def scaling_factor 1 end
Private Instance Methods
Creates a PDF object representing the wrapped font for the given PDF document.
# File lib/hexapdf/font/type1_wrapper.rb, line 213 def create_pdf_object(document) fd = document.wrap({Type: :FontDescriptor, FontName: @wrapped_font.font_name.intern, FontWeight: @wrapped_font.weight_class, FontBBox: @wrapped_font.bounding_box, ItalicAngle: @wrapped_font.italic_angle || 0, Ascent: @wrapped_font.ascender || 0, Descent: @wrapped_font.descender || 0, CapHeight: @wrapped_font.cap_height, XHeight: @wrapped_font.x_height, StemH: @wrapped_font.dominant_horizontal_stem_width, StemV: @wrapped_font.dominant_vertical_stem_width || 0}) fd.flag(:fixed_pitch) if @wrapped_font.metrics.is_fixed_pitch fd.flag(@wrapped_font.metrics.character_set == 'Special' ? :symbolic : :nonsymbolic) fd.must_be_indirect = true dict = document.wrap({Type: :Font, Subtype: :Type1, BaseFont: @wrapped_font.font_name.intern, FontDescriptor: fd}) dict.font_wrapper = self document.register_listener(:complete_objects) do min, max = @encoding.code_to_name.keys.minmax dict[:FirstChar] = min dict[:LastChar] = max dict[:Widths] = (min..max).map {|code| glyph(@encoding.name(code)).width } if VALID_ENCODING_NAMES.include?(@encoding.encoding_name) dict[:Encoding] = @encoding.encoding_name elsif @encoding != @wrapped_font.encoding differences = [min] (min..max).each {|code| differences << @encoding.name(code) } dict[:Encoding] = {Differences: differences} end end dict end