Class: HexaPDF::Font::TrueType::Subsetter

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

Overview

Subsets a TrueType font in the context of PDF.

TrueType fonts can be embedded into PDF either as a simple font or as a composite font. This subsetter implements the functionality needed when embedding a TrueType subset for a composite font.

This means in particular that the resulting font file cannot be used outside of the PDF.

Instance Method Summary collapse

Constructor Details

#initialize(font) ⇒ Subsetter

Creates a new Subsetter for the given TrueType Font object.



48
49
50
51
52
# File 'lib/hexapdf/font/true_type/subsetter.rb', line 48

def initialize(font)
  @font = font
  @glyph_map = {0 => 0}
  @last_id = 0
end

Instance Method Details

#build_fontObject

Builds the subset font file and returns it as a binary string.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/hexapdf/font/true_type/subsetter.rb', line 65

def build_font
  glyf, locations = build_glyf_table
  loca = build_loca_table(locations)
  hmtx = build_hmtx_table
  head = build_head_table(modified: Time.now, loca_type: 1)
  hhea = build_hhea_table(@glyph_map.size)
  maxp = build_maxp_table(@glyph_map.size)

  tables = {
    'head' => head,
    'hhea' => hhea,
    'maxp' => maxp,
    'glyf' => glyf,
    'loca' => loca,
    'hmtx' => hmtx,
  }
  tables['cvt '] = @font[:"cvt "].raw_data if @font[:"cvt "]
  tables['fpgm'] = @font[:fpgm].raw_data if @font[:fpgm]
  tables['prep'] = @font[:prep].raw_data if @font[:prep]

  search_range = 2**(tables.length.bit_length - 1) * 16
  entry_selector = tables.length.bit_length - 1
  range_shift = tables.length * 16 - search_range

  font_data = "\x0\x1\x0\x0".b + \
    [tables.length, search_range, entry_selector, range_shift].pack('n4')

  offset = font_data.length + tables.length * 16
  checksum = Table.calculate_checksum(font_data)

  tables.each do |tag, data|
    table_checksum = Table.calculate_checksum(data)
    # tag, offset, data.length are all 32bit uint, table_checksum for header and body
    checksum += tag.unpack('N').first + 2 * table_checksum + offset + data.length
    font_data << [tag, table_checksum, offset, data.length].pack('a4N3')
    offset += data.length
  end

  head[8, 4] = [0xB1B0AFBA - checksum].pack('N')
  tables.each_value {|data| font_data << data}

  font_data
end

#use_glyph(glyph_id) ⇒ Object

Includes the glyph with the given ID in the subset and returns the new subset glyph ID.

Can be called multiple times with the same glyph ID, always returning the correct new subset glyph ID.



58
59
60
61
62
# File 'lib/hexapdf/font/true_type/subsetter.rb', line 58

def use_glyph(glyph_id)
  return @glyph_map[glyph_id] if @glyph_map.key?(glyph_id)
  @last_id += 1
  @glyph_map[glyph_id] = @last_id
end