Module: PDF::Core

Defined in:
lib/pdf/core.rb,
lib/pdf/core/page.rb,
lib/pdf/core/text.rb,
lib/pdf/core/utils.rb,
lib/pdf/core/stream.rb,
lib/pdf/core/filters.rb,
lib/pdf/core/renderer.rb,
lib/pdf/core/name_tree.rb,
lib/pdf/core/reference.rb,
lib/pdf/core/pdf_object.rb,
lib/pdf/core/annotations.rb,
lib/pdf/core/byte_string.rb,
lib/pdf/core/filter_list.rb,
lib/pdf/core/destinations.rb,
lib/pdf/core/object_store.rb,
lib/pdf/core/outline_item.rb,
lib/pdf/core/outline_root.rb,
lib/pdf/core/page_geometry.rb,
lib/pdf/core/document_state.rb,
lib/pdf/core/graphics_state.rb,
lib/pdf/core/literal_string.rb

Overview

PDF::Core is concerned with low-level PDF functions such as serialization, content streams and such.

It’s extracted from Prawn but at the moment is not entirely independent.

Defined Under Namespace

Modules: Annotations, Destinations, Errors, Filters, NameTree, PageGeometry, Text, Utils Classes: ByteString, DocumentState, FilterList, GraphicState, GraphicStateStack, LiteralString, ObjectStore, OutlineItem, OutlineRoot, Page, Reference, Renderer, Stream

Constant Summary collapse

ESCAPED_NAME_CHARACTERS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Characters to escape in name objects

(1..32).to_a + [35, 40, 41, 47, 60, 62] + (127..255).to_a
STRING_ESCAPE_MAP =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

How to escape special characters in literal strings

{ '(' => '\(', ')' => '\)', '\\' => '\\\\', "\r" => '\r' }.freeze

Class Method Summary collapse

Class Method Details

.pdf_object(obj, in_content_stream = false) ⇒ String

Serializes Ruby objects to their PDF equivalents. Most primitive objects will work as expected, but please note that Name objects are represented by Ruby Symbol objects and Dictionary objects are represented by Ruby hashes (keyed by symbols)

Examples:

pdf_object(true)      #=> "true"
pdf_object(false)     #=> "false"
pdf_object(1.2124)    #=> "1.2124"
pdf_object('foo bar') #=> "(foo bar)"
pdf_object(:Symbol)   #=> "/Symbol"
pdf_object(['foo',:bar, [1,2]]) #=> "[foo /bar [1 2]]"

Parameters:

Returns:

  • (String)

Raises:



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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/pdf/core/pdf_object.rb', line 77

def pdf_object(obj, in_content_stream = false)
  case obj
  when NilClass then 'null'
  when TrueClass then 'true'
  when FalseClass then 'false'
  when Numeric
    num_string = obj.is_a?(Integer) ? String(obj) : real(obj)

    # Truncate trailing fraction zeroes
    num_string.sub!(/(\d*)((\.0*$)|(\.0*[1-9]*)0*$)/, '\1\4')
    num_string
  when Array
    "[#{obj.map { |e| pdf_object(e, in_content_stream) }.join(' ')}]"
  when PDF::Core::LiteralString
    obj = obj.gsub(/[\\\r()]/, STRING_ESCAPE_MAP)
    "(#{obj})"
  when Time
    obj = "#{obj.strftime('D:%Y%m%d%H%M%S%z').chop.chop}'00'"
    obj = obj.gsub(/[\\\r()]/, STRING_ESCAPE_MAP)
    "(#{obj})"
  when PDF::Core::ByteString
    "<#{obj.unpack1('H*')}>"
  when String
    obj = utf8_to_utf16(obj) unless in_content_stream
    "<#{string_to_hex(obj)}>"
  when Symbol
    (@symbol_str_cache ||= {})[obj] ||= (+'/') << obj.to_s.unpack('C*').map { |n|
      if ESCAPED_NAME_CHARACTERS.include?(n)
        "##{n.to_s(16).upcase}"
      else
        n.chr
      end
    }.join
  when ::Hash
    output = +'<< '
    obj
      .sort_by { |k, _v| k.to_s }
      .each do |(k, v)|
      unless k.is_a?(String) || k.is_a?(Symbol)
        raise PDF::Core::Errors::FailedObjectConversion,
          'A PDF Dictionary must be keyed by names'
      end
      output << pdf_object(k.to_sym, in_content_stream) << ' ' <<
        pdf_object(v, in_content_stream) << "\n"
    end
    output << '>>'
  when PDF::Core::Reference
    obj.to_s
  when PDF::Core::NameTree::Node, PDF::Core::OutlineRoot, PDF::Core::OutlineItem
    pdf_object(obj.to_hash)
  when PDF::Core::NameTree::Value
    "#{pdf_object(obj.name)} #{pdf_object(obj.value)}"
  else
    raise PDF::Core::Errors::FailedObjectConversion,
      "This object cannot be serialized to PDF (#{obj.inspect})"
  end
end

.real(num) ⇒ String

Serializes floating number into a string

Parameters:

  • num (Numeric)

Returns:

  • (String)


11
12
13
14
15
# File 'lib/pdf/core/pdf_object.rb', line 11

def real(num)
  result = format('%.5f', num)
  result.sub!(/((?<!\.)0)+\z/, '')
  result
end

.real_params(array) ⇒ String

Serializes a n array of numbers. This is specifically for use in PDF content streams.

Parameters:

  • array (Array<Numeric>)

Returns:

  • (String)


22
23
24
# File 'lib/pdf/core/pdf_object.rb', line 22

def real_params(array)
  array.map { |e| real(e) }.join(' ')
end

.string_to_hex(str) ⇒ String

Encodes any string into a hex representation. The result is a string with only 0-9 and a-f characters. That result is valid ASCII so tag it as such to account for behaviour of different ruby VMs.

Parameters:

  • str (String)

Returns:

  • (String)


42
43
44
# File 'lib/pdf/core/pdf_object.rb', line 42

def string_to_hex(str)
  str.unpack1('H*').force_encoding(::Encoding::US_ASCII)
end

.utf8_to_utf16(str) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Converts string to UTF-16BE encoding as expected by PDF.

Parameters:

  • str (String)

Returns:

  • (String)


31
32
33
34
# File 'lib/pdf/core/pdf_object.rb', line 31

def utf8_to_utf16(str)
  (+"\xFE\xFF").force_encoding(::Encoding::UTF_16BE) <<
    str.encode(::Encoding::UTF_16BE)
end