Module: Origami::Object

Included in:
Array, Boolean, Dictionary, Name, Number, Reference, Stream, String
Defined in:
lib/origami/object.rb,
lib/origami/obfuscation.rb

Overview

Parent module representing a PDF Object. PDF specification declares a set of primitive object types :

  • Null

  • Boolean

  • Integer

  • Real

  • Name

  • String

  • Array

  • Dictionary

  • Stream

Constant Summary collapse

TOKENS =

:nodoc:

%w{ obj endobj }
@@regexp_obj =
Regexp.new(WHITESPACES + "(\\d+)" + WHITESPACES + "(\\d+)" + WHITESPACES + TOKENS.first + WHITESPACES)
@@regexp_endobj =
Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#file_offsetObject

Returns the value of attribute file_offset.



262
263
264
# File 'lib/origami/object.rb', line 262

def file_offset
  @file_offset
end

#generationObject

Returns the value of attribute generation.



262
263
264
# File 'lib/origami/object.rb', line 262

def generation
  @generation
end

#noObject

Returns the value of attribute no.



262
263
264
# File 'lib/origami/object.rb', line 262

def no
  @no
end

#objstm_offsetObject

Returns the value of attribute objstm_offset.



262
263
264
# File 'lib/origami/object.rb', line 262

def objstm_offset
  @objstm_offset
end

#parentObject

Returns the value of attribute parent.



263
264
265
# File 'lib/origami/object.rb', line 263

def parent
  @parent
end

Class Method Details

.parse(stream) ⇒ Object

:nodoc:



451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# File 'lib/origami/object.rb', line 451

def parse(stream) #:nodoc:
  offset = stream.pos

  #
  # End of body ?
  #
  return nil if stream.match?(/xref/) or stream.match?(/trailer/) or stream.match?(/startxref/)
 
  if stream.scan(@@regexp_obj).nil?
    raise InvalidObjectError, 
      "Object shall begin with '%d %d obj' statement"
  end
    
  no = stream[2].to_i
  gen = stream[4].to_i

  type = typeof(stream) 
  if type.nil?
    raise InvalidObjectError, 
      "Cannot determine object (no:#{no},gen:#{gen}) type"
  end
    
  begin
    newObj = type.parse(stream)
  rescue Exception => e
    raise InvalidObjectError, 
      "Failed to parse object (no:#{no},gen:#{gen})\n\t -> [#{e.class}] #{e.message}"
  end

  newObj.set_indirect(true)
  newObj.no = no
  newObj.generation = gen
  newObj.file_offset = offset
    
  if stream.skip(@@regexp_endobj).nil?
    raise UnterminatedObjectError.new("Object shall end with 'endobj' statement", newObj)
  end
    
  newObj
end

.skip_until_next_obj(stream) ⇒ Object

:nodoc:



492
493
494
495
496
497
498
# File 'lib/origami/object.rb', line 492

def skip_until_next_obj(stream) #:nodoc:
  stream.pos += 1 until stream.match?(@@regexp_obj) or 
    stream.match?(/xref/) or stream.match?(/trailer/) or stream.match?(/startxref/) or
    stream.eos?
  
  not stream.eos?
end

.typeof(stream, noref = false) ⇒ Object

:nodoc:



424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/origami/object.rb', line 424

def typeof(stream, noref = false) #:nodoc:
  stream.skip(REGEXP_WHITESPACES)

  case stream.peek(1)
    when '/' then return Name
    when '<'
      return (stream.peek(2) == '<<') ? Stream : HexaString
    when '(' then return ByteString
    when '[' then return Origami::Array
    when 'n' then 
      return Null if stream.peek(4) == 'null'
    when 't' then
      return Boolean if stream.peek(4) == 'true'
    when 'f' then 
      return Boolean if stream.peek(5) == 'false'
  else
    if not noref and stream.check(Reference::REGEXP_TOKEN) then return Reference
    elsif stream.check(Real::REGEXP_TOKEN) then return Real
    elsif stream.check(Integer::REGEXP_TOKEN) then return Integer
    else
      nil
    end
  end
  
  nil
end

Instance Method Details

#<=>(obj) ⇒ Object

Compare two objects from their respective numbers.



307
308
309
# File 'lib/origami/object.rb', line 307

def <=>(obj)
  [@no, @generation] <=> [obj.no, obj.generation]
end

#copyObject

Deep copy of an object.



321
322
323
# File 'lib/origami/object.rb', line 321

def copy
  Marshal.load(Marshal.dump(self))
end

#indirect_parentObject

Returns the indirect object which contains this object. If the current object is already indirect, returns self.



377
378
379
380
381
382
# File 'lib/origami/object.rb', line 377

def indirect_parent 
  obj = self
  obj = obj.parent until obj.is_indirect?
    
  obj
end

#initialize(*cons) ⇒ Object

Creates a new PDF Object.



268
269
270
271
272
273
# File 'lib/origami/object.rb', line 268

def initialize(*cons)
  @indirect = false
  @no, @generation = 0, 0
  
  super(*cons) unless cons.empty?
end

#is_indirect?Boolean

Returns whether the objects is indirect, which means that it is not embedded into another object.

Returns:



314
315
316
# File 'lib/origami/object.rb', line 314

def is_indirect?
  @indirect
end

#pdfObject

Returns the PDF which the object belongs to.



408
409
410
411
412
413
# File 'lib/origami/object.rb', line 408

def pdf
  if self.is_indirect? then @pdf
  else
    @parent.pdf if @parent
  end
end

#pdf_version_requiredObject

:nodoc:



502
503
504
# File 'lib/origami/object.rb', line 502

def pdf_version_required #:nodoc:
  [ 1.0, 0 ]
end

#post_buildObject

Generic method called just after the object is finalized. At this time, any indirect object has its own number and generation identifier.



300
301
302
# File 'lib/origami/object.rb', line 300

def post_build
  self
end

#pre_buildObject

Generic method called just before the object is finalized. At this time, no number nor generation allocation has yet been done.



292
293
294
# File 'lib/origami/object.rb', line 292

def pre_build
  self
end

#referenceObject

Returns an indirect reference to this object, or a Null object is this object is not indirect.



328
329
330
331
332
333
334
335
336
337
# File 'lib/origami/object.rb', line 328

def reference
  unless self.is_indirect?
    raise InvalidObjectError, "Cannot reference a direct object"
  end

  ref = Reference.new(@no, @generation)
  ref.parent = self

  ref
end

#set_indirect(dir) ⇒ Object

Sets whether the object is indirect or not. Indirect objects are allocated numbers at build time.



279
280
281
282
283
284
285
286
# File 'lib/origami/object.rb', line 279

def set_indirect(dir)
  unless dir == true or dir == false
    raise TypeError, "The argument must be boolean"
  end
  
  @indirect = dir
  self
end

#set_pdf(pdf) ⇒ Object



415
416
417
418
419
420
# File 'lib/origami/object.rb', line 415

def set_pdf(pdf)
  if self.is_indirect? then @pdf = pdf
  else
    raise InvalidObjectError, "You cannot set the PDF parent of a direct object"
  end
end

#sizeObject

Returns the size of this object once converted to PDF code.



401
402
403
# File 'lib/origami/object.rb', line 401

def size
  to_s.size
end

#solveObject

Returns self.



394
395
396
# File 'lib/origami/object.rb', line 394

def solve
  self
end

#to_oObject

Returns self.



387
388
389
# File 'lib/origami/object.rb', line 387

def to_o
  self
end

#to_s(data) ⇒ Object Also known as: output, to_obfuscated_str

Outputs this object into PDF code.

data

The object data.



518
519
520
521
522
523
524
525
526
# File 'lib/origami/object.rb', line 518

def to_s(data)
  
  content = ""
  content << "#{no} #{generation} obj" << EOL if self.is_indirect?
  content << data
  content << EOL << "endobj" << EOL if self.is_indirect?
  
  content
end

#typeObject Also known as: real_type

Returns the symbol type of this Object.



509
510
511
# File 'lib/origami/object.rb', line 509

def type
  self.class.to_s.split("::").last.to_sym
end

#xrefsObject

Returns an array of references pointing to the current object.



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
# File 'lib/origami/object.rb', line 342

def xrefs
  unless self.is_indirect?
    raise InvalidObjectError, "Cannot find xrefs to a direct object"
  end

  if self.pdf.nil?
    raise InvalidObjectError, "Not attached to any PDF"
  end

  xref_cache = Hash.new([])
  @pdf.root_objects.each do |obj|
    case obj
      when Dictionary,Array then
        xref_cache.update(obj.xref_cache) do |ref, cache1, cache2|
          cache1.concat(cache2)
        end

      when Stream then
        obj.dictionary.xref_cache.each do |ref, cache|
          cache.map!{obj}
        end

        xref_cache.update(obj.dictionary.xref_cache) do |ref, cache1, cache2|
          cache1.concat(cache2)
        end
    end
  end

  xref_cache[self.reference]
end