Class: PDF::Reader::Encoding

Inherits:
Object
  • Object
show all
Defined in:
lib/pdf/reader/encoding.rb

Overview

Util class for working with string encodings in PDF files. Mostly used to convert strings of various PDF-dialect encodings into UTF-8.

Constant Summary collapse

CONTROL_CHARS =

:nodoc:

[0,1,2,3,4,5,6,7,8,11,12,14,15,16,17,18,19,20,21,22,23,
24,25,26,27,28,29,30,31]
UNKNOWN_CHAR =

Signature:

  • Integer # ▯

0x25AF
DEFAULT_MAPPING =
(0..255).each_with_object({}) { |i, h|
  h[i] = CONTROL_CHARS.include?(i) ? UNKNOWN_CHAR : i
}.freeze
FILE_MAPPINGS =

Cache mapping files to avoid re-reading and re-parsing the same file for every Encoding that uses it (e.g. many fonts sharing StandardEncoding).

{}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(enc) ⇒ Encoding

Returns a new instance of Encoding.

Signature:

  • (Hash[Symbol, untyped] | Symbol | nil) -> void



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/pdf/reader/encoding.rb', line 49

def initialize(enc)
  # maps from character codes to UTF-8 strings.
  @string_cache  = {} #: Hash[Integer, String]

  @enc_name = :StandardEncoding #: Symbol
  if enc.kind_of?(Hash)
    @enc_name = enc[:Encoding] || enc[:BaseEncoding]
  elsif enc && enc.respond_to?(:to_sym)
    @enc_name = enc.to_sym
  end

  @unpack   = get_unpack(@enc_name) #: String
  @map_file = get_mapping_file(@enc_name) #: String | nil
  @differences = nil #: Hash[Integer, Integer] | nil
  @glyphlist = nil #: PDF::Reader::GlyphHash | nil

  # maps from character codes to Unicode codepoints
  # If we have a mapping file, use the cached parsed version (loaded once per file)
  if @map_file
    @mapping = (
      FILE_MAPPINGS[@map_file] ||= build_file_mapping(@map_file)
    ).dup #: Hash[Integer, Integer]
  else
    @mapping = DEFAULT_MAPPING.dup
  end #: Hash[Integer, Integer]

  if enc.is_a?(Hash) && enc[:Differences]
    self.differences = enc[:Differences]
  end
end

Instance Attribute Details

#unpackObject (readonly)

Signature:

  • String



46
47
48
# File 'lib/pdf/reader/encoding.rb', line 46

def unpack
  @unpack
end

Instance Method Details

#differencesObject

Signature:

  • () -> Hash[Integer, Integer]



108
109
110
111
# File 'lib/pdf/reader/encoding.rb', line 108

def differences
  # this method is only used by the spec tests
  @differences ||= {}
end

#differences=(diff) ⇒ Object

set the differences table for this encoding. should be an array in the following format:

[25, :A, 26, :B]

The array alternates between a decimal byte number and a glyph name to map to that byte

To save space the following array is also valid and equivalent to the previous one

[25, :A, :B]

Signature:

  • (Array[Integer | Symbol]) -> Hash[Integer, Integer]



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/pdf/reader/encoding.rb', line 90

def differences=(diff)
  PDF::Reader::Error.validate_type(diff, "diff", Array)

  @differences = {}
  byte = 0
  diff.each do |val|
    if val.kind_of?(Numeric)
      byte = val.to_i
    elsif codepoint = glyphlist.name_to_unicode(val)
      @differences[byte] = val
      @mapping[byte] = codepoint
      byte += 1
    end
  end
  @differences
end

#int_to_name(glyph_code) ⇒ Object

convert an integer glyph code into an Adobe glyph name.

int_to_name(65)
=> [:A]

Signature:

  • (Integer) -> Array[Symbol]



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/pdf/reader/encoding.rb', line 144

def int_to_name(glyph_code)
  if @enc_name == :"Identity-H" || @enc_name == :"Identity-V"
    []
  elsif differences[glyph_code]
    [differences[glyph_code]]
  elsif @mapping[glyph_code]
    val = @mapping[glyph_code]
    if val
      glyphlist.unicode_to_name(val)
    else
      []
    end
  else
    []
  end
end

#int_to_utf8_string(glyph_code) ⇒ Object

Signature:

  • (Integer) -> String



134
135
136
# File 'lib/pdf/reader/encoding.rb', line 134

def int_to_utf8_string(glyph_code)
  @string_cache[glyph_code] ||= internal_int_to_utf8_string(glyph_code)
end

#to_utf8(str) ⇒ Object

convert the specified string to utf8

  • unpack raw bytes into codepoints
  • replace any that have entries in the differences table with a glyph name
  • convert codepoints from source encoding to Unicode codepoints
  • convert any glyph names to Unicode codepoints
  • replace characters that didn't convert to Unicode nicely with something valid
  • pack the final array of Unicode codepoints into a utf-8 string
  • mark the string as utf-8 if we're running on a M17N aware VM

Signature:

  • (String) -> String



125
126
127
128
129
130
131
# File 'lib/pdf/reader/encoding.rb', line 125

def to_utf8(str)
  if utf8_conversion_impossible?
    little_boxes(str.unpack(unpack).size)
  else
    convert_to_utf8(str)
  end
end