Class: HexaPDF::Font::TrueTypeWrapper

Inherits:
Object
  • Object
show all
Defined in:
lib/hexapdf/font/true_type_wrapper.rb

Overview

This class wraps a generic TrueType font object and provides the methods needed for working with the font in a PDF context.

TrueType fonts can be represented in two ways in PDF: As a simple font with Subtype TrueType or as a composite font using a Type2 CIDFont. The wrapper only supports the composite font case because:

  • By using a composite font more than 256 characters can be encoded with one font object.

  • Fonts for vertical writing can potentially be used.

  • The PDF specification recommends using a composite font (see PDF2.0 s9.9.1 at the end).

Additionally, TrueType fonts are always embedded.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(document, font, pdf_object: nil, subset: true) ⇒ TrueTypeWrapper

Creates a new object wrapping the TrueType font for the PDF document.

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 subset is true, the font is subset.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 144

def initialize(document, font, pdf_object: nil, subset: true)
  @wrapped_font = font

  @subsetter = (subset ? HexaPDF::Font::TrueType::Subsetter.new(font) : nil)

  @cmap = font[:cmap].preferred_table
  if @cmap.nil?
    raise HexaPDF::Error, "No mapping table for Unicode characters found for TTF " \
      "font #{font.full_name}"
  end
  @pdf_object = pdf_object || create_pdf_object(document)

  @id_to_glyph = {}
  @codepoint_to_glyph = {}
  @encoded_glyphs = {}
  @last_char_code = 0
end

Instance Attribute Details

#pdf_objectObject (readonly)

Returns the PDF object associated with the wrapper.



136
137
138
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 136

def pdf_object
  @pdf_object
end

#wrapped_fontObject (readonly)

Returns the wrapped TrueType font object.



133
134
135
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 133

def wrapped_font
  @wrapped_font
end

Instance Method Details

#bold?Boolean

Returns true if the font contains bold glyphs.

Returns:

  • (Boolean)


173
174
175
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 173

def bold?
  @wrapped_font.weight > 500
end

#custom_glyph(id, string) ⇒ Object

Returns a custom Glyph object which represents the given string via the given glyph id.

This functionality can be used to associate a single glyph id with multiple, different strings for replacement glyph purposes. When used in such a way, the used glyph id is often 0 which represents the missing glyph.



208
209
210
211
212
213
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 208

def custom_glyph(id, string)
  if id < 0 || id >= @wrapped_font[:maxp].num_glyphs
    raise HexaPDF::Error, "Glyph ID #{id} is invalid for font '#{@wrapped_font.full_name}'"
  end
  Glyph.new(self, id, string)
end

#decode_codepoint(codepoint) ⇒ Object

Returns a glyph object for the given Unicode codepoint.

The configuration option ‘font.on_missing_glyph’ is invoked if no glyph for a given codepoint is available.



226
227
228
229
230
231
232
233
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 226

def decode_codepoint(codepoint)
  @codepoint_to_glyph[codepoint] ||=
    if (gid = @cmap[codepoint])
      glyph(gid, +'' << codepoint)
    else
      @pdf_object.document.config['font.on_missing_glyph'].call(+'' << codepoint, self)
    end
end

#decode_utf8(str) ⇒ Object

Returns an array of glyph objects representing the characters in the UTF-8 encoded string.

See #decode_codepoint for details.



218
219
220
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 218

def decode_utf8(str)
  str.codepoints.map! {|c| @codepoint_to_glyph[c] || decode_codepoint(c) }
end

#encode(glyph) ⇒ Object

Encodes the glyph and returns the code string.



236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 236

def encode(glyph)
  (@encoded_glyphs[glyph] ||=
    begin
      raise HexaPDF::MissingGlyphError.new(glyph) if glyph.kind_of?(InvalidGlyph)
      @subsetter.use_glyph(glyph.id) if @subsetter
      @last_char_code += 1
      # Handle codes for ASCII characters \r (13), (, ) (40, 41) and \ (92) specially so that
      # they never appear in the output (PDF serialization would need to escape them)
      if @last_char_code == 13 || @last_char_code == 40 || @last_char_code == 92
        @last_char_code += (@last_char_code == 40 ? 2 : 1)
      end
      [[@last_char_code].pack('n'), @last_char_code]
    end)[0]
end

#font_typeObject

Returns the type of the font, i.e. :TrueType.



163
164
165
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 163

def font_type
  :TrueType
end

#glyph(id, str = nil) ⇒ Object

Returns a Glyph object for the given glyph ID and str pair.

The optional argument str should be the string representation of the glyph. It is possible that multiple strings map to the same glyph (e.g. hyphen and soft-hyphen could be represented by the same glyph).

Note: Although this method is public, it should normally not be used by application code!



194
195
196
197
198
199
200
201
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 194

def glyph(id, str = nil)
  @id_to_glyph[[id, str]] ||=
    if id >= 0 && id < @wrapped_font[:maxp].num_glyphs
      Glyph.new(self, id, str || (+'' << (@cmap.gid_to_code(id) || 0xFFFD)))
    else
      @pdf_object.document.config['font.on_missing_glyph'].call("\u{FFFD}", self)
    end
end

#italic?Boolean

Returns true if the font contains glyphs with an incline (italic or slant).

Returns:

  • (Boolean)


178
179
180
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 178

def italic?
  @wrapped_font.italic_angle.to_i != 0
end

#scaling_factorObject

Returns the scaling factor for converting font units into PDF units.



168
169
170
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 168

def scaling_factor
  @scaling_factor ||= 1000.0 / @wrapped_font[:head].units_per_em
end

#subset?Boolean

Returns true if the wrapped TrueType font will be subset.

Returns:

  • (Boolean)


183
184
185
# File 'lib/hexapdf/font/true_type_wrapper.rb', line 183

def subset?
  !@subsetter.nil?
end