origami-pdf-1.2.7/ 0000755 0001750 0001750 00000000000 12427003762 014134 5 ustar terceiro terceiro origami-pdf-1.2.7/.hgtags 0000755 0001750 0001750 00000002415 12144410635 015414 0 ustar terceiro terceiro dc00b559947ef9f0f3471e9d0d538d907ff35dcd 1.0.0-beta1b
31a39df2872e786ddc1765c70cacddc912708ccb 1.0.0-beta2
499e086b89c5cb70e9890b939932e88457826c66 1.0.0-beta3
39ee51ab45091aa36de0da2b8f85e93a0bd00f3e 1.0.1
b3463c2fdc2bf0243144d100bafbf4a87403888c 1.0.2
5a44566a8cfa94881e6e70bee8c07c505294f912 remove
5a44566a8cfa94881e6e70bee8c07c505294f912 remove
0000000000000000000000000000000000000000 remove
0000000000000000000000000000000000000000 remove
0000000000000000000000000000000000000000 remove
b3463c2fdc2bf0243144d100bafbf4a87403888c 1.0.2
0000000000000000000000000000000000000000 1.0.2
0000000000000000000000000000000000000000 1.0.2
aeb1ea9cad6ec4c26a0b1b35bdab41d1b0d433a2 1.0.2
5917a29476349c8c2d0a5547536f59e1936f9159 1.1.0
43f5a9659eb854088484c71c212adb420cbb32ae 1.1.2
43f5a9659eb854088484c71c212adb420cbb32ae 1.1.2
0000000000000000000000000000000000000000 1.1.2
0000000000000000000000000000000000000000 1.1.2
bf60acfadf526f71badea98d8c11525b8468e700 1.1.2
a71faae1a909ad877094cde7489c132d90403e59 1.2.0
69427fb939d3967c3f651073f19e8203a8149a17 1.2.1
111adcdcd4a2041fc2f4a2f06390d22fd8096f42 1.2.2
89b51d85d992c95bebb5f45d4904fb7433c1da4f 1.2.3
bccb50013f4a0f9be868e3dcb120054b3d31345f 1.2.4
e61b63a8b4f043c80fac47601a5e7eed04bd2978 1.2.5
35ddff08cdbc30bd6892d60176d56ed990444c21 1.2.6
origami-pdf-1.2.7/lib/ 0000755 0001750 0001750 00000000000 12427006355 014703 5 ustar terceiro terceiro origami-pdf-1.2.7/lib/origami.rb 0000644 0001750 0001750 00000003301 12142214376 016652 0 ustar terceiro terceiro =begin
= File
origami.rb
= Info
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
if RUBY_VERSION < '1.9'
class Fixnum
def ord; self; end
end
class Hash
alias key index
end
end
module Origami
VERSION = "1.2.6"
REVISION = "$Revision$" #:nodoc:
#
# Global
# options for Origami.
#
OPTIONS =
{
:enable_type_checking => true, # set to false to disable type consistency checks during compilation.
:enable_type_guessing => true, # set to false to prevent the parser to guess the type of special dictionary and streams (not recommended).
:enable_type_propagation => true, # set to false to prevent the parser to propagate type from parents to children.
:use_openssl => true, # set to false to use Origami crypto backend.
:ignore_bad_references => false, # set to interpret invalid references as Null objects, instead of raising an exception.
:ignore_zlib_errors => false, # set to true to ignore exceptions on invalid Flate streams.
}
end
require 'origami/pdf'
require 'origami/extensions/fdf'
require 'origami/extensions/ppklite'
origami-pdf-1.2.7/lib/origami/ 0000755 0001750 0001750 00000000000 12427006355 016332 5 ustar terceiro terceiro origami-pdf-1.2.7/lib/origami/functions.rb 0000644 0001750 0001750 00000005425 12101464040 020661 0 ustar terceiro terceiro =begin
= File
functions.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Function
module Type
SAMPLED = 0
EXPONENTIAL = 2
STITCHING = 3
POSTSCRIPT = 4
end
def self.included(receiver)
receiver.field :FunctionType, :Type => Integer, :Required => true
receiver.field :Domain, :Type => Array, :Required => true
receiver.field :Range, :Type => Array
end
class Sampled < Stream
include Function
field :FunctionType, :Type => Integer, :Default => Type::SAMPLED, :Version => "1.3", :Required => true
field :Range, :Type => Array, :Required => true
field :Size, :Type => Array, :Required => true
field :BitsPerSample, :Type => Integer, :Required => true
field :Order, :Type => Integer, :Default => 1
field :Encode, :Type => Array
field :Decode, :Type => Array
end
class Exponential < Dictionary
include StandardObject
include Function
field :FunctionType, :Type => Integer, :Default => Type::EXPONENTIAL, :Version => "1.3", :Required => true
field :C0, :Type => Array, :Default => [ 0.0 ]
field :C1, :Type => Array, :Default => [ 1.0 ]
field :N, :Type => Number, :Required => true
end
class Stitching < Dictionary
include StandardObject
include Function
field :FunctionType, :Type => Integer, :Default => Type::STITCHING, :Version => "1.3", :Required => true
field :Functions, :Type => Array, :Required => true
field :Bounds, :Type => Array, :Required => true
field :Encode, :Type => Array, :Required => true
end
class PostScript < Stream
include Function
field :FunctionType, :Type => Integer, :Default => Type::POSTSCRIPT, :Version => "1.3", :Required => true
field :Range, :Type => Array, :Required => true
end
end
end
origami-pdf-1.2.7/lib/origami/object.rb 0000644 0001750 0001750 00000036556 12142214376 020142 0 ustar terceiro terceiro =begin
= File
object.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
class Bignum #:nodoc:
def to_o
Origami::Integer.new(self)
end
end
class Fixnum #:nodoc:
def to_o
Origami::Integer.new(self)
end
end
class Array #:nodoc:
def to_o
Origami::Array.new(self)
end
end
class Float #:nodoc:
def to_o
Origami::Real.new(self)
end
end
class Hash #:nodoc:
def to_o
Origami::Dictionary.new(self)
end
end
class TrueClass #:nodoc:
def to_o
Origami::Boolean.new(true)
end
end
class FalseClass #:nodoc:
def to_o
Origami::Boolean.new(false)
end
end
class NilClass #:nodoc:
def to_o
Origami::Null.new
end
end
class Symbol #:nodoc:
def to_o
Origami::Name.new(self)
end
def value
self
end
end
class String #:nodoc:
def to_o
Origami::ByteString.new(self)
end
def is_binary_data?
( self.count( "\x00" ) > 0 ) unless empty?
end
end
#
# Module for parsing/generating PDF files.
#
module Origami
#
# Mixin' module for objects which can store their options into an inner Dictionary.
#
module StandardObject #:nodoc:
DEFAULT_ATTRIBUTES = { :Type => Object, :Version => "1.2" } #:nodoc:
def self.included(receiver) #:nodoc:
receiver.instance_variable_set(:@fields, Hash.new(DEFAULT_ATTRIBUTES))
receiver.extend(ClassMethods)
end
module ClassMethods #:nodoc:all
def inherited(subclass)
subclass.instance_variable_set(:@fields, Marshal.load(Marshal.dump(@fields)))
end
def fields
@fields
end
def field(name, attributes)
if attributes[:Required] == true and attributes.has_key?(:Default) and attributes[:Type] == Name
self.add_type_info(self, name, attributes[:Default])
end
if not @fields.has_key?(name)
@fields[name] = attributes
else
@fields[name].merge! attributes
end
define_field_methods(name)
end
def define_field_methods(field)
reader = lambda { obj = self[field]; obj.is_a?(Reference) ? obj.solve : obj }
writer = lambda { |value| self[field] = value }
set = lambda { |value| self[field] = value; self }
send(:define_method, field.id2name, reader)
send(:define_method, field.id2name + "=", writer)
send(:define_method, "set" + field.id2name, set)
end
#
# Returns an array of required fields for the current Object.
#
def required_fields
fields = []
@fields.each_pair { |name, attributes|
fields << name if attributes[:Required] == true
}
fields
end
def hint_type(name)
if @fields.has_key?(name)
@fields[name][:Type]
end
end
end
def pre_build #:nodoc:
set_default_values
do_type_check if Origami::OPTIONS[:enable_type_checking] == true
super
end
#
# Check if an attribute is set in the current Object.
# _attr_:: The attribute name.
#
def has_field? (field)
not self[field].nil?
end
#
# Returns the version and level required by the current Object.
#
def pdf_version_required #:nodoc:
max = [ 1.0, 0 ]
self.each_key do |field|
attributes = self.class.fields[field.value]
current_version = attributes.has_key?(:Version) ? attributes[:Version].to_f : 0
current_level = attributes[:ExtensionLevel] || 0
current = [ current_version, current_level ]
max = current if (current <=> max) > 0
sub = self[field.value].pdf_version_required
max = sub if (sub <=> max) > 0
end
max
end
def set_default_value(field) #:nodoc:
if self.class.fields[field][:Default]
self[field] = self.class.fields[field][:Default]
self[field].pre_build
end
end
def set_default_values #:nodoc:
self.class.required_fields.each do |field|
set_default_value(field) unless has_field?(field)
end
end
def do_type_check #:nodoc:
self.class.fields.each_pair do |field, attributes|
if not self[field].nil? and not attributes[:Type].nil?
types = attributes[:Type].is_a?(::Array) ? attributes[:Type] : [ attributes[:Type] ]
if not self[field].is_a?(Reference) and types.all? {|type| not self[field].is_a?(type.native_type)}
puts "Warning: in object #{self.class}, field `#{field.to_s}' has unexpected type #{self[field].class}"
end
end
end
end
end
class InvalidObjectError < Exception #:nodoc:
end
class UnterminatedObjectError < Exception #:nodoc:
attr_reader :obj
def initialize(msg,obj)
super(msg)
@obj = obj
end
end
WHITESPACES = "([ \\f\\t\\r\\n\\0]|%[^\\n]*\\n)*" #:nodoc:
WHITECHARS_NORET = "[ \\f\\t\\0]*" #:nodoc:
EOL = "\r\n" #:nodoc:
WHITECHARS = "[ \\f\\t\\r\\n\\0]*" #:nodoc:
REGEXP_WHITESPACES = Regexp.new(WHITESPACES) #:nodoc:
#
# Parent module representing a PDF Object.
# PDF specification declares a set of primitive object types :
# * Null
# * Boolean
# * Integer
# * Real
# * Name
# * String
# * Array
# * Dictionary
# * Stream
#
module Object
TOKENS = %w{ obj endobj } #:nodoc:
@@regexp_obj = Regexp.new(WHITESPACES + "(\\d+)" + WHITESPACES + "(\\d+)" + WHITESPACES + TOKENS.first + WHITESPACES)
@@regexp_endobj = Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)
attr_accessor :no, :generation, :file_offset, :objstm_offset
attr_accessor :parent
#
# Creates a new PDF Object.
#
def initialize(*cons)
@indirect = false
@no, @generation = 0, 0
super(*cons) unless cons.empty?
end
#
# Sets whether the object is indirect or not.
# Indirect objects are allocated numbers at build time.
#
def set_indirect(bool)
unless bool == true or bool == false
raise TypeError, "The argument must be boolean"
end
if not bool
@no = @generation = 0
@pdf = nil
end
@indirect = bool
self
end
#
# Generic method called just before the object is finalized.
# At this time, no number nor generation allocation has yet been done.
#
def pre_build
self
end
#
# Generic method called just after the object is finalized.
# At this time, any indirect object has its own number and generation identifier.
#
def post_build
self
end
#
# Compare two objects from their respective numbers.
#
def <=>(obj)
[@no, @generation] <=> [obj.no, obj.generation]
end
#
# Returns whether the objects is indirect, which means that it is not embedded into another object.
#
def is_indirect?
@indirect
end
#
# Deep copy of an object.
#
def copy
saved_pdf = @pdf
saved_parent = @parent
saved_xref_cache = @xref_cache
@pdf = @parent = nil # do not process parent object and document in the copy
# Perform the recursive copy (quite dirty).
copyobj = Marshal.load(Marshal.dump(self))
# restore saved values
@pdf = saved_pdf
@parent = saved_parent
copyobj.set_pdf(saved_pdf) if copyobj.is_indirect?
copyobj.parent = parent
copyobj
end
#
# Returns an indirect reference to this object, or a Null object is this object is not indirect.
#
def reference
unless self.is_indirect?
raise InvalidObjectError, "Cannot reference a direct object"
end
ref = Reference.new(@no, @generation)
ref.parent = self
ref
end
#
# Returns an array of references pointing to the current object.
#
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
#
# Creates an exportable version of current object.
# The exportable version is a copy of _self_ with solved references, no owning PDF and no parent.
# References to Catalog or PageTreeNode objects have been destroyed.
#
# When exported, an object can be moved into another document without hassle.
#
def export
exported_obj = self.logicalize
exported_obj.no = exported_obj.generation = 0
exported_obj.set_pdf(nil) if exported_obj.is_indirect?
exported_obj.parent = nil
exported_obj.xref_cache.clear
exported_obj
end
#
# Returns a logicalized copy of _self_.
# See logicalize!
#
def logicalize #:nodoc:
self.copy.logicalize!
end
#
# Transforms recursively every references to the copy of their respective object.
# Catalog and PageTreeNode objects are excluded to limit the recursion.
#
def logicalize! #:nodoc:
def resolve_all_references(obj, browsed = [], ref_cache = {})
return if browsed.include?(obj)
browsed.push(obj)
if obj.is_a?(ObjectStream)
obj.each do |subobj|
resolve_all_references(obj, browsed, ref_cache)
end
end
if obj.is_a?(Dictionary) or obj.is_a?(Array)
obj.map! do |subobj|
if subobj.is_a?(Reference)
new_obj =
if ref_cache.has_key?(subobj)
ref_cache[subobj]
else
ref_cache[subobj] = subobj.solve.copy
end
new_obj.no = new_obj.generation = 0
new_obj.parent = obj
new_obj unless new_obj.is_a?(Catalog) or new_obj.is_a?(PageTreeNode)
else
subobj
end
end
obj.each do |subobj|
resolve_all_references(subobj, browsed, ref_cache)
end
elsif obj.is_a?(Stream)
resolve_all_references(obj.dictionary, browsed, ref_cache)
end
end
resolve_all_references(self)
end
#
# Returns the indirect object which contains this object.
# If the current object is already indirect, returns self.
#
def indirect_parent
obj = self
obj = obj.parent until obj.is_indirect?
obj
end
#
# Returns self.
#
def to_o
self
end
#
# Returns self.
#
def solve
self
end
#
# Returns the size of this object once converted to PDF code.
#
def size
to_s.size
end
#
# Returns the PDF which the object belongs to.
#
def pdf
if self.is_indirect? then @pdf
else
@parent.pdf if @parent
end
end
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
class << self
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
def parse(stream, parser = nil) #: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, parser)
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
def skip_until_next_obj(stream) #:nodoc:
[ @@regexp_obj, /xref/, /trailer/, /startxref/ ].each do |re|
if stream.scan_until(re)
stream.pos -= stream.matched_size
return true
end
end
false
end
end
def pdf_version_required #:nodoc:
[ 1.0, 0 ]
end
#
# Returns the symbol type of this Object.
#
def type
self.class.to_s.split("::").last.to_sym
end
def self.native_type; Origami::Object end #:nodoc:
#
# Returns the native PDF type of this Object.
#
def native_type
self.class.native_type
end
def cast_to(type) #:nodoc:
if type.native_type != self.native_type
raise TypeError, "Incompatible cast from #{self.class} to #{type}"
end
self
end
#
# Outputs this object into PDF code.
# _data_:: The object data.
#
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
alias output to_s
end
end
origami-pdf-1.2.7/lib/origami/metadata.rb 0000644 0001750 0001750 00000012025 12101464040 020423 0 ustar terceiro terceiro =begin
= File
metadata.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'rexml/document'
module Origami
class PDF
#
# Returns true if the document has a document information dictionary.
#
def has_document_info?
has_attr? :Info
end
#
# Returns true if the document has a catalog metadata stream.
#
def has_metadata?
self.Catalog.Metadata.is_a?(Stream)
end
#
# Returns the document information dictionary if present.
#
def get_document_info
get_doc_attr :Info
end
def title; get_document_info_field(:Title) end
def author; get_document_info_field(:Author) end
def subject; get_document_info_field(:Subject) end
def keywords; get_document_info_field(:Keywords) end
def creator; get_document_info_field(:Creator) end
def producer; get_document_info_field(:Producer) end
def creation_date; get_document_info_field(:CreationDate) end
def mod_date; get_document_info_field(:ModDate) end
#
# Returns a Hash of the information found in the metadata stream
#
def get_metadata
metadata_stm = self.Catalog.Metadata
if metadata_stm.is_a?(Stream)
doc = REXML::Document.new(metadata_stm.data)
info = {}
doc.elements.each('*/*/rdf:Description') do |description|
description.attributes.each_attribute do |attr|
case attr.prefix
when 'pdf','xap'
info[attr.name] = attr.value
end
end
description.elements.each('*') do |element|
value = (element.elements['.//rdf:li'] || element).text
info[element.name] = value.to_s
end
end
info
end
end
#
# Modifies or creates a metadata stream.
#
def create_metadata(info = {})
skeleton = <<-XMP
XMP
xml =
if self.Catalog.Metadata.is_a?(Stream)
self.Catalog.Metadata.data
else
skeleton
end
doc = REXML::Document.new(xml)
desc = doc.elements['*/*/rdf:Description']
info.each do |name, value|
elt = REXML::Element.new "pdf:#{name}"
elt.text = value
desc.elements << elt
end
xml = ""; doc.write(xml, 3)
if self.Catalog.Metadata.is_a?(Stream)
self.Catalog.Metadata.data = xml
else
self.Catalog.Metadata = Stream.new(xml)
end
self.Catalog.Metadata
end
private
def get_document_info_field(field) #:nodoc:
if has_document_info?
doc_info = get_document_info
if doc_info.has_key?(field)
case obj = get_document_info[field].solve
when String then obj.value
when Stream then obj.data
end
end
end
end
end
#
# Class representing an information Dictionary, containing title, author, date of creation and the like.
#
class Metadata < Dictionary
include StandardObject
field :Title, :Type => String, :Version => "1.1"
field :Author, :Type => String
field :Subject, :Type => String, :Version => "1.1"
field :Keywords, :Type => String, :Version => "1.1"
field :Creator, :Type => String
field :Producer, :Type => String
field :CreationDate, :Type => ByteString
field :ModDate, :Type => ByteString, :Version => "1.1"
field :Trapped, :Type => Name, :Default => :Unknown, :Version => "1.3"
end
#
# Class representing a metadata Stream.
# This stream can contain the same information as the Metadata dictionary, but is storing in XML data.
#
class MetadataStream < Stream
include StandardObject
field :Type, :Type => Name, :Default => :Metadata, :Required => true
field :Subtype, :Type => Name, :Default =>:XML, :Required => true
end
end
origami-pdf-1.2.7/lib/origami/javascript.rb 0000644 0001750 0001750 00000050047 12130614216 021023 0 ustar terceiro terceiro =begin
= File
javascript.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
begin
require 'v8'
class V8::Object
#def inspect
# case self
# when V8::Array,V8::Function then super
# else
# "{#{self.to_a.map{|k,v| "#{k}:#{v.inspect}"}.join(', ')}}"
# end
#end
end
class PDF
module JavaScript
module Platforms
WINDOWS = "WIN"
UNIX = "UNIX"
MAC = "MAC"
end
module Viewers
ADOBE_READER = "Reader"
end
class MissingArgError < Exception
def initialize; super("Missing required argument.") end
end
class TypeError < Exception
def initialize; super("Incorrect argument type.") end
end
class InvalidArgsError < Exception
def initialize; super("Incorrect arguments.") end
end
class NotAllowedError < Exception
def initialize; super("Security settings prevent access to this property or method.") end
end
class HelpError < Exception
def initialize; super("Help") end
end
class GeneralError < Exception
def initialize; super("Operation failed.") end
end
class Arg
attr_reader :name, :type, :required, :default
def initialize(declare = {})
@name = declare[:name]
@type = declare[:type]
@required = declare[:required]
@default = declare[:default]
end
def self.[](declare = {})
self.new(declare)
end
def self.inspect(obj)
case obj
when V8::Function then "function #{obj.name}"
when V8::Array then obj.to_a.inspect
when V8::Object
"{#{obj.to_a.map{|k,v| "#{k}:#{Arg.inspect(v)}"}.join(', ')}}"
else
obj.inspect
end
end
end
class AcrobatObject
def initialize(engine)
@engine = engine
end
def self.check_method_args(args, def_args)
if args.first.is_a?(V8::Object)
args = args.first
members = args.entries.map{|k,v| k}
argv = []
def_args.each do |def_arg|
raise MissingArgError if def_arg.required and not members.include?(def_arg.name)
if members.include?(def_arg.name)
arg = args[def_arg.name]
raise TypeError if def_arg.type and not arg.is_a?(def_arg.type)
else
arg = def_arg.default
end
argv.push(arg)
end
args = argv
else
i = 0
def_args.each do |def_arg|
raise MissingArgError if def_arg.required and i >= args.length
raise TypeError if def_arg.type and not args[i].is_a?(def_arg.type)
args.push(def_arg.default) if i >= args.length
i = i + 1
end
end
args
end
def self.acro_method(name, *def_args, &b)
define_method(name) do |*args|
if @engine.options[:log_method_calls]
@engine.options[:console].puts(
"LOG: #{self.class}.#{name}(#{args.map{|arg| Arg.inspect(arg)}.join(',')})"
)
end
args = AcrobatObject.check_method_args(args, def_args)
self.instance_exec(*args, &b) if b
end
end
def self.acro_method_protected(name, *def_args, &b)
define_method(name) do |*args|
if @engine.options[:log_method_calls]
@engine.options[:console].puts(
"LOG: #{self.class}.#{name}(#{args.map{|arg| arg.inspect}.join(',')})"
)
end
raise NotAllowedError
args = AcrobatObject.check_method_args(args, def_args)
self.instance_exec(*args, &b) if b
end
end
def to_s
"[object #{self.class.to_s.split('::').last}]"
end
alias inspect to_s
end
class AcroTimer < AcrobatObject
def initialize(engine, timeout, code, repeat)
@thr = Thread.start(engine, timeout, code, repeat) do
loop do
sleep(timeout / 1000.0)
engine.exec(code)
break if not repeat
end
end
end
end
class TimeOut < AcroTimer
def initialize(engine, timeout, code)
super(engine, timeout, code, false)
end
end
class Interval < AcroTimer
def initialize(engine, timeout, code)
super(engine, timeout, code, true)
end
end
class ReadStream < AcrobatObject
def initialize(engine, data)
super(engine)
@data = data
end
acro_method 'read',
Arg[:name => 'nBytes', :type => Numeric, :required => true] do |nBytes|
@data.slice!(0, nBytes).unpack("H*")[0]
end
end
class Acrohelp < AcrobatObject; end
class Global < AcrobatObject
def initialize(engine)
super(engine)
@vars = {}
end
def []=(name, value)
@vars[name] ||= {:callbacks => []}
@vars[name][:value] = value
@vars[name][:callbacks].each do |callback|
callback.call(value)
end
end
def [](name)
@vars[name][:value] if @vars.include?(name)
end
acro_method 'setPersistent',
Arg[:name => 'cVariable', :required => true],
Arg[:name => 'bPersist', :required => true] do |cVariable, bPersist|
raise GeneralError unless @vars.include?(cVariable)
end
acro_method 'subscribe',
Arg[:name => 'cVariable', :required => true],
Arg[:name => 'fCallback', :type => V8::Function, :required => true] do |cVariable, fCallback|
if @vars.include?(cVariable)
@vars[cVariable][:callbacks].push(fCallback)
fCallback.call(@vars[cVariable][:value])
end
end
end
class Doc < AcrobatObject
attr_reader :info
attr_accessor :disclosed
attr_reader :hidden
class Info < AcrobatObject
def initialize(engine, doc)
super(engine)
@doc = doc
end
def title; @doc.title.to_s end
def author; @doc.author.to_s end
def subject; @doc.subject.to_s end
def keywords; @doc.keywords.to_s end
def creator; @doc.creator.to_s end
def creationDate; @doc.creation_date.to_s end
def modDate; @doc.mod_date.to_s end
end
def initialize(*args)
engine, pdf = args # XXX: Bypass therubyracer bug #238. Temporary.
super(engine)
@pdf = pdf
@disclosed = false
@hidden = false
@info = Info.new(@engine, pdf)
end
### PROPERTIES ###
def numFields
fields = @pdf.fields
if fields.nil?
0
else
fields.size
end
end
def numPages; @pdf.pages.size end
def title; @info.title end
def author; @info.author end
def subject; @info.subject end
def keywords; @info.keywords end
def creator; @info.creator end
def creationDate; @info.creationDate end
def modDate; @info.modDate end
def metadata
meta = @pdf.Catalog.Metadata
(meta.data if meta.is_a?(Stream)).to_s
end
def filesize; @pdf.original_filesize end
def path; @pdf.original_filename.to_s end
def documentFileName; File.basename(self.path) end
def URL; "file://#{self.path}" end
def baseURL; '' end
def dataObjects
data_objs = []
@pdf.ls_names(Names::Root::EMBEDDEDFILES).each do |name, file_desc|
if file_desc and file_desc.EF and (f = file_desc.EF.F)
data_objs.push Data.new(@engine, name, f.data.size) if f.is_a?(Stream)
end
end
data_objs
end
### METHODS ###
acro_method 'closeDoc'
acro_method 'getDataObject',
Arg[:name => 'cName', :type => ::String, :required => true] do |cName|
file_desc = @pdf.resolve_name(Names::Root::EMBEDDEDFILES, cName)
if file_desc and file_desc.EF and (f = file_desc.EF.F)
Data.new(@engine, cName, f.data.size) if f.is_a?(Stream)
else raise TypeError
end
end
acro_method 'getDataObjectContents',
Arg[:name => 'cName', :type => ::String, :required => true],
Arg[:name => 'bAllowAuth', :default => false] do |cName, bAllowAuth|
file_desc = @pdf.resolve_name(Names::Root::EMBEDDEDFILES, cName)
if file_desc and file_desc.EF and (f = file_desc.EF.F)
ReadStream.new(@engine, f.data) if f.is_a?(Stream)
else raise TypeError
end
end
acro_method 'exportDataObject',
Arg[:name => 'cName', :type => ::String, :required => true],
Arg[:name => 'cDIPath' ],
Arg[:name => 'bAllowAuth'],
Arg[:name => 'nLaunch'] do |cName, cDIPath, bAllowAuth, nLaunch|
file_desc = @pdf.resolve_name(Names::Root::EMBEDDEDFILES, cName)
if file_desc and file_desc.EF and (f = file_desc.EF.F)
else raise TypeError
end
raise TypeError if f.nil?
end
acro_method 'getField',
Arg[:name => 'cName', :type => ::Object, :required => true] do |cName|
field = @pdf.get_field(cName)
Field.new(@engine, field) if field
end
acro_method 'getNthFieldName',
Arg[:name => 'nIndex', :type => ::Object, :required => true] do |nIndex|
nIndex =
case nIndex
when false then 0
when true then 1
else
@engine.parseInt.call(nIndex)
end
raise TypeError if (nIndex.is_a?(Float) and nIndex.nan?) or nIndex < 0
fields = @pdf.fields
(Field.new(@engine, fields[nIndex]).name if fields and fields[nIndex]).to_s
end
end
class App < AcrobatObject
attr_reader :platform, :viewerVariation, :viewerVersion
def platform; @engine.options[:platform] end
def viewerType; @engine.options[:viewerType] end
def viewerVariation; @engine.options[:viewerVariation] end
def viewerVersion; @engine.options[:viewerVersion] end
def activeDocs; [] end
### METHODS ###
acro_method 'setInterval',
Arg[:name => 'cExpr', :required => true],
Arg[:name => 'nMilliseconds', :type => Numeric, :required => true] do |cExpr, nMilliseconds|
cExpr = cExpr.is_a?(::String) ? cExpr : ''
Interval.new(@engine, nMilliseconds, cExpr)
end
acro_method 'setTimeOut',
Arg[:name => 'cExpr', :required => true],
Arg[:name => 'nMilliseconds', :type => Numeric, :required => true] do |cExpr, nMilliseconds|
cExpr = cExpr.is_a?(::String) ? cExpr : ''
TimeOut.new(@engine, nMilliseconds, cExpr)
end
acro_method 'clearInterval',
Arg[:name => 'oInterval', :type => Interval, :required => true] do |oInterval|
oInterval.instance_variable_get(:@thr).terminate
nil
end
acro_method 'clearTimeOut',
Arg[:name => 'oInterval', :type => TimeOut, :required => true] do |oInterval|
oInterval.instance_variable_get(:@thr).terminate
nil
end
acro_method_protected 'addMenuItem'
acro_method_protected 'addSubMenu'
acro_method 'addToolButton'
acro_method_protected 'beginPriv'
acro_method 'beep'
acro_method_protected 'browseForDoc'
acro_method_protected 'endPriv'
end
class Console < AcrobatObject
def println(*args)
raise MissingArgError unless args.length > 0
@engine.options[:console].puts(args.first.to_s)
end
acro_method 'show'
acro_method 'clear'
acro_method 'hide'
end
class Util < AcrobatObject
acro_method 'streamFromString',
Arg[:name => 'cString', :type => ::Object, :required => true],
Arg[:name => 'cCharset', :type => ::Object, :default => 'utf-8'] do |cString, cCharset|
ReadStream.new(@engine, cString.to_s)
end
acro_method 'stringFromStream',
Arg[:name => 'oStream', :type => ReadStream, :required => true],
Arg[:name => 'cCharset', :type => ::Object, :default => 'utf-8'] do |oStream, cCharset|
oStream.instance_variable_get(:@data).dup
end
end
class Field < AcrobatObject
def initialize(engine, field)
super(engine)
@field = field
end
def doc; Doc.new(@field.pdf) end
def name
(@field.T.value if @field.has_key?(:T)).to_s
end
def value
@field.V.value if @field.has_key?(:V)
end
def valueAsString
self.value.to_s
end
def type
(if @field.has_key?(:FT)
case @field.FT.value
when PDF::Field::Type::BUTTON
if @fields.has_key?(:Ff)
flags = @field.Ff.value
if (flags & Origami::Annotation::Widget::Button::Flags::PUSHBUTTON) != 0
'button'
elsif (flags & Origami::Annotation::Widget::Button::Flags::RADIO) != 0
'radiobox'
else
'checkbox'
end
end
when PDF::Field::Type::TEXT then 'text'
when PDF::Field::Type::SIGNATURE then 'signature'
when PDF::Field::Type::CHOICE
if @field.has_key?(:Ff)
if (@field.Ff.value & Origami::Annotation::Widget::Choice::Flags::COMBO).zero?
'listbox'
else
'combobox'
end
end
end
end).to_s
end
end
class Data < AcrobatObject
attr_reader :name, :path, :size
attr_reader :creationDate, :modDate
attr_reader :description, :MIMEType
def initialize(engine, name, size, path = nil, creationDate = nil, modDate = nil, description = nil, mimeType = nil)
super(engine)
@name, @path, @size = name, path, size
@creationDate, @modDate = creationDate, modDate
@description, @MIMEType = description, mimeType
end
end
end
class JavaScript::EngineError < Exception; end
class JavaScript::Engine
attr_reader :global
attr_reader :context
attr_reader :options
attr_reader :parseInt
def initialize(pdf)
@options =
{
:viewerVersion => JavaScript::Platforms::WINDOWS,
:viewerType => JavaScript::Viewers::ADOBE_READER,
:viewerVariation => JavaScript::Viewers::ADOBE_READER,
:platform => 9,
:console => STDOUT,
:log_method_calls => false
}
@global = JavaScript::Doc.new(self, pdf)
app = JavaScript::App.new(self)
acrohelp = JavaScript::Acrohelp.new(self)
global = JavaScript::Global.new(self)
console = JavaScript::Console.new(self)
util = JavaScript::Util.new(self)
@context = V8::Context.new(:with => @global)
@context['app'] = app
@context['acrohelp'] = acrohelp
@context['console'] = console
@context['global'] = global
@context['util'] = util
@parseInt = @context['parseInt']
@hooks = {}
end
#
# Evaluates a JavaScript code in the current context.
#
def exec(script)
@context.eval(script)
end
#
# Set a hook on a JavaScript method.
#
def hook(name, &callback)
ns = name.split('.')
previous = @context
ns.each do |n|
raise JavaScript::EngineError, "#{name} does not exist" if previous.nil?
previous = previous[n]
end
case previous
when V8::Function, UnboundMethod, nil then
@context[name] = lambda do |*args|
callback[previous, *args]
end
@hooks[name] = [previous, callback]
else
raise JavaScript::EngineError, "#{name} is not a function"
end
end
#
# Removes an existing hook on a JavaScript method.
#
def unhook(name)
if @hooks.has_key?(name)
@context[name] = @hooks[name][0]
end
end
#
# Returns an Hash of all defined members in specified object name.
#
def members(obj)
members = {}
list = @context.eval <<-JS
(function(base){
var members = [];
for (var i in base) members.push([i, base[i]]);
return members;
})(#{obj})
JS
list.each do |var|
members[var[0]] = var[1]
end
members
end
#
# Returns all members in the global scope.
#
def scope
members('this')
end
#
# Binds the V8 remote debugging agent on the specified TCP _port_.
#
def enable_debugger(port = 5858)
V8::C::Debug.EnableAgent("Origami", port)
end
def debugger_break
exec 'debugger'
end
end
end
module String
#
# Evaluates the current String as JavaScript.
#
def eval_js
self.pdf.eval_js(self.value)
end
end
class Stream
#
# Evaluates the current Stream as JavaScript.
#
def eval_js
self.pdf.eval_js(self.data)
end
end
class PDF
#
# Executes a JavaScript script in the current document context.
#
def eval_js(code)
js_engine.exec(code)
end
#
# Returns the JavaScript engine (if JavaScript support is present).
#
def js_engine
@js_engine ||= PDF::JavaScript::Engine.new(self)
end
end
rescue LoadError
end
end
origami-pdf-1.2.7/lib/origami/webcapture.rb 0000644 0001750 0001750 00000004561 12101464040 021012 0 ustar terceiro terceiro =begin
= File
webcapture.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Webcapture
class SpiderInfo < Dictionary
include StandardObject
field :V, :Type => Real, :Default => 1.0, :Version => "1.3", :Required => true
field :C, :Type => Array
end
class Command < Dictionary
module Flags
SAMESITE = 1 << 1
SAMEPATH = 1 << 2
SUBMIT = 1 << 3
end
include StandardObject
field :URL, :Type => String, :Required => true
field :L, :Type => Integer, :Default => 1
field :F, :Type => Integer, :Default => 0
field :P, :Type => [ String, Stream ]
field :CT, :Type => String, :Default => "application/x-www-form-urlencoded"
field :H, :Type => String
field :S, :Type => Dictionary
end
class CommandSettings < Dictionary
include StandardObject
field :G, :Type => Dictionary
field :C, :Type => Dictionary
end
class SourceInformation < Dictionary
include StandardObject
module SubmissionType
NOFORM = 0
GETFORM = 1
POSTFORM = 2
end
field :AU, :Type => [ String, Dictionary ], :Required => true
field :TS, :Type => String
field :E, :Type => String
field :S, :Type => Integer, :Default => 0
field :C, :Type => Dictionary
end
end
end
origami-pdf-1.2.7/lib/origami/file.rb 0000644 0001750 0001750 00000014376 12122127175 017605 0 ustar terceiro terceiro =begin
= File
file.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
#
# Attachs an embedded file to the PDF.
# _path_:: The path to the file to attach.
# _options_:: A set of options to configure the attachment.
#
def attach_file(path, options = {})
#
# Default options.
#
params =
{
:Register => true, # Shall the file be registered in the name directory ?
:EmbeddedName => nil, # The inner filename of the attachment.
:Filter => :FlateDecode, # The stream filter used to store data.
}.update(options)
if path.is_a? FileSpec
filespec = path
params[:EmbeddedName] ||= ''
else
if path.respond_to?(:read)
fd = path
params[:EmbeddedName] ||= ''
else
fd = File.open(File.expand_path(path), 'r').binmode
params[:EmbeddedName] ||= File.basename(path)
end
fstream = EmbeddedFileStream.new
if ''.respond_to? :force_encoding
fstream.data = fd.read.force_encoding('binary') # 1.9
else
fstream.data = fd.read
end
fd.close
fstream.setFilter(params[:Filter])
filespec = FileSpec.new(:F => fstream)
end
name = params[:EmbeddedName]
fspec = FileSpec.new.setType(:Filespec).setF(name.dup).setEF(
filespec
)
register(
Names::Root::EMBEDDEDFILES,
name.dup,
fspec
) if params[:Register] == true
fspec
end
#
# Lookup embedded file in the embedded files name directory.
#
def get_embedded_file_by_name(name)
resolve_name Names::Root::EMBEDDEDFILES, name
end
#
# Calls block for each named embedded file.
#
def each_named_embedded_file(&b)
each_name(Names::Root::EMBEDDEDFILES, &b)
end
end
#
# Class used to convert system-dependent pathes into PDF pathes.
# PDF path specification offers a single form for representing file pathes over operating systems.
#
class Filename
class << self
#
# Converts UNIX file path into PDF file path.
#
def Unix(file)
ByteString.new(file)
end
#
# Converts MacOS file path into PDF file path.
#
def Mac(file)
ByteString.new("/" + file.gsub(":", "/"))
end
#
# Converts Windows file path into PDF file path.
#
def DOS(file)
path = ""
# Absolute vs relative path
if file.include? ":"
path << "/"
file.sub!(":","")
end
file.gsub!("\\", "/")
ByteString.new(path + file)
end
end
end
#
# Class representing a file specification.
# File specifications can be used to reference external files, as well as embedded files and URIs.
#
class FileSpec < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :FileSpec
field :FS, :Type => Name, :Default => :URL
field :F, :Type => [ ByteString, Stream ]
field :UF, :Type => String
field :DOS, :Type => ByteString
field :Mac, :Type => ByteString
field :Unix, :Type => ByteString
field :ID, :Type => Array
field :V, :Type => Boolean, :Default => false, :Version => "1.2"
field :EF, :Type => Dictionary, :Version => "1.3"
field :RF, :Type => Dictionary, :Version => "1.3"
field :Desc, :Type => ByteString, :Version => "1.6"
field :CI, :Type => Dictionary, :Version => "1.7"
field :Thumb, :Type => Stream, :Version => "1.7", :ExtensionLevel => 3
end
#
# Class representing a Uniform Resource Locator (URL)
#
class URL < FileSpec
field :Type, :Type => Name, :Default => :URL, :Required => true
def initialize(url)
super(:F => url)
end
end
#
# A class representing a file outside the current PDF file.
#
class ExternalFile < FileSpec
field :Type, :Type => Name, :Default => :FileSpec #, :Required => true
#
# Creates a new external file specification.
# _dos_:: The Windows path to this file.
# _mac_:: The MacOS path to this file.
# _unix_:: The UNIX path to this file.
#
def initialize(dos, mac = "", unix = "")
if not mac.empty? or not unix.empty?
super(:DOS => Filename.DOS(dos), :Mac => Filename.Mac(mac), :Unix => Filename.Unix(unix))
else
super(:F => dos)
end
end
end
#
# Class representing the data of an embedded file.
#
class EmbeddedFileStream < Stream
include StandardObject
field :Type, :Type => Name, :Default => :EmbeddedFile
field :Subtype, :Type => Name
field :Params, :Type => Dictionary
end
#
# Class representing parameters for a EmbeddedFileStream.
#
class EmbeddedFileParameters < Dictionary
include StandardObject
field :Size, :Type => Integer
field :CreationDate, :Type => ByteString
field :ModDate, :Type => ByteString
field :Mac, :Type => Dictionary
field :Checksum, :Type => String
end
end
origami-pdf-1.2.7/lib/origami/destinations.rb 0000644 0001750 0001750 00000011660 12101464040 021353 0 ustar terceiro terceiro =begin
= File
destinations.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume DelugrÈ
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
#
# Lookup destination in the destination name directory.
#
def get_destination_by_name(name)
resolve_name Names::Root::DESTS, name
end
#
# Calls block for each named destination.
#
def each_named_dest(&b)
each_name(Names::Root::DESTS, &b)
end
end
#
# A destination represents a specified location into the document.
#
module Destination
attr_reader :top, :left, :right, :bottom, :zoom
#
# Class representing a Destination zooming on a part of a document.
#
class Zoom < Origami::Array
include Destination
#
# Creates a new zoom Destination.
# _pageref_:: A Reference to a Page.
# _left_, _top_:: Coords in the Page.
# _zoom_:: Zoom factor.
#
def initialize(pageref, left = 0, top = 0, zoom = 0)
@left, @top, @zoom = left, top, zoom
super([pageref, :XYZ, left, top, zoom])
end
end
#
# Class representing a Destination showing a Page globally.
#
class GlobalFit < Origami::Array
include Destination
#
# Creates a new global fit Destination.
# _pageref_:: A Reference to a Page.
#
def initialize(pageref)
super([pageref, :Fit])
end
end
#
# Class representing a Destination fitting a Page horizontally.
#
class HorizontalFit < Origami::Array
include Destination
#
# Creates a new horizontal fit destination.
# _pageref_:: A Reference to a Page.
# _top_:: The vertical coord in the Page.
#
def initialize(pageref, top = 0)
@top = top
super([pageref, :FitH, top])
end
end
#
# Class representing a Destination fitting a Page vertically.
# _pageref_:: A Reference to a Page.
# _left_:: The horizontal coord in the Page.
#
class VerticalFit < Origami::Array
include Destination
def initialize(pageref, left = 0)
@left = left
super([pageref, :FitV, left])
end
end
#
# Class representing a Destination fitting the view on a rectangle in a Page.
#
class RectangleFit < Origami::Array
include Destination
#
# Creates a new rectangle fit Destination.
# _pageref_:: A Reference to a Page.
# _left_, _bottom_, _right_, _top_:: The rectangle to fit in.
#
def initialize(pageref, left = 0, bottom = 0, right = 0, top = 0)
@left, @bottom, @right, @top = left, bottom, right, top
super([pageref, :FitR, left, bottom, right, top])
end
end
#
# Class representing a Destination fitting the bounding box of a Page.
#
class GlobalBoundingBoxFit < Origami::Array
include Destination
#
# Creates a new bounding box fit Destination.
# _pageref_:: A Reference to a Page.
#
def initialize(pageref)
super([pageref, :FitB])
end
end
#
# Class representing a Destination fitting horizontally the bouding box a Page.
#
class HorizontalBoudingBoxFit < Origami::Array
include Destination
#
# Creates a new horizontal bounding box fit Destination.
# _pageref_:: A Reference to a Page.
# _top_:: The vertical coord.
#
def initialize(pageref, top = 0)
@top = top
super([pageref, :FitBH, top])
end
end
#
# Class representing a Destination fitting vertically the bounding box of a Page.
#
class VerticalBoundingBoxFit < Origami::Array
include Destination
#
# Creates a new vertical bounding box fit Destination.
# _pageref_:: A Reference to a Page.
# _left_:: The horizontal coord.
#
def initialize(pageref, left = 0)
@left = left
super([pageref, :FitBV, left])
end
end
end
end
origami-pdf-1.2.7/lib/origami/dictionary.rb 0000644 0001750 0001750 00000017430 12142214376 021027 0 ustar terceiro terceiro =begin
= File
dictionary.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume DelugrÈ
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class InvalidDictionaryObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing a Dictionary Object.
# Dictionaries are containers associating a Name to an embedded Object.
#
class Dictionary < Hash
include Origami::Object
TOKENS = %w{ << >> } #:nodoc:
@@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first) + WHITESPACES)
@@regexp_close = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.last) + WHITESPACES)
@@cast_fingerprints = {}
attr_reader :strings_cache, :names_cache, :xref_cache
#
# Creates a new Dictionary.
# _hash_:: The hash representing the new Dictionary.
#
def initialize(hash = {})
raise TypeError, "Expected type Hash, received #{hash.class}." unless hash.is_a?(Hash)
super()
@strings_cache = []
@names_cache = []
@xref_cache = {}
hash.each_pair do |k,v|
@names_cache.push(k.to_o)
case val = v.to_o
when String then @strings_cache.push(val)
when Name then @names_cache.push(val)
when Reference then
(@xref_cache[val] ||= []).push(self)
when Dictionary,Array then
@strings_cache.concat(val.strings_cache)
@names_cache.concat(val.names_cache)
@xref_cache.update(val.xref_cache) do |ref, cache1, cache2|
cache1.concat(cache2)
end
val.strings_cache.clear
val.names_cache.clear
val.xref_cache.clear
end
self[k.to_o] = val unless k.nil?
end
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
if stream.skip(@@regexp_open).nil?
raise InvalidDictionaryObjectError, "No token '#{TOKENS.first}' found"
end
pairs = {}
while stream.skip(@@regexp_close).nil? do
key = Name.parse(stream, parser)
type = Object.typeof(stream)
if type.nil?
raise InvalidDictionaryObjectError, "Invalid object for field #{key.to_s}"
end
value = type.parse(stream, parser)
pairs[key] = value
end
dict =
if Origami::OPTIONS[:enable_type_guessing]
guessed_type = self.guess_type(pairs)
if Origami::OPTIONS[:enable_type_propagation]
guessed_type.new(
Hash[
pairs.map {|key, value|
hint_type = guessed_type.hint_type(key.value)
if hint_type.is_a?(::Array) and not value.is_a?(Reference) # Choose best match
hint_type.find {|type| type.native_type == value.native_type}
end
if hint_type.is_a?(Class) and hint_type.native_type == value.native_type
[key, value.cast_to(hint_type)]
elsif hint_type and value.is_a?(Reference) and parser
parser.defer_type_cast(value, hint_type)
[key, value]
else
[key, value]
end
}])
else
guessed_type.new(pairs)
end
else
self.new(pairs)
end
dict.file_offset = offset
dict
end
alias to_h to_hash
def to_s(indent = 1) #:nodoc:
if indent > 0
content = TOKENS.first + EOL
self.each_pair do |key,value|
content << "\t" * indent + key.to_s + " " + (value.is_a?(Dictionary) ? value.to_s(indent + 1) : value.to_s) + EOL
end
content << "\t" * (indent - 1) + TOKENS.last
else
content = TOKENS.first.dup
self.each_pair do |key,value|
content << "#{key.to_s} #{value.is_a?(Dictionary) ? value.to_s(0) : value.to_s}"
end
content << TOKENS.last
end
super(content)
end
def map!(&b)
self.each_pair do |k,v|
self[k] = b.call(v)
end
end
def merge(dict)
Dictionary.new(super(dict))
end
def []=(key,val)
unless key.is_a?(Symbol) or key.is_a?(Name)
fail "Expecting a Name for a Dictionary entry, found #{key.class} instead."
end
key = key.to_o
if not val.nil?
val = val.to_o
super(key,val)
key.parent = self
val.parent = self unless val.is_indirect? or val.parent.equal?(self)
val
else
delete(key)
end
end
def [](key)
super(key.to_o)
end
def has_key?(key)
super(key.to_o)
end
def delete(key)
super(key.to_o)
end
def cast_to(type)
super(type)
cast = type.new(self)
cast.parent = self.parent
cast.no, cast.generation = self.no, self.generation
if self.is_indirect?
cast.set_indirect(true)
cast.set_pdf(self.pdf)
cast.file_offset = self.file_offset # cast can replace self
end
cast.xref_cache.update(self.xref_cache)
cast.names_cache.concat(self.names_cache)
cast.strings_cache.concat(self.strings_cache)
cast
end
alias each each_value
alias value to_h
def method_missing(field, *args) #:nodoc:
raise NoMethodError, "No method `#{field}' for #{self.class}" unless field.to_s[0,1] =~ /[A-Z]/
if field.to_s[-1,1] == '='
self[field.to_s[0..-2].to_sym] = args.first
else
obj = self[field];
obj.is_a?(Reference) ? obj.solve : obj
end
end
def copy
copy = self.class.new
self.each_pair do |k,v|
copy[k] = v.copy
end
copy.parent = @parent
copy.no, copy.generation = @no, @generation
copy.set_indirect(true) if is_indirect?
copy.set_pdf(@pdf) if is_indirect?
copy
end
def self.native_type; Dictionary end
def self.add_type_info(typeclass, key, value) #:nodoc:
if not @@cast_fingerprints.has_key?(typeclass) and typeclass.superclass != Dictionary and
@@cast_fingerprints.has_key?(typeclass.superclass)
@@cast_fingerprints[typeclass] = @@cast_fingerprints[typeclass.superclass].dup
end
@@cast_fingerprints[typeclass] ||= {}
@@cast_fingerprints[typeclass][key.to_o] = value.to_o
end
def self.guess_type(hash) #:nodoc:
best_type = self
@@cast_fingerprints.each_pair do |typeclass, keys|
best_type = typeclass if keys.all? { |k,v|
hash.has_key?(k) and hash[k] == v
} and typeclass < best_type
end
best_type
end
def self.hint_type(name); nil end #:nodoc:
end #class
end
# Origami
origami-pdf-1.2.7/lib/origami/parser.rb 0000644 0001750 0001750 00000021176 12142214376 020160 0 ustar terceiro terceiro =begin
= File
parser.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'strscan'
module Origami
module Console
if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
require "Win32API"
getStdHandle = Win32API.new("kernel32", "GetStdHandle", 'L', 'L')
@@getConsoleScreenBufferInfo = Win32API.new("kernel32", "GetConsoleScreenBufferInfo", 'LP', 'L')
@@setConsoleTextAttribute = Win32API.new("kernel32", "SetConsoleTextAttribute", 'LN', 'I')
@@hOut = getStdHandle.call(-11)
end
module Colors #:nodoc;
if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
BLACK = 0
BLUE = 1
GREEN = 2
CYAN = 3
RED = 4
MAGENTA = 5
YELLOW = 6
GREY = 7
WHITE = 8
else
GREY = '0;0'
BLACK = '0;30'
RED = '0;31'
GREEN = '0;32'
YELLOW = '0;33'
BLUE = '0;34'
MAGENTA = '0;35'
CYAN = '0;36'
WHITE = '0;37'
BRIGHT_GREY = '1;30'
BRIGHT_RED = '1;31'
BRIGHT_GREEN = '1;32'
BRIGHT_YELLOW = '1;33'
BRIGHT_BLUE = '1;34'
BRIGHT_MAGENTA = '1;35'
BRIGHT_CYAN = '1;36'
BRIGHT_WHITE = '1;37'
end
end
def self.set_fg_color(color, bright = false, fd = STDOUT) #:nodoc:
if RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
screen_info = "\x00" * 30
current =
if @@getConsoleScreenBufferInfo.call(@@hOut, screen_info) == 1
screen_info[8,2].unpack('v')[0]
else
Colors::GREY
end
color |= Colors::WHITE if bright
@@setConsoleTextAttribute.call(@@hOut, color)
yield
@@setConsoleTextAttribute.call(@@hOut, current)
else
col, nocol = [color, Colors::GREY].map! { |key| "\033[#{key}m" }
fd << col
yield
fd << nocol
end
end
unless RUBY_PLATFORM =~ /win32/ or RUBY_PLATFORM =~ /mingw32/
def self.colorize(text, color, bright = false)
col, nocol = [color, Colors::GREY].map! { |key| "\033[#{key}m" }
"#{col}#{text}#{nocol}"
end
end
def self.colorprint(text, color, bright = false, fd = STDOUT) #:nodoc:
set_fg_color(color, bright, fd) {
fd << text
}
end
end
class Parser #:nodoc:
class ParsingError < Exception #:nodoc:
end
#
# Do not output debug information.
#
VERBOSE_QUIET = 0
#
# Output some useful information.
#
VERBOSE_INFO = 1
#
# Output debug information.
#
VERBOSE_DEBUG = 2
#
# Output every objects read
#
VERBOSE_INSANE = 3
attr_accessor :options
def initialize(options = {}) #:nodoc:
# Type information for indirect objects.
@deferred_casts = {}
#Default options values
@options =
{
:verbosity => VERBOSE_INFO, # Verbose level.
:ignore_errors => true, # Try to keep on parsing when errors occur.
:callback => Proc.new {}, # Callback procedure whenever a structure is read.
:logger => STDERR, # Where to output parser messages.
:colorize_log => true # Colorize parser output?
}
@options.update(options)
end
def parse(stream)
data =
if stream.respond_to? :read
if ''.respond_to? :force_encoding
StringScanner.new(stream.read.force_encoding('binary')) # 1.9 compat
else
StringScanner.new(stream.read)
end
elsif stream.is_a? ::String
@filename = stream
if ''.respond_to? :force_encoding
StringScanner.new(File.open(stream, "r", :encoding => 'binary').binmode.read)
else
StringScanner.new(File.open(stream, "r").binmode.read)
end
elsif stream.is_a? StringScanner
stream
else
raise TypeError
end
@logger = @options[:logger]
@data = data
@data.pos = 0
end
def parse_object(pos = @data.pos) #:nodoc:
@data.pos = pos
begin
obj = Object.parse(@data, self)
return if obj.nil?
trace "Read #{obj.type} object#{
if obj.class != obj.native_type
" (" + obj.native_type.to_s.split('::').last + ")"
end
}, #{obj.reference}"
@options[:callback].call(obj)
obj
rescue UnterminatedObjectError => e
error e.message
obj = e.obj
Object.skip_until_next_obj(@data)
@options[:callback].call(obj)
obj
rescue Exception => e
error "Breaking on: #{(@data.peek(10) + "...").inspect} at offset 0x#{@data.pos.to_s(16)}"
error "Last exception: [#{e.class}] #{e.message}"
if not @options[:ignore_errors]
error "Manually fix the file or set :ignore_errors parameter."
raise
end
debug 'Skipping this indirect object.'
raise if not Object.skip_until_next_obj(@data)
retry
end
end
def parse_xreftable(pos = @data.pos) #:nodoc:
@data.pos = pos
begin
info "...Parsing xref table..."
xreftable = XRef::Section.parse(@data)
@options[:callback].call(xreftable)
xreftable
rescue Exception => e
debug "Exception caught while parsing xref table : " + e.message
warn "Unable to parse xref table! Xrefs might be stored into an XRef stream."
@data.pos -= 'trailer'.length unless @data.skip_until(/trailer/).nil?
nil
end
end
def parse_trailer(pos = @data.pos) #:nodoc:
@data.pos = pos
begin
info "...Parsing trailer..."
trailer = Trailer.parse(@data, self)
@options[:callback].call(trailer)
trailer
rescue Exception => e
debug "Exception caught while parsing trailer : " + e.message
warn "Unable to parse trailer!"
abort("Manually fix the file or set :ignore_errors parameter.") if not @options[:ignore_errors]
raise
end
end
def defer_type_cast(reference, type) #:nodoc:
@deferred_casts[reference] = type
end
def target_filename
@filename
end
def target_filesize
@data.string.size if @data
end
def target_data
@data.string.dup if @data
end
private
def error(str = "") #:nodoc:
if @options[:colorize_log]
Console.colorprint("[error] #{str}\n", Console::Colors::RED, false, @logger)
else
@logger.puts "[error] #{str}"
end
end
def warn(str = "") #:nodoc:
if @options[:verbosity] >= VERBOSE_INFO
if @options[:colorize_log]
Console.colorprint("[info ] Warning: #{str}\n", Console::Colors::YELLOW, false, @logger)
else
@logger.puts "[info ] #{str}"
end
end
end
def info(str = "") #:nodoc:
if @options[:verbosity] >= VERBOSE_INFO
if @options[:colorize_log]
Console.colorprint("[info ] ", Console::Colors::GREEN, false, @logger)
@logger.puts str
else
@logger.puts "[info ] #{str}"
end
end
end
def debug(str = "") #:nodoc:
if @options[:verbosity] >= VERBOSE_DEBUG
if @options[:colorize_log]
Console.colorprint("[debug] ", Console::Colors::MAGENTA, false, @logger)
@logger.puts str
else
@logger.puts "[debug] #{str}"
end
end
end
def trace(str = "") #:nodoc:
if @options[:verbosity] >= VERBOSE_INSANE
if @options[:colorize_log]
Console.colorprint("[trace] ", Console::Colors::CYAN, false, @logger)
@logger.puts str
else
@logger.puts "[trace] #{str}"
end
end
end
end
end
origami-pdf-1.2.7/lib/origami/acroform.rb 0000644 0001750 0001750 00000020631 12122127175 020465 0 ustar terceiro terceiro =begin
= File
acroform.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
#
# Returns true if the document contains an acrobat form.
#
def has_form?
(not self.Catalog.nil?) and self.Catalog.has_key? :AcroForm
end
#
# Creates a new AcroForm with specified fields.
#
def create_acroform(*fields)
acroform = self.Catalog.AcroForm ||= InteractiveForm.new.set_indirect(true)
self.add_fields(*fields)
acroform
end
#
# Add a field to the Acrobat form.
# _field_:: The Field to add.
#
def add_fields(*fields)
raise TypeError, "Expected Field arguments" unless fields.all? { |f| f.is_a?(Field) }
self.Catalog.AcroForm ||= InteractiveForm.new.set_indirect(true)
self.Catalog.AcroForm.Fields ||= []
self.Catalog.AcroForm.Fields.concat(fields)
fields.each do |field| field.set_indirect(true) end
self
end
#
# Returns an array of Acroform fields.
#
def fields
if self.has_form?
if self.Catalog.AcroForm.has_key?(:Fields)
self.Catalog.AcroForm[:Fields].map {|field| field.solve}
end
end
end
#
# Iterates over each Acroform Field.
#
def each_field(&b)
if self.has_form?
if self.Catalog.AcroForm.has_key?(:Fields)
self.Catalog.AcroForm[:Fields].each {|field| b.call(field.solve)}
end
end
end
#
# Returns the corresponding named Field.
#
def get_field(name)
self.each_field do |field|
return field if field[:T].solve == name
end
end
end
#
# Class representing a interactive form Dictionary.
#
class InteractiveForm < Dictionary
include StandardObject
#
# Flags relative to signature fields.
#
module SigFlags
SIGNATURESEXIST = 1 << 0
APPENDONLY = 1 << 1
end
field :Fields, :Type => Array, :Required => true, :Default => []
field :NeedAppearances, :Type => Boolean, :Default => false
field :SigFlags, :Type => Integer, :Default => 0
field :CO, :Type => Array, :Version => "1.3"
field :DR, :Type => Dictionary
field :DA, :Type => String
field :Q, :Type => Integer
field :XFA, :Type => [ Stream, Array ]
end
module Field
#
# Types of fields.
#
module Type
BUTTON = :Btn
TEXT = :Tx
CHOICE = :Ch
SIGNATURE = :Sig
end
#
# Flags relative to fields.
#
module Flags
READONLY = 1 << 0
REQUIRED = 1 << 1
NOEXPORT = 1 << 2
end
module TextAlign
LEFT = 0
CENTER = 1
RIGHT = 2
end
def self.included(receiver) #:nodoc:
receiver.field :FT, :Type => Name, :Required => true
receiver.field :Parent, :Type => Dictionary
receiver.field :Kids, :Type => Array
receiver.field :T, :Type => String
receiver.field :TU, :Type => String, :Version => "1.3"
receiver.field :TM, :Type => String, :Version => "1.3"
receiver.field :Ff, :Type => Integer, :Default => 0
receiver.field :V, :Type => Object
receiver.field :DV, :Type => Object
receiver.field :AA, :Type => Dictionary, :Version => "1.2"
# Variable text fields
receiver.field :DA, :Type => String, :Default => "/F1 10 Tf 0 g", :Required => true
receiver.field :Q, :Type => Integer, :Default => TextAlign::LEFT
receiver.field :DS, :Type => ByteString, :Version => "1.5"
receiver.field :RV, :Type => [ String, Stream ], :Version => "1.5"
end
def pre_build #:nodoc:
if not self.T
self.T = "undef#{::Array.new(5) {(0x30 + rand(10)).chr}.join}"
end
super
end
def onKeyStroke(action)
unless action.is_a?(Action)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.K = action
end
def onFormat(action)
unless action.is_a?(Action)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.F = action
end
def onValidate(action)
unless action.is_a?(Action)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.V = action
end
def onCalculate(action)
unless action.is_a?(Action)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.C = action
end
class Subform < Dictionary
include StandardObject
include Field
def add_fields(*fields)
self.Kids ||= []
self.Kids.concat(fields)
fields.each do |field| field.Parent = self end
self
end
end
class AdditionalActions < Dictionary
include StandardObject
field :K, :Type => Dictionary, :Version => "1.3"
field :F, :Type => Dictionary, :Version => "1.3"
field :V, :Type => Dictionary, :Version => "1.3"
field :C, :Type => Dictionary, :Version => "1.3"
end
class SignatureLock < Dictionary
include StandardObject
module Actions
ALL = :All
INCLUDE = :Include
EXCLUDE = :Exclude
end
field :Type, :Type => Name, :Default => :SigFieldLock
field :Action, :Type => Name, :Required => true
field :Fields, :Type => Array
def pre_build
if self.Action and self.Action != Actions::ALL
self.Fields ||= []
end
super
end
end
class SignatureSeedValue < Dictionary
include StandardObject
module Digest
SHA1 = :SHA1
SHA256 = :SHA256
SHA384 = :SHA384
SHA512 = :SHA512
RIPEMD160 = :RIPEMD160
end
field :Type, :Type => Name, :Default => :SV
field :Filter, :Type => Name
field :SubFilter, :Type => Array
field :DigestMethod, :Type => Array, :Default => Digest::SHA1, :Version => "1.7"
field :V, :Type => Real, :Default => 1.0
field :Cert, :Type => Dictionary
field :Reasons, :Type => Array
field :MDP, :Type => Dictionary, :Version => "1.6"
field :TimeStamp, :Type => Dictionary, :Version => "1.6"
field :LegalAttestation, :Type => Array, :Version => "1.6"
field :AddRevInfo, :Type => Boolean, :Default => false, :Version => "1.7"
field :Ff, :Type => Integer, :Default => 0
end
class CertificateSeedValue < Dictionary
include StandardObject
module URL
BROWSER = :Browser
ASSP = :ASSP
end
field :Type, :Type => Name, :Default => :SVCert
field :Subject, :Type => Array
field :SubjectDN, :Type => Array, :Version => "1.7"
field :KeyUsage, :Type => Array, :Version => "1.7"
field :Issuer, :Type => Array
field :OID, :Type => Array
field :URL, :Type => ByteString
field :URLType, :Type => Name, :Default => URL::BROWSER, :Version => "1.7"
field :Ff, :Type => Integer, :Default => 0
end
end
end
origami-pdf-1.2.7/lib/origami/boolean.rb 0000644 0001750 0001750 00000004361 12142214376 020300 0 ustar terceiro terceiro =begin
= File
boolean.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume DelugrÈ
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class InvalidBooleanObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing a Boolean Object.
# A Boolean Object can be *true* or *false*.
#
class Boolean
include Origami::Object
TOKENS = [ %w{ true false } ] #:nodoc:
@@regexp = Regexp.new(WHITESPACES + "(#{TOKENS.first.join('|')})")
#
# Creates a new Boolean value.
# _value_:: *true* or *false*.
#
def initialize(value)
unless value.is_a?(TrueClass) or value.is_a?(FalseClass)
raise TypeError, "Expected type TrueClass or FalseClass, received #{value.class}."
end
super()
@value = (value == nil || value == false) ? false : true
end
def to_s #:nodoc:
super(@value.to_s)
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
if stream.scan(@@regexp).nil?
raise InvalidBooleanObjectError
end
value = stream[2] == "true" ? true : false
bool = Boolean.new(value)
bool.file_offset = offset
bool
end
#
# Converts self into a Ruby boolean, that is TrueClass or FalseClass instance.
#
def value
@value
end
def self.native_type ; Boolean end
def false?
@value == false
end
def true?
@value == true
end
def ==(bool)
@value == bool
end
end
end
origami-pdf-1.2.7/lib/origami/extensions/ 0000755 0001750 0001750 00000000000 12427006355 020531 5 ustar terceiro terceiro origami-pdf-1.2.7/lib/origami/extensions/fdf.rb 0000644 0001750 0001750 00000013704 12101464040 021606 0 ustar terceiro terceiro =begin
= File
formats/fdf.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/object'
require 'origami/name'
require 'origami/dictionary'
require 'origami/reference'
require 'origami/boolean'
require 'origami/numeric'
require 'origami/string'
require 'origami/array'
require 'origami/trailer'
require 'origami/xreftable'
require 'origami/parsers/fdf'
module Origami
#
# Class representing an AcroForm Forms Data Format file.
#
class FDF
class Header
MAGIC = /\A%FDF-(\d)\.(\d)/
attr_accessor :majorversion, :minorversion
#
# Creates a file header, with the given major and minor versions.
# _majorversion_:: Major version.
# _minorversion_:: Minor version.
#
def initialize(majorversion = 2, minorversion = 1)
@majorversion, @minorversion = majorversion, minorversion
end
def self.parse(stream) #:nodoc:
if not stream.scan(MAGIC).nil?
maj = stream[1].to_i
min = stream[2].to_i
else
raise InvalidHeader, "Invalid header format"
end
FDF::Header.new(maj,min)
end
def to_s
"%FDF-#{@majorversion}.#{@minorversion}" + EOL
end
def to_sym #:nodoc:
"#{@majorversion}.#{@minorversion}".to_sym
end
def to_f #:nodoc:
to_sym.to_s.to_f
end
end
class Revision #:nodoc;
attr_accessor :pdf
attr_accessor :body, :xreftable, :trailer
def initialize(adbk)
@pdf = adbk
@body = {}
@xreftable = nil
@trailer = nil
end
def trailer=(trl)
trl.pdf = @pdf
@trailer = trl
end
end
attr_accessor :header, :revisions
def initialize #:nodoc:
@header = FDF::Header.new
@revisions = [ Revision.new(self) ]
@revisions.first.trailer = Trailer.new
end
def objects
def append_subobj(root, objset)
if objset.find{ |o| o.object_id == root.object_id }.nil?
objset << root
if root.is_a?(Array) or root.is_a?(Dictionary)
root.each { |subobj| append_subobj(subobj, objset) unless subobj.is_a?(Reference) }
end
end
end
objset = []
@revisions.first.body.values.each do |object|
unless object.is_a?(Reference)
append_subobj(object, objset)
end
end
objset
end
def <<(object)
object.set_indirect(true)
if object.no.zero?
maxno = 1
while get_object(maxno) do maxno = maxno.succ end
object.generation = 0
object.no = maxno
end
@revisions.first.body[object.reference] = object
object.reference
end
def Catalog
get_object(@trailer.Root)
end
def save(filename)
bin = ""
bin << @header.to_s
lastno, brange = 0, 0
xrefs = [ XRef.new(0, XRef::LASTFREE, XRef::FREE) ]
xrefsection = XRef::Section.new
@revisions.first.body.values.sort.each { |obj|
if (obj.no - lastno).abs > 1
xrefsection << XRef::Subsection.new(brange, xrefs)
brange = obj.no
xrefs.clear
end
xrefs << XRef.new(bin.size, obj.generation, XRef::USED)
lastno = obj.no
bin << obj.to_s
}
xrefsection << XRef::Subsection.new(brange, xrefs)
@xreftable = xrefsection
@trailer ||= Trailer.new
@trailer.Size = rev.body.size + 1
@trailer.startxref = bin.size
bin << @xreftable.to_s
bin << @trailer.to_s
fd = File.open(filename, "w").binmode
fd << bin
fd.close
show_entries
end
alias saveas save
private
def rebuildxrefs #:nodoc:
startxref = @header.to_s.size
@revisions.first.body.values.each { |object|
startxref += object.to_s.size
}
@xreftable = buildxrefs(@revisions.first.body)
@trailer ||= Trailer.new
@trailer.Size = @revisions.first.body.size + 1
@trailer.startxref = startxref
self
end
def buildxrefs(objects) #:nodoc:
lastno = 0
brange = 0
xrefs = [ XRef.new(0, XRef::LASTFREE, XRef::FREE) ]
xrefsection = XRef::Section.new
objects.sort.each { |object|
if (object.no - lastno).abs > 1
xrefsection << XRef::Subsection.new(brange, xrefs)
brange = object.no
xrefs.clear
end
xrefs << XRef.new(get_object_offset(object.no, object.generation), object.generation, XRef::USED)
lastno = object.no
}
xrefsection << XRef::Subsection.new(brange, xrefs)
xrefsection
end
def get_object_offset(no,generation) #:nodoc:
bodyoffset = @header.to_s.size
objectoffset = bodyoffset
@revisions.first.body.values.each { |object|
if object.no == no and object.generation == generation then return objectoffset
else
objectoffset += object.to_s.size
end
}
nil
end
end
end
origami-pdf-1.2.7/lib/origami/extensions/ppklite.rb 0000644 0001750 0001750 00000031233 12101464040 022514 0 ustar terceiro terceiro =begin
= File
formats/ppklite.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/object'
require 'origami/name'
require 'origami/dictionary'
require 'origami/reference'
require 'origami/boolean'
require 'origami/numeric'
require 'origami/string'
require 'origami/array'
require 'origami/trailer'
require 'origami/xreftable'
require 'origami/parsers/ppklite'
require 'openssl'
module Origami
module Adobe
#
# Class representing an Adobe Reader certificate store.
#
class PPKLite
#
# Class representing a certificate store header.
#
class Header
MAGIC = /\A%PPKLITE-(\d)\.(\d)/
attr_accessor :majorversion, :minorversion
#
# Creates a file header, with the given major and minor versions.
# _majorversion_:: Major version.
# _minorversion_:: Minor version.
#
def initialize(majorversion = 2, minorversion = 1)
@majorversion, @minorversion = majorversion, minorversion
end
def self.parse(stream) #:nodoc:
if not stream.scan(MAGIC).nil?
maj = stream[1].to_i
min = stream[2].to_i
else
raise InvalidHeader, "Invalid header format"
end
PPKLite::Header.new(maj,min)
end
#
# Outputs self into PDF code.
#
def to_s
"%PPKLITE-#{@majorversion}.#{@minorversion}" + EOL
end
def to_sym #:nodoc:
"#{@majorversion}.#{@minorversion}".to_sym
end
def to_f #:nodoc:
to_sym.to_s.to_f
end
end
class Revision #:nodoc;
attr_accessor :pdf
attr_accessor :body, :xreftable, :trailer
def initialize(adbk)
@pdf = adbk
@body = {}
@xreftable = nil
@trailer = nil
end
def trailer=(trl)
trl.pdf = @pdf
@trailer = trl
end
end
attr_accessor :header, :revisions
def initialize #:nodoc:
@header = PPKLite::Header.new
@revisions = [ Revision.new(self) ]
@revisions.first.trailer = Trailer.new
end
def objects
def append_subobj(root, objset)
if objset.find{ |o| o.object_id == root.object_id }.nil?
objset << root
if root.is_a?(Array) or root.is_a?(Dictionary)
root.each { |subobj| append_subobj(subobj, objset) unless subobj.is_a?(Reference) }
end
end
end
objset = []
@revisions.first.body.values.each do |object|
unless object.is_a?(Reference)
append_subobj(object, objset)
end
end
objset
end
def <<(object)
object.set_indirect(true)
if object.no.zero?
maxno = 1
while get_object(maxno) do maxno = maxno.succ end
object.generation = 0
object.no = maxno
end
@revisions.first.body[object.reference] = object
object.reference
end
def Catalog
get_object(@trailer.Root)
end
def save(filename)
bin = ""
bin << @header.to_s
lastno, brange = 0, 0
xrefs = [ XRef.new(0, XRef::LASTFREE, XRef::FREE) ]
xrefsection = XRef::Section.new
@revisions.first.body.values.sort.each { |obj|
if (obj.no - lastno).abs > 1
xrefsection << XRef::Subsection.new(brange, xrefs)
brange = obj.no
xrefs.clear
end
xrefs << XRef.new(bin.size, obj.generation, XRef::USED)
lastno = obj.no
bin << obj.to_s
}
xrefsection << XRef::Subsection.new(brange, xrefs)
@xreftable = xrefsection
@trailer ||= Trailer.new
@trailer.Size = rev.body.size + 1
@trailer.startxref = bin.size
bin << @xreftable.to_s
bin << @trailer.to_s
fd = File.open(filename, "w").binmode
fd << bin
fd.close
show_entries
end
alias saveas save
#
# Prints registered users in the address book
#
def show_users
puts "----------"
puts "Users list"
puts "----------"
@revisions.first.body.values.each { |obj| if obj.is_a?(User) then obj.show; puts end }
nil
end
#
# Prints registered certificates in the addressbook
#
def show_certs
puts "-----------------"
puts "Certificates list"
puts "-----------------"
@revisions.first.body.values.each { |obj| if obj.is_a?(Certificate) then obj.show; puts end }
nil
end
#
# Prints certificate with the specified id
#
def show_cert(id)
@revisions.first.body.values.find_all { |obj| obj.is_a?(Certificate) and obj.ID == id }.each do |cert|
cert.show
puts
end
nil
end
#
# Returns a Certificate dictionary corresponding to the specified id
#
def get_cert(id)
@revisions.first.body.values.find { |obj| obj.is_a?(Certificate) and obj.ID == id }
end
def show_user(id)
users = @revisions.first.body.values.find_all { |obj| obj.is_a?(User) and obj.ID == id }.each do |user|
user.show
puts
end
nil
end
#
# Prints users and certificates registered in the address book
#
def show_entries
show_users
show_certs
puts "End of address book."
end
#
# Add a certificate into the address book
#
def add_certificate(certfile, attributes, viewable = false, editable = false)
cert = Certificate.new
cert.Cert = OpenSSL::X509::Certificate.new(certfile).to_der
cert.ID = self.Catalog.PPK.AddressBook.NextID
self.Catalog.PPK.AddressBook.NextID += 1
cert.Trust = attributes
cert.Viewable = viewable
cert.Editable = editable
self.Catalog.PPK.AddressBook.Entries.push(self << cert)
show_certs
end
alias to_s show_entries
alias to_str show_entries
class Catalog < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Catalog, :Required => true
field :PPK, :Type => Dictionary, :Required => true
def initialize(hash = {}) #:nodoc:
super(hash)
end
end
class PPK < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :PPK, :Required => true
field :User, :Type => Dictionary, :Required => true
field :AddressBook, :Type => Dictionary, :Required => true
field :V, :Type => Integer, :Default => 0x10001, :Required => true
def initialize(hash = {}) #:nodoc:
super(hash)
end
end
class UserList < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :User, :Required => true
def initialize(hash = {})
super(hash)
end
end
class AddressList < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :AddressBook, :Required => true
field :NextID, :Type => Integer
field :Entries, :Type => Array, :Default => [], :Required => true
def initialize(hash = {}) #:nodoc:
super(hash)
end
end
module Descriptor
CERTIFICATE = 1
USER = 2
def self.included(receiver) #:nodoc:
receiver.field :ID, :Type => Integer, :Required => true
receiver.field :ABEType, :Type => Integer, :Default => Descriptor::CERTIFICATE, :Required => true
end
def initialize(hash = {}) #:nodoc:
super(hash)
end
end
class User < Dictionary
include StandardObject
include Descriptor
field :ABEType, :Type => Integer, :Default => Descriptor::USER, :Required => true
field :Name, :Type => String, :Required => true
field :Encrypt, :Type => Integer
field :Certs, :Type => Array, :Default => [], :Required => true
def show
puts "ID: #{self.ID}"
puts "Name: #{self.Name}"
puts "Certificates: " + self.Certs.join(", ")
end
end
class Certificate < Dictionary
include StandardObject
include Descriptor
module Flags
CAN_CERTIFY = 1 << 1
ALLOW_DYNAMIC_CONTENT = 1 << 2
UNKNOWN_1 = 1 << 3
ALLOW_HIGH_PRIV_JS = 1 << 4
UNKNOWN_2 = 1 << 5
IS_ROOT_CA = 1 << 6
#~ FULL_TRUST = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4 | 1 << 5 | 1 << 6
FULL_TRUST = 8190
end
field :ABEType, :Type => Integer, :Default => Descriptor::CERTIFICATE, :Required => true
field :Usage, :Type => Integer, :Default => 1, :Required => true
field :Viewable, :Type => Boolean, :Default => true
field :Editable, :Type => Boolean, :Default => true
field :Cert, :Type => String, :Required => true
field :Trust, :Type => Integer, :Default => Flags::UNKNOWN_2, :Required => true
def show
puts "ID: #{self.ID}"
puts "Viewable: #{self.Viewable}"
puts "Editable: #{self.Editable}"
puts "Trust attributes: #{self.Trust}"
end
end
def get_object(no, generation = 0) #:nodoc:
case no
when Reference
target = no
when ::Integer
target = Reference.new(no, generation)
when Origami::Object
return no
end
@revisions.first.body[target]
end
private
def rebuildxrefs #:nodoc:
startxref = @header.to_s.size
@revisions.first.body.values.each { |object|
startxref += object.to_s.size
}
@xreftable = buildxrefs(@revisions.first.body)
@trailer ||= Trailer.new
@trailer.Size = @revisions.first.body.size + 1
@trailer.startxref = startxref
self
end
def buildxrefs(objects) #:nodoc:
lastno = 0
brange = 0
xrefs = [ XRef.new(0, XRef::LASTFREE, XRef::FREE) ]
xrefsection = XRef::Section.new
objects.sort.each { |object|
if (object.no - lastno).abs > 1
xrefsection << XRef::Subsection.new(brange, xrefs)
brange = object.no
xrefs.clear
end
xrefs << XRef.new(get_object_offset(object.no, object.generation), object.generation, XRef::USED)
lastno = object.no
}
xrefsection << XRef::Subsection.new(brange, xrefs)
xrefsection
end
def get_object_offset(no,generation) #:nodoc:
bodyoffset = @header.to_s.size
objectoffset = bodyoffset
@revisions.first.body.values.each { |object|
if object.no == no and object.generation == generation then return objectoffset
else
objectoffset += object.to_s.size
end
}
nil
end
end
end
end
origami-pdf-1.2.7/lib/origami/filters.rb 0000644 0001750 0001750 00000020157 12140763267 020340 0 ustar terceiro terceiro =begin
= File
filters.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
#
# Filters are algorithms used to encode data into a PDF Stream.
#
module Filter
class InvalidFilterDataError < Exception # :nodoc:
attr_reader :decoded_data
def initialize(message, decoded_data = nil)
super(message)
@decoded_data = decoded_data
end
end
module Utils
class BitWriterError < Exception #:nodoc:
end
#
# Class used to forge a String from a stream of bits.
# Internally used by some filters.
#
class BitWriter
def initialize
@data = ''
@last_byte = nil
@ptr_bit = 0
end
#
# Writes _data_ represented as Fixnum to a _length_ number of bits.
#
def write(data, length)
return BitWriterError, "Invalid data length" unless length > 0 and (1 << length) > data
# optimization for aligned byte writing
if length == 8 and @last_byte.nil? and @ptr_bit == 0
@data << data.chr
return self
end
while length > 0
if length >= 8 - @ptr_bit
length -= 8 - @ptr_bit
@last_byte ||= 0
@last_byte |= (data >> length) & ((1 << (8 - @ptr_bit)) - 1)
data &= (1 << length) - 1
@data << @last_byte.chr
@last_byte = nil
@ptr_bit = 0
else
@last_byte ||= 0
@last_byte |= (data & ((1 << length) - 1)) << (8 - @ptr_bit - length)
@ptr_bit += length
if @ptr_bit == 8
@data << @last_byte.chr
@last_byte = nil
@ptr_bit = 0
end
length = 0
end
end
self
end
#
# Returns the data size in bits.
#
def size
(@data.size << 3) + @ptr_bit
end
#
# Finalizes the stream.
#
def final
@data << @last_byte.chr if @last_byte
@last_byte = nil
@p = 0
self
end
#
# Outputs the stream as a String.
#
def to_s
@data.dup
end
end
class BitReaderError < Exception #:nodoc:
end
#
# Class used to read a String as a stream of bits.
# Internally used by some filters.
#
class BitReader
BRUIJIN_TABLE = ::Array.new(32)
BRUIJIN_TABLE.size.times { |i|
BRUIJIN_TABLE[((0x77cb531 * (1 << i)) >> 27) & 31] = i
}
def initialize(data)
@data = data
reset
end
#
# Resets the read pointer.
#
def reset
@ptr_byte, @ptr_bit = 0, 0
self
end
#
# Returns true if end of data has been reached.
#
def eod?
@ptr_byte >= @data.size
end
#
# Returns the read pointer position in bits.
#
def pos
(@ptr_byte << 3) + @ptr_bit
end
#
# Returns the data size in bits.
#
def size
@data.size << 3
end
#
# Sets the read pointer position in bits.
#
def pos=(bits)
raise BitReaderError, "Pointer position out of data" if bits > self.size
pbyte = bits >> 3
pbit = bits - (pbyte << 3)
@ptr_byte, @ptr_bit = pbyte, pbit
bits
end
#
# Reads _length_ bits as a Fixnum and advances read pointer.
#
def read(length)
n = self.peek(length)
self.pos += length
n
end
#
# Reads _length_ bits as a Fixnum. Does not advance read pointer.
#
def peek(length)
return BitReaderError, "Invalid read length" unless length > 0
return BitReaderError, "Insufficient data" if self.pos + length > self.size
n = 0
ptr_byte, ptr_bit = @ptr_byte, @ptr_bit
while length > 0
byte = @data[ptr_byte].ord
if length > 8 - ptr_bit
length -= 8 - ptr_bit
n |= ( byte & ((1 << (8 - ptr_bit)) - 1) ) << length
ptr_byte += 1
ptr_bit = 0
else
n |= (byte >> (8 - ptr_bit - length)) & ((1 << length) - 1)
length = 0
end
end
n
end
#
# Used for bit scanning.
# Counts leading zeros. Does not advance read pointer.
#
def clz
count = 0
if @ptr_bit != 0
bits = peek(8 - @ptr_bit)
count = clz32(bits << (32 - (8 - @ptr_bit)))
return count if count < (8 - @ptr_bit)
end
delta = 0
while @data.size > @ptr_byte + delta * 4
word = @data[@ptr_byte + delta * 4, 4] # next 32 bits
z = clz32((word << (4 - word.size)).unpack("N")[0])
count += z
delta += 1
return count if z < 32 - ((4 - word.size) << 3)
end
count
end
#
# Used for bit scanning.
# Count leading ones. Does not advance read pointer.
#
def clo
count = 0
if @ptr_bit != 0
bits = peek(8 - @ptr_bit)
count = clz32(~(bits << (32 - (8 - @ptr_bit))) & 0xff)
return count if count < (8 - @ptr_bit)
end
delta = 0
while @data.size > @ptr_byte + delta * 4
word = @data[@ptr_byte + delta * 4, 4] # next 32 bits
z = clz32(~((word << (4 - word.size)).unpack("N")[0]) & 0xffff_ffff)
count += z
delta += 1
return count if z < 32 - ((4 - word.size) << 3)
end
count
end
private
def bitswap8(i) #:nodoc
((i * 0x0202020202) & 0x010884422010) % 1023
end
def bitswap32(i) #:nodoc:
(bitswap8((i >> 0) & 0xff) << 24) |
(bitswap8((i >> 8) & 0xff) << 16) |
(bitswap8((i >> 16) & 0xff) << 8) |
(bitswap8((i >> 24) & 0xff) << 0)
end
def ctz32(i) #:nodoc:
if i == 0 then 32
else
BRUIJIN_TABLE[(((i & -i) * 0x77cb531) >> 27) & 31]
end
end
def clz32(i) #:nodoc:
ctz32 bitswap32 i
end
end
end
module ClassMethods
#
# Decodes the given data.
# _stream_:: The data to decode.
#
def decode(stream, params = {})
self.new(params).decode(stream)
end
#
# Encodes the given data.
# _stream_:: The data to encode.
#
def encode(stream, params = {})
self.new(params).encode(stream)
end
end
def initialize(parameters = {})
@params = parameters
end
def self.included(receiver)
receiver.extend(ClassMethods)
end
end
end
require 'origami/filters/ascii'
require 'origami/filters/lzw'
require 'origami/filters/flate'
require 'origami/filters/runlength'
require 'origami/filters/ccitt'
require 'origami/filters/dct'
require 'origami/filters/jbig2'
require 'origami/filters/jpx'
require 'origami/filters/crypt'
origami-pdf-1.2.7/lib/origami/xreftable.rb 0000644 0001750 0001750 00000025134 12101464040 020624 0 ustar terceiro terceiro =begin
= File
xreftable.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
#
# Tries to strip any xrefs information off the document.
#
def remove_xrefs
def delete_xrefstm(xrefstm)
prev = xrefstm.Prev
delete_object(xrefstm.reference)
if prev.is_a?(Integer) and (prev_stm = get_object_by_offset(prev)).is_a?(XRefStream)
delete_xrefstm(prev_stm)
end
end
@revisions.reverse_each do |rev|
if rev.has_xrefstm?
delete_xrefstm(rev.xrefstm)
end
if rev.trailer.has_dictionary? and rev.trailer.XRefStm.is_a?(Integer)
xrefstm = get_object_by_offset(rev.trailer.XRefStm)
delete_xrefstm(xrefstm) if xrefstm.is_a?(XRefStream)
end
rev.xrefstm = rev.xreftable = nil
end
end
end
class InvalidXRefError < Exception #:nodoc:
end
#
# Class representing a Cross-reference information.
#
class XRef
FREE = "f"
USED = "n"
FIRSTFREE = 65535
@@regexp = /(\d{10}) (\d{5}) (n|f)(\r\n| \r| \n)/
attr_accessor :offset, :generation, :state
#
# Creates a new XRef.
# _offset_:: The file _offset_ of the referenced Object.
# _generation_:: The generation number of the referenced Object.
# _state_:: The state of the referenced Object (FREE or USED).
#
def initialize(offset, generation, state)
@offset, @generation, @state = offset, generation, state
end
def self.parse(stream) #:nodoc:
if stream.scan(@@regexp).nil?
raise InvalidXRefError, "Invalid XRef format"
end
offset = stream[1].to_i
generation = stream[2].to_i
state = stream[3]
XRef.new(offset, generation, state)
end
#
# Outputs self into PDF code.
#
def to_s
off = ("0" * (10 - @offset.to_s.length)) + @offset.to_s
gen = ("0" * (5 - @generation.to_s.length)) + @generation.to_s
"#{off} #{gen} #{@state}" + EOL
end
def to_xrefstm_data(type_w, field1_w, field2_w)
type_w <<= 3
field1_w <<= 3
field2_w <<= 3
type = ((@state == FREE) ? "\000" : "\001").unpack("B#{type_w}")[0]
offset = @offset.to_s(2)
offset = '0' * (field1_w - offset.size) + offset
generation = @generation.to_s(2)
generation = '0' * (field2_w - generation.size) + generation
[ type , offset, generation ].pack("B#{type_w}B#{field1_w}B#{field2_w}")
end
class InvalidXRefSubsectionError < Exception #:nodoc:
end
#
# Class representing a cross-reference subsection.
# A subsection contains a continute set of XRef.
#
class Subsection
@@regexp = Regexp.new("(\\d+) (\\d+)" + WHITESPACES + "(\\r?\\n|\\r\\n?)")
attr_reader :range
#
# Creates a new XRef subsection.
# _start_:: The number of the first object referenced in the subsection.
# _entries_:: An array of XRef.
#
def initialize(start, entries = [])
@entries = entries.dup
@range = Range.new(start, start + entries.size - 1)
end
def self.parse(stream) #:nodoc:
if stream.scan(@@regexp).nil?
raise InvalidXRefSubsectionError, "Bad subsection format"
end
start = stream[1].to_i
size = stream[2].to_i
xrefs = []
size.times do
xrefs << XRef.parse(stream)
end
XRef::Subsection.new(start, xrefs)
end
#
# Returns whether this subsection contains information about a particular object.
# _no_:: The Object number.
#
def has_object?(no)
@range.include?(no)
end
#
# Returns XRef associated with a given object.
# _no_:: The Object number.
#
def [](no)
@entries[no - @range.begin]
end
#
# Processes each XRef in the subsection.
#
def each(&b)
@entries.each(&b)
end
#
# Outputs self into PDF code.
#
def to_s
section = "#{@range.begin} #{@range.end - @range.begin + 1}" + EOL
@entries.each { |xref|
section << xref.to_s
}
section
end
end
class InvalidXRefSectionError < Exception #:nodoc:
end
#
# Class representing a Cross-reference table.
# A section contains a set of XRefSubsection.
#
class Section
@@regexp_open = Regexp.new(WHITESPACES + "xref" + WHITESPACES + "(\\r?\\n|\\r\\n?)")
@@regexp_sub = Regexp.new("(\\d+) (\\d+)" + WHITESPACES + "(\\r?\\n|\\r\\n?)")
#
# Creates a new XRef section.
# _subsections_:: An array of XRefSubsection.
#
def initialize(subsections = [])
@subsections = subsections
end
def self.parse(stream) #:nodoc:
if stream.skip(@@regexp_open).nil?
raise InvalidXRefSectionError, "No xref token found"
end
subsections = []
while stream.match?(@@regexp_sub) do
subsections << XRef::Subsection.parse(stream)
end
XRef::Section.new(subsections)
end
#
# Appends a new subsection.
# _subsection_:: A XRefSubsection.
#
def <<(subsection)
@subsections << subsection
end
#
# Returns a XRef associated with a given object.
# _no_:: The Object number.
#
def [](no)
@subsections.each { |s|
return s[no] if s.has_object?(no)
}
nil
end
alias :find :[]
#
# Processes each XRefSubsection.
#
def each(&b)
@subsections.each(&b)
end
#
# Outputs self into PDF code.
#
def to_s
"xref" << EOL << @subsections.join
end
end
end
#
# An xref poiting to an Object embedded in an ObjectStream.
#
class XRefToCompressedObj
attr_accessor :objstmno, :index
def initialize(objstmno, index)
@objstmno = objstmno
@index = index
end
def to_xrefstm_data(type_w, field1_w, field2_w)
type_w <<= 3
field1_w <<= 3
field2_w <<= 3
type = "\002".unpack("B#{type_w}")[0]
objstmno = @objstmno.to_s(2)
objstmno = '0' * (field1_w - objstmno.size) + objstmno
index = @index.to_s(2)
index = '0' * (field2_w - index.size) + index
[ type , objstmno, index ].pack("B#{type_w}B#{field1_w}B#{field2_w}")
end
end
class InvalidXRefStreamObjectError < InvalidStreamObjectError ; end
#
# Class representing a XRef Stream.
#
class XRefStream < Stream
XREF_FREE = 0
XREF_USED = 1
XREF_COMPRESSED = 2
include Enumerable
include StandardObject
#
# Xref fields
#
field :Type, :Type => Name, :Default => :XRef, :Required => true, :Version => "1.5"
field :Size, :Type => Integer, :Required => true
field :Index, :Type => Array
field :Prev, :Type => Integer
field :W, :Type => Array, :Required => true
#
# Trailer fields
#
field :Root, :Type => Dictionary, :Required => true
field :Encrypt, :Type => Dictionary
field :Info, :Type => Dictionary
field :ID, :Type => Array
def initialize(data = "", dictionary = {})
super(data, dictionary)
@xrefs = nil
end
def entries
load! if @xrefs.nil?
@xrefs
end
#
# Returns XRef entries present in this stream.
#
def pre_build #:nodoc:
load! if @xrefs.nil?
self.W = [ 1, 2, 2 ] unless has_field?(:W)
self.Size = @xrefs.length + 1
save!
super
end
#
# Adds an XRef to this Stream.
#
def <<(xref)
load! if @xrefs.nil?
@xrefs << xref
end
#
# Iterates over each XRef present in the stream.
#
def each(&b)
load! if @xrefs.nil?
@xrefs.each(&b)
end
#
# Returns an XRef matching this object number.
#
def find(no)
load! if @xrefs.nil?
ranges = self.Index || [ 0, @xrefs.length ]
index = 0
(ranges.size / 2).times do |i|
brange = ranges[i*2].to_i
size = ranges[i*2+1].to_i
return @xrefs[index + no - brange] if Range.new(brange, brange + size - 1) === no
index += size
end
nil
end
def clear
self.data = ''
@xrefs = []
self.Index = []
end
private
def load! #:nodoc:
if @xrefs.nil? and has_field?(:W)
widths = self.W
if not widths.is_a?(Array) or widths.length != 3 or widths.any?{|width| not width.is_a?(Integer) }
raise InvalidXRefStreamObjectError, "W field must be an array of 3 integers"
end
decode!
type_w = self.W[0]
field1_w = self.W[1]
field2_w = self.W[2]
entrymask = "B#{type_w << 3}B#{field1_w << 3}B#{field2_w << 3}"
size = @data.size / (type_w + field1_w + field2_w)
xentries = @data.unpack(entrymask * size).map!{|field| field.to_i(2) }
@xrefs = []
size.times do |i|
type,field1,field2 = xentries[i*3].ord,xentries[i*3+1].ord,xentries[i*3+2].ord
case type
when XREF_FREE
@xrefs << XRef.new(field1, field2, XRef::FREE)
when XREF_USED
@xrefs << XRef.new(field1, field2, XRef::USED)
when XREF_COMPRESSED
@xrefs << XRefToCompressedObj.new(field1, field2)
end
end
else
@xrefs = []
end
end
def save! #:nodoc:
self.data = ""
type_w, field1_w, field2_w = self.W
@xrefs.each do |xref| @data << xref.to_xrefstm_data(type_w, field1_w, field2_w) end
encode!
end
end
end
origami-pdf-1.2.7/lib/origami/xfa.rb 0000644 0001750 0001750 00000241570 12101464040 017432 0 ustar terceiro terceiro =begin
= File
xfa.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'rexml/document'
module Origami
class PDF
def create_xfa_form(xdp, *fields)
acroform = create_acroform(*fields)
acroform.XFA = Stream.new(xdp, :Filter => :FlateDecode)
acroform
end
end
module XFA
class XFAError < Exception #:nodoc:
end
module ClassMethods
def xfa_attribute(name)
read_xml_attr = lambda { return self.attributes[name.to_s] }
write_xml_attr = lambda { |value| self.attributes[name.to_s] = value }
send(:define_method, "attr_#{name.to_s}", read_xml_attr)
send(:define_method, "attr_#{name.to_s}=", write_xml_attr)
end
def xfa_node(name, type, range = (0..(1.0/0)))
add_child = lambda { |*attr|
elt = self.add_element(type.new)
unless attr.empty?
attr.first.each do |k,v|
elt.attributes[k.to_s] = v
end
end
return elt
}
send(:define_method, "add_#{name}", add_child)
end
def mime_type(type)
send(:define_method, "mime_type") { return type }
end
end
def self.included(receiver)
receiver.extend(ClassMethods)
end
class Element < REXML::Element
include XFA
end
end
module XDP
module Packet
#
# This packet encloses the configuration settings.
#
class Config < XFA::Element
mime_type 'text/xml'
def initialize
super("config")
add_attribute 'xmlns:xfa', 'http://www.xfa.org/schema/xci/3.0/'
end
class URI < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(uri = "")
super('uri')
self.text = uri
end
end
class Debug < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'uri', Config::URI, 0..1
def initialize
super('debug')
end
end
class AdjustData < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(coercion = "0")
super('adjustData')
self.text = coercion
end
end
class Attributes < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
PRESERVE = "preserve"
DELEGATE = "delegate"
IGNORE = "ignore"
def initialize(attr = PRESERVE)
super('attributes')
self.text = attr
end
end
class IncrementalLoad < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
NONE = "none"
FORWARDONLY = "forwardOnly"
def initialize(incload = NONE)
super('incrementalLoad')
self.text = incload
end
end
class Locale < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(locale = "")
super('locale')
self.text = locale
end
end
class LocaleSet < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(uri = "")
super('localeSet')
self.text = uri
end
end
class OutputXSL < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'uri', Config::URI, 0..1
def initialize
super('outputXSL')
end
end
class Range < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(range = "")
super('range')
self.text = range
end
end
class Record < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(record = "")
super('record')
self.text = ""
end
end
class StartNode < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(somexpr = "")
super('startNode')
self.text = somexpr
end
end
class Window < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(win = "0")
super('window')
self.text = win
end
end
class XSL < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'debug', Config::Debug, 0..1
xfa_node 'uri', Config::URI, 0..1
def initialize
super('xsl')
end
end
class ExcludeNS < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(ns = "")
super('excludeNS')
self.text = ns
end
end
class GroupParent < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(parentname = "")
super('groupParent')
self.text = parentname
end
end
class IfEmpty < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
DATAVALUE = "dataValue"
DATAGROUP = "dataGroup"
IGNORE = "ignore"
REMOVE = "remove"
def initialize(default = DATAVALUE)
super('ifEmpty')
self.text = default
end
end
class NameAttr < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(name)
super('nameAttr')
self.text = name
end
end
class Picture < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(clause = "")
super('picture')
self.text = clause
end
end
class Presence < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
PRESERVE = "preserve"
DISSOLVE = "dissolve"
DISSOLVESTRUCTURE = "dissolveStructure"
IGNORE = "ignore"
REMOVE = "remove"
def initialize(action = PRESERVE)
super('presence')
self.text = action
end
end
class Rename < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(nodename = "")
super('rename')
self.text = nodename
end
end
class Whitespace < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
PRESERVE = "preserve"
LTRIM = "ltrim"
NORMALIZE = "normalize"
RTRIM = "rtrim"
TRIM = "trim"
def initialize(action = PRESERVE)
super('whitespace')
self.text = action
end
end
class Transform < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_attribute 'ref'
xfa_node 'groupParent', Config::GroupParent, 0..1
xfa_node 'ifEmpty', Config::IfEmpty, 0..1
xfa_node 'nameAttr', Config::NameAttr, 0..1
xfa_node 'picture', Config::Picture, 0..1
xfa_node 'presence', Config::Presence, 0..1
xfa_node 'rename', Config::Rename, 0..1
xfa_node 'whitespace', Config::Whitespace, 0..1
end
class Data < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'adjustData', Config::AdjustData, 0..1
xfa_node 'attributes', Config::Attributes, 0..1
xfa_node 'incrementalLoad', Config::IncrementalLoad, 0..1
xfa_node 'outputXSL', Config::OutputXSL, 0..1
xfa_node 'range', Config::Range, 0..1
xfa_node 'record', Config::Record, 0..1
xfa_node 'startNode', Config::StartNode, 0..1
xfa_node 'uri', Config::URI, 0..1
xfa_node 'window', Config::Window, 0..1
xfa_node 'xsl', Config::XSL, 0..1
xfa_node 'excludeNS', Config::ExcludeNS
xfa_node 'transform', Config::Transform
def initialize
super('data')
end
end
class Severity < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
IGNORE = "ignore"
ERROR = "error"
INFORMATION = "information"
TRACE = "trace"
WARNING = "warning"
def initialize(level = IGNORE)
super('severity')
self.text = level
end
end
class MsgId < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(uid = "1")
super('msgId')
self.text = uid
end
end
class Message < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'msgId', Config::MsgId, 0..1
xfa_node 'severity', Config::Severity, 0..1
def initialize
super('message')
end
end
class Messaging < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'message', Config::Message
def initialize
super('messaging')
end
end
class SuppressBanner < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
ALLOWED = "0"
DENIED = "1"
def initialize(display = ALLOWED)
super('suppressBanner')
self.text = display
end
end
class Base < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(uri = "")
super('base')
self.text = uri
end
end
class Relevant < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(token = "")
super('relevant')
self.text = token
end
end
class StartPage < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
def initialize(pagenum = "0")
super('startPage')
self.text = pagenum
end
end
class Template < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'base', Config::Base, 0..1
xfa_node 'relevant', Config::Relevant, 0..1
xfa_node 'startPage', Config::StartPage, 0..1
xfa_node 'uri', Config::URI, 0..1
xfa_node 'xsl', Config::XSL, 0..1
def initialize
super('template')
end
end
class ValidationMessaging < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
ALL_INDIVIDUALLY = "allMessagesIndividually"
ALL_TOGETHER = "allMessagesTogether"
FIRST_ONLY = "firstMessageOnly"
NONE = "noMessages"
def initialize(validate = ALL_INDIVIDUALLY)
super('validationMessaging')
self.text = validate
end
end
class VersionControl < XFA::Element
xfa_attribute 'lock'
xfa_attribute 'outputBelow'
xfa_attribute 'sourceAbove'
xfa_attribute 'sourceBelow'
def initialize
super('versionControl')
end
end
class Mode < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
APPEND = "append"
OVERWRITE = "overwrite"
def initialize(mode = APPEND)
super('mode')
self.text = mode
end
end
class Threshold < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
TRACE = "trace"
ERROR = "error"
INFORMATION = "information"
WARN = "warn"
def initialize(threshold = TRACE)
super('threshold')
self.text = threshold
end
end
class To < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
NULL = "null"
MEMORY = "memory"
STD_ERR = "stderr"
STD_OUT = "stdout"
SYSTEM = "system"
URI = "uri"
def initialize(dest = NULL)
super('to')
self.text = dest
end
end
class Log < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'mode', Config::Mode, 0..1
xfa_node 'threshold', Config::Threshold, 0..1
xfa_node 'to', Config::To, 0..1
xfa_node 'uri', Config::URI, 0..1
def initialize
super('log')
end
end
class Common < XFA::Element
xfa_attribute 'desc'
xfa_attribute 'lock'
xfa_node 'data', Config::Data, 0..1
xfa_node 'locale', Config::Locale, 0..1
xfa_node 'localeSet', Config::LocaleSet, 0..1
xfa_node 'messaging', Config::Messaging, 0..1
xfa_node 'suppressBanner', Config::SuppressBanner, 0..1
xfa_node 'template', Config::Template, 0..1
xfa_node 'validationMessaging', Config::ValidationMessaging, 0..1
xfa_node 'versionControl', Config::VersionControl, 0..1
xfa_node 'log', Config::Log
def initialize
super("common")
end
end
end
#
# The _connectionSet_ packet describes the connections used to initiate or conduct web services.
#
class ConnectionSet < XFA::Element
mime_type 'text/xml'
def initialize
super("connectionSet")
add_attribute 'xmlns', 'http://www.xfa.org/schema/xfa-connection-set/2.8/'
end
class EffectiveInputPolicy < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('effectiveInputPolicy')
end
end
class EffectiveOutputPolicy < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('effectiveOutputPolicy')
end
end
class Operation < XFA::Element
xfa_attribute 'id'
xfa_attribute 'input'
xfa_attribute 'name'
xfa_attribute 'output'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(name = "")
super('operation')
self.text = name
end
end
class SOAPAction < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(uri = "")
super('soapAction')
self.text = uri
end
end
class SOAPAddress < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(addr = "")
super('soapAddress')
self.text = addr
end
end
class WSDLAddress < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(addr = "")
super('wsdlAddress')
self.text = addr
end
end
class WSDLConnection < XFA::Element
xfa_attribute 'dataDescription'
xfa_attribute 'name'
xfa_node 'effectiveInputPolicy', ConnectionSet::EffectiveInputPolicy, 0..1
xfa_node 'effectiveOutputPolicy', ConnectionSet::EffectiveOutputPolicy, 0..1
xfa_node 'operation', ConnectionSet::Operation, 0..1
xfa_node 'soapAction', ConnectionSet::SOAPAction, 0..1
xfa_node 'soapAddress', ConnectionSet::SOAPAddress, 0..1
xfa_node 'wsdlAddress', ConnectionSet::WSDLAddress, 0..1
def initialize
super('wsdlConnection')
end
end
class URI < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(uri = "")
super('uri')
self.text = uri
end
end
class RootElement < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(root = '')
super('rootElement')
self.text = root
end
end
class XSDConnection < XFA::Element
xfa_attribute 'dataDescription'
xfa_attribute 'name'
xfa_node 'rootElement', ConnectionSet::RootElement, 0..1
xfa_node 'uri', ConnectionSet::URI, 0..1
def initialize
super('xsdConnection')
end
end
class XMLConnection < XFA::Element
xfa_attribute 'dataDescription'
xfa_attribute 'name'
xfa_node 'uri', ConnectionSet::URI, 0..1
def initialize
super('xmlConnection')
end
end
xfa_node 'wsdlConnection', ConnectionSet::WSDLConnection
xfa_node 'xmlConnection', ConnectionSet::XMLConnection
xfa_node 'xsdConnection', ConnectionSet::XSDConnection
end
#
# The _datasets_ element enclosed XML data content that may have originated from an XFA form and/or
# may be intended to be consumed by an XFA form.
#
class Datasets < XFA::Element
mime_type 'text/xml'
class Data < XFA::Element
def initialize
super('xfa:data')
end
end
def initialize
super("xfa:datasets")
add_attribute 'xmlns:xfa', 'http://www.xfa.org/schema/xfa-data/1.0/'
end
end
#
# The _localeSet_ packet encloses information about locales.
#
class LocaleSet < XFA::Element
mime_type 'text/xml'
def initialize
super("localeSet")
add_attribute 'xmlns', 'http://www.xfa.org/schema/xfa-locale-set/2.7/'
end
end
#
# An XDF _pdf_ element encloses a PDF packet.
#
class PDF < XFA::Element
mime_type 'application/pdf'
xfa_attribute :href
def initialize
super("pdf")
add_attribute 'xmlns', 'http://ns.adobe.com/xdp/pdf/'
end
def enclose_pdf(pdfdata)
require 'base64'
b64data = Base64.encode64(pdfdata).chomp!
doc = elements['document'] || add_element('document')
chunk = doc.elements['chunk'] || doc.add_element('chunk')
chunk.text = b64data
self
end
def has_enclosed_pdf?
chunk = elements['document/chunk']
not chunk.nil? and not chunk.text.nil?
end
def remove_enclosed_pdf
elements.delete('document') if has_enclosed_pdf?
end
def enclosed_pdf
return nil unless has_enclosed_pdf?
require 'base64'
Base64.decode64(elements['document/chunk'].text)
end
end
#
# The _signature_ packet encloses a detached digital signature.
#
class Signature < XFA::Element
mime_type ''
def initialize
super("signature")
add_attribute 'xmlns', 'http://www.w3.org/2000/09/xmldsig#'
end
end
#
# The _sourceSet_ packet contains ADO database queries, used to describe data
# binding to ADO data sources.
#
class SourceSet < XFA::Element
mime_type 'text/xml'
def initialize
super("sourceSet")
add_attribute 'xmlns', 'http://www.xfa.org/schema/xfa-source-set/2.8/'
end
end
#
# The _stylesheet_ packet encloses a single XSLT stylesheet.
#
class StyleSheet < XFA::Element
mime_type 'text/css'
def initialize(id)
super("xsl:stylesheet")
add_attribute 'version', '1.0'
add_attribute 'xmlns:xsl', 'http://www.w3.org/1999/XSL/Transform'
add_attribute 'id', id.to_s
end
end
#
# This packet contains the form template.
#
class Template < XFA::Element
mime_type 'application/x-xfa-template'
class Boolean < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
NO = 0
YES = 1
def initialize(bool = nil)
super('boolean')
self.text = bool
end
end
class Date < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(date = nil)
super('date')
self.text = date
end
end
class DateTime < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(datetime = nil)
super('dateTime')
self.text = datetime
end
end
class Decimal < XFA::Element
xfa_attribute 'fracDigits'
xfa_attribute 'id'
xfa_attribute 'leadDigits'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(number = nil)
super('decimal')
self.text = number
end
end
class ExData < XFA::Element
xfa_attribute 'contentType'
xfa_attribute 'href'
xfa_attribute 'id'
xfa_attribute 'maxLength'
xfa_attribute 'name'
xfa_attribute 'rid'
xfa_attribute 'transferEncoding'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(data = nil)
super('exData')
self.text = data
end
end
class Float < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(float = nil)
super('float')
self.text = float
end
end
class Image < XFA::Element
xfa_attribute 'aspect'
xfa_attribute 'contentType'
xfa_attribute 'href'
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'transferEncoding'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(data = nil)
super('image')
self.text = data
end
end
class Integer < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(int = nil)
super('integer')
self.text = int
end
end
class Text < XFA::Element
xfa_attribute 'id'
xfa_attribute 'maxChars'
xfa_attribute 'name'
xfa_attribute 'rid'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(text = "")
super('text')
self.text = text
end
end
class Time < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(time = nil)
super('time')
self.text = time
end
end
class Extras < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'boolean', Template::Boolean
xfa_node 'date', Template::Date
xfa_node 'dateTime', Template::DateTime
xfa_node 'decimal', Template::Decimal
xfa_node 'exData', Template::ExData
xfa_node 'extras', Template::Extras
xfa_node 'float', Template::Float
xfa_node 'image', Template::Image
xfa_node 'integer', Template::Integer
xfa_node 'text', Template::Text
xfa_node 'time', Template::Time
def initialize
super('extras')
end
end
class Speak < XFA::Element
xfa_attribute 'disable'
xfa_attribute 'id'
xfa_attribute 'priority'
xfa_attribute 'rid'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(text = "")
super('speak')
self.text = text
end
end
class ToolTip < XFA::Element
xfa_attribute 'id'
xfa_attribute 'rid'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(text = "")
super('toolTip')
end
end
class Assist < XFA::Element
xfa_attribute 'id'
xfa_attribute 'role'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'speak', Template::Speak, 0..1
xfa_node 'toolTip', Template::ToolTip, 0..1
def initialize
super('assist')
end
end
class Picture < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(data = nil)
super('picture')
self.text = data
end
end
class Bind < XFA::Element
xfa_attribute 'match'
xfa_attribute 'ref'
xfa_node 'picture', Template::Picture, 0..1
def initialize
super('bind')
end
end
class Bookend < XFA::Element
xfa_attribute 'id'
xfa_attribute 'leader'
xfa_attribute 'trailer'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('bookend')
end
end
class Color < XFA::Element
xfa_attribute 'cSpace'
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'value'
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('color')
self.cSpace = "SRGB"
end
end
class Corner < XFA::Element
xfa_attribute 'id'
xfa_attribute 'inverted'
xfa_attribute 'join'
xfa_attribute 'presence'
xfa_attribute 'radius'
xfa_attribute 'stroke'
xfa_attribute 'thickness'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'color', Template::Color, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('corner')
end
end
class Edge < XFA::Element
xfa_attribute 'cap'
xfa_attribute 'id'
xfa_attribute 'presence'
xfa_attribute 'stroke'
xfa_attribute 'thickness'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'color', Template::Color, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('edge')
end
end
class Linear < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'color', Template::Color, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('linear')
end
end
class Pattern < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'color', Template::Color, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('pattern')
end
end
class Radial < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'color', Template::Color, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('radial')
end
end
class Solid < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('solid')
end
end
class Stipple < XFA::Element
xfa_attribute 'id'
xfa_attribute 'rate'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'color', Template::Color, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('stipple')
end
end
class Fill < XFA::Element
xfa_attribute 'id'
xfa_attribute 'presence'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'color', Template::Color, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'linear', Template::Linear, 0..1
xfa_node 'pattern', Template::Pattern, 0..1
xfa_node 'radial', Template::Radial, 0..1
xfa_node 'solid', Template::Solid, 0..1
xfa_node 'stipple', Template::Stipple, 0..1
def initialize
super('fill')
end
end
class Margin < XFA::Element
xfa_attribute 'bottomInset'
xfa_attribute 'id'
xfa_attribute 'leftInset'
xfa_attribute 'rightInset'
xfa_attribute 'topInset'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('margin')
end
end
class Border < XFA::Element
xfa_attribute 'break'
xfa_attribute 'hand'
xfa_attribute 'id'
xfa_attribute 'presence'
xfa_attribute 'relevant'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'corner', Template::Corner, 0..4
xfa_node 'edge', Template::Edge, 0..4
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'fill', Template::Fill, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('border')
end
end
class Break < XFA::Element
xfa_attribute 'after'
xfa_attribute 'afterTarget'
xfa_attribute 'before'
xfa_attribute 'beforeTarget'
xfa_attribute 'bookendLeader'
xfa_attribute 'bookendTrailer'
xfa_attribute 'id'
xfa_attribute 'overflowLeader'
xfa_attribute 'overflowTarget'
xfa_attribute 'overflowTrailer'
xfa_attribute 'startNew'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('break')
end
end
class Message < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'text', Template::Text
def initialize
super('message')
end
end
class Script < XFA::Element
xfa_attribute 'binding'
xfa_attribute 'contentType'
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'runAt'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(script = "")
super('script')
self.text = script
end
end
class JavaScript < Script
def initialize(script = "")
super(script)
self.contentType = 'application/x-javascript'
end
end
class FormCalcScript < Script
def initialize(script = "")
super(script)
self.contentType = 'application/x-formcalc'
end
end
class Calculate < XFA::Element
xfa_attribute 'id'
xfa_attribute 'override'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'message', Template::Message, 0..1
xfa_node 'script', Template::Script, 0..1
def initialize
super('calculate')
end
end
class Desc < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'boolean', Template::Boolean
xfa_node 'date', Template::Date
xfa_node 'dateTime', Template::DateTime
xfa_node 'decimal', Template::Decimal
xfa_node 'exData', Template::ExData
xfa_node 'float', Template::Float
xfa_node 'image', Template::Image
xfa_node 'integer', Template::Integer
xfa_node 'text', Template::Text
xfa_node 'time', Template::Time
def initialize
super('desc')
end
end
class Keep < XFA::Element
xfa_attribute 'id'
xfa_attribute 'intact'
xfa_attribute 'next'
xfa_attribute 'previous'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
NONE = "none"
CONTENTAREA = "contentArea"
PAGEAREA = "pageArea"
def initialize
super('keep')
end
end
class Occur < XFA::Element
xfa_attribute 'id'
xfa_attribute 'initial'
xfa_attribute 'max'
xfa_attribute 'min'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('occur')
end
end
class Overflow < XFA::Element
xfa_attribute 'id'
xfa_attribute 'leader'
xfa_attribute 'target'
xfa_attribute 'trailer'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('overflow')
end
end
class Medium < XFA::Element
xfa_attribute 'id'
xfa_attribute 'imagingBBox'
xfa_attribute 'long'
xfa_attribute 'orientation'
xfa_attribute 'short'
xfa_attribute 'stock'
xfa_attribute 'trayIn'
xfa_attribute 'trayOut'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('medium')
end
end
class Font < XFA::Element
xfa_attribute 'baselineShift'
xfa_attribute 'fontHorizontalScale'
xfa_attribute 'fontVerticalScale'
xfa_attribute 'id'
xfa_attribute 'kerningMode'
xfa_attribute 'letterSpacing'
xfa_attribute 'lineThrough'
xfa_attribute 'lineThroughPeriod'
xfa_attribute 'overline'
xfa_attribute 'overlinePeriod'
xfa_attribute 'posture'
xfa_attribute 'size'
xfa_attribute 'typeface'
xfa_attribute 'underline'
xfa_attribute 'underlinePeriod'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'weight'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'fill', Template::Fill, 0..1
def initialize
super('font')
end
end
class Hyphenation < XFA::Element
xfa_attribute 'excludeAllCaps'
xfa_attribute 'excludeInitialCap'
xfa_attribute 'hyphenate'
xfa_attribute 'id'
xfa_attribute 'pushCharacterCount'
xfa_attribute 'remainCharacterCount'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'wordCharacterCount'
def initialize
super('hyphenation')
end
end
class Para < XFA::Element
xfa_attribute 'hAlign'
xfa_attribute 'id'
xfa_attribute 'lineHeight'
xfa_attribute 'marginLeft'
xfa_attribute 'marginRight'
xfa_attribute 'orphans'
xfa_attribute 'preserve'
xfa_attribute 'radixOffset'
xfa_attribute 'spaceAbove'
xfa_attribute 'spaceBelow'
xfa_attribute 'tabDefault'
xfa_attribute 'tabStops'
xfa_attribute 'textIndent'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'vAlign'
xfa_attribute 'widows'
xfa_node 'hyphenation', Template::Hyphenation, 0..1
def initialize
super('para')
end
end
class Arc < XFA::Element
xfa_attribute 'circular'
xfa_attribute 'hand'
xfa_attribute 'id'
xfa_attribute 'startAngle'
xfa_attribute 'sweepAngle'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'edge', Template::Edge, 0..1
xfa_node 'fill', Template::Fill, 0..1
def initialize
super('arc')
end
end
class Line < XFA::Element
xfa_attribute 'hand'
xfa_attribute 'id'
xfa_attribute 'slope'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'edge', Template::Edge, 0..1
def initialize
super('line')
end
end
class Corner < XFA::Element
xfa_attribute 'id'
xfa_attribute 'inverted'
xfa_attribute 'join'
xfa_attribute 'presence'
xfa_attribute 'radius'
xfa_attribute 'stroke'
xfa_attribute 'thickness'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'color', Template::Color, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('corner')
end
end
class Rectangle < XFA::Element
xfa_attribute 'hand'
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'corner', Template::Corner, 0..4
xfa_node 'edge', Template::Edge, 0..4
xfa_node 'fill', Template::Fill, 0..4
def initialize
super('rectangle')
end
end
class Value < XFA::Element
xfa_attribute 'id'
xfa_attribute 'override'
xfa_attribute 'relevant'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'arc', Template::Arc, 0..1
xfa_node 'boolean', Template::Boolean, 0..1
xfa_node 'date', Template::Date, 0..1
xfa_node 'dateTime', Template::DateTime, 0..1
xfa_node 'decimal', Template::Decimal, 0..1
xfa_node 'exData', Template::ExData, 0..1
xfa_node 'float', Template::Float, 0..1
xfa_node 'image', Template::Image, 0..1
xfa_node 'integer', Template::Integer, 0..1
xfa_node 'line', Template::Line, 0..1
xfa_node 'rectangle', Template::Rectangle, 0..1
xfa_node 'text', Template::Text, 0..1
xfa_node 'time', Template::Time, 0..1
def initialize
super('value')
end
end
class Caption < XFA::Element
xfa_attribute 'id'
xfa_attribute 'placement'
xfa_attribute 'presence'
xfa_attribute 'reserve'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'font', Template::Font, 0..1
xfa_node 'margin', Template::Margin, 0..1
xfa_node 'para', Template::Para, 0..1
xfa_node 'value', Template::Value, 0..1
def initialize
super('caption')
end
end
class Traverse < XFA::Element
xfa_attribute 'id'
xfa_attribute 'operation'
xfa_attribute 'ref'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'script', Template::Script, 0..1
def initialize
super('traverse')
end
end
class Traversal < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'traverse', Template::Traverse
def initialize
super('traversal')
end
end
class Certificate < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(b64data = nil)
super('certificate')
self.text = b64data
end
end
class Encrypt < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'certificate', Template::Certificate, 0..1
def initialize
super('encrypt')
end
end
class Barcode < XFA::Element
xfa_attribute 'charEncoding'
xfa_attribute 'checksum'
xfa_attribute 'dataColumnCount'
xfa_attribute 'dataLength'
xfa_attribute 'dataPrep'
xfa_attribute 'dataRowCount'
xfa_attribute 'endChar'
xfa_attribute 'errorConnectionLevel'
xfa_attribute 'id'
xfa_attribute 'moduleHeight'
xfa_attribute 'moduleWidth'
xfa_attribute 'printCheckDigit'
xfa_attribute 'rowColumnRatio'
xfa_attribute 'startChar'
xfa_attribute 'textLocation'
xfa_attribute 'truncate'
xfa_attribute 'type'
xfa_attribute 'upsMode'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'wideNarrowRatio'
xfa_node 'encrypt', Template::Encrypt, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('barcode')
end
end
class Button < XFA::Element
xfa_attribute 'highlight'
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('button')
end
end
class CheckButton < XFA::Element
xfa_attribute 'id'
xfa_attribute 'mark'
xfa_attribute 'shape'
xfa_attribute 'size'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'border', Template::Border, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('checkButton')
end
end
class ChoiceList < XFA::Element
xfa_attribute 'commitOn'
xfa_attribute 'id'
xfa_attribute 'open'
xfa_attribute 'textEntry'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'border', Template::Border, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('choiceList')
end
end
class Comb < XFA::Element
xfa_attribute 'id'
xfa_attribute 'numberOfCells'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('comb')
end
end
class DateTimeEdit < XFA::Element
xfa_attribute 'hScrollPolicy'
xfa_attribute 'id'
xfa_attribute 'picker'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'border', Template::Border, 0..1
xfa_node 'comb', Template::Comb, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('dateTimeEdit')
end
end
class DefaultUI < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('defaultUi')
end
end
class ImageEdit < XFA::Element
xfa_attribute 'data'
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'border', Template::Border, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('imageEdit')
end
end
class NumericEdit < XFA::Element
xfa_attribute 'hScrollPolicy'
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'border', Template::Border, 0..1
xfa_node 'comb', Template::Comb, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('numericEdit')
end
end
class PasswordEdit < XFA::Element
xfa_attribute 'hScrollPolicy'
xfa_attribute 'id'
xfa_attribute 'passwordChar'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'border', Template::Border, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('passwordEdit')
end
end
class AppearanceFilter < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(name = "")
super('appearanceFilter')
self.text = name
end
end
class Issuers < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'certificate', Template::Certificate
def initialize
super('issuers')
end
end
class KeyUsage < XFA::Element
xfa_attribute 'crlSign'
xfa_attribute 'dataEncipherment'
xfa_attribute 'decipherOnly'
xfa_attribute 'digitalSignature'
xfa_attribute 'encipherOnly'
xfa_attribute 'id'
xfa_attribute 'keyAgreement'
xfa_attribute 'keyCertSign'
xfa_attribute 'keyEncipherment'
xfa_attribute 'nonRepudiation'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('keyUsage')
end
end
class OID < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(oid = "")
super('oid')
self.text = oid
end
end
class OIDs < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'oid', Template::OID
def initialize
super('oids')
end
end
class Signing < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'certificate', Template::Certificate
def initialize
super('signing')
end
end
class SubjectDN < XFA::Element
xfa_attribute 'delimiter'
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(data = "")
super('subjectDN')
self.text = data
end
end
class SubjectDNs < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'subjectDN', Template::SubjectDN, 0..1
def initialize
super('subjectDNs')
end
end
class Certificates < XFA::Element
xfa_attribute 'credentialServerPolicy'
xfa_attribute 'id'
xfa_attribute 'url'
xfa_attribute 'urlPolicy'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'issuers', Template::Issuers, 0..1
xfa_node 'keyUsage', Template::KeyUsage, 0..1
xfa_node 'oids', Template::OIDs, 0..1
xfa_node 'signing', Template::Signing, 0..1
xfa_node 'subjectDNs', Template::SubjectDNs, 0..1
def initialize
super('certificates')
end
end
class DigestMethod < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(method = "")
super('digestMethod')
self.text = method
end
end
class DigestMethods < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'digestMethod', Template::DigestMethod
def initialize
super('digestMethods')
end
end
class Encoding < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(encoding = "")
super('encoding')
self.text = encoding
end
end
class Encodings < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'encoding', Template::Encoding
def initialize
super('encodings')
end
end
class Handler < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(handler = "")
super('handler')
self.text = handler
end
end
class LockDocument < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(lock = "default")
super('lockDocument')
self.text = lock
end
end
class MDP < XFA::Element
xfa_attribute 'id'
xfa_attribute 'permissions'
xfa_attribute 'signatureType'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('mdp')
end
end
class Reason < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(reason = "")
super('reason')
self.text = reason
end
end
class Reasons < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'reason', Template::Reason
def initialize
super('reasons')
end
end
class TimeStamp < XFA::Element
xfa_attribute 'id'
xfa_attribute 'server'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('timeStamp')
end
end
class Filter < XFA::Element
xfa_attribute 'addRevocationInfo'
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'version'
xfa_node 'appearanceFilter', Template::AppearanceFilter, 0..1
xfa_node 'certificates', Template::Certificates, 0..1
xfa_node 'digestMethods', Template::DigestMethods, 0..1
xfa_node 'encodings', Template::Encodings, 0..1
xfa_node 'handler', Template::Handler, 0..1
xfa_node 'lockDocument', Template::LockDocument, 0..1
xfa_node 'mdp', Template::MDP, 0..1
xfa_node 'reasons', Template::Reasons, 0..1
xfa_node 'timeStamp', Template::TimeStamp, 0..1
def initialize
super('filter')
end
end
class Ref < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize(somexpr = nil)
super('ref')
self.text = somexpr
end
end
class Manifest < XFA::Element
xfa_attribute 'action'
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'ref', Template::Ref, 0..1
def initialize
super('manifest')
end
end
class Signature < XFA::Element
xfa_attribute 'id'
xfa_attribute 'type'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'border', Template::Border, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'filter', Template::Filter, 0..1
xfa_node 'manifest', Template::Manifest, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('signature')
end
end
class TextEdit < XFA::Element
xfa_attribute 'allowRichText'
xfa_attribute 'hScrollPolicy'
xfa_attribute 'id'
xfa_attribute 'multiLine'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'vScrollPolicy'
xfa_node 'border', Template::Border, 0..1
xfa_node 'comb', Template::Comb, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'margin', Template::Margin, 0..1
def initialize
super('textEdit')
end
end
class UI < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'picture', Template::Picture, 0..1
xfa_node 'barcode', Template::Barcode, 0..1
xfa_node 'button', Template::Button, 0..1
xfa_node 'checkButton', Template::CheckButton, 0..1
xfa_node 'choiceList', Template::ChoiceList, 0..1
xfa_node 'dateTimeEdit', Template::DateTimeEdit, 0..1
xfa_node 'defaultUi', Template::DefaultUI, 0..1
xfa_node 'imageEdit', Template::ImageEdit, 0..1
xfa_node 'numericEdit', Template::NumericEdit, 0..1
xfa_node 'passwordEdit', Template::PasswordEdit, 0..1
xfa_node 'signature', Template::Signature, 0..1
xfa_node 'textEdit', Template::TextEdit, 0..1
def initialize
super('ui')
end
end
class SetProperty < XFA::Element
xfa_attribute 'connection'
xfa_attribute 'ref'
xfa_attribute 'target'
def initialize
super('setProperty')
end
end
class Draw < XFA::Element
xfa_attribute 'anchorType'
xfa_attribute 'colSpan'
xfa_attribute 'h'
xfa_attribute 'id'
xfa_attribute 'locale'
xfa_attribute 'maxH'
xfa_attribute 'maxW'
xfa_attribute 'minH'
xfa_attribute 'minW'
xfa_attribute 'name'
xfa_attribute 'presence'
xfa_attribute 'relevant'
xfa_attribute 'rotate'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'w'
xfa_attribute 'x'
xfa_attribute 'y'
xfa_node 'assist', Template::Assist, 0..1
xfa_node 'border', Template::Border, 0..1
xfa_node 'caption', Template::Caption, 0..1
xfa_node 'desc', Template::Desc, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'font', Template::Font, 0..1
xfa_node 'keep', Template::Keep, 0..1
xfa_node 'margin', Template::Margin, 0..1
xfa_node 'para', Template::Para, 0..1
xfa_node 'traversal', Template::Traversal, 0..1
xfa_node 'ui', Template::UI, 0..1
xfa_node 'value', Template::Value, 0..1
xfa_node 'setProperty', Template::SetProperty
def initialize
super('draw')
end
end
class Validate < XFA::Element
xfa_attribute 'formatTest'
xfa_attribute 'id'
xfa_attribute 'nullTest'
xfa_attribute 'scriptTest'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'message', Template::Message, 0..1
xfa_node 'picture', Template::Picture, 0..1
xfa_node 'script', Template::Script, 0..1
def initialize
super('validate')
end
end
class Connect < XFA::Element
xfa_attribute 'connection'
xfa_attribute 'id'
xfa_attribute 'ref'
xfa_attribute 'usage'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'picture', Template::Picture, 0..1
def initialize
super('connect')
end
end
class Execute < XFA::Element
xfa_attribute 'connection'
xfa_attribute 'executeType'
xfa_attribute 'id'
xfa_attribute 'runAt'
xfa_attribute 'use'
xfa_attribute 'usehref'
def initialize
super('execute')
end
end
class SignData < XFA::Element
xfa_attribute 'id'
xfa_attribute 'operation'
xfa_attribute 'ref'
xfa_attribute 'target'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'filter', Template::Filter, 0..1
xfa_node 'manifest', Template::Manifest, 0..1
def initialize
super('signData')
end
end
class Submit < XFA::Element
xfa_attribute 'embedPDF'
xfa_attribute 'format'
xfa_attribute 'id'
xfa_attribute 'target'
xfa_attribute 'textEncoding'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'xdpContent'
xfa_node 'encrypt', Template::Encrypt, 0..1
xfa_node 'signData', Template::SignData
def initialize
super('submit')
end
end
class Event < XFA::Element
xfa_attribute 'activity'
xfa_attribute 'id'
xfa_attribute 'listen'
xfa_attribute 'name'
xfa_attribute 'ref'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'execute', Template::Execute, 0..1
xfa_node 'script', Template::Script, 0..1
xfa_node 'signData', Template::SignData, 0..1
xfa_node 'submit', Template::Submit, 0..1
def initialize
super('event')
end
end
class Format < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'picture', Template::Picture, 0..1
def initialize
super('format')
end
end
class Items < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'presence'
xfa_attribute 'ref'
xfa_attribute 'save'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'boolean', Template::Boolean
xfa_node 'date', Template::Date
xfa_node 'dateTime', Template::DateTime
xfa_node 'decimal', Template::Decimal
xfa_node 'exData', Template::ExData
xfa_node 'float', Template::Float
xfa_node 'image', Template::Image
xfa_node 'integer', Template::Integer
xfa_node 'text', Template::Text
xfa_node 'time', Template::Time
def initialize
super('items')
end
end
class BindItems < XFA::Element
xfa_attribute 'connection'
xfa_attribute 'labelRef'
xfa_attribute 'ref'
xfa_attribute 'valueRef'
def initialize
super('bindItems')
end
end
class Field < XFA::Element
xfa_attribute 'access'
xfa_attribute 'accessKey'
xfa_attribute 'anchorType'
xfa_attribute 'colSpan'
xfa_attribute 'h'
xfa_attribute 'id'
xfa_attribute 'locale'
xfa_attribute 'maxH'
xfa_attribute 'maxW'
xfa_attribute 'minH'
xfa_attribute 'minW'
xfa_attribute 'name'
xfa_attribute 'presence'
xfa_attribute 'relevant'
xfa_attribute 'rotate'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'w'
xfa_attribute 'x'
xfa_attribute 'y'
xfa_node 'assist', Template::Assist, 0..1
xfa_node 'bind', Template::Bind, 0..1
xfa_node 'border', Template::Border, 0..1
xfa_node 'calculate', Template::Calculate, 0..1
xfa_node 'caption', Template::Caption, 0..1
xfa_node 'desc', Template::Desc, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'font', Template::Font, 0..1
xfa_node 'format', Template::Format, 0..1
xfa_node 'items', Template::Items, 0..2
xfa_node 'keep', Template::Keep, 0..1
xfa_node 'margin', Template::Margin, 0..1
xfa_node 'para', Template::Para, 0..1
xfa_node 'traversal', Template::Traversal, 0..1
xfa_node 'ui', Template::UI, 0..1
xfa_node 'validate', Template::Validate, 0..1
xfa_node 'value', Template::Value, 0..1
xfa_node 'bindItems', Template::BindItems
xfa_node 'connect', Template::Connect
xfa_node 'event', Template::Event
xfa_node 'setProperty', Template::SetProperty
def initialize
super('field')
end
end
class ExclGroup < XFA::Element
xfa_attribute 'access'
xfa_attribute 'accessKey'
xfa_attribute 'anchorType'
xfa_attribute 'colSpan'
xfa_attribute 'h'
xfa_attribute 'id'
xfa_attribute 'layout'
xfa_attribute 'maxH'
xfa_attribute 'maxW'
xfa_attribute 'minH'
xfa_attribute 'minW'
xfa_attribute 'name'
xfa_attribute 'presence'
xfa_attribute 'relevant'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'w'
xfa_attribute 'x'
xfa_attribute 'y'
xfa_node 'assist', Template::Assist, 0..1
xfa_node 'bind', Template::Bind, 0..1
xfa_node 'border', Template::Border, 0..1
xfa_node 'calculate', Template::Calculate, 0..1
xfa_node 'caption', Template::Caption, 0..1
xfa_node 'desc', Template::Desc, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'margin', Template::Margin, 0..1
xfa_node 'para', Template::Para, 0..1
xfa_node 'traversal', Template::Traversal, 0..1
xfa_node 'validate', Template::Validate, 0..1
xfa_node 'connect', Template::Connect
xfa_node 'event', Template::Event
xfa_node 'field', Template::Field
xfa_node 'setProperty', Template::SetProperty
def initialize
super('exclGroup')
end
end
class BreakAfter < XFA::Element
xfa_attribute 'id'
xfa_attribute 'leader'
xfa_attribute 'startNew'
xfa_attribute 'target'
xfa_attribute 'targetType'
xfa_attribute 'trailer'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'script', Template::Script, 0..1
def initialize
super('breakAfter')
end
end
class BreakBefore < XFA::Element
xfa_attribute 'id'
xfa_attribute 'leader'
xfa_attribute 'startNew'
xfa_attribute 'target'
xfa_attribute 'targetType'
xfa_attribute 'trailer'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'script', Template::Script, 0..1
def initialize
super('breakBefore')
end
end
class Subform < XFA::Element ; end
class SubformSet < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'relation'
xfa_attribute 'relevant'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'bookend', Template::Bookend, 0..1
xfa_node 'break', Template::Break, 0..1
xfa_node 'desc', Template::Desc, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'occur', Template::Occur, 0..1
xfa_node 'overflow', Template::Overflow, 0..1
xfa_node 'breakAfter', Template::BreakAfter
xfa_node 'breakBefore', Template::BreakBefore
xfa_node 'subform', Template::Subform
xfa_node 'subformSet', Template::SubformSet
def initialize
super('subformSet')
end
end
class Area < XFA::Element
xfa_attribute 'colSpan'
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'relevant'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'x'
xfa_attribute 'y'
xfa_node 'desc', Template::Desc, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'area', Template::Area
xfa_node 'draw', Template::Draw
xfa_node 'exclGroup', Template::ExclGroup
xfa_node 'field', Template::Field
xfa_node 'subform', Template::Subform
xfa_node 'subformSet', Template::SubformSet
def initialize
super('area')
end
end
class ContentArea < XFA::Element
xfa_attribute 'h'
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'relevant'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'w'
xfa_attribute 'x'
xfa_attribute 'y'
xfa_node 'desc', Template::Desc, 0..1
xfa_node 'extras', Template::Extras, 0..1
def initialize
super('contentArea')
end
end
class PageArea < XFA::Element
xfa_attribute 'blankOrNotBlank'
xfa_attribute 'id'
xfa_attribute 'initialNumber'
xfa_attribute 'name'
xfa_attribute 'numbered'
xfa_attribute 'oddOrEven'
xfa_attribute 'pagePosition'
xfa_attribute 'relevant'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'desc', Template::Desc, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'medium', Template::Medium, 0..1
xfa_node 'occur', Template::Occur, 0..1
xfa_node 'area', Template::Area
xfa_node 'contentArea', Template::ContentArea
xfa_node 'draw', Template::Draw
xfa_node 'exclGroup', Template::ExclGroup
xfa_node 'field', Template::Field
xfa_node 'subform', Template::Subform
def initialize
super('pageArea')
end
end
class PageSet < XFA::Element
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'relation'
xfa_attribute 'relevant'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'occur', Template::Occur, 0..1
xfa_node 'pageArea', Template::PageArea
xfa_node 'pageSet', Template::PageSet
ORDERED_OCCURENCE = "orderedOccurence"
DUPLEX_PAGINATED = "duplexPaginated"
SIMPLEX_PAGINATED = "simplexPaginated"
def initialize
super('pageSet')
end
end
class Variables < XFA::Element
xfa_attribute 'id'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'boolean', Template::Boolean
xfa_node 'date', Template::Date
xfa_node 'dateTime', Template::DateTime
xfa_node 'decimal', Template::Decimal
xfa_node 'exData', Template::ExData
xfa_node 'float', Template::Float
xfa_node 'image', Template::Image
xfa_node 'integer', Template::Integer
xfa_node 'manifest', Template::Manifest
xfa_node 'script', Template::Script
xfa_node 'text', Template::Text
xfa_node 'time', Template::Time
def initialize
super('variables')
end
end
class ExObject < XFA::Element
xfa_attribute 'archive'
xfa_attribute 'classId'
xfa_attribute 'codeBase'
xfa_attribute 'codeType'
xfa_attribute 'id'
xfa_attribute 'name'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'boolean', Template::Boolean
xfa_node 'date', Template::Date
xfa_node 'dateTime', Template::DateTime
xfa_node 'decimal', Template::Decimal
xfa_node 'exData', Template::ExData
xfa_node 'exObject', Template::ExObject
xfa_node 'float', Template::Float
xfa_node 'image', Template::Image
xfa_node 'integer', Template::Integer
xfa_node 'text', Template::Text
xfa_node 'time', Template::Time
def initialize
super('exObject')
end
end
class Proto < XFA::Element
xfa_node 'appearanceFilter', Template::AppearanceFilter
xfa_node 'arc', Template::Arc
xfa_node 'area', Template::Area
xfa_node 'assist', Template::Assist
xfa_node 'barcode', Template::Barcode
xfa_node 'bindItems', Template::BindItems
xfa_node 'bookend', Template::Bookend
xfa_node 'boolean', Template::Boolean
xfa_node 'border', Template::Border
xfa_node 'break', Template::Break
xfa_node 'breakAfter', Template::BreakAfter
xfa_node 'breakBefore', Template::BreakBefore
xfa_node 'button', Template::Button
xfa_node 'calculate', Template::Calculate
xfa_node 'caption', Template::Caption
xfa_node 'certificate', Template::Certificate
xfa_node 'certificates', Template::Certificates
xfa_node 'checkButton', Template::CheckButton
xfa_node 'choiceList', Template::ChoiceList
xfa_node 'color', Template::Color
xfa_node 'comb', Template::Comb
xfa_node 'connect', Template::Connect
xfa_node 'contentArea', Template::ContentArea
xfa_node 'corner', Template::Corner
xfa_node 'date', Template::Date
xfa_node 'dateTime', Template::DateTime
xfa_node 'dateTimeEdit', Template::DateTimeEdit
xfa_node 'decimal', Template::Decimal
xfa_node 'defaultUi', Template::DefaultUI
xfa_node 'desc', Template::Desc
xfa_node 'digestMethod', Template::DigestMethod
xfa_node 'digestMethods', Template::DigestMethods
xfa_node 'draw', Template::Draw
xfa_node 'edge', Template::Edge
xfa_node 'encoding', Template::Encoding
xfa_node 'encodings', Template::Encodings
xfa_node 'encrypt', Template::Encrypt
xfa_node 'event', Template::Event
xfa_node 'exData', Template::ExData
xfa_node 'exObject', Template::ExObject
xfa_node 'exclGroup', Template::ExclGroup
xfa_node 'execute', Template::Execute
xfa_node 'extras', Template::Extras
xfa_node 'field', Template::Field
xfa_node 'fill', Template::Fill
xfa_node 'filter', Template::Filter
xfa_node 'float', Template::Float
xfa_node 'font', Template::Font
xfa_node 'format', Template::Format
xfa_node 'handler', Template::Handler
xfa_node 'hyphenation', Template::Hyphenation
xfa_node 'image', Template::Image
xfa_node 'imageEdit', Template::ImageEdit
xfa_node 'integer', Template::Integer
xfa_node 'issuers', Template::Issuers
xfa_node 'items', Template::Items
xfa_node 'keep', Template::Keep
xfa_node 'keyUsage', Template::KeyUsage
xfa_node 'line', Template::Line
xfa_node 'linear', Template::Linear
xfa_node 'lockDocument', Template::LockDocument
xfa_node 'manifest', Template::Manifest
xfa_node 'margin', Template::Margin
xfa_node 'mdp', Template::MDP
xfa_node 'medium', Template::Medium
xfa_node 'message', Template::Message
xfa_node 'numericEdit', Template::NumericEdit
xfa_node 'occur', Template::Occur
xfa_node 'oid', Template::OID
xfa_node 'oids', Template::OIDs
xfa_node 'overflow', Template::Overflow
xfa_node 'pageArea', Template::PageArea
xfa_node 'pageSet', Template::PageSet
xfa_node 'para', Template::Para
xfa_node 'passwordEdit', Template::PasswordEdit
xfa_node 'pattern', Template::Pattern
xfa_node 'picture', Template::Picture
xfa_node 'radial', Template::Radial
xfa_node 'reason', Template::Reason
xfa_node 'reasons', Template::Reasons
xfa_node 'rectangle', Template::Rectangle
xfa_node 'ref', Template::Ref
xfa_node 'script', Template::Script
xfa_node 'setProperty', Template::SetProperty
xfa_node 'signData', Template::SignData
xfa_node 'signature', Template::Signature
xfa_node 'signing', Template::Signing
xfa_node 'solid', Template::Solid
xfa_node 'speak', Template::Speak
xfa_node 'stipple', Template::Stipple
xfa_node 'subform', Template::Subform
xfa_node 'subformSet', Template::SubformSet
xfa_node 'subjectDN', Template::SubjectDN
xfa_node 'subjectDNs', Template::SubjectDNs
xfa_node 'submit', Template::Submit
xfa_node 'text', Template::Text
xfa_node 'textEdit', Template::TextEdit
xfa_node 'time', Template::Time
xfa_node 'timeStamp', Template::TimeStamp
xfa_node 'toolTip', Template::ToolTip
xfa_node 'traversal', Template::Traversal
xfa_node 'traverse', Template::Traverse
xfa_node 'ui', Template::UI
xfa_node 'validate', Template::Validate
xfa_node 'value', Template::Value
xfa_node 'variables', Template::Variables
def initialize
super('proto')
end
end
class Subform < XFA::Element
xfa_attribute 'access'
xfa_attribute 'allowMacro'
xfa_attribute 'anchorType'
xfa_attribute 'colSpan'
xfa_attribute 'columnWidths'
xfa_attribute 'h'
xfa_attribute 'id'
xfa_attribute 'layout'
xfa_attribute 'locale'
xfa_attribute 'maxH'
xfa_attribute 'maxW'
xfa_attribute 'minH'
xfa_attribute 'minW'
xfa_attribute 'name'
xfa_attribute 'presence'
xfa_attribute 'relevant'
xfa_attribute 'restoreState'
xfa_attribute 'scope'
xfa_attribute 'use'
xfa_attribute 'usehref'
xfa_attribute 'w'
xfa_attribute 'x'
xfa_attribute 'y'
xfa_node 'assist', Template::Assist, 0..1
xfa_node 'bind', Template::Bind, 0..1
xfa_node 'bookend', Template::Bookend, 0..1
xfa_node 'border', Template::Border, 0..1
xfa_node 'break', Template::Break, 0..1
xfa_node 'calculate', Template::Calculate, 0..1
xfa_node 'desc', Template::Desc, 0..1
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'keep', Template::Keep, 0..1
xfa_node 'margin', Template::Margin, 0..1
xfa_node 'occur', Template::Occur, 0..1
xfa_node 'overflow', Template::Overflow, 0..1
xfa_node 'pageSet', Template::PageSet, 0..1
xfa_node 'para', Template::Para, 0..1
xfa_node 'traversal', Template::Traversal, 0..1
xfa_node 'validate', Template::Validate, 0..1
xfa_node 'variables', Template::Variables, 0..1
xfa_node 'area', Template::Area
xfa_node 'breakAfter', Template::BreakAfter
xfa_node 'breakBefore', Template::BreakBefore
xfa_node 'connect', Template::Connect
xfa_node 'draw', Template::Draw
xfa_node 'event', Template::Event
xfa_node 'exObject', Template::ExObject
xfa_node 'exclGroup', Template::ExclGroup
xfa_node 'field', Template::Field
xfa_node 'proto', Template::Proto
xfa_node 'setProperty', Template::SetProperty
xfa_node 'subform', Template::Subform
xfa_node 'subformSet', Template::SubformSet
def initialize
super('subform')
end
end
xfa_attribute 'baseProfile'
xfa_node 'extras', Template::Extras, 0..1
xfa_node 'subform', Template::Subform
def initialize
super("template")
add_attribute 'xmlns:xfa', 'http://www.xfa.org/schema/xfa-template/3.0/'
end
end
#
# The _xdc_ packet encloses application-specific XFA driver configuration instruction.
#
class XDC < XFA::Element
mime_type ''
def initialize
super("xsl:xdc")
add_attribute 'xmlns:xdc', 'http://www.xfa.org/schema/xdc/1.0/'
end
end
#
# The _xfdf_ (annotations) packet enclosed collaboration annotations placed upon a PDF document.
#
class XFDF < XFA::Element
mime_type 'application/vnd.adobe.xfdf'
def initialize
super("xfdf")
add_attribute 'xmlns', 'http://ns.adobe.com/xfdf/'
add_attribute 'xml:space', 'preserve'
end
end
#
# An _XMP_ packet contains XML representation of PDF metadata.
#
class XMPMeta < XFA::Element
mime_type 'application/rdf+xml'
def initialize
super("xmpmeta")
add_attribute 'xmlns', 'http://ns.adobe.com/xmpmeta/'
add_attribute 'xml:space', 'preserve'
end
end
end
class XDP < XFA::Element
xfa_attribute 'uuid'
xfa_attribute 'timeStamp'
xfa_node 'config', Origami::XDP::Packet::Config, 0..1
xfa_node 'connectionSet', Origami::XDP::Packet::ConnectionSet, 0..1
xfa_node 'datasets', Origami::XDP::Packet::Datasets, 0..1
xfa_node 'localeSet', Origami::XDP::Packet::LocaleSet, 0..1
xfa_node 'pdf', Origami::XDP::Packet::PDF, 0..1
xfa_node 'sourceSet', Origami::XDP::Packet::SourceSet, 0..1
xfa_node 'styleSheet', Origami::XDP::Packet::StyleSheet, 0..1
xfa_node 'template', Origami::XDP::Packet::Template, 0..1
xfa_node 'xdc', Origami::XDP::Packet::XDC, 0..1
xfa_node 'xfdf', Origami::XDP::Packet::XFDF, 0..1
xfa_node 'xmpmeta', Origami::XDP::Packet::XMPMeta, 0..1
def initialize
super('xdp:xdp')
add_attribute 'xmlns:xdp', 'http://ns.adobe.com/xdp/'
end
end
class Package < REXML::Document
def initialize(package = nil)
super(package || REXML::XMLDecl.new.to_s)
add_element Origami::XDP::XDP.new if package.nil?
end
end
end
end
origami-pdf-1.2.7/lib/origami/trailer.rb 0000644 0001750 0001750 00000010745 12142214376 020326 0 ustar terceiro terceiro =begin
= File
trailer.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'digest/md5'
module Origami
class PDF
private
def has_attr?(attr) #:nodoc:
not get_doc_attr(attr).nil?
end
def get_doc_attr(attr) #:nodoc:
@revisions.reverse_each do |rev|
if rev.trailer.has_dictionary? and not rev.trailer.dictionary[attr].nil?
return rev.trailer.send(attr)
else
xrefstm = get_object_by_offset(rev.trailer.startxref)
if xrefstm.is_a?(XRefStream) and xrefstm.has_field?(attr)
return xrefstm.send(attr)
end
end
end
nil
end
def get_trailer_info #:nodoc:
#
# First look for a standard trailer dictionary
#
if @revisions.last.trailer.has_dictionary?
@revisions.last.trailer
#
# Otherwise look for a xref stream.
#
else
xrefstm = get_object_by_offset(@revisions.last.trailer.startxref)
xrefstm if xrefstm.is_a?(XRefStream)
end
end
def gen_id
fileInfo = get_trailer_info
if fileInfo.nil?
raise InvalidPDFError, "Cannot access trailer information"
end
id = Digest::MD5.hexdigest( rand.to_s )
fileInfo.ID = [ id, id ]
end
end
class InvalidTrailerError < Exception #:nodoc:
end
# Forward declarations.
class Catalog < Dictionary; end
class Metadata < Dictionary; end
#
# Class representing a PDF file Trailer.
#
class Trailer
include StandardObject
TOKENS = %w{ trailer %%EOF } #:nodoc:
XREF_TOKEN = "startxref" #:nodoc:
@@regexp_open = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
@@regexp_xref = Regexp.new(WHITESPACES + XREF_TOKEN + WHITESPACES + "(\\d+)")
@@regexp_close = Regexp.new(WHITESPACES + TOKENS.last + WHITESPACES)
attr_accessor :pdf
attr_accessor :startxref
attr_reader :dictionary
field :Size, :Type => Integer, :Required => true
field :Prev, :Type => Integer
field :Root, :Type => Catalog, :Required => true
field :Encrypt, :Type => Dictionary
field :Info, :Type => Metadata
field :ID, :Type => Array
field :XRefStm, :Type => Integer
#
# Creates a new Trailer.
# _startxref_:: The file _offset_ to the XRef::Section.
# _dictionary_:: A hash of attributes to set in the Trailer Dictionary.
#
def initialize(startxref = 0, dictionary = {})
@startxref, self.dictionary = startxref, dictionary && Dictionary.new(dictionary)
end
def self.parse(stream, parser = nil) #:nodoc:
if stream.skip(@@regexp_open)
dictionary = Dictionary.parse(stream, parser)
else
dictionary = nil
end
if not stream.scan(@@regexp_xref)
#raise InvalidTrailerError, "Cannot get startxref value"
end
startxref = (stream[3] && stream[3].to_i)
if not stream.scan(@@regexp_close)
#raise InvalidTrailerError, "No %%EOF token found"
end
Trailer.new(startxref, dictionary && dictionary.to_h)
end
def [](key)
@dictionary[key] if has_dictionary?
end
def []=(key,val)
@dictionary[key] = val
end
def dictionary=(dict)
dict.parent = self if dict
@dictionary = dict
end
def has_dictionary?
not @dictionary.nil?
end
#
# Outputs self into PDF code.
#
def to_s
content = ""
if self.has_dictionary?
content << TOKENS.first << EOL << @dictionary.to_s << EOL
end
content << XREF_TOKEN << EOL << @startxref.to_s << EOL << TOKENS.last << EOL
content
end
end
end
origami-pdf-1.2.7/lib/origami/actions.rb 0000644 0001750 0001750 00000022720 12122110015 020277 0 ustar terceiro terceiro =begin
= File
actions.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
#
# Lookup script in the scripts name directory.
#
def get_script_by_name(name)
resolve_name Names::Root::JAVASCRIPT, name
end
#
# Calls block for each named JavaScript script.
#
def each_named_script(&b)
each_name(Names::Root::JAVASCRIPT, &b)
end
end
#
# Class representing an action to launch in a PDF.
#
class Action < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Action
field :S, :Type => Name, :Required => true
field :Next, :Type => [ Array, Dictionary ], :Version => "1.2"
#
# Class representing a action going to a destination in the current document.
#
class GoTo < Action
field :S, :Type => Name, :Default => :GoTo, :Required => true
field :D, :Type => [ Array, Name, ByteString ], :Required => true
#
# Creates a new GoTo Action.
# _hash_:: A hash of options to set for this jump.
#
def self.[](hash = {})
if hash.is_a? Destination
self.new(:S => :GoTo, :D => hash)
else
self.new(hash)
end
end
end
def self.GoTo(hash = {})
Action::GoTo[hash]
end
#
# Class representing an action launching an URL.
#
class URI < Action
field :S, :Type => Name, :Default => :URI, :Required => true
field :URI, :Type => ByteString, :Required => true
field :IsMap, :Type => Boolean, :Default => false
#
# Creates a new URI Action.
# _uri_:: The URI to launch.
# _ismap_::
#
def self.[](uri, ismap = false)
self.new(:URI => uri, :IsMap => ismap)
end
end
def self.URI(uri, ismap = false)
Action::URI[uri, ismap]
end
#
# Class representing a JavaScript Action.
#
class JavaScript < Action
field :S, :Type => Name, :Default => :JavaScript, :Required => true
field :JS, :Type => [ Stream, String ], :Required => true
#
# Creates a new JavaScript Action.
# _script_:: The script to be executed.
#
def self.[](script)
self.new(:JS => script)
end
end
def self.JavaScript(script)
Action::JavaScript[script]
end
#
# Class representing an Action which run a command on the current system.
#
class Launch < Action
field :S, :Type => Name, :Default => :Launch, :Required => true
field :F, :Type => [ ByteString, Dictionary ]
field :Win, :Type => Dictionary
field :Mac, :Type => Object
field :Unix, :Type => Object
field :NewWindow, :Type => Boolean
#
# Dictionary for passing parameter to Windows applications during Launch.
#
class WindowsLaunchParams < Dictionary
include StandardObject
field :F, :Type => ByteString, :Required => true
field :D, :Type => ByteString
field :O, :Type => ByteString, :Default => "open"
field :P, :Type => ByteString
end
end
#
# Class representing a Named Action.
# Named actions are predefined GoTo actions.
#
class Named < Action
field :S, :Type => Name, :Default => :Named, :Required => true
field :N, :Type => Name, :Required => true
def self.[](type)
self.new(:N => type)
end
NEXTPAGE = self[:NextPage]
PREVPAGE = self[:PrevPage]
FIRSTPAGE = self[:FirstPage]
LASTPAGE = self[:LastPage]
PRINT = self[:Print]
end
def self.Named(type)
Action::Named[type]
end
#
# Class representing a GoTo Action to an external file.
#
class GoToR < Action
field :S, :Type => Name, :Default => :GoToR, :Required => true
field :F, :Type => [ ByteString, Dictionary ], :Required => true
field :D, :Type => [ Array, Name, ByteString ], :Required => true
field :NewWindow, :Type => Boolean, :Version => "1.2"
#
# Creates a new GoTo remote Action.
# _file_:: A FileSpec describing the file.
# _dest_:: A Destination in the file.
# _newwindow_:: Specifies whether the file has to be opened in a new window.
#
def self.[](file, dest = Destination::GlobalFit.new(0), newwindow = false)
self.new(:F => file, :D => dest, :NewWindow => newwindow)
end
end
def self.GoToR(file, dest = Destination::GlobalFit.new(0), newwindow = false)
Action::GoToR[file, dest, newwindow]
end
#
# Class representing a GoTo Action to an embedded pdf file.
#
class GoToE < Action
field :S, :Type => Name, :Default => :GoToE, :Required => true
field :F, :Type => [ Dictionary, ByteString ]
field :D, :Type => [ Array, Name, ByteString ], :Required => true
field :NewWindow, :Type => Boolean
field :T, :Type => Dictionary
#
# A class representing a target for a GoToE to an embedded file.
#
class EmbeddedTarget < Dictionary
include StandardObject
module Relationship
PARENT = :P
CHILD = :C
end
field :R, :Type => Name, :Required => true
field :N, :Type => ByteString
field :P, :Type => [ Integer, ByteString ]
field :A, :Type => [ Integer, ByteString ]
field :T, :Type => Dictionary
end
def self.[](filename, dest, newwindow = false)
self.new(:T => EmbeddedTarget.new(:R => :C, :N => filename), :D => dest, :NewWindow => newwindow)
end
end
def self.GoToE(filename, dest, newwindow = false)
Action::GoToE[filename, dest, newwindow]
end
#
# (PDF 1.2) Send data to a uniform resource locator. p703
#
class SubmitForm < Action
module Flags
INCLUDEEXCLUDE = 1 << 0
INCLUDENOVALUEFIELDS = 1 << 1
EXPORTFORMAT = 1 << 2
GETMETHOD = 1 << 3
SUBMITCOORDINATES = 1 << 4
XFDF = 1 << 5
INCLUDEAPPENDSAVES = 1 << 6
INCLUDEANNOTATIONS = 1 << 7
SUBMITPDF = 1 << 8
CANONICALFORMAT = 1 << 9
EXCLNONUSERANNOTS = 1 << 10
EXCLFKEY = 1 << 11
EMBEDFORM = 1 << 12
end
field :S, :Type => Name, :Default => :SubmitForm, :Required => true
field :F, :Type => Dictionary
field :Fields, :Type => Array
field :Flags, :Type => Integer, :Default => 0
def self.[](url, fields = [], flags = 0)
url = FileSpec.new(:FS => :URL, :F => url) unless url.is_a? FileSpec
self.new(:F => url, :Fields => fields, :Flags => flags)
end
end
def self.SubmitForm(url, fields = [], flags = 0)
Action::SubmitForm[url, fields, flags]
end
class ImportData < Action
field :S, :Type => Name, :Default => :ImportData, :Required => true
field :F, :Type => Dictionary, :Required => true
def self.[](file)
file = FileSpec.new(:FS => :File, :F => file) unless file.is_a? FileSpec
self.new(:F => file)
end
end
def self.ImportData(file)
Action::ImportData[file]
end
class RichMediaExecute < Action
field :S, :Type => Name, :Default => :RichMediaExecute, :Version => "1.7", :ExtensionLevel => 3, :Required => true
field :TA, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3, :Required => true
field :TI, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :CMD, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3, :Required => true
class Command < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :RichMediaCommand, :Version => "1.7", :ExtensionLevel => 3
field :C, :Type => String, :Version => "1.7", :ExtensionLevel => 3, :Required => true
field :A, :Type => Object, :Version => "1.7", :ExtensionLevel => 3
end
def self.[](annotation, command, *params)
self.new(:TA => annotation, :CMD => Command.new(:C => command, :A => params))
end
end
def self.RichMediaExecute(annotation, command, *params)
Action::RichMediaExecute[annotation, command, *params]
end
end
end
origami-pdf-1.2.7/lib/origami/encryption.rb 0000644 0001750 0001750 00000126111 12146177126 021056 0 ustar terceiro terceiro =begin
= File
encryption.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume DelugrÈ
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
begin
require 'openssl' if Origami::OPTIONS[:use_openssl]
rescue LoadError
Origami::OPTIONS[:use_openssl] = false
end
require 'digest/md5'
require 'digest/sha2'
module Origami
class EncryptionError < Exception #:nodoc:
end
class EncryptionInvalidPasswordError < EncryptionError #:nodoc:
end
class EncryptionNotSupportedError < EncryptionError #:nodoc:
end
class PDF
#
# Returns whether the PDF file is encrypted.
#
def is_encrypted?
has_attr? :Encrypt
end
#
# Decrypts the current document (only RC4 40..128 bits).
# _passwd_:: The password to decrypt the document.
#
def decrypt(passwd = "")
unless self.is_encrypted?
raise EncryptionError, "PDF is not encrypted"
end
encrypt_dict = get_doc_attr(:Encrypt)
handler = Encryption::Standard::Dictionary.new(encrypt_dict.dup)
unless handler.Filter == :Standard
raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter.to_s}'"
end
case handler.V.to_i
when 1,2 then str_algo = stm_algo = Encryption::ARC4
when 4,5
if handler[:CF].is_a?(Dictionary)
cfs = handler[:CF]
if handler[:StrF].is_a?(Name) and cfs[handler[:StrF]].is_a?(Dictionary)
cfdict = cfs[handler[:StrF]]
str_algo =
if cfdict[:CFM] == :V2 then Encryption::ARC4
elsif cfdict[:CFM] == :AESV2 then Encryption::AES
elsif cfdict[:CFM] == :None then Encryption::Identity
elsif cfdict[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
else
raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
end
else
str_algo = Encryption::Identity
end
if handler[:StmF].is_a?(Name) and cfs[handler[:StmF]].is_a?(Dictionary)
cfdict = cfs[handler[:StmF]]
stm_algo =
if cfdict[:CFM] == :V2 then Encryption::ARC4
elsif cfdict[:CFM] == :AESV2 then Encryption::AES
elsif cfdict[:CFM] == :None then Encryption::Identity
elsif cfdict[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
else
raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
end
else
stm_algo = Encryption::Identity
end
else
str_algo = stm_algo = Encryption::Identity
end
else
raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
end
doc_id = get_doc_attr(:ID)
unless doc_id.is_a?(Array)
raise EncryptionError, "Document ID was not found or is invalid" unless handler.V.to_i == 5
else
doc_id = doc_id.first
end
if handler.is_user_password?(passwd, doc_id)
encryption_key = handler.compute_user_encryption_key(passwd, doc_id)
elsif handler.is_owner_password?(passwd, doc_id)
if handler.V.to_i < 5
user_passwd = handler.retrieve_user_password(passwd)
encryption_key = handler.compute_user_encryption_key(user_passwd, doc_id)
else
encryption_key = handler.compute_owner_encryption_key(passwd)
end
else
raise EncryptionInvalidPasswordError
end
#self.extend(Encryption::EncryptedDocument)
#self.encryption_dict = encrypt_dict
#self.encryption_key = encryption_key
#self.stm_algo = self.str_algo = algorithm
encrypt_metadata = (handler.EncryptMetadata != false)
self.extend(Encryption::EncryptedDocument)
self.encryption_dict = handler
self.encryption_key = encryption_key
self.stm_algo,self.str_algo = stm_algo,str_algo
#
# Should be fixed to exclude only the active XRefStream
#
metadata = self.Catalog.Metadata
self.indirect_objects.each do |indobj|
encrypted_objects = []
case indobj
when String,Stream then encrypted_objects << indobj
when Dictionary,Array then encrypted_objects |= indobj.strings_cache
end
encrypted_objects.each do |obj|
case obj
when String
next if obj.equal?(encrypt_dict[:U]) or
obj.equal?(encrypt_dict[:O]) or
obj.equal?(encrypt_dict[:UE]) or
obj.equal?(encrypt_dict[:OE]) or
obj.equal?(encrypt_dict[:Perms]) or
(obj.parent.is_a?(Signature::DigitalSignature) and obj.equal?(obj.parent[:Contents]))
obj.extend(Encryption::EncryptedString) unless obj.is_a?(Encryption::EncryptedString)
obj.encryption_handler = handler
obj.encryption_key = encryption_key
obj.algorithm = str_algo
obj.decrypt!
when Stream
next if obj.is_a?(XRefStream) or (not encrypt_metadata and obj.equal?(metadata))
obj.extend(Encryption::EncryptedStream) unless obj.is_a?(Encryption::EncryptedStream)
obj.encryption_handler = handler
obj.encryption_key = encryption_key
obj.algorithm = stm_algo
end
end
end
self
end
#
# Encrypts the current document with the provided passwords.
# The document will be encrypted at writing-on-disk time.
# _userpasswd_:: The user password.
# _ownerpasswd_:: The owner password.
# _options_:: A set of options to configure encryption.
#
def encrypt(options = {})
if self.is_encrypted?
raise EncryptionError, "PDF is already encrypted"
end
#
# Default encryption options.
#
params =
{
:user_passwd => '',
:owner_passwd => '',
:cipher => 'rc4', # :RC4 or :AES
:key_size => 128, # Key size in bits
:hardened => false, # Use newer password validation (since Reader X)
:encrypt_metadata => true, # Metadata shall be encrypted?
:permissions => Encryption::Standard::Permissions::ALL # Document permissions
}.update(options)
userpasswd, ownerpasswd = params[:user_passwd], params[:owner_passwd]
case params[:cipher].upcase
when 'RC4'
algorithm = Encryption::ARC4
if (40..128) === params[:key_size] and params[:key_size] % 8 == 0
if params[:key_size] > 40
version = 2
revision = 3
else
version = 1
revision = 2
end
else
raise EncryptionError, "Invalid RC4 key length"
end
when 'AES'
algorithm = Encryption::AES
if params[:key_size] == 128
version = revision = 4
elsif params[:key_size] == 256
version = 5
if params[:hardened]
revision = 6
else
revision = 5
end
else
raise EncryptionError, "Invalid AES key length (Only 128 and 256 bits keys are supported)"
end
else
raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
end
doc_id = (get_doc_attr(:ID) || gen_id).first
handler = Encryption::Standard::Dictionary.new
handler.Filter = :Standard #:nodoc:
handler.V = version
handler.R = revision
handler.Length = params[:key_size]
handler.P = -1 # params[:Permissions]
if revision >= 4
handler.EncryptMetadata = params[:encrypt_metadata]
handler.CF = Dictionary.new
cryptfilter = Encryption::CryptFilterDictionary.new
cryptfilter.AuthEvent = :DocOpen
if revision == 4
cryptfilter.CFM = :AESV2
else
cryptfilter.CFM = :AESV3
end
cryptfilter.Length = params[:key_size] >> 3
handler.CF[:StdCF] = cryptfilter
handler.StmF = handler.StrF = :StdCF
end
handler.set_passwords(ownerpasswd, userpasswd, doc_id)
encryption_key = handler.compute_user_encryption_key(userpasswd, doc_id)
fileInfo = get_trailer_info
fileInfo[:Encrypt] = self << handler
self.extend(Encryption::EncryptedDocument)
self.encryption_dict = handler
self.encryption_key = encryption_key
self.stm_algo = self.str_algo = algorithm
self
end
end
#
# Module to provide support for encrypting and decrypting PDF documents.
#
module Encryption
#
# Generates _n_ random bytes from a fast PRNG.
#
def self.rand_bytes(n)
::Array.new(n) { rand(256) }.pack("C*")
end
#
# Generates _n_ random bytes from a crypto PRNG.
#
def self.strong_rand_bytes(n)
if Origami::OPTIONS[:use_openssl]
OpenSSL::Random.random_bytes(n)
elsif RUBY_VERSION >= '1.9'
Random.new.bytes(n)
else
self.rand_bytes(n)
end
end
module EncryptedDocument
attr_writer :encryption_key
attr_writer :encryption_dict
attr_writer :stm_algo
attr_writer :str_algo
private
def physicalize(options = {})
def build(obj, revision, options) #:nodoc:
if obj.is_a?(EncryptedObject) # already built
if options[:decrypt] == true
obj.pre_build
obj.decrypt!
obj.decrypted = false # makes it believe no encryption pass is required
obj.post_build
end
return
end
if obj.is_a?(ObjectStream)
obj.each do |subobj|
build(subobj, revision, options)
end
end
obj.pre_build
case obj
when String
if not obj.equal?(@encryption_dict[:U]) and
not obj.equal?(@encryption_dict[:O]) and
not obj.equal?(@encryption_dict[:UE]) and
not obj.equal?(@encryption_dict[:OE]) and
not obj.equal?(@encryption_dict[:Perms]) and
not (obj.parent.is_a?(Signature::DigitalSignature) and obj.equal?(obj.parent[:Contents])) and
not obj.indirect_parent.parent.is_a?(ObjectStream)
obj.extend(EncryptedString)
obj.decrypted = true
obj.encryption_handler = @encryption_dict
obj.encryption_key = @encryption_key
obj.algorithm = @str_algo
end
when Stream
return if obj.is_a?(XRefStream)
return if obj.equal?(self.Catalog.Metadata) and not @encryption_dict.EncryptMetadata
obj.extend(EncryptedStream)
obj.decrypted = true
obj.encryption_handler = @encryption_dict
obj.encryption_key = @encryption_key
obj.algorithm = @stm_algo
when Dictionary, Array
obj.map! do |subobj|
if subobj.is_indirect?
if get_object(subobj.reference)
subobj.reference
else
ref = add_to_revision(subobj, revision)
build(subobj, revision, options)
ref
end
else
subobj
end
end
obj.each do |subobj|
build(subobj, revision, options)
end
end
obj.post_build
end
# stack up every root objects
indirect_objects_by_rev.each do |obj, revision|
build(obj, revision, options)
end
# remove encrypt dictionary if requested
if options[:decrypt]
delete_object(get_trailer_info[:Encrypt])
get_trailer_info[:Encrypt] = nil
end
self
end
end
#
# Module for encrypted PDF objects.
#
module EncryptedObject #:nodoc
attr_writer :encryption_key
attr_writer :algorithm
attr_writer :encryption_handler
attr_accessor :decrypted
def self.extended(obj)
obj.decrypted = false
end
def post_build
encrypt!
super
end
private
def compute_object_key
if @encryption_handler.V < 5
parent = self.indirect_parent
no, gen = parent.no, parent.generation
k = @encryption_key + [no].pack("I")[0..2] + [gen].pack("I")[0..1]
key_len = (k.length > 16) ? 16 : k.length
k << "sAlT" if @algorithm == Encryption::AES
Digest::MD5.digest(k)[0, key_len]
else
@encryption_key
end
end
end
#
# Module for encrypted String.
#
module EncryptedString
include EncryptedObject
def encrypt!
if @decrypted
key = compute_object_key
encrypted_data =
if @algorithm == ARC4 or @algorithm == Identity
@algorithm.encrypt(key, self.value)
else
iv = Encryption.rand_bytes(AES::BLOCKSIZE)
@algorithm.encrypt(key, iv, self.value)
end
@decrypted = false
self.replace(encrypted_data)
self.freeze
end
self
end
def decrypt!
unless @decrypted
key = compute_object_key
self.replace(@algorithm.decrypt(key, self.to_str))
@decrypted = true
end
self
end
end
#
# Module for encrypted Stream.
#
module EncryptedStream
include EncryptedObject
def encrypt!
if @decrypted
encode!
key = compute_object_key
@rawdata =
if @algorithm == ARC4 or @algorithm == Identity
@algorithm.encrypt(key, self.rawdata)
else
iv = Encryption.rand_bytes(AES::BLOCKSIZE)
@algorithm.encrypt(key, iv, @rawdata)
end
@decrypted = false
@rawdata.freeze
self.freeze
end
self
end
def decrypt!
unless @decrypted
key = compute_object_key
self.rawdata = @algorithm.decrypt(key, @rawdata)
@decrypted = true
end
self
end
end
#
# Identity transformation.
#
module Identity
def Identity.encrypt(key, data)
data
end
def Identity.decrypt(key, data)
data
end
end
#
# Pure Ruby implementation of the aRC4 symmetric algorithm
#
class ARC4
#
# Encrypts data using the given key
#
def ARC4.encrypt(key, data)
ARC4.new(key).encrypt(data)
end
#
# Decrypts data using the given key
#
def ARC4.decrypt(key, data)
ARC4.new(key).decrypt(data)
end
#
# Creates and initialises a new aRC4 generator using given key
#
def initialize(key)
if Origami::OPTIONS[:use_openssl]
@key = key
else
@state = init(key)
end
end
#
# Encrypt/decrypt data with the aRC4 encryption algorithm
#
def cipher(data)
return "" if data.empty?
if Origami::OPTIONS[:use_openssl]
rc4 = OpenSSL::Cipher::RC4.new.encrypt
rc4.key_len = @key.length
rc4.key = @key
output = rc4.update(data) << rc4.final
else
output = ""
i, j = 0, 0
data.each_byte do |byte|
i = i.succ & 0xFF
j = (j + @state[i]) & 0xFF
@state[i], @state[j] = @state[j], @state[i]
output << (@state[@state[i] + @state[j] & 0xFF] ^ byte).chr
end
end
output
end
alias encrypt cipher
alias decrypt cipher
private
def init(key) #:nodoc:
state = (0..255).to_a
j = 0
256.times do |i|
j = ( j + state[i] + key[i % key.size].ord ) & 0xFF
state[i], state[j] = state[j], state[i]
end
state
end
end
#
# Pure Ruby implementation of the AES symmetric algorithm.
# Using mode CBC.
#
class AES
NROWS = 4
NCOLS = 4
BLOCKSIZE = NROWS * NCOLS
ROUNDS =
{
16 => 10,
24 => 12,
32 => 14
}
#
# Rijndael S-box
#
SBOX =
[
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
]
#
# Inverse of the Rijndael S-box
#
RSBOX =
[
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
]
RCON =
[
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
]
attr_writer :iv
def AES.encrypt(key, iv, data)
AES.new(key, iv).encrypt(data)
end
def AES.decrypt(key, data)
AES.new(key, nil).decrypt(data)
end
def initialize(key, iv, use_padding = true)
unless key.size == 16 or key.size == 24 or key.size == 32
raise EncryptionError, "Key must have a length of 128, 192 or 256 bits."
end
if not iv.nil? and iv.size != BLOCKSIZE
raise EncryptionError, "Initialization vector must have a length of #{BLOCKSIZE} bytes."
end
@key = key
@iv = iv
@use_padding = use_padding
end
def encrypt(data)
if @iv.nil?
raise EncryptionError, "No initialization vector has been set."
end
if @use_padding
padlen = BLOCKSIZE - (data.size % BLOCKSIZE)
data << (padlen.chr * padlen)
end
if Origami::OPTIONS[:use_openssl]
aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").encrypt
aes.iv = @iv
aes.key = @key
aes.padding = 0
@iv + aes.update(data) + aes.final
else
cipher = []
cipherblock = []
nblocks = data.size / BLOCKSIZE
first_round = true
nblocks.times do |n|
plainblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
if first_round
BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
else
BLOCKSIZE.times do |i| plainblock[i] ^= cipherblock[i] end
end
first_round = false
cipherblock = aesEncrypt(plainblock)
cipher.concat(cipherblock)
end
@iv + cipher.pack("C*")
end
end
def decrypt(data)
unless data.size % BLOCKSIZE == 0
raise EncryptionError,
"Data must be 16-bytes padded (data size = #{data.size} bytes)"
end
@iv = data.slice!(0, BLOCKSIZE)
if Origami::OPTIONS[:use_openssl]
aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").decrypt
aes.iv = @iv
aes.key = @key
aes.padding = 0
plain = (aes.update(data) + aes.final).unpack("C*")
else
plain = []
plainblock = []
prev_cipherblock = []
nblocks = data.size / BLOCKSIZE
first_round = true
nblocks.times do |n|
cipherblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
plainblock = aesDecrypt(cipherblock)
if first_round
BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
else
BLOCKSIZE.times do |i| plainblock[i] ^= prev_cipherblock[i] end
end
first_round = false
prev_cipherblock = cipherblock
plain.concat(plainblock)
end
end
if @use_padding
padlen = plain[-1]
unless (1..16) === padlen
raise EncryptionError, "Incorrect padding length : #{padlen}"
end
padlen.times do
pad = plain.pop
raise EncryptionError,
"Incorrect padding byte : 0x#{pad.to_s 16}" if pad != padlen
end
end
plain.pack("C*")
end
private
def rol(row, n = 1) #:nodoc
n.times do row.push row.shift end ; row
end
def ror(row, n = 1) #:nodoc:
n.times do row.unshift row.pop end ; row
end
def galoisMult(a, b) #:nodoc:
p = 0
8.times do
p ^= a if b[0] == 1
highBit = a[7]
a <<= 1
a ^= 0x1b if highBit == 1
b >>= 1
end
p % 256
end
def scheduleCore(word, iter) #:nodoc:
rol(word)
word.map! do |byte| SBOX[byte] end
word[0] ^= RCON[iter]
word
end
def transpose(m) #:nodoc:
[
m[NROWS * 0, NROWS],
m[NROWS * 1, NROWS],
m[NROWS * 2, NROWS],
m[NROWS * 3, NROWS]
].transpose.flatten
end
#
# AES round methods.
#
def createRoundKey(expandedKey, round = 0) #:nodoc:
transpose(expandedKey[round * BLOCKSIZE, BLOCKSIZE])
end
def addRoundKey(roundKey) #:nodoc:
BLOCKSIZE.times do |i| @state[i] ^= roundKey[i] end
end
def subBytes #:nodoc:
BLOCKSIZE.times do |i| @state[i] = SBOX[ @state[i] ] end
end
def rsubBytes #:nodoc:
BLOCKSIZE.times do |i| @state[i] = RSBOX[ @state[i] ] end
end
def shiftRows #:nodoc:
NROWS.times do |i|
@state[i * NCOLS, NCOLS] = rol(@state[i * NCOLS, NCOLS], i)
end
end
def rshiftRows #:nodoc:
NROWS.times do |i|
@state[i * NCOLS, NCOLS] = ror(@state[i * NCOLS, NCOLS], i)
end
end
def mixColumnWithField(column, field) #:nodoc:
p = field
column[0], column[1], column[2], column[3] =
galoisMult(column[0], p[0]) ^ galoisMult(column[3], p[1]) ^ galoisMult(column[2], p[2]) ^ galoisMult(column[1], p[3]),
galoisMult(column[1], p[0]) ^ galoisMult(column[0], p[1]) ^ galoisMult(column[3], p[2]) ^ galoisMult(column[2], p[3]),
galoisMult(column[2], p[0]) ^ galoisMult(column[1], p[1]) ^ galoisMult(column[0], p[2]) ^ galoisMult(column[3], p[3]),
galoisMult(column[3], p[0]) ^ galoisMult(column[2], p[1]) ^ galoisMult(column[1], p[2]) ^ galoisMult(column[0], p[3])
end
def mixColumn(column) #:nodoc:
mixColumnWithField(column, [ 2, 1, 1, 3 ])
end
def rmixColumn(column) #:nodoc:
mixColumnWithField(column, [ 14, 9, 13, 11 ])
end
def mixColumns #:nodoc:
NCOLS.times do |c|
column = []
NROWS.times do |r| column << @state[c + r * NCOLS] end
mixColumn(column)
NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
end
end
def rmixColumns #:nodoc:
NCOLS.times do |c|
column = []
NROWS.times do |r| column << @state[c + r * NCOLS] end
rmixColumn(column)
NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
end
end
def expandKey(key) #:nodoc:
key = key.unpack("C*")
size = key.size
expandedSize = 16 * (ROUNDS[key.size] + 1)
rconIter = 1
expandedKey = key[0, size]
while expandedKey.size < expandedSize
temp = expandedKey[-4, 4]
if expandedKey.size % size == 0
scheduleCore(temp, rconIter)
rconIter = rconIter.succ
end
temp.map! do |b| SBOX[b] end if size == 32 and expandedKey.size % size == 16
temp.each do |b| expandedKey << (expandedKey[-size] ^ b) end
end
expandedKey
end
def aesRound(roundKey) #:nodoc:
subBytes
#puts "after subBytes: #{@state.inspect}"
shiftRows
#puts "after shiftRows: #{@state.inspect}"
mixColumns
#puts "after mixColumns: #{@state.inspect}"
addRoundKey(roundKey)
#puts "roundKey = #{roundKey.inspect}"
#puts "after addRoundKey: #{@state.inspect}"
end
def raesRound(roundKey) #:nodoc:
addRoundKey(roundKey)
rmixColumns
rshiftRows
rsubBytes
end
def aesEncrypt(block) #:nodoc:
@state = transpose(block)
expandedKey = expandKey(@key)
rounds = ROUNDS[@key.size]
aesMain(expandedKey, rounds)
end
def aesDecrypt(block) #:nodoc:
@state = transpose(block)
expandedKey = expandKey(@key)
rounds = ROUNDS[@key.size]
raesMain(expandedKey, rounds)
end
def aesMain(expandedKey, rounds) #:nodoc:
#puts "expandedKey: #{expandedKey.inspect}"
roundKey = createRoundKey(expandedKey)
addRoundKey(roundKey)
for i in 1..rounds-1
roundKey = createRoundKey(expandedKey, i)
aesRound(roundKey)
end
roundKey = createRoundKey(expandedKey, rounds)
subBytes
shiftRows
addRoundKey(roundKey)
transpose(@state)
end
def raesMain(expandedKey, rounds) #:nodoc:
roundKey = createRoundKey(expandedKey, rounds)
addRoundKey(roundKey)
rshiftRows
rsubBytes
(rounds - 1).downto(1) do |i|
roundKey = createRoundKey(expandedKey, i)
raesRound(roundKey)
end
roundKey = createRoundKey(expandedKey)
addRoundKey(roundKey)
transpose(@state)
end
end
#
# Class representing a crypt filter Dictionary
#
class CryptFilterDictionary < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :CryptFilter
field :CFM, :Type => Name, :Default => :None
field :AuthEvent, :Type => Name, :Default => :DocOpen
field :Length, :Type => Integer
end
#
# Common class for encryption dictionaries.
#
class EncryptionDictionary < Dictionary
include StandardObject
field :Filter, :Type => Name, :Default => :Standard, :Required => true
field :SubFilter, :Type => Name, :Version => "1.3"
field :V, :Type => Integer, :Default => 0
field :Length, :Type => Integer, :Default => 40, :Version => "1.4"
field :CF, :Type => Dictionary, :Version => "1.5"
field :StmF, :Type => Name, :Default => :Identity, :Version => "1.5"
field :StrF, :Type => Name, :Default => :Identity, :Version => "1.5"
field :EFF, :Type => Name, :Version => "1.6"
end
#
# The standard security handler for PDF encryption.
#
module Standard
PADDING = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A" #:nodoc:
PADDING.force_encoding('binary') if RUBY_VERSION >= '1.9'
#
# Permission constants for encrypted documents.
#
module Permissions
RESERVED = 1 << 6 | 1 << 7 | 0xFFFFF000
PRINT = 1 << 2 | RESERVED
MODIFY_CONTENTS = 1 << 3 | RESERVED
COPY_CONTENTS = 1 << 4 | RESERVED
MODIFY_ANNOTATIONS = 1 << 5 | RESERVED
FILLIN_FORMS = 1 << 8 | RESERVED
EXTRACT_CONTENTS = 1 << 9 | RESERVED
ASSEMBLE_DOC = 1 << 10 | RESERVED
HIGH_QUALITY_PRINT = 1 << 11 | RESERVED
ALL = PRINT | MODIFY_CONTENTS | COPY_CONTENTS | MODIFY_ANNOTATIONS | FILLIN_FORMS | EXTRACT_CONTENTS | ASSEMBLE_DOC | HIGH_QUALITY_PRINT
end
#
# Class defining a standard encryption dictionary.
#
class Dictionary < EncryptionDictionary
field :R, :Type => Number, :Required => true
field :O, :Type => String, :Required => true
field :U, :Type => String, :Required => true
field :OE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
field :UE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
field :Perms, :Type => String, :Version => '1.7', :ExtensionLevel => 3
field :P, :Type => Integer, :Default => 0, :Required => true
field :EncryptMetadata, :Type => Boolean, :Default => true, :Version => "1.5"
def pdf_version_required #:nodoc:
if self.R > 5
[ 1.7, 8 ]
else
super
end
end
#
# Computes the key that will be used to encrypt/decrypt the document contents with user password.
#
def compute_user_encryption_key(userpassword, fileid)
if self.R < 5
padded = pad_password(userpassword)
padded.force_encoding('binary') if RUBY_VERSION >= '1.9'
padded << self.O
padded << [ self.P ].pack("i")
padded << fileid
encrypt_metadata = self.EncryptMetadata != false
padded << [ -1 ].pack("i") if self.R >= 4 and not encrypt_metadata
key = Digest::MD5.digest(padded)
50.times { key = Digest::MD5.digest(key[0, self.Length / 8]) } if self.R >= 3
if self.R == 2
key[0, 5]
elsif self.R >= 3
key[0, self.Length / 8]
end
else
passwd = password_to_utf8(userpassword)
uks = self.U[40, 8]
if self.R == 5
ukey = Digest::SHA256.digest(passwd + uks)
else
ukey = compute_hardened_hash(passwd, uks)
end
iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
AES.new(ukey, nil, false).decrypt(iv + self.UE.value)
end
end
#
# Computes the key that will be used to encrypt/decrypt the document contents with owner password.
# Revision 5 and above.
#
def compute_owner_encryption_key(ownerpassword)
if self.R >= 5
passwd = password_to_utf8(ownerpassword)
oks = self.O[40, 8]
if self.R == 5
okey = Digest::SHA256.digest(passwd + oks + self.U)
else
okey = compute_hardened_hash(passwd, oks, self.U)
end
iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
AES.new(okey, nil, false).decrypt(iv + self.OE.value)
end
end
#
# Set up document passwords.
#
def set_passwords(ownerpassword, userpassword, salt = nil)
if self.R < 5
key = compute_owner_key(ownerpassword)
upadded = pad_password(userpassword)
owner_key = ARC4.encrypt(key, upadded)
19.times { |i| owner_key = ARC4.encrypt(xor(key,i+1), owner_key) } if self.R >= 3
self.O = owner_key
self.U = compute_user_password(userpassword, salt)
else
upass = password_to_utf8(userpassword)
opass = password_to_utf8(ownerpassword)
uvs, uks, ovs, oks = ::Array.new(4) { Encryption.rand_bytes(8) }
file_key = Encryption.strong_rand_bytes(32)
iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
if self.R == 5
self.U = Digest::SHA256.digest(upass + uvs) + uvs + uks
self.O = Digest::SHA256.digest(opass + ovs + self.U) + ovs + oks
ukey = Digest::SHA256.digest(upass + uks)
okey = Digest::SHA256.digest(opass + oks + self.U)
else
self.U = compute_hardened_hash(upass, uvs) + uvs + uks
self.O = compute_hardened_hash(opass, ovs, self.U) + ovs + oks
ukey = compute_hardened_hash(upass, uks)
okey = compute_hardened_hash(opass, oks, self.U)
end
self.UE = AES.new(ukey, iv, false).encrypt(file_key)[iv.size, 32]
self.OE = AES.new(okey, iv, false).encrypt(file_key)[iv.size, 32]
perms =
[ self.P ].pack("V") + # 0-3
[ -1 ].pack("V") + # 4-7
(self.EncryptMetadata == true ? "T" : "F") + # 8
"adb" + # 9-11
[ 0 ].pack("V") # 12-15
self.Perms = AES.new(file_key, iv, false).encrypt(perms)[iv.size, 16]
file_key
end
end
#
# Checks user password.
# For version 2,3 and 4, _salt_ is the document ID.
# For version 5 and 6, _salt_ is the User Key Salt.
#
def is_user_password?(pass, salt)
if self.R == 2
compute_user_password(pass, salt) == self.U
elsif self.R == 3 or self.R == 4
compute_user_password(pass, salt)[0, 16] == self.U[0, 16]
elsif self.R == 5
uvs = self.U[32, 8]
Digest::SHA256.digest(password_to_utf8(pass) + uvs) == self.U[0, 32]
elsif self.R == 6
uvs = self.U[32, 8]
compute_hardened_hash(password_to_utf8(pass), uvs) == self.U[0, 32]
end
end
#
# Checks owner password.
# For version 2,3 and 4, _salt_ is the document ID.
# For version 5, _salt_ is (Owner Key Salt + U)
#
def is_owner_password?(pass, salt)
if self.R < 5
user_password = retrieve_user_password(pass)
is_user_password?(user_password, salt)
elsif self.R == 5
ovs = self.O[32, 8]
Digest::SHA256.digest(password_to_utf8(pass) + ovs + self.U) == self.O[0, 32]
elsif self.R == 6
ovs = self.O[32, 8]
compute_hardened_hash(password_to_utf8(pass), ovs, self.U[0,48]) == self.O[0, 32]
end
end
#
# Retrieve user password from owner password.
# Cannot be used with revision 5.
#
def retrieve_user_password(ownerpassword)
key = compute_owner_key(ownerpassword)
if self.R == 2
ARC4.decrypt(key, self.O)
elsif self.R == 3 or self.R == 4
user_password = ARC4.decrypt(xor(key, 19), self.O)
19.times { |i| user_password = ARC4.decrypt(xor(key, 18-i), user_password) }
user_password
end
end
private
#
# Used to encrypt/decrypt the O field.
# Rev 2,3,4: O = crypt(user_pass, owner_key).
# Rev 5: unused.
#
def compute_owner_key(ownerpassword) #:nodoc:
opadded = pad_password(ownerpassword)
hash = Digest::MD5.digest(opadded)
50.times { hash = Digest::MD5.digest(hash) } if self.R >= 3
if self.R == 2
hash[0, 5]
elsif self.R >= 3
hash[0, self.Length / 8]
end
end
#
# Compute the value of the U field.
# Cannot be used with revision 5.
#
def compute_user_password(userpassword, salt) #:nodoc:
if self.R == 2
key = compute_user_encryption_key(userpassword, salt)
user_key = ARC4.encrypt(key, PADDING)
elsif self.R == 3 or self.R == 4
key = compute_user_encryption_key(userpassword, salt)
upadded = PADDING + salt
hash = Digest::MD5.digest(upadded)
user_key = ARC4.encrypt(key, hash)
19.times { |i| user_key = ARC4.encrypt(xor(key,i+1), user_key) }
user_key.ljust(32, 0xFF.chr)
end
end
#
# Computes hardened hash used in revision 6 (extension level 8).
#
def compute_hardened_hash(password, salt, vector = '')
block_size = 32
input = Digest::SHA256.digest(password + salt + vector) + "\x00" * 32
key = input[0, 16]
iv = input[16, 16]
digest, aes, h, x = nil, nil, nil, nil
i = 0
while i < 64 or i < x[-1].ord + 32
j = 0
block = input[0, block_size]
if Origami::OPTIONS[:use_openssl]
aes = OpenSSL::Cipher::Cipher.new("aes-128-cbc").encrypt
aes.iv = iv
aes.key = key
aes.padding = 0
else
fail "You need OpenSSL support to encrypt/decrypt documents with this method"
end
64.times do |j|
x = ''
x += aes.update(password) unless password.empty?
x += aes.update(block)
x += aes.update(vector) unless vector.empty?
if j == 0
block_size = 32 + (x.unpack("C16").inject(0) {|a,b| a+b} % 3) * 16
digest = Digest::SHA2.new(block_size << 3)
end
digest.update(x)
end
h = digest.digest
key = h[0, 16]
input[0, block_size] = h[0, block_size]
iv = h[16, 16]
i = i + 1
end
h[0, 32]
end
def xor(str, byte) #:nodoc:
str.split(//).map!{|c| (c[0].ord ^ byte).chr }.join
end
def pad_password(password) #:nodoc:
return PADDING.dup if password.empty? # Fix for Ruby 1.9 bug
password[0,32].ljust(32, PADDING)
end
def password_to_utf8(passwd) #:nodoc:
Origami::ByteString.new(passwd).to_utf8[0, 127]
end
end
end
end
end
__END__
def hexprint(str)
hex = ""
str.each_byte do |b|
digit = b.to_s(16)
digit = "0" + digit if digit.size == 1
hex << digit
end
puts hex.upcase
end
origami-pdf-1.2.7/lib/origami/header.rb 0000644 0001750 0001750 00000004521 12101464040 020075 0 ustar terceiro terceiro =begin
= File
header.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
class InvalidHeaderError < Exception #:nodoc:
end
#
# Class representing a PDF Header.
#
class Header
MINVERSION = 0
MAXVERSION = 7
MAGIC = /%PDF-(\d+)\.(\d+)/
attr_accessor :majorversion, :minorversion
#
# Creates a file header, with the given major and minor versions.
# _majorversion_:: Major PDF version, must be 1.
# _minorversion_:: Minor PDF version, must be between 0 and 7.
#
def initialize(majorversion = 1, minorversion = 4)
#if majorversion.to_i != 1 || ! ((MINVERSION..MAXVERSION) === minorversion.to_i)
# Console.colorprint("[info ] Warning: Invalid file version : #{majorversion}.#{minorversion}\n", Console::Colors::YELLOW, false, STDERR)
#end
@majorversion, @minorversion = majorversion, minorversion
end
def self.parse(stream) #:nodoc:
if not stream.scan(MAGIC).nil?
maj = stream[1].to_i
min = stream[2].to_i
else
raise InvalidHeaderError, "Invalid header format : #{stream.peek(15).inspect}"
end
PDF::Header.new(maj,min)
end
#
# Outputs self into PDF code.
#
def to_s
"%PDF-#{@majorversion}.#{@minorversion}" + EOL
end
def to_sym #:nodoc:
"#{@majorversion}.#{@minorversion}".to_sym
end
def to_f #:nodoc:
to_sym.to_s.to_f
end
end
end
end
origami-pdf-1.2.7/lib/origami/export.rb 0000644 0001750 0001750 00000021223 12101464040 020164 0 ustar terceiro terceiro =begin
= File
export.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume DelugrÈ
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
#
# Exports the document to a dot Graphiz file.
# _filename_:: The path where to save the file.
#
def export_to_graph(filename)
def appearance(object) #:nodoc:
label = object.type.to_s
case object
when Catalog
fontcolor = "red"
color = "mistyrose"
shape = "ellipse"
when Name, Number
label = object.value
fontcolor = "brown"
color = "lightgoldenrodyellow"
shape = "polygon"
when String
label = object.value unless (object.is_binary_data? or object.length > 50)
fontcolor = "red"
color = "white"
shape = "polygon"
when Array
fontcolor = "darkgreen"
color = "lightcyan"
shape = "ellipse"
else
fontcolor = "blue"
color = "aliceblue"
shape = "ellipse"
end
{ :label => label, :fontcolor => fontcolor, :color => color, :shape => shape }
end
def add_edges(pdf, fd, object) #:nodoc:
if object.is_a?(Array) or object.is_a?(ObjectStream)
object.each { |subobj|
subobj = subobj.solve if subobj.is_a?(Reference)
fd << "\t#{object.object_id} -> #{subobj.object_id}\n" unless subobj.nil?
}
elsif object.is_a?(Dictionary)
object.each_pair { |name, subobj|
subobj = subobj.solve if subobj.is_a?(Reference)
fd << "\t#{object.object_id} -> #{subobj.object_id} [label=\"#{name.value}\",fontsize=9];\n" unless subobj.nil?
}
end
if object.is_a?(Stream)
object.dictionary.each_pair { |key, value|
value = value.solve if value.is_a?(Reference)
fd << "\t#{object.object_id} -> #{value.object_id} [label=\"#{key.value}\",fontsize=9];\n" unless value.nil?
}
end
end
graphname = "PDF" if graphname.nil? or graphname.empty?
fd = File.open(filename, "w")
begin
fd << "digraph #{graphname} {\n\n"
objects = self.objects(:include_keys => false).find_all{ |obj| not obj.is_a?(Reference) }
objects.each { |object|
attr = appearance(object)
fd << "\t#{object.object_id} [label=\"#{attr[:label]}\",shape=#{attr[:shape]},color=#{attr[:color]},style=filled,fontcolor=#{attr[:fontcolor]},fontsize=16];\n"
if object.is_a?(Stream)
object.dictionary.each { |value|
unless value.is_a?(Reference)
attr = appearance(value)
fd << "\t#{value.object_id} [label=\"#{attr[:label]}\",shape=#{attr[:shape]},color=#{attr[:color]},style=filled,fontcolor=#{attr[:fontcolor]},fontsize=16];\n"
end
}
end
add_edges(self, fd, object)
}
fd << "\n}"
ensure
fd.close
end
end
#
# Exports the document to a GraphML file.
# _filename_:: The path where to save the file.
#
def export_to_graphml(filename)
def declare_node(id, attr) #:nodoc:
" \n" <<
" \n" <<
" \n" <<
" #{attr[:label]}\n" <<
#~ " \n" <<
" \n" <<
" \n" <<
" \n"
end
def declare_edge(id, src, dest, label = nil) #:nodoc:
" \n" <<
" \n" <<
" \n" <<
" \n" <<
" \n" <<
" #{label.to_s}\n" <<
" \n" <<
" \n" <<
" \n"
end
def appearance(object) #:nodoc:
label = object.type.to_s
case object
when Catalog
fontcolor = "red"
color = "mistyrose"
shape = "doublecircle"
when Name, Number
label = object.value
fontcolor = "orange"
color = "lightgoldenrodyellow"
shape = "polygon"
when String
label = object.value unless (object.is_binary_data? or object.length > 50)
fontcolor = "red"
color = "white"
shape = "polygon"
when Array
fontcolor = "green"
color = "lightcyan"
shape = "ellipse"
else
fontcolor = "blue"
color = "aliceblue"
shape = "ellipse"
end
{ :label => label, :fontcolor => fontcolor, :color => color, :shape => shape }
end
def add_edges(pdf, fd, object, id) #:nodoc:
if object.is_a?(Array) or object.is_a?(ObjectStream)
object.each { |subobj|
subobj = subobj.solve if subobj.is_a?(Reference)
unless subobj.nil?
fd << declare_edge("e#{id}", "n#{object.object_id}", "n#{subobj.object_id}")
id = id + 1
end
}
elsif object.is_a?(Dictionary)
object.each_pair { |name, subobj|
subobj = subobj.solve if subobj.is_a?(Reference)
unless subobj.nil?
fd << declare_edge("e#{id}", "n#{object.object_id}", "n#{subobj.object_id}", name.value)
id = id + 1
end
}
end
if object.is_a?(Stream)
object.dictionary.each_pair { |key, value|
value = value.solve if value.is_a?(Reference)
unless value.nil?
fd << declare_edge("e#{id}", "n#{object.object_id}", "n#{value.object_id}", key.value)
id = id + 1
end
}
end
id
end
@@edge_nb = 1
graphname = "PDF" if graphname.nil? or graphname.empty?
fd = File.open(filename, "w")
edge_nb = 1
begin
fd << '' << "\n"
fd << '' << "\n"
fd << '' << "\n"
fd << '' << "\n"
fd << "\n"
objects = self.objects(:include_keys => false).find_all{ |obj| not obj.is_a?(Reference) }
objects.each { |object|
fd << declare_node("n#{object.object_id}", appearance(object))
if object.is_a?(Stream)
object.dictionary.each { |value|
unless value.is_a?(Reference)
fd << declare_node(value.object_id, appearance(value))
end
}
end
edge_nb = add_edges(self, fd, object, edge_nb)
}
fd << '' << "\n"
fd << ''
ensure
fd.close
end
end
end
end
origami-pdf-1.2.7/lib/origami/numeric.rb 0000644 0001750 0001750 00000010231 12142214376 020314 0 ustar terceiro terceiro =begin
= File
numeric.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'delegate'
module Origami
class InvalidIntegerObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing a PDF number (Integer, or Real).
#
module Number
include Origami::Object
def ~
self.class.new(~self.to_i)
end
def |(val)
self.class.new(self.to_i | val)
end
def &(val)
self.class.new(self.to_i & val)
end
def ^(val)
self.class.new(self.to_i ^ val)
end
def <<(val)
self.class.new(self.to_i << val)
end
def >>(val)
self.class.new(self.to_i >> val)
end
def +(val)
self.class.new(self.to_i + val)
end
def -(val)
self.class.new(self.to_i - val)
end
def -@
self.class.new(-self.to_i)
end
def *(val)
self.class.new(self.to_i * val)
end
def /(val)
self.class.new(self.to_i / val)
end
def abs
self.class.new(self.to_i.abs)
end
def **(val)
self.class.new(self.to_i ** val)
end
def to_s
super(value.to_s)
end
module ClassMethods #:nodoc:all
def native_type; Number end
end
def self.included(receiver) #:nodoc:
receiver.extend(ClassMethods)
end
def self.native_type; Number end #:nodoc:
end
#
# Class representing an Integer Object.
#
class Integer < DelegateClass(Bignum)
include Number
TOKENS = [ "(\\+|-)?[\\d]+[^.]?" ] #:nodoc:
REGEXP_TOKEN = Regexp.new(TOKENS.first)
@@regexp = Regexp.new(WHITESPACES + "((\\+|-)?[\\d]+)")
#
# Creates a new Integer from a Ruby Fixnum / Bignum.
# _i_:: The Integer value.
#
def initialize(i = 0)
unless i.is_a?(::Integer)
raise TypeError, "Expected type Fixnum or Bignum, received #{i.class}."
end
super(i)
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
if not stream.scan(@@regexp)
raise InvalidIntegerObjectError, "Invalid integer format"
end
value = stream[2].to_i
int = Integer.new(value)
int.file_offset = offset
int
end
alias value to_i
end
class InvalidRealObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing a Real number Object.
# PDF real numbers are arbitrary precision numbers, depending on architectures.
#
class Real < DelegateClass(Float)
include Number
TOKENS = [ "(\\+|-)?([\\d]*\\.[\\d]+|[\\d]+\\.[\\d]*)([eE](\\+|-)?[\\d]+)?" ] #:nodoc:
REGEXP_TOKEN = Regexp.new(TOKENS.first)
@@regexp = Regexp.new(WHITESPACES + "(" + TOKENS.first + ")")
#
# Creates a new Real from a Ruby Float.
# _f_:: The new Real value.
#
def initialize(f = 0)
unless f.is_a?(Float)
raise TypeError, "Expected type Float, received #{f.class}."
end
super(f)
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
if not stream.scan(@@regexp)
raise InvalidRealObjectError, "Invalid real number format"
end
value = stream[2].to_f
real = Real.new(value)
real.file_offset = offset
real
end
alias value to_f
def to_s
sprintf("%f", self).sub(/\.0*$|(\.\d*[^0])0*$/, '\1')
end
end
end
origami-pdf-1.2.7/lib/origami/reference.rb 0000644 0001750 0001750 00000005360 12142214376 020617 0 ustar terceiro terceiro =begin
= File
reference.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class InvalidReferenceError < Exception #:nodoc:
end
#
# Class representing a Reference Object.
# Reference are like symbolic links pointing to a particular object into the file.
#
class Reference
include Origami::Object
TOKENS = [ "(\\d+)" + WHITESPACES + "(\\d+)" + WHITESPACES + "R" ] #:nodoc:
REGEXP_TOKEN = Regexp.new(TOKENS.first, Regexp::MULTILINE)
@@regexp = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
attr_accessor :refno, :refgen
def initialize(refno, refgen)
@refno, @refgen = refno, refgen
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
if stream.scan(@@regexp).nil?
raise InvalidReferenceError, "Bad reference to indirect objet format"
end
refno = stream[2].to_i
refgen = stream[4].to_i
ref = Reference.new(refno,refgen)
ref.file_offset = offset
ref
end
def solve
pdfdoc = self.pdf
if pdfdoc.nil?
raise InvalidReferenceError, "Not attached to any PDF"
end
target = pdfdoc.get_object(self)
if target.nil? and not Origami::OPTIONS[:ignore_bad_references]
raise InvalidReferenceError, "Cannot resolve reference : #{self.to_s}"
end
target or Null.new
end
def eql?(ref) #:nodoc
ref.is_a?(Reference) and ref.refno == @refno and ref.refgen == @refgen
end
def hash #:nodoc:
self.to_a.hash
end
def <=>(ref) #:nodoc
self.to_a <=> ref.to_a
end
#
# Returns a Ruby array with the object number and the generation this reference is pointing to.
#
def to_a
[@refno, @refgen]
end
def to_s #:nodoc:
super("#{@refno} #{@refgen} R")
end
#
# Returns self.
#
def value
self
end
def self.native_type ; Reference end
end
end
origami-pdf-1.2.7/lib/origami/name.rb 0000644 0001750 0001750 00000007205 12142214376 017601 0 ustar terceiro terceiro =begin
= File
name.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
if RUBY_VERSION < '1.9'
class EmptySymbol
def ==(sym)
sym.is_a?(EmptySymbol)
end
def id2name
""
end
alias to_s id2name
def to_sym
self
end
def to_o
Name.new("")
end
def inspect
":"
end
end
end
module Origami
REGULARCHARS = "([^ \\t\\r\\n\\0\\[\\]<>()%\\/]|#[a-fA-F0-9][a-fA-F0-9])*" #:nodoc:
class InvalidNameObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing a Name Object.
# Name objects are strings which identify some PDF file inner structures.
#
class Name #< DelegateClass(Symbol)
include Origami::Object
TOKENS = %w{ / } #:nodoc:
@@regexp = Regexp.new(WHITESPACES + TOKENS.first + "(" + REGULARCHARS + ")" + WHITESPACES) #:nodoc
#
# Creates a new Name.
# _name_:: A symbol representing the new Name value.
#
def initialize(name = "")
unless name.is_a?(Symbol) or name.is_a?(::String)
raise TypeError, "Expected type Symbol or String, received #{name.class}."
end
@value = name.to_s
super()
end
if RUBY_VERSION < '1.9'
def value
( @value.empty? ) ? EmptySymbol.new : @value.to_sym
end
else
def value
@value.to_sym
end
end
def ==(object) #:nodoc:
self.eql?(object) or @value.to_sym == object
end
def eql?(object) #:nodoc:
object.is_a?(Name) and self.to_s == object.to_s
end
def hash #:nodoc:
@value.hash
end
def to_s #:nodoc:
super(TOKENS.first + Name.expand(@value))
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
name =
if stream.scan(@@regexp).nil?
raise InvalidNameObjectError, "Bad name format"
else
value = stream[2]
Name.new(value.include?('#') ? contract(value) : value)
end
name.file_offset = offset
name
end
def self.contract(name) #:nodoc:
i = 0
name = name.dup
while i < name.length
if name[i,1] == "#"
digits = name[i+1, 2]
unless /^[A-Za-z0-9]{2}$/ === digits
raise InvalidNameObjectError, "Irregular use of # token"
end
char = digits.hex.chr
if char == "\0"
raise InvalidNameObjectError, "Null byte forbidden inside name definition"
end
name[i, 3] = char
end
i = i + 1
end
name
end
def self.expand(name) #:nodoc:
forbiddenchars = /[ #\t\r\n\0\[\]<>()%\/]/
name.gsub(forbiddenchars) do |c|
"#" + c[0].ord.to_s(16).rjust(2,"0")
end
end
def self.native_type ; Name end
end
end
origami-pdf-1.2.7/lib/origami/font.rb 0000644 0001750 0001750 00000016043 12142214376 017627 0 ustar terceiro terceiro =begin
= File
font.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
#
# Embedded font stream.
#
class FontStream < Stream
field :Subtype, :Type => Name
field :Length1, :Type => Integer
field :Length2, :Type => Integer
field :Length3, :Type => Integer
end
#
# Class representing a font details in a document.
#
class FontDescriptor < Dictionary
include StandardObject
FIXEDPITCH = 1 << 1
SERIF = 1 << 2
SYMBOLIC = 1 << 3
SCRIPT = 1 << 4
NONSYMBOLIC = 1 << 6
ITALIC = 1 << 7
ALLCAP = 1 << 17
SMALLCAP = 1 << 18
FORCEBOLD = 1 << 19
field :Type, :Type => Name, :Default => :FontDescriptor, :Required => true
field :FontName, :Type => Name, :Required => true
field :FontFamily, :Type => ByteString, :Version => "1.5"
field :FontStretch, :Type => Name, :Default => :Normal, :Version => "1.5"
field :FontWeight, :Type => Integer, :Default => 400, :Version => "1.5"
field :Flags, :Type => Integer, :Required => true
field :FontBBox, :Type => Array
field :ItalicAngle, :Type => Number, :Required => true
field :Ascent, :Type => Number
field :Descent, :Type => Number
field :Leading, :Type => Number, :Default => 0
field :CapHeight, :Type => Number
field :XHeight, :Type => Number, :Default => 0
field :StemV, :Type => Number
field :StemH, :Type => Number, :Default => 0
field :AvgWidth, :Type => Number, :Default => 0
field :MaxWidth, :Type => Number, :Default => 0
field :MissingWidth, :Type => Number, :Default => 0
field :FontFile, :Type => FontStream
field :FontFile2, :Type => FontStream, :Version => "1.1"
field :FontFile3, :Type => FontStream, :Version => "1.2"
field :CharSet, :Type => ByteString, :Version => "1.1"
end
#
# Class representing a character encoding in a document.
#
class Encoding < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Encoding
field :BaseEncoding, :Type => Name
field :Differences, :Type => Array
end
#
# Class representing a rendering font in a document.
#
class Font < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Font, :Required => true
field :Subtype, :Type => Name, :Required => true
field :Name, :Type => Name
field :FirstChar, :Type => Integer
field :LastChar, :Type => Integer
field :Widths, :Type => Array
field :FontDescriptor, :Type => FontDescriptor
field :Encoding, :Type => [ Name, Encoding ], :Default => :MacRomanEncoding
field :ToUnicode, :Type => Stream, :Version => "1.2"
# TODO: Type0 and CID Fonts
#
# Type1 Fonts.
#
class Type1 < Font
field :BaseFont, :Type => Name, :Required => true
field :Subtype, :Type => Name, :Default => :Type1, :Required => true
#
# 14 standard Type1 fonts.
#
module Standard
class TimesRoman < Type1
field :BaseFont, :Type => Name, :Default => :"Times-Roman", :Required => true
end
class Helvetica < Type1
field :BaseFont, :Type => Name, :Default => :Helvetica, :Required => true
end
class Courier < Type1
field :BaseFont, :Type => Name, :Default => :Courier, :Required => true
end
class Symbol < Type1
field :BaseFont, :Type => Name, :Default => :Symbol, :Required => true
end
class TimesBold < Type1
field :BaseFont, :Type => Name, :Default => :"Times-Bold", :Required => true
end
class HelveticaBold < Type1
field :BaseFont, :Type => Name, :Default => :"Helvetica-Bold", :Required => true
end
class CourierBold < Type1
field :BaseFont, :Type => Name, :Default => :"Courier-Bold", :Required => true
end
class ZapfDingbats < Type1
field :BaseFont, :Type => Name, :Default => :ZapfDingbats, :Required => true
end
class TimesItalic < Type1
field :BaseFont, :Type => Name, :Default => :"Times-Italic", :Required => true
end
class HelveticaOblique < Type1
field :BaseFont, :Type => Name, :Default => :"Helvetica-Oblique", :Required => true
end
class CourierOblique < Type1
field :BaseFont, :Type => Name, :Default => :"Courier-Oblique", :Required => true
end
class TimesBoldItalic < Type1
field :BaseFont, :Type => Name, :Default => :"Times-BoldItalic", :Required => true
end
class HelveticaBoldOblique < Type1
field :BaseFont, :Type => Name, :Default => :"Helvetica-BoldOblique", :Required => true
end
class CourierBoldOblique < Type1
field :BaseFont, :Type => Name, :Default => :"Courier-BoldOblique", :Required => true
end
end
end
#
# TrueType Fonts
#
class TrueType < Font
field :Subtype, :Type => Name, :Default => :TrueType, :Required => true
end
#
# Type 3 Fonts
#
class Type3 < Font
field :Subtype, :Type => Name, :Default => :Type3, :Required => true
field :FontBBox, :Type => Array, :Required => true
field :FontMatrix, :Type => Array, :Required => true
field :CharProcs, :Type => Dictionary, :Required => true
field :Resources, :Type => Dictionary, :Version => "1.2"
end
end
end
origami-pdf-1.2.7/lib/origami/null.rb 0000644 0001750 0001750 00000003100 12142214376 017621 0 ustar terceiro terceiro =begin
= File
null.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class InvalidNullObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing Null Object.
#
class Null
include Origami::Object
TOKENS = %w{ null } #:nodoc:
@@regexp = Regexp.new(WHITESPACES + TOKENS.first)
def initialize
super
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
if stream.skip(@@regexp).nil?
raise InvalidNullObjectError
end
null = Null.new
null.file_offset = offset
null
end
#
# Returns *nil*.
#
def value
nil
end
def to_s #:nodoc:
super(TOKENS.first)
end
def self.native_type ; Null end
end
end
origami-pdf-1.2.7/lib/origami/annotations.rb 0000644 0001750 0001750 00000064422 12122127175 021220 0 ustar terceiro terceiro =begin
= File
annotations.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume DelugrÈ
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
#
# Class representing an annotation.
# Annotations are objects which user can interact with.
#
class Annotation < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Annot
field :Subtype, :Type => Name, :Required => true
field :Rect, :Type => Array, :Default => [ 0 , 0 , 0 , 0 ], :Required => true
field :Contents, :Type => String
field :P, :Type => Dictionary, :Version => "1.3"
field :NM, :Type => String, :Version => "1.4"
field :M, :Type => ByteString, :Version => "1.1"
field :F, :Type => Integer, :Default => 0, :Version => "1.1"
field :AP, :Type => Dictionary, :Version => "1.2"
field :AS, :Type => Name, :Version => "1.2"
field :Border, :Type => Array, :Default => [ 0 , 0 , 1 ]
field :C, :Type => Array, :Version => "1.1"
field :StructParent, :Type => Integer, :Version => "1.3"
field :OC, :Type => Dictionary, :Version => "1.5"
def set_normal_appearance(apstm)
self.AP ||= AppearanceDictionary.new
self.AP[:N] = apstm
self
end
def set_rollover_appearance(apstm)
self.AP ||= AppearanceDictionary.new
self.AP[:R] = apstm
self
end
def set_down_appearance(apstm)
self.AP ||= AppearanceStream.new
self.AP[:D] = apstm
self
end
module Triggerable
def onMouseOver(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.E = action
end
def onMouseOut(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.X = action
end
def onMouseDown(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.D = action
end
def onMouseUp(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.U = action
end
def onFocus(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.Fo = action
end
def onBlur(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.Bl = action
end
def onPageOpen(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.PO = action
end
def onPageClose(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.PC = action
end
def onPageVisible(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.PV = action
end
def onPageInvisible(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= AdditionalActions.new
self.AA.PI = action
end
end
#
# Annotation flags
#
module Flags
INVISIBLE = 1 << 0
HIDDEN = 1 << 1
PRINT = 1 << 2
NOZOOM = 1 << 3
NOROTATE = 1 << 4
NOVIEW = 1 << 5
READONLY = 1 << 6
LOCKED = 1 << 7
TOGGLENOVIEW = 1 << 8
LOCKEDCONTENTS = 1 << 9
end
module Markup
def self.included(receiver)
receiver.field :T, :Type => String, :Version => "1.1"
receiver.field :Popup, :Type => Dictionary, :Version => "1.3"
receiver.field :CA, :Type => Number, :Default => 1.0, :Version => "1.4"
receiver.field :RC, :Type => [String, Stream], :Version => "1.5"
receiver.field :CreationDate, :Type => String, :Version => "1.5"
receiver.field :IRT, :Type => Dictionary, :Version => "1.5"
receiver.field :Subj, :Type => String, :Version => "1.5"
receiver.field :RT, :Type => Name, :Default => :R, :Version => "1.6"
receiver.field :IT, :Type => Name, :Version => "1.6"
receiver.field :ExData, :Type => Dictionary, :Version => "1.7"
end
end
class AppearanceDictionary < Dictionary
include StandardObject
field :N, :Type => [ Stream, Dictionary ], :Required => true
field :R, :Type => [ Stream, Dictionary ]
field :D, :Type => [ Stream, Dictionary ]
end
class AppearanceStream < Graphics::FormXObject ; end
class BorderStyle < Dictionary
include StandardObject
SOLID = :S
DASHED = :D
BEVELED = :B
INSET = :I
UNDERLINE = :U
field :Type, :Type => Name, :Default => :Border
field :W, :Type => Number, :Default => 1
field :S, :Type => Name, :Default => SOLID
field :D, :Type => Array, :Default => [ 3 ]
end
class AppearanceCharacteristics < Dictionary
include StandardObject
module CaptionStyle
CAPTIONONLY = 0
ICONONLY = 1
CAPTIONBELOW = 2
CAPTIONABOVE = 3
CAPTIONRIGHT = 4
CAPTIONLEFT = 5
CAPTIONOVERLAID = 6
end
field :R, :Type => Integer, :Default => 0
field :BC, :Type => Array
field :BG, :Type => Array
field :CA, :Type => String
field :RC, :Type => String
field :AC, :Type => String
field :I, :Type => Stream
field :RI, :Type => Stream
field :IX, :Type => Stream
field :IF, :Type => Dictionary
field :TP, :Type => Integer, :Default => CaptionStyle::CAPTIONONLY
end
class Shape < Annotation
include Markup
field :Subtype, :Type => Name, :Required => true
field :BS, :Type => Dictionary
field :IC, :Type => Array
field :BE, :Type => Dictionary, :Version => "1.5"
field :RD, :Type => Array, :Version => "1.5"
end
class Square < Shape
field :Subtype, :Type => Name, :Default => :Square, :Required => true
end
class Circle < Shape
field :Subtype, :Type => Name, :Default => :Circle, :Required => true
end
#
# Text annotation
#
class Text < Annotation
include Markup
module TextName
COMMENT = :C
KEY = :K
NOTE = :N
HELP = :H
NEWPARAGRAPH = :NP
PARAGRAPH = :P
INSERT = :I
end
field :Subtype, :Type => Name, :Default => :Text, :Required => true
field :Open, :Type => Boolean, :Default => false
field :Name, :Type => Name, :Default => TextName::NOTE
field :State, :Type => String, :Version => "1.5"
field :StateModel, :Type => String, :Version => "1.5"
def pre_build
model = self.StateModel
state = self.State
case model
when "Marked"
state = "Unmarked" if state.nil?
when "Review"
state = "None" if state.nil?
end
super
end
end
#
# FreeText Annotation
#
class FreeText < Annotation
include Markup
module Intent
FREETEXT = :FreeText
FREETEXTCALLOUT = :FreeTextCallout
FREETEXTTYPEWRITER = :FreeTextTypeWriter
end
field :Subtype, :Type => Name, :Default => :FreeText, :Required => true
field :DA, :Type => String, :Default => "/F1 10 Tf 0 g", :Required => true
field :Q, :Type => Integer, :Default => Field::TextAlign::LEFT, :Version => "1.4"
field :RC, :Type => [String, Stream], :Version => "1.5"
field :DS, :Type => String, :Version => "1.5"
field :CL, :Type => Array, :Version => "1.6"
field :IT, :Type => Name, :Default => Intent::FREETEXT, :Version => "1.6"
field :BE, :Type => Dictionary, :Version => "1.6"
field :RD, :Type => Array, :Version => "1.6"
field :BS, :Type => Dictionary, :Version => "1.6"
field :LE, :Type => Name, :Default => :None, :Version => "1.6"
end
#
# Class representing an link annotation.
#
class Link < Annotation
#
# The annotations highlighting mode, the visual effect to be used when the mouse button is pressed or held down inside its active area.
#
module Highlight
# No highlighting
NONE = :N
# Invert the contents of the annotation rectangle.
INVERT = :I
# Invert the annotations border.
OUTLINE = :O
# Display the annotation as if it were being pushed below the surface of the page
PUSH = :P
end
field :Subtype, :Type => Name, :Default => :Link, :Required => true
field :A, :Type => Dictionary, :Version => "1.1"
field :Dest, :Type => [ Array, Name, ByteString ]
field :H, :Type => Name, :Default => Highlight::INVERT, :Version => "1.2"
field :AP, :Type => Dictionary, :Version => "1.3"
field :QuadPoints, :Type => Array, :Version => "1.6"
end
#
# Class representing a file attachment annotation.
#
class FileAttachment < Annotation
include Markup
# Icons to be displayed for file attachment.
module Icons
GRAPH = :Graph
PAPERCLIP = :Paperclip
PUSHPIN = :PushPin
TAG = :Tag
end
field :Subtype, :Type => Name, :Default => :FileAttachment, :Required => true
field :FS, :Type => Dictionary, :Required => true
field :Name, :Type => Name, :Default => Icons::PUSHPIN
end
#
# Class representing a screen Annotation.
# A screen annotation specifies a region of a page upon which media clips may be played. It also serves as an object from which actions can be triggered.
#
class Screen < Annotation
include Triggerable
field :Subtype, :Type => Name, :Default => :Screen, :Required => true
field :T, :Type => String
field :MK, :Type => Dictionary
field :A, :Type => Dictionary, :Version => "1.1"
field :AA, :Type => Dictionary, :Version => "1.2"
end
class Sound < Annotation
include Markup
module Icons
SPEAKER = :Speaker
MIC = :Mic
end
field :Subtype, :Type => Name, :Default => :Sound, :Required => true
field :Sound, :Type => Stream, :Required => true
field :Name, :Type => Name, :Default => Icons::SPEAKER
end
class RichMedia < Annotation
field :Subtype, :Type => Name, :Default => :RichMedia, :Version => "1.7", :ExtensionLevel => 3, :Required => true
field :RichMediaSettings, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :RichMediaContent, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3, :Required => true
class Settings < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :RichMediaSettings, :Version => "1.7", :ExtensionLevel => 3
field :Activation, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :Deactivation, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
end
class Activation < Dictionary
include StandardObject
USER_ACTION = :XA
PAGE_OPEN = :PO
PAGE_VISIBLE = :PV
field :Type, :Type => Name, :Default => :RichMediaActivation, :Version => "1.7", :ExtensionLevel => 3
field :Condition, :Type => Name, :Default => USER_ACTION, :Version => "1.7", :ExtensionLevel => 3
field :Animation, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :View, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :Configuration, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :Presentation, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :Scripts, :Type => Array, :Version => "1.7", :ExtensionLevel => 3
end
class Deactivation < Dictionary
include StandardObject
USER_ACTION = :XD
PAGE_CLOSE = :PC
PAGE_INVISIBLE = :PV
field :Type, :Type => Name, :Default => :RichMediaDeactivation, :Version => "1.7", :ExtensionLevel => 3
field :Condition, :Type => Name, :Default => USER_ACTION, :Version => "1.7", :ExtensionLevel => 3
end
class Animation < Dictionary
include StandardObject
NONE = :None
LINEAR = :Linear
OSCILLATING = :Oscillating
field :Type, :Type => Name, :Default => :RichMediaAnimation, :Version => "1.7", :ExtensionLevel => 3
field :Subtype, :Type => Name, :Default => NONE, :Version => "1.7", :ExtensionLevel => 3
field :PlayCount, :Type => Integer, :Default => -1, :Version => "1.7", :ExtensionLevel => 3
field :Speed, :Type => Number, :Default => 1, :Version => "1.7", :ExtensionLevel => 3
end
class Presentation < Dictionary
include StandardObject
WINDOWED = :Windowed
EMBEDDED = :Embedded
field :Type, :Type => Name, :Default => :RichMediaPresentation, :Version => "1.7", :ExtensionLevel => 3
field :Style, :Type => Name, :Default => EMBEDDED, :Version => "1.7", :ExtensionLevel => 3
field :Window, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :Transparent, :Type => Boolean, :Default => false, :Version => "1.7", :ExtensionLevel => 3
field :NavigationPane, :Type => Boolean, :Default => false, :Version => "1.7", :ExtensionLevel => 3
field :Toolbar, :Type => Boolean, :Version => "1.7", :ExtensionLevel => 3
field :PassContextClick, :Type => Boolean, :Default => false, :Version => "1.7", :ExtensionLevel => 3
end
class Window < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :RichMediaWindow, :Version => "1.7", :ExtensionLevel => 3
field :Width, :Type => Dictionary, :Default => {:Default => 288, :Max => 576, :Min => 72}, :Version => "1.7", :ExtensionLevel => 3
field :Height, :Type => Dictionary, :Default => {:Default => 216, :Max => 432, :Min => 72}, :Version => "1.7", :ExtensionLevel => 3
field :Position, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
end
class Position < Dictionary
include StandardObject
NEAR = :Near
CENTER = :Center
FAR = :Far
field :Type, :Type => Name, :Default => :RichMediaPosition, :Version => "1.7", :ExtensionLevel => 3
field :HAlign, :Type => Name, :Default => FAR, :Version => "1.7", :ExtensionLevel => 3
field :VAlign, :Type => Name, :Default => NEAR, :Version => "1.7", :ExtensionLevel => 3
field :HOffset, :Type => Number, :Default => 18, :Version => "1.7", :ExtensionLevel => 3
field :VOffset, :Type => Number, :Default => 18, :Version => "1.7", :ExtensionLevel => 3
end
class Content < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :RichMediaContent, :Version => "1.7", :ExtensionLevel => 3
field :Assets, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :Configurations, :Type => Array, :Version => "1.7", :ExtensionLevel => 3
field :Views, :Type => Array, :Version => "1.7", :ExtensionLevel => 3
end
class Configuration < Dictionary
include StandardObject
U3D = :"3D"
FLASH = :Flash
SOUND = :Sound
VIDEO = :Video
field :Type, :Type => Name, :Default => :RichMediaConfiguration, :Version => "1.7", :ExtensionLevel => 3
field :Subtype, :Type => Name, :Version => "1.7", :ExtensionLevel => 3
field :Name, :Type => String, :Version => "1.7", :ExtensionLevel => 3
field :Instances, :Type => Array, :Version => "1.7", :ExtensionLevel => 3
end
class Instance < Dictionary
include StandardObject
U3D = :"3D"
FLASH = :Flash
SOUND = :Sound
VIDEO = :Video
field :Type, :Type => Name, :Default => :RichMediaInstance, :Version => "1.7", :ExtensionLevel => 3
field :Subtype, :Type => Name, :Version => "1.7", :ExtensionLevel => 3
field :Params, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :Asset, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
end
class Parameters < Dictionary
include StandardObject
module Binding
NONE = :None
FOREGROUND = :Foreground
BACKGROUND = :Background
MATERIAL = :Material
end
field :Type, :Type => Name, :Default => :RichMediaParams, :Version => "1.7", :ExtensionLevel => 3
field :FlashVars, :Type => [String, Stream], :Version => "1.7", :ExtensionLevel => 3
field :Binding, :Type => Name, :Default => Binding::NONE, :Version => "1.7", :ExtensionLevel => 3
field :BindingMaterialName, :Type => String, :Version => "1.7", :ExtensionLevel => 3
field :CuePoints, :Type => Array, :Default => [], :Version => "1.7", :ExtensionLevel => 3
field :Settings, :Type => [String, Stream], :Version => "1.7", :ExtensionLevel => 3
end
class CuePoint < Dictionary
include StandardObject
NAVIGATION = :Navigation
EVENT = :Event
field :Type, :Type => Name, :Default => :CuePoint, :Version => "1.7", :ExtensionLevel => 3
field :Subtype, :Type => Name, :Version => "1.7", :ExtensionLevel => 3
field :Name, :Type => String, :Version => "1.7", :ExtensionLevel => 3, :Required => true
field :Time, :Type => Number, :Version => "1.7", :ExtensionLevel => 3, :Required => true
field :A, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3, :Required => true
end
end
#
# Class representing a widget Annotation.
# Interactive forms use widget annotations to represent the appearance of fields and to manage user interactions.
#
class Widget < Annotation
include Field
include Triggerable
module Highlight
# No highlighting
NONE = :N
# Invert the contents of the annotation rectangle.
INVERT = :I
# Invert the annotations border.
OUTLINE = :O
# Display the annotation as if it were being pushed below the surface of the page
PUSH = :P
# Same as P.
TOGGLE = :T
end
field :Subtype, :Type => Name, :Default => :Widget, :Required => true
field :H, :Type => Name, :Default => Highlight::INVERT
field :MK, :Type => Dictionary
field :A, :Type => Dictionary, :Version => "1.1"
field :AA, :Type => Dictionary, :Version => "1.2"
field :BS, :Type => Dictionary, :Version => "1.2"
def onActivate(action)
unless action.is_a?(Action)
raise TypeError, "An Action object must be passed."
end
self.A = action
end
class Button < Widget
module Flags
NOTOGGLETOOFF = 1 << 14
RADIO = 1 << 15
PUSHBUTTON = 1 << 16
RADIOSINUNISON = 1 << 26
end
field :FT, :Type => Name, :Default => Field::Type::BUTTON, :Required => true
end
class PushButton < Button
def pre_build
self.Ff ||= 0
self.Ff |= Button::Flags::PUSHBUTTON
super
end
end
class CheckBox < Button
def pre_build
self.Ff ||= 0
self.Ff &= ~Button::Flags::RADIO
self.Ff &= ~Button::Flags::PUSHBUTTON
super
end
end
class Radio < Button
def pre_build
self.Ff ||= 0
self.Ff &= ~Button::Flags::PUSHBUTTON
self.Ff |= Button::Flags::RADIO
super
end
end
class Text < Widget
module Flags
MULTILINE = 1 << 12
PASSWORD = 1 << 13
FILESELECT = 1 << 20
DONOTSPELLCHECK = 1 << 22
DONOTSCROLL = 1 << 23
COMB = 1 << 24
RICHTEXT = 1 << 25
end
field :FT, :Type => Name, :Default => Field::Type::TEXT, :Required => true
field :MaxLen, :Type => Integer
end
class Choice < Widget
module Flags
COMBO = 1 << 17
EDIT = 1 << 18
SORT = 1 << 19
MULTISELECT = 1 << 21
DONOTSPELLCHECK = 1 << 22
COMMITONSELCHANGE = 1 << 26
end
field :FT, :Type => Name, :Default => Field::Type::CHOICE, :Required => true
field :Opt, :Type => Array
field :TI, :Type => Integer, :Default => 0
field :I, :Type => Array, :Version => "1.4"
end
class ComboBox < Choice
def pre_build
self.Ff ||= 0
self.Ff |= Choice::Flags::COMBO
super
end
end
class ListBox < Choice
def pre_build
self.Ff ||= 0
self.Ff &= ~Choice::Flags::COMBO
end
end
class Signature < Widget
field :FT, :Type => Name, :Default => Field::Type::SIGNATURE
field :Lock, :Type => Dictionary, :Version => "1.5"
field :SV, :Type => Dictionary, :Version => "1.5"
end
end
#
# Class representing additional actions which can be associated with an annotation having an AA field.
#
class AdditionalActions < Dictionary
include StandardObject
field :E, :Type => Dictionary, :Version => "1.2" # Mouse Enter
field :X, :Type => Dictionary, :Version => "1.2" # Mouse Exit
field :D, :Type => Dictionary, :Version => "1.2" # Mouse Down
field :U, :Type => Dictionary, :Version => "1.2" # Mouse Up
field :Fo, :Type => Dictionary, :Version => "1.2" # Focus
field :Bl, :Type => Dictionary, :Version => "1.2" # Blur
field :PO, :Type => Dictionary, :Version => "1.2" # Page Open
field :PC, :Type => Dictionary, :Version => "1.2" # Page Close
field :PV, :Type => Dictionary, :Version => "1.2" # Page Visible
field :PI, :Type => Dictionary, :Version => "1.2" # Page Invisible
end
end
end
origami-pdf-1.2.7/lib/origami/linearization.rb 0000644 0001750 0001750 00000022344 12101464040 021520 0 ustar terceiro terceiro =begin
= File
linearization.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
class LinearizationError < Exception #:nodoc:
end
#
# Returns whether the current document is linearized.
#
def is_linearized?
begin
obj = @revisions.first.objects.sort_by{|obj| obj.file_offset}.first
rescue
return false
end
obj.is_a?(Dictionary) and obj.has_key? :Linearized
end
#
# Tries to delinearize the document if it has been linearized.
# This operation is xrefs destructive, should be fixed in the future to merge tables.
#
def delinearize!
raise LinearizationError, 'Not a linearized document' unless is_linearized?
#
# Saves the first trailer.
#
prev_trailer = @revisions.first.trailer
lin_dict = @revisions.first.objects.first
hints = lin_dict[:H]
#
# Removes hint streams used by linearization.
#
if hints.is_a?(::Array)
if hints.length > 0 and hints[0].is_a?(Integer)
hint_stream = get_object_by_offset(hints[0])
delete_object(hint_stream.reference) if hint_stream.is_a?(Stream)
end
if hints.length > 2 and hints[2].is_a?(Integer)
overflow_stream = get_object_by_offset(hints[2])
delete_object(overflow_stream.reference) if overflow_stream.is_a?(Stream)
end
end
#
# Update the trailer.
#
last_trailer = (@revisions.last.trailer ||= Trailer.new)
last_trailer.dictionary ||= Dictionary.new
if prev_trailer.has_dictionary?
last_trailer.dictionary =
last_trailer.dictionary.merge(prev_trailer.dictionary)
else
xrefstm = get_object_by_offset(last_trailer.startxref)
raise LinearizationError,
'Cannot find trailer info while delinearizing document' unless xrefstm.is_a?(XRefStream)
last_trailer.dictionary[:Root] = xrefstm[:Root]
last_trailer.dictionary[:Encrypt] = xrefstm[:Encrypt]
last_trailer.dictionary[:Info] = xrefstm[:Info]
last_trailer.dictionary[:ID] = xrefstm[:ID]
end
#
# Remove all xrefs.
# Fix: Should be merged instead.
#
remove_xrefs
#
# Remove the linearization revision.
#
remove_revision(0)
self
end
end
#
# Class representing a linearization dictionary.
#
class Linearization < Dictionary
include StandardObject
field :Linearized, :Type => Real, :Default => 1.0, :Required => true
field :L, :Type => Integer, :Required => true
field :H, :Type => Array, :Required => true
field :O, :Type => Integer, :Required => true
field :E, :Type => Integer, :Required => true
field :N, :Type => Integer, :Required => true
field :T, :Type => Integer, :Required => true
field :P, :Type => Integer, :Default => 0
def initialize(hash = {})
super(hash, true)
end
end
class InvalidHintTableError < Exception #:nodoc:
end
module HintTable
module ClassMethods
def header_item_size(number, size)
@header_items_size[number] = size
end
def get_header_item_size(number)
@header_items_size[number]
end
def entry_item_size(number, size)
@entry_items_size[number] = size
end
def get_entry_item_size(number)
@entry_items_size[number]
end
def nb_header_items
@header_items_size.size
end
def nb_entry_items
@entry_items_size.size
end
end
def self.included(receiver)
receiver.instance_variable_set(:@header_items_size, {})
receiver.instance_variable_set(:@entry_items_size, {})
receiver.extend(ClassMethods)
end
attr_accessor :header_items
attr_accessor :entries
def initialize
@header_items = {}
@entries = []
end
def to_s
data = ""
nitems = self.class.nb_header_items
for no in (1..nitems)
unless @header_items.include?(no)
raise InvalidHintTableError, "Missing item #{no} in header section of #{self.class}"
end
value = @header_items[no]
item_size = self.class.get_header_item_size(no)
item_size = ((item_size + 7) >> 3) << 3
item_data = value.to_s(2)
item_data = "0" * (item_size - item_data.size) + item_data
data << [ item_data ].pack("B*")
end
i = 0
nitems = self.class.nb_entry_items
@entries.each do |entry|
for no in (1..items)
unless entry.include?(no)
raise InvalidHintTableError, "Missing item #{no} in entry #{i} of #{self.class}"
end
value = entry[no]
item_size = self.class.get_entry_item_size(no)
item_size = ((item_size + 7) >> 3) << 3
item_data = value.to_s(2)
item_data = "0" * (item_size - item_data.size) + item_data
data << [ item_data ].pack("B*")
end
i = i + 1
end
data
end
class PageOffsetTable
include HintTable
header_item_size 1, 32
header_item_size 2, 32
header_item_size 3, 16
header_item_size 4, 32
header_item_size 5, 16
header_item_size 6, 32
header_item_size 7, 16
header_item_size 8, 32
header_item_size 9, 16
header_item_size 10, 16
header_item_size 11, 16
header_item_size 12, 16
header_item_size 13, 16
entry_item_size 1, 16
entry_item_size 2, 16
entry_item_size 3, 16
entry_item_size 4, 16
entry_item_size 5, 16
entry_item_size 6, 16
entry_item_size 7, 16
end
class SharedObjectTable
include HintTable
header_item_size 1, 32
header_item_size 2, 32
header_item_size 3, 32
header_item_size 4, 32
header_item_size 5, 16
header_item_size 6, 32
header_item_size 7, 16
entry_item_size 1, 16
entry_item_size 2, 1
entry_item_size 3, 128
entry_item_size 4, 16
end
end
class InvalidHintStreamObjectError < InvalidStreamObjectError #:nodoc:
end
class HintStream < Stream
attr_accessor :page_offset_table
attr_accessor :shared_objects_table
attr_accessor :thumbnails_table
attr_accessor :outlines_table
attr_accessor :threads_table
attr_accessor :named_destinations_table
attr_accessor :interactive_forms_table
attr_accessor :information_dictionary_table
attr_accessor :logical_structure_table
attr_accessor :page_labels_table
attr_accessor :renditions_table
attr_accessor :embedded_files_table
field :S, :Type => Integer, :Required => true # SHared objects
field :T, :Type => Integer # Thumbnails
field :O, :Type => Integer # Outlines
field :A, :Type => Integer # Threads
field :E, :Type => Integer # Named destinations
field :V, :Type => Integer # Interactive forms
field :I, :Type => Integer # Information dictionary
field :C, :Type => Integer # Logical structure
field :L, :Type => Integer # Page labels
field :R, :Type => Integer # Renditions
field :B, :Type => Integer # Embedded files
def pre_build
if @page_offset_table.nil?
raise InvalidHintStreamObjectError, "No page offset hint table"
end
if @shared_objects_table.nil?
raise InvalidHintStreamObjectError, "No shared objects hint table"
end
@data = ""
save_table(@page_offset_table)
save_table(@shared_objects_table, :S)
save_table(@thumbnails_table, :T)
save_table(@outlines_table, :O)
save_table(@threads_table, :A)
save_table(@named_destinations_table, :E)
save_table(@interactive_forms_table, :V)
save_table(@information_dictionary_table, :I)
save_table(@logical_structure_table, :C)
save_table(@page_labels_table, :L)
save_table(@renditions_table, :R)
save_table(@embedded_files_table, :B)
super
end
private
def save_table(table, name = nil)
unless table.nil?
self[name] = @data.size if name
@data << table.to_s
end
end
end
end
origami-pdf-1.2.7/lib/origami/outline.rb 0000644 0001750 0001750 00000003772 12101464040 020333 0 ustar terceiro terceiro =begin
= File
outline.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class Outline < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Outlines
field :First, :Type => Dictionary
field :Last, :Type => Dictionary
field :Count, :Type => Integer
end
class OutlineItem < Dictionary
include StandardObject
module Style
ITALIC = 1 << 0
BOLD = 1 << 1
end
field :Title, :Type => String, :Required => true
field :Parent, :Type => Dictionary, :Required => true
field :Prev, :Type => Dictionary
field :Next, :Type => Dictionary
field :First, :Type => Dictionary
field :Last, :Type => Dictionary
field :Count, :Type => Integer
field :Dest, :Type => [ Name, String, Array ]
field :A, :Type => Dictionary, :Version => "1.1"
field :SE, :Type => Dictionary, :Version => "1.3"
field :C, :Type => Array, :Default => [ 0.0, 0.0, 0.0 ], :Version => "1.4"
field :F, :Type => Integer, :Default => 0, :Version => "1.4"
end
end
origami-pdf-1.2.7/lib/origami/parsers/ 0000755 0001750 0001750 00000000000 12427006355 020011 5 ustar terceiro terceiro origami-pdf-1.2.7/lib/origami/parsers/fdf.rb 0000644 0001750 0001750 00000002301 11646064605 021075 0 ustar terceiro terceiro =begin
= File
parsers/fdf.rb
= Info
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/parser'
module Origami
class FDF
class Parser < Origami::Parser
def parse(stream) #:nodoc:
super
fdf = Adobe::FDF.new
fdf.header = Adobe::FDF::Header.parse(stream)
@options[:callback].call(fdf.header)
loop do
break if (object = parse_object).nil?
fdf << object
end
fdf.revisions.first.xreftable = parse_xreftable
fdf.revisions.first.trailer = parse_trailer
fdf
end
end
end
end
origami-pdf-1.2.7/lib/origami/parsers/ppklite.rb 0000644 0001750 0001750 00000005757 11646064605 022030 0 ustar terceiro terceiro =begin
= File
parsers/ppklite.rb
= Info
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/parser'
module Origami
module Adobe
class PPKLite
class Parser < Origami::Parser
def parse(stream) #:nodoc:
super
addrbk = Adobe::PPKLite.new
addrbk.header = Adobe::PPKLite::Header.parse(stream)
@options[:callback].call(addrbk.header)
loop do
break if (object = parse_object).nil?
addrbk << object
end
addrbk.revisions.first.xreftable = parse_xreftable
addrbm.revisions.first.trailer = parse_trailer
book_specialize_entries(addrbk)
addrbk
end
def book_specialize_entries(addrbk) #:nodoc:
addrbk.revisions.first.body.each_pair do |ref, obj|
if obj.is_a?(Dictionary)
if obj[:Type] == :Catalog
o = Adobe::PPKLite::Catalog.new(obj)
o.generation, o.no, o.file_offset = obj.generation, obj.no, obj.file_offset
if o.PPK.is_a?(Dictionary) and o.PPK[:Type] == :PPK
o.PPK = Adobe::PPKLite::PPK.new(o.PPK)
if o.PPK.User.is_a?(Dictionary) and o.PPK.User[:Type] == :User
o.PPK.User = Adobe::PPKLite::UserList.new(o.PPK.User)
end
if o.PPK.AddressBook.is_a?(Dictionary) and o.PPK.AddressBook[:Type] == :AddressBook
o.PPK.AddressBook = Adobe::PPKLite::AddressList.new(o.PPK.AddressBook)
end
end
addrbk.revisions.first.body[ref] = o
elsif obj[:ABEType] == Adobe::PPKLite::Descriptor::USER
o = Adobe::PPKLite::User.new(obj)
o.generation, o.no, o.file_offset = obj.generation, obj.no, obj.file_offset
addrbk.revisions.first.body[ref] = o
elsif obj[:ABEType] == Adobe::PPKLite::Descriptor::CERTIFICATE
o = Adobe::PPKLite::Certificate.new(obj)
o.generation, o.no, o.file_offset = obj.generation, obj.no, obj.file_offset
addrbk.revisions.first.body[ref] = o
end
end
end
end
end
end
end
end
origami-pdf-1.2.7/lib/origami/parsers/pdf.rb 0000644 0001750 0001750 00000006051 12142214376 021107 0 ustar terceiro terceiro =begin
= File
parsers/pdf.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/parser'
module Origami
class PDF
class Parser < Origami::Parser
def initialize(params = {})
options =
{
:password => '', # Default password being tried when opening a protected document.
:prompt_password => Proc.new {
print "Password: "
gets.chomp
}, # Callback procedure to prompt password when document is encrypted.
:force => false # Force PDF header detection
}.update(params)
super(options)
end
private
def parse_initialize #:nodoc:
if @options[:force] == true
@data.skip_until(/%PDF-/).nil?
@data.pos = @data.pos - 5
end
pdf = PDF.new(self)
info "...Reading header..."
begin
pdf.header = PDF::Header.parse(@data)
@options[:callback].call(pdf.header)
rescue InvalidHeaderError => e
if @options[:ignore_errors] == true
warn "PDF header is invalid, ignoring..."
else
raise e
end
end
pdf
end
def parse_finalize(pdf) #:nodoc:
warn "This file has been linearized." if pdf.is_linearized?
if Origami::OPTIONS[:enable_type_propagation]
info "...Propagating types..."
@deferred_casts.each_pair do |ref, type|
type = [ type ] unless type.is_a?(::Array)
type.each do |hint|
pdf.cast_object(ref, hint)
end
end
end
#
# Decrypt encrypted file contents
#
if pdf.is_encrypted?
warn "This document contains encrypted data!"
passwd = @options[:password]
begin
pdf.decrypt(passwd)
rescue EncryptionInvalidPasswordError
if passwd.empty?
passwd = @options[:prompt_password].call
retry unless passwd.empty?
end
raise EncryptionInvalidPasswordError
end
end
if pdf.is_signed?
warn "This document has been signed!"
end
pdf
end
end
end
end
require 'origami/parsers/pdf/linear'
origami-pdf-1.2.7/lib/origami/parsers/pdf/ 0000755 0001750 0001750 00000000000 12427006355 020562 5 ustar terceiro terceiro origami-pdf-1.2.7/lib/origami/parsers/pdf/linear.rb 0000644 0001750 0001750 00000004116 12142214376 022361 0 ustar terceiro terceiro =begin
= File
parsers/linear.rb
= Info
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/parser'
require 'origami/pdf'
module Origami
class PDF
#
# Create a new PDF linear Parser.
#
class LinearParser < Parser
def parse(stream)
super
pdf = parse_initialize
#
# Parse each revision
#
revision = 0
until @data.eos? do
begin
pdf.add_new_revision unless revision.zero?
revision = revision + 1
info "...Parsing revision #{pdf.revisions.size}..."
loop do
break if (object = parse_object).nil?
pdf.insert(object)
end
pdf.revisions.last.xreftable = parse_xreftable
trailer = parse_trailer
pdf.revisions.last.trailer = trailer
xrefstm = pdf.get_object_by_offset(trailer.startxref) ||
(pdf.get_object_by_offset(trailer.XRefStm) if trailer.has_field? :XRefStm)
if not xrefstm.nil?
warn "Found a XRefStream for this revision at #{xrefstm.reference}"
pdf.revisions.last.xrefstm = xrefstm
end
rescue SystemExit
raise
rescue Exception => e
error "Cannot read : " + (@data.peek(10) + "...").inspect
error "Stopped on exception : " + e.message
break
end
end
parse_finalize(pdf)
end
end
end
end
origami-pdf-1.2.7/lib/origami/pdf.rb 0000644 0001750 0001750 00000072621 12204651545 017440 0 ustar terceiro terceiro =begin
= File
pdf.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/object'
require 'origami/null'
require 'origami/name'
require 'origami/dictionary'
require 'origami/reference'
require 'origami/boolean'
require 'origami/numeric'
require 'origami/string'
require 'origami/array'
require 'origami/stream'
require 'origami/filters'
require 'origami/trailer'
require 'origami/xreftable'
require 'origami/header'
require 'origami/functions'
require 'origami/catalog'
require 'origami/font'
require 'origami/page'
require 'origami/graphics'
require 'origami/destinations'
require 'origami/outline'
require 'origami/actions'
require 'origami/file'
require 'origami/acroform'
require 'origami/annotations'
require 'origami/3d'
require 'origami/signature'
require 'origami/webcapture'
require 'origami/metadata'
require 'origami/export'
require 'origami/webcapture'
require 'origami/encryption'
require 'origami/linearization'
require 'origami/obfuscation'
require 'origami/xfa'
require 'origami/javascript'
require 'origami/outputintents'
require 'origami/parsers/pdf'
module Origami
class InvalidPDFError < Exception #:nodoc:
end
#
# Main class representing a PDF file and its inner contents.
# A PDF file contains a set of Revision.
#
class PDF
#
# Class representing a particular revision in a PDF file.
# Revision contains :
# * A Body, which is a sequence of Object.
# * A XRef::Section, holding XRef information about objects in body.
# * A Trailer.
#
class Revision
attr_accessor :pdf
attr_accessor :body, :xreftable, :xrefstm, :trailer
def initialize(pdf)
@pdf = pdf
@body = {}
@xreftable = nil
@xrefstm = nil
@trailer = nil
end
def trailer=(trl)
trl.pdf = @pdf
@trailer = trl
end
def has_xreftable?
not @xreftable.nil?
end
def has_xrefstm?
not @xrefstm.nil?
end
def objects
@body.values
end
end
attr_accessor :header, :revisions
class << self
#
# Reads and parses a PDF file from disk.
#
def read(filename, options = {})
filename = File.expand_path(filename) if filename.is_a?(::String)
PDF::LinearParser.new(options).parse(filename)
end
#
# Creates a new PDF and saves it.
# If a block is passed, the PDF instance can be processed before saving.
#
def create(output, options = {})
pdf = PDF.new
yield(pdf) if block_given?
pdf.save(output, options)
end
alias write create
#
# Deserializes a PDF dump.
#
def deserialize(filename)
Zlib::GzipReader.open(filename) { |gz|
pdf = Marshal.load(gz.read)
}
pdf
end
end
#
# Creates a new PDF instance.
# _parser_:: The Parser object creating the document. If none is specified, some default structures are automatically created to get a minimal working document.
#
def initialize(parser = nil)
@header = PDF::Header.new
@revisions = []
add_new_revision
@revisions.first.trailer = Trailer.new
if parser
@parser = parser
else
init
end
end
#
# Original file name if parsed from disk, nil otherwise.
#
def original_filename
@parser.target_filename if @parser
end
#
# Original file size if parsed from a data stream, nil otherwise.
#
def original_filesize
@parser.target_filesize if @parser
end
#
# Original data parsed to create this document, nil if created from scratch.
#
def original_data
@parser.target_data if @parser
end
#
# Serializes the current PDF.
#
def serialize(filename)
parser = @parser
@parser = nil # do not serialize the parser
Zlib::GzipWriter.open(filename) { |gz|
gz.write Marshal.dump(self)
}
@parser = parser
self
end
#
# Saves the current document.
# _filename_:: The path where to save this PDF.
#
def save(path, params = {})
options =
{
:delinearize => true,
:recompile => true,
:decrypt => false
}
options.update(params)
if self.frozen? # incompatible flags with frozen doc (signed)
options[:recompile] =
options[:rebuildxrefs] =
options[:noindent] =
options[:obfuscate] = false
end
if path.respond_to?(:write)
fd = path
else
path = File.expand_path(path)
fd = File.open(path, 'w').binmode
end
intents_as_pdfa1 if options[:intent] =~ /pdf[\/-]?A1?/i
self.delinearize! if options[:delinearize] and self.is_linearized?
compile(options) if options[:recompile]
fd.write output(options)
fd.close
self
end
alias write save
#
# Saves the file up to given revision number.
# This can be useful to visualize the modifications over different incremental updates.
# _revision_:: The revision number to save.
# _filename_:: The path where to save this PDF.
#
def save_upto(revision, filename)
save(filename, :up_to_revision => revision)
end
#
# Returns an array of Objects whose content is matching _pattern_.
#
# def grep(*patterns)
#
# patterns.map! do |pattern|
# pattern.is_a?(::String) ? Regexp.new(Regexp.escape(pattern)) : pattern
# end
#
# unless patterns.all? { |pattern| pattern.is_a?(Regexp) }
# raise TypeError, "Expected a String or Regexp"
# end
#
# result = []
# objects.each do |obj|
# begin
# case obj
# when String, Name
# result << obj if patterns.any?{|pattern| obj.value.to_s.match(pattern)}
# when Stream
# result << obj if patterns.any?{|pattern| obj.data.match(pattern)}
# end
# rescue Exception => e
# puts "[#{e.class}] #{e.message}"
#
# next
# end
# end
#
# result
# end
#
# Returns an array of strings and streams matching the given pattern.
#
def grep(*patterns) #:nodoc:
patterns.map! do |pattern|
if pattern.is_a?(::String)
Regexp.new(Regexp.escape(pattern), Regexp::IGNORECASE)
else
pattern
end
end
unless patterns.all? { |pattern| pattern.is_a?(Regexp) }
raise TypeError, "Expected a String or Regexp"
end
objset = []
self.indirect_objects.each do |indobj|
case indobj
when Stream then
objset.push indobj
objset.concat(indobj.dictionary.strings_cache)
objset.concat(indobj.dictionary.names_cache)
when Name,String then objset.push indobj
when Dictionary,Array then
objset.concat(indobj.strings_cache)
objset.concat(indobj.names_cache)
end
end
objset.delete_if do |obj|
begin
case obj
when String, Name
not patterns.any?{|pattern| obj.value.to_s.match(pattern)}
when Stream
not patterns.any?{|pattern| obj.data.match(pattern)}
end
rescue Exception => e
true
end
end
end
#
# Returns an array of Objects whose name (in a Dictionary) is matching _pattern_.
#
def ls(*patterns)
return objects(:include_keys => false) if patterns.empty?
result = []
patterns.map! do |pattern|
pattern.is_a?(::String) ? Regexp.new(Regexp.escape(pattern)) : pattern
end
objects(:only_keys => true).each do |key|
if patterns.any?{ |pattern| key.value.to_s.match(pattern) }
value = key.parent[key]
result << ( value.is_a?(Reference) ? value.solve : value )
end
end
result
end
#
# Returns an array of Objects whose name (in a Dictionary) is matching _pattern_.
# Do not follow references.
#
def ls_no_follow(*patterns)
return objects(:include_keys => false) if patterns.empty?
result = []
patterns.map! do |pattern|
pattern.is_a?(::String) ? Regexp.new(Regexp.escape(pattern)) : pattern
end
objects(:only_keys => true).each do |key|
if patterns.any?{ |pattern| key.value.to_s.match(pattern) }
value = key.parent[key]
result << value
end
end
result
end
#
# Returns an array of objects matching specified block.
#
def find(params = {}, &b)
options =
{
:only_indirect => false
}
options.update(params)
objset = (options[:only_indirect] == true) ?
self.indirect_objects : self.objects
objset.find_all(&b)
end
#
# Returns an array of objects embedded in the PDF body.
# _include_objstm_:: Whether it shall return objects embedded in object streams.
# Note : Shall return to an iterator for Ruby 1.9 comp.
#
def objects(params = {})
def append_subobj(root, objset, opts)
if objset.find{ |o| root.equal?(o) }.nil?
objset << root unless opts[:only_keys]
if root.is_a?(Dictionary)
root.each_pair { |name, value|
objset << name if opts[:only_keys]
append_subobj(name, objset, opts) if opts[:include_keys] and not opts[:only_keys]
append_subobj(value, objset, opts)
}
elsif root.is_a?(Array) or (root.is_a?(ObjectStream) and opts[:include_objectstreams])
root.each { |subobj| append_subobj(subobj, objset, opts) }
end
end
end
options =
{
:include_objectstreams => true,
:include_keys => true,
:only_keys => false
}
options.update(params)
options[:include_keys] |= options[:only_keys]
objset = []
@revisions.each do |revision|
revision.objects.each do |object|
append_subobj(object, objset, options)
end
end
objset
end
#
# Return an array of indirect objects.
#
def indirect_objects
@revisions.inject([]) do |set, rev| set.concat(rev.objects) end
end
alias :root_objects :indirect_objects
#
# Adds a new object to the PDF file.
# If this object has no version number, then a new one will be automatically computed and assignated to him.
# It returns a Reference to this Object.
# _object_:: The object to add.
#
def <<(object)
owner = object.pdf
#
# Does object belongs to another PDF ?
#
if owner and not owner.equal?(self)
import object
else
add_to_revision(object, @revisions.last)
end
end
alias :insert :<<
#
# Similar to PDF#insert or PDF#<<, but for an object belonging to another document.
# Object will be recursively copied and new version numbers will be assigned.
# Returns the new reference to the imported object.
# _object_:: The object to import.
#
def import(object)
self.insert(object.export)
end
#
# Adds a new object to a specific revision.
# If this object has no version number, then a new one will be automatically computed and assignated to him.
# It returns a Reference to this Object.
# _object_:: The object to add.
# _revision_:: The revision to add the object to.
#
def add_to_revision(object, revision)
object.set_indirect(true)
object.set_pdf(self)
object.no, object.generation = alloc_new_object_number if object.no == 0
revision.body[object.reference] = object
object.reference
end
#
# Ends the current Revision, and starts a new one.
#
def add_new_revision
root = @revisions.last.trailer[:Root] unless @revisions.empty?
@revisions << Revision.new(self)
@revisions.last.trailer = Trailer.new
@revisions.last.trailer.Root = root
self
end
#
# Removes a whole document revision.
# _index_:: Revision index, first is 0.
#
def remove_revision(index)
if index < 0 or index > @revisions.size
raise IndexError, "Not a valid revision index"
end
if @revisions.size == 1
raise InvalidPDFError, "Cannot remove last revision"
end
@revisions.delete_at(index)
self
end
#
# Looking for an object present at a specified file offset.
#
def get_object_by_offset(offset) #:nodoc:
self.indirect_objects.find { |obj| obj.file_offset == offset }
end
#
# Remove an object.
#
def delete_object(no, generation = 0)
case no
when Reference
target = no
when ::Integer
target = Reference.new(no, generation)
else
raise TypeError, "Invalid parameter type : #{no.class}"
end
@revisions.each do |rev|
rev.body.delete(target)
end
end
#
# Search for an indirect object in the document.
# _no_:: Reference or number of the object.
# _generation_:: Object generation.
#
def get_object(no, generation = 0, use_xrefstm = true) #:nodoc:
case no
when Reference
target = no
when ::Integer
target = Reference.new(no, generation)
when Origami::Object
return no
else
raise TypeError, "Invalid parameter type : #{no.class}"
end
#
# Search through accessible indirect objects.
#
@revisions.each do |rev|
return rev.body[target] if rev.body.include?(target)
end
# Look into XRef streams.
if use_xrefstm == true
if @revisions.last.has_xrefstm?
xrefstm = @revisions.last.xrefstm
done = []
while xrefstm.is_a?(XRefStream) and not done.include?(xrefstm)
xref = xrefstm.find(target.refno)
#
# We found a matching XRef.
#
if xref.is_a?(XRefToCompressedObj)
objstm = get_object(xref.objstmno, 0, false)
object = objstm.extract_by_index(xref.index)
if object.is_a?(Origami::Object) and object.no == target.refno
return object
else
return objstm.extract(target.refno)
end
elsif xrefstm.has_field?(:Prev)
done << xrefstm
xrefstm = get_object_by_offset(xrefstm.Prev)
else
break
end
end
end
#
# Lastly search directly into Object streams (might be very slow).
#
@revisions.each do |rev|
streams = rev.objects.find_all{|obj| obj.is_a?(ObjectStream) and obj.include?(target.refno)}
return streams.first.extract(target.refno) unless streams.empty?
end
nil
end
end
alias :[] :get_object
def cast_object(reference, type) #:nodoc:
@revisions.each do |rev|
if rev.body.include?(reference) and type < rev.body[reference].class
rev.body[reference] = rev.body[reference].cast_to(type)
end
end
end
#
# Returns a new number/generation for future object.
#
def alloc_new_object_number
no = 1
# Deprecated number allocation policy (first available)
#no = no + 1 while get_object(no)
objset = self.indirect_objects
self.indirect_objects.find_all{|obj| obj.is_a?(ObjectStream)}.each do |objstm|
objstm.each{|obj| objset << obj}
end
allocated = objset.collect{|obj| obj.no}.compact
no = allocated.max + 1 unless allocated.empty?
[ no, 0 ]
end
##########################
private
##########################
#
# Compute and update XRef::Section for each Revision.
#
def rebuildxrefs
size = 0
startxref = @header.to_s.size
@revisions.each do |revision|
revision.objects.each do |object|
startxref += object.to_s.size
end
size += revision.body.size
revision.xreftable = buildxrefs(revision.objects)
revision.trailer ||= Trailer.new
revision.trailer.Size = size + 1
revision.trailer.startxref = startxref
startxref += revision.xreftable.to_s.size + revision.trailer.to_s.size
end
self
end
#
# This method is meant to recompute, verify and correct main PDF structures, in order to output a proper file.
# * Allocates objects references.
# * Sets some objects missing required values.
#
def compile(options = {})
#
# A valid document must have at least one page.
#
append_page if pages.empty?
#
# Allocates object numbers and creates references.
# Invokes object finalization methods.
#
if self.is_a?(Encryption::EncryptedDocument)
physicalize(options)
else
physicalize
end
#
# Sets the PDF version header.
#
version, level = version_required
@header.majorversion = version[0,1].to_i
@header.minorversion = version[2,1].to_i
set_extension_level(version, level) if level > 0
self
end
#
# Cleans the document from its references.
# Indirects objects are made direct whenever possible.
# TODO: Circuit-checking to avoid infinite induction
#
def logicalize #:nodoc:
fail "Not yet supported"
processed = []
def convert(root) #:nodoc:
replaced = []
if root.is_a?(Dictionary) or root.is_a?(Array)
root.each { |obj|
convert(obj)
}
root.map! { |obj|
if obj.is_a?(Reference)
target = obj.solve
# Streams can't be direct objects
if target.is_a?(Stream)
obj
else
replaced << obj
target
end
else
obj
end
}
end
replaced
end
@revisions.each do |revision|
revision.objects.each do |obj|
processed.concat(convert(obj))
end
end
end
#
# Converts a logical PDF view into a physical view ready for writing.
#
def physicalize
#
# Indirect objects are added to the revision and assigned numbers.
#
def build(obj, revision) #:nodoc:
#
# Finalize any subobjects before building the stream.
#
if obj.is_a?(ObjectStream)
obj.each do |subobj|
build(subobj, revision)
end
end
obj.pre_build
if obj.is_a?(Dictionary) or obj.is_a?(Array)
obj.map! do |subobj|
if subobj.is_indirect?
if get_object(subobj.reference)
subobj.reference
else
ref = add_to_revision(subobj, revision)
build(subobj, revision)
ref
end
else
subobj
end
end
obj.each do |subobj|
build(subobj, revision)
end
elsif obj.is_a?(Stream)
build(obj.dictionary, revision)
end
obj.post_build
end
indirect_objects_by_rev.each do |obj, revision|
build(obj, revision)
end
self
end
#
# Returns the final binary representation of the current document.
#
def output(params = {})
has_objstm = self.indirect_objects.any?{|obj| obj.is_a?(ObjectStream)}
options =
{
:rebuildxrefs => true,
:noindent => false,
:obfuscate => false,
:use_xrefstm => has_objstm,
:use_xreftable => (not has_objstm),
:up_to_revision => @revisions.size
}
options.update(params)
options[:up_to_revision] = @revisions.size if options[:up_to_revision] > @revisions.size
# Reset to default params if no xrefs are chosen (hybrid files not supported yet)
if options[:use_xrefstm] == options[:use_xreftable]
options[:use_xrefstm] = has_objstm
options[:use_xreftable] = (not has_objstm)
end
# Get trailer dictionary
trailer_info = get_trailer_info
if trailer_info.nil?
raise InvalidPDFError, "No trailer information found"
end
trailer_dict = trailer_info.dictionary
prev_xref_offset = nil
xrefstm_offset = nil
xreftable_offset = nil
# Header
bin = ""
bin << @header.to_s
# For each revision
@revisions[0, options[:up_to_revision]].each do |rev|
# Create xref table/stream.
if options[:rebuildxrefs] == true
lastno_table, lastno_stm = 0, 0
brange_table, brange_stm = 0, 0
xrefs_stm = [ XRef.new(0, 0, XRef::FREE) ]
xrefs_table = [ XRef.new(0, XRef::FIRSTFREE, XRef::FREE) ]
if options[:use_xreftable] == true
xrefsection = XRef::Section.new
end
if options[:use_xrefstm] == true
xrefstm = rev.xrefstm || XRefStream.new
if xrefstm == rev.xrefstm
xrefstm.clear
else
add_to_revision(xrefstm, rev)
end
end
end
objset = rev.objects
objset.find_all{|obj| obj.is_a?(ObjectStream)}.each do |objstm|
objset.concat objstm.objects
end if options[:rebuildxrefs] == true and options[:use_xrefstm] == true
# For each object, in number order
objset.sort.each do |obj|
# Create xref entry.
if options[:rebuildxrefs] == true
# Adding subsections if needed
if options[:use_xreftable] and (obj.no - lastno_table).abs > 1
xrefsection << XRef::Subsection.new(brange_table, xrefs_table)
xrefs_table.clear
brange_table = obj.no
end
if options[:use_xrefstm] and (obj.no - lastno_stm).abs > 1
xrefs_stm.each do |xref| xrefstm << xref end
xrefstm.Index ||= []
xrefstm.Index << brange_stm << xrefs_stm.length
xrefs_stm.clear
brange_stm = obj.no
end
# Process embedded objects
if options[:use_xrefstm] and obj.parent != obj and obj.parent.is_a?(ObjectStream)
index = obj.parent.index(obj.no)
xrefs_stm << XRefToCompressedObj.new(obj.parent.no, index)
lastno_stm = obj.no
else
xrefs_stm << XRef.new(bin.size, obj.generation, XRef::USED)
xrefs_table << XRef.new(bin.size, obj.generation, XRef::USED)
lastno_table = lastno_stm = obj.no
end
end
if obj.parent == obj or not obj.parent.is_a?(ObjectStream)
# Finalize XRefStm
if options[:rebuildxrefs] == true and options[:use_xrefstm] == true and obj == xrefstm
xrefstm_offset = bin.size
xrefs_stm.each do |xref| xrefstm << xref end
xrefstm.W = [ 1, (xrefstm_offset.to_s(2).size + 7) >> 3, 2 ]
if xrefstm.DecodeParms.is_a?(Dictionary) and xrefstm.DecodeParms.has_key?(:Columns)
xrefstm.DecodeParms[:Columns] = xrefstm.W[0] + xrefstm.W[1] + xrefstm.W[2]
end
xrefstm.Index ||= []
xrefstm.Index << brange_stm << xrefs_stm.size
xrefstm.dictionary = xrefstm.dictionary.merge(trailer_dict)
xrefstm.Prev = prev_xref_offset
rev.trailer.dictionary = nil
add_to_revision(xrefstm, rev)
xrefstm.pre_build
xrefstm.post_build
end
# Output object code
if (obj.is_a?(Dictionary) or obj.is_a?(Stream)) and options[:noindent]
bin << obj.to_s(0)
else
bin << obj.to_s
end
end
end
rev.trailer ||= Trailer.new
# XRef table
if options[:rebuildxrefs] == true
if options[:use_xreftable] == true
table_offset = bin.size
xrefsection << XRef::Subsection.new(brange_table, xrefs_table)
rev.xreftable = xrefsection
rev.trailer.dictionary = trailer_dict
rev.trailer.Size = objset.size + 1
rev.trailer.Prev = prev_xref_offset
rev.trailer.XRefStm = xrefstm_offset if options[:use_xrefstm] == true
end
startxref = options[:use_xreftable] == true ? table_offset : xrefstm_offset
rev.trailer.startxref = prev_xref_offset = startxref
end # end each rev
# Trailer
bin << rev.xreftable.to_s if options[:use_xreftable] == true
bin << (options[:obfuscate] == true ? rev.trailer.to_obfuscated_str : rev.trailer.to_s)
end
bin
end
#
# Instanciates basic structures required for a valid PDF file.
#
def init
catalog = (self.Catalog = (get_doc_attr(:Root) || Catalog.new))
catalog.Pages = PageTreeNode.new.set_indirect(true)
@revisions.last.trailer.Root = catalog.reference
self
end
def filesize #:nodoc:
output(:rebuildxrefs => false).size
end
def version_required #:nodoc:
max = [ 1.0, 0 ]
@revisions.each { |revision|
revision.objects.each { |object|
current = object.pdf_version_required
max = current if (current <=> max) > 0
}
}
max[0] = max[0].to_s
max
end
def indirect_objects_by_rev #:nodoc:
@revisions.inject([]) do |set,rev|
objset = rev.objects
set.concat(objset.zip(::Array.new(objset.length, rev)))
end
end
#
# Compute and update XRef::Section for each Revision.
#
def rebuild_dummy_xrefs #:nodoc
def build_dummy_xrefs(objects)
lastno = 0
brange = 0
xrefs = [ XRef.new(0, XRef::FIRSTFREE, XRef::FREE) ]
xrefsection = XRef::Section.new
objects.sort.each { |object|
if (object.no - lastno).abs > 1
xrefsection << XRef::Subsection.new(brange, xrefs)
brange = object.no
xrefs.clear
end
xrefs << XRef.new(0, 0, XRef::FREE)
lastno = object.no
}
xrefsection << XRef::Subsection.new(brange, xrefs)
xrefsection
end
size = 0
startxref = @header.to_s.size
@revisions.each do |revision|
revision.objects.each do |object|
startxref += object.to_s.size
end
size += revision.body.size
revision.xreftable = build_dummy_xrefs(revision.objects)
revision.trailer ||= Trailer.new
revision.trailer.Size = size + 1
revision.trailer.startxref = startxref
startxref += revision.xreftable.to_s.size + revision.trailer.to_s.size
end
self
end
#
# Build a xref section from a set of objects.
#
def buildxrefs(objects) #:nodoc:
lastno = 0
brange = 0
xrefs = [ XRef.new(0, XRef::FIRSTFREE, XRef::FREE) ]
xrefsection = XRef::Section.new
objects.sort.each { |object|
if (object.no - lastno).abs > 1
xrefsection << XRef::Subsection.new(brange, xrefs)
brange = object.no
xrefs.clear
end
xrefs << XRef.new(get_object_offset(object.no, object.generation), object.generation, XRef::USED)
lastno = object.no
}
xrefsection << XRef::Subsection.new(brange, xrefs)
xrefsection
end
def delete_revision(ngen) #:nodoc:
@revisions.delete_at[ngen]
end
def get_revision(ngen) #:nodoc:
@revisions[ngen].body
end
def get_object_offset(no,generation) #:nodoc:
objectoffset = @header.to_s.size
@revisions.each do |revision|
revision.objects.sort.each do |object|
if object.no == no and object.generation == generation then return objectoffset
else
objectoffset += object.to_s.size
end
end
objectoffset += revision.xreftable.to_s.size
objectoffset += revision.trailer.to_s.size
end
nil
end
end
end
origami-pdf-1.2.7/lib/origami/graphics/ 0000755 0001750 0001750 00000000000 12427006355 020132 5 ustar terceiro terceiro origami-pdf-1.2.7/lib/origami/graphics/render.rb 0000644 0001750 0001750 00000002676 12101464040 021735 0 ustar terceiro terceiro =begin
= File
graphics/render.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Graphics
module Canvas
attr_reader :gs
def initialize
@gs = Graphics::State.new
end
def clear
@gs.reset
end
def write_text(s); end
def stroke_path; end
def fill_path; end
end
class DummyCanvas
include Canvas
end
class TextCanvas
include Canvas
def initialize(output = STDOUT, columns = 80, lines = 25)
super()
@output = output
@columns, @lines = columns, lines
end
def write_text(s)
@output.print(s)
end
end
end
end
origami-pdf-1.2.7/lib/origami/graphics/xobject.rb 0000644 0001750 0001750 00000066672 12144406641 022134 0 ustar terceiro terceiro =begin
= File
graphics/xobject.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
#
# A class representing a Stream containing the contents of a Page.
#
class ContentStream < Stream
DEFAULT_SIZE = 12
DEFAULT_FONT = :F1
DEFAULT_LEADING = 20
DEFAULT_STROKE_COLOR = Graphics::Color::GrayScale.new(0.0)
DEFAULT_FILL_COLOR = Graphics::Color::GrayScale.new(1.0)
DEFAULT_LINECAP = Graphics::LineCapStyle::BUTT_CAP
DEFAULT_LINEJOIN = Graphics::LineJoinStyle::MITER_JOIN
DEFAULT_DASHPATTERN = Graphics::DashPattern.new([], 0)
DEFAULT_LINEWIDTH = 1.0
attr_reader :instructions
attr_accessor :canvas
def initialize(rawdata = "", dictionary = {})
@instructions = nil
@canvas = Graphics::DummyCanvas.new
super(rawdata, dictionary)
end
def render(engine)
load! if @instructions.nil?
@instructions.each do |instruction|
instruction.render(engine)
end
nil
end
def pre_build #:nodoc:
load! if @instructions.nil?
if @canvas.gs.text_state.is_in_text_object?
@instructions << PDF::Instruction.new('ET').render(@canvas)
end
@data = @instructions.join
super
end
def instructions
load! if @instructions.nil?
@instructions
end
def draw_image(name, attr = {})
load! if @instructions.nil?
x, y = attr[:x], attr[:y]
@instructions << PDF::Instruction.new('q')
@instructions << PDF::Instruction.new('cm', 300, 0, 0, 300, x, y)
@instructions << PDF::Instruction.new('Do', name)
@instructions << PDF::Instruction.new('Q')
end
#
# Draw a straight line from the point at coord _from_, to the point at coord _to_.
#
def draw_line(from, to, attr = {})
draw_polygon([from, to], attr)
end
#
# Draw a polygon from a array of coordinates.
#
def draw_polygon(coords = [], attr = {})
load! if @instructions.nil?
stroke_color = attr[:stroke_color] || DEFAULT_STROKE_COLOR
fill_color = attr[:fill_color] || DEFAULT_FILL_COLOR
line_cap = attr[:line_cap] || DEFAULT_LINECAP
line_join = attr[:line_join] || DEFAULT_LINEJOIN
line_width = attr[:line_width] || DEFAULT_LINEWIDTH
dash_pattern = attr[:dash] || DEFAULT_DASHPATTERN
stroke = attr[:stroke].nil? ? true : attr[:stroke]
fill = attr[:fill].nil? ? false : attr[:fill]
stroke = true if fill == false and stroke == false
set_fill_color(fill_color) if fill
set_stroke_color(stroke_color) if stroke
set_line_width(line_width)
set_line_cap(line_cap)
set_line_join(line_join)
set_dash_pattern(dash_pattern)
if @canvas.gs.text_state.is_in_text_object?
@instructions << PDF::Instruction.new('ET').render(@canvas)
end
unless coords.size < 1
x,y = coords.slice!(0)
@instructions << PDF::Instruction.new('m',x,y).render(@canvas)
coords.each do |px,py|
@instructions << PDF::Instruction.new('l',px,py).render(@canvas)
end
@instructions << (i =
if stroke and not fill
PDF::Instruction.new('s')
elsif fill and not stroke
PDF::Instruction.new('f')
elsif fill and stroke
PDF::Instruction.new('b')
end
)
i.render(@canvas)
end
self
end
#
# Draw a rectangle at position (_x_,_y_) with defined _width_ and _height_.
#
def draw_rectangle(x, y, width, height, attr = {})
load! if @instructions.nil?
stroke_color = attr[:stroke_color] || DEFAULT_STROKE_COLOR
fill_color = attr[:fill_color] || DEFAULT_FILL_COLOR
line_cap = attr[:line_cap] || DEFAULT_LINECAP
line_join = attr[:line_join] || DEFAULT_LINEJOIN
line_width = attr[:line_width] || DEFAULT_LINEWIDTH
dash_pattern = attr[:dash] || DEFAULT_DASHPATTERN
stroke = attr[:stroke].nil? ? true : attr[:stroke]
fill = attr[:fill].nil? ? false : attr[:fill]
stroke = true if fill == false and stroke == false
set_fill_color(fill_color) if fill
set_stroke_color(stroke_color) if stroke
set_line_width(line_width)
set_line_cap(line_cap)
set_line_join(line_join)
set_dash_pattern(dash_pattern)
if @canvas.gs.text_state.is_in_text_object?
@instructions << PDF::Instruction.new('ET').render(@canvas)
end
@instructions << PDF::Instruction.new('re', x,y,width,height).render(@canvas)
@instructions << (i =
if stroke and not fill
PDF::Instruction.new('S')
elsif fill and not stroke
PDF::Instruction.new('f')
elsif fill and stroke
PDF::Instruction.new('B')
end
)
i.render(@canvas)
self
end
#
# Adds text to the content stream with custom formatting attributes.
# _text_:: Text to write.
# _attr_:: Formatting attributes.
#
def write(text, attr = {})
load! if @instructions.nil?
x,y = attr[:x], attr[:y]
font = attr[:font] || DEFAULT_FONT
size = attr[:size] || DEFAULT_SIZE
leading = attr[:leading] || DEFAULT_LEADING
color = attr[:color] || attr[:fill_color] || DEFAULT_STROKE_COLOR
stroke_color = attr[:stroke_color] || DEFAULT_STROKE_COLOR
line_width = attr[:line_width] || DEFAULT_LINEWIDTH
word_spacing = attr[:word_spacing]
char_spacing = attr[:char_spacing]
scale = attr[:scale]
rise = attr[:rise]
rendering = attr[:rendering]
@instructions << PDF::Instruction.new('ET').render(@canvas) if (x or y) and @canvas.gs.text_state.is_in_text_object?
unless @canvas.gs.text_state.is_in_text_object?
@instructions << PDF::Instruction.new('BT').render(@canvas)
end
set_text_font(font, size)
set_text_pos(x, y) if x or y
set_text_leading(leading) if leading
set_text_rendering(rendering) if rendering
set_text_rise(rise) if rise
set_text_scale(scale) if scale
set_text_word_spacing(word_spacing) if word_spacing
set_text_char_spacing(char_spacing) if char_spacing
set_fill_color(color)
set_stroke_color(stroke_color)
set_line_width(line_width)
write_text_block(text)
self
end
def paint_shading(shade)
load! if @instructions.nil?
@instructions << PDF::Instruction.new('sh', shade).render(@canvas)
self
end
def set_text_font(fontname, size)
load! if @instructions.nil?
if fontname != @canvas.gs.text_state.font or size != @canvas.gs.text_state.font_size
@instructions << PDF::Instruction.new('Tf', fontname, size).render(@canvas)
end
self
end
def set_text_pos(tx,ty)
load! if @instructions.nil?
@instructions << PDF::Instruction.new('Td', tx, ty).render(@canvas)
self
end
def set_text_leading(leading)
load! if @instructions.nil?
if leading != @canvas.gs.text_state.leading
@instructions << PDF::Instruction.new('TL', leading).render(@canvas)
end
self
end
def set_text_rendering(rendering)
load! if @instructions.nil?
if rendering != @canvas.gs.text_state.rendering_mode
@instructions << PDF::Instruction.new('Tr', rendering).render(@canvas)
end
self
end
def set_text_rise(rise)
load! if @instructions.nil?
if rise != @canvas.gs.text_state.text_rise
@instructions << PDF::Instruction.new('Ts', rise).render(@canvas)
end
self
end
def set_text_scale(scaling)
load! if @instructions.nil?
if scale != @canvas.gs.text_state.scaling
@instructions << PDF::Instruction.new('Tz', scaling).render(@canvas)
end
self
end
def set_text_word_spacing(word_spacing)
load! if @instructions.nil?
if word_spacing != @canvas.gs.text_state.word_spacing
@instructions << PDF::Instruction.new('Tw', word_spacing).render(@canvas)
end
self
end
def set_text_char_spacing(char_spacing)
load! if @instructions.nil?
if char_spacing != @canvas.gs.text_state.char_spacing
@instructions << PDF::Instruction.new('Tc', char_spacing).render(@canvas)
end
self
end
def set_fill_color(color)
load! if @instructions.nil?
@instructions << ( i =
if (color.respond_to? :r and color.respond_to? :g and color.respond_to? :b) or (color.is_a?(::Array) and color.size == 3)
r = (color.respond_to?(:r) ? color.r : color[0]).to_f / 255
g = (color.respond_to?(:g) ? color.g : color[1]).to_f / 255
b = (color.respond_to?(:b) ? color.b : color[2]).to_f / 255
PDF::Instruction.new('rg', r, g, b) if @canvas.gs.nonstroking_color != [r,g,b]
elsif (color.respond_to? :c and color.respond_to? :m and color.respond_to? :y and color.respond_to? :k) or (color.is_a?(::Array) and color.size == 4)
c = (color.respond_to?(:c) ? color.c : color[0]).to_f
m = (color.respond_to?(:m) ? color.m : color[1]).to_f
y = (color.respond_to?(:y) ? color.y : color[2]).to_f
k = (color.respond_to?(:k) ? color.k : color[3]).to_f
PDF::Instruction.new('k', c, m, y, k) if @canvas.gs.nonstroking_color != [c,m,y,k]
elsif color.respond_to?:g or (0.0..1.0) === color
g = color.respond_to?(:g) ? color.g : color
PDF::Instruction.new('g', g) if @canvas.gs.nonstroking_color != [ g ]
else
raise TypeError, "Invalid color : #{color}"
end
)
i.render(@canvas) if i
self
end
def set_stroke_color(color)
load! if @instructions.nil?
@instructions << ( i =
if (color.respond_to? :r and color.respond_to? :g and color.respond_to? :b) or (color.is_a?(::Array) and color.size == 3)
r = (color.respond_to?(:r) ? color.r : color[0]).to_f / 255
g = (color.respond_to?(:g) ? color.g : color[1]).to_f / 255
b = (color.respond_to?(:b) ? color.b : color[2]).to_f / 255
PDF::Instruction.new('RG', r, g, b) if @canvas.gs.stroking_color != [r,g,b]
elsif (color.respond_to? :c and color.respond_to? :m and color.respond_to? :y and color.respond_to? :k) or (color.is_a?(::Array) and color.size == 4)
c = (color.respond_to?(:c) ? color.c : color[0]).to_f
m = (color.respond_to?(:m) ? color.m : color[1]).to_f
y = (color.respond_to?(:y) ? color.y : color[2]).to_f
k = (color.respond_to?(:k) ? color.k : color[3]).to_f
PDF::Instruction.new('K', c, m, y, k) if @canvas.gs.stroking_color != [c,m,y,k]
elsif color.respond_to?:g or (0.0..1.0) === color
g = color.respond_to?(:g) ? color.g : color
PDF::Instruction.new('G', g) if @canvas.gs.stroking_color != [ g ]
else
raise TypeError, "Invalid color : #{color}"
end
)
i.render(@canvas) if i
self
end
def set_dash_pattern(pattern)
load! if @instructions.nil?
unless @canvas.gs.dash_pattern.eql? pattern
@instructions << PDF::Instruction.new('d', pattern.array, pattern.phase).render(@canvas)
end
self
end
def set_line_width(width)
load! if @instructions.nil?
if @canvas.gs.line_width != width
@instructions << PDF::Instruction.new('w', width).render(@canvas)
end
self
end
def set_line_cap(cap)
load! if @instructions.nil?
if @canvas.gs.line_cap != cap
@instructions << PDF::Instruction.new('J', cap).render(@canvas)
end
self
end
def set_line_join(join)
load! if @instructions.nil?
if @canvas.gs.line_join != join
@instructions << PDF::Instruction.new('j', join).render(@canvas)
end
self
end
private
def load!
decode!
code = StringScanner.new self.data
@instructions = []
until code.eos?
insn = PDF::Instruction.parse(code)
@instructions << insn if insn
end
self
end
def write_text_block(text)
lines = text.split("\n").map!{|line| line.to_s}
@instructions << PDF::Instruction.new('Tj', lines.slice!(0)).render(@canvas)
lines.each do |line|
@instructions << PDF::Instruction.new("'", line).render(@canvas)
end
end
end #class ContentStream
class Page < Dictionary
def render(engine) #:nodoc:
contents = self.Contents
contents = [ contents ] unless contents.is_a? Array
contents.each do |stream|
stream = stream.cast_to(ContentStream) unless stream.is_a? ContentStream
stream.render(engine)
end
end
# TODO :nodoc:
def draw_image
raise NotImplementedError
end
# See ContentStream#draw_line.
def draw_line(from, to, attr = {})
last_content_stream.draw_line(from, to, attr); self
end
# See ContentStream#draw_polygon.
def draw_polygon(coords = [], attr = {})
last_content_stream.draw_polygon(coords, attr); self
end
# See ContentStream#draw_rectangle.
def draw_rectangle(x, y, width, height, attr = {})
last_content_stream.draw_rectangle(x, y, width, height, attr); self
end
# See ContentStream#write.
def write(text, attr = {})
last_content_stream.write(text, attr); self
end
# TODO :nodoc:
def paint_shading(shade)
raise NotImplementedError
end
# TODO :nodoc:
def set_text_font(font, size)
raise NotImplementedError
end
# See ContentStream#set_text_pos.
def set_text_pos(tx, ty)
last_content_stream.set_text_pos(tx, ty); self
end
# See ContentStream#set_text_leading.
def set_text_leading(leading)
last_content_stream.set_text_leading(leading); self
end
# See ContentStream#set_text_rendering.
def set_text_rendering(rendering)
last_content_stream.set_text_rendering(rendering); self
end
# See ContentStream#set_text_rise.
def set_text_rise(rise)
last_content_stream.set_text_rise(rise); self
end
# See ContentStream#set_text_scale.
def set_text_scale(scaling)
last_content_stream.set_text_scale(scaling); self
end
# See ContentStream#set_text_word_spacing.
def set_text_word_spacing(word_spacing)
last_content_stream.set_text_word_spacing(word_spacing); self
end
# See ContentStream#set_text_char_spacing.
def set_text_char_spacing(char_spacing)
last_content_stream.set_text_char_spacing(char_spacing); self
end
# See ContentStream#set_fill_color.
def set_fill_color(color)
last_content_stream.set_fill_color(color); self
end
# See ContentStream#set_stroke_color.
def set_stroke_color(color)
last_content_stream.set_stroke_color(color); self
end
# See ContentStream#set_dash_pattern.
def set_dash_pattern(pattern)
last_content_stream.set_dash_pattern(pattern); self
end
# See ContentStream#set_line_width.
def set_line_width(width)
last_content_stream.set_line_width(width); self
end
# See ContentStream#set_line_cap.
def set_line_cap(cap)
last_content_stream.set_line_cap(cap); self
end
# See ContentStream#set_line_join.
def set_line_join(join)
last_content_stream.set_line_join(join); self
end
private
def last_content_stream #:nodoc:
contents = (self.Contents ||= ContentStream.new)
contents.is_a?(Array) ? contents.last : contents
end
end # class Page
module Graphics
module XObject
def self.included(receiver)
receiver.field :Type, :Type => Name, :Default => :XObject
end
end
class FormXObject < ContentStream
include XObject
include ResourcesHolder
field :Subtype, :Type => Name, :Default => :Form, :Required => true
field :FormType, :Type => Integer, :Default => 1
field :BBox, :Type => Array, :Required => true
field :Matrix, :Type => Array, :Default => [1, 0, 0, 1, 0, 0]
field :Resources, :Type => Resources, :Version => "1.2"
field :Group, :Type => Dictionary, :Version => "1.4"
field :Ref, :Type => Dictionary, :Version => "1.4"
field :Metadata, :Type => Stream, :Version => "1.4"
field :PieceInfo, :Type => Dictionary, :Version => "1.3"
field :LastModified, :Type => String, :Version => "1.3"
field :StructParent, :Type => Integer, :Version => "1.3"
field :StructParents, :Type => Integer, :Version => "1.3"
field :OPI, :Type => Dictionary, :Version => "1.2"
field :OC, :Type => Dictionary, :Version => "1.5"
field :Name, :Type => Name
field :Measure, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :PtData, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
def pre_build
self.Resources = Resources.new.pre_build unless has_field?(:Resources)
super
end
end
class ImageXObject < Stream
include XObject
field :Subtype, :Type => Name, :Default => :Image, :Required => true
field :Width, :Type => Integer, :Required => true
field :Height, :Type => Integer, :Required => true
field :ColorSpace, :Type => [ Name, Array ]
field :BitsPerComponent, :Type => Integer
field :Intent, :Type => Name, :Version => "1.1"
field :ImageMask, :Type => Boolean, :Default => false
field :Mask, :Type => [ Stream, Array ], :Version => "1.3"
field :Decode, :Type => Array
field :Interpolate, :Type => Boolean, :Default => false
field :Alternates, :Type => Array, :Version => "1.3"
field :SMask, :Type => Stream, :Version => "1.4"
field :SMaskInData, :Type => Integer, :Default => 0, :Version => "1.5"
field :Name, :Type => Name
field :StructParent, :Type => Integer, :Version => "1.3"
field :ID, :Type => String, :Version => "1.3"
field :OPI, :Type => Dictionary, :Version => "1.2"
field :Metadata, :Type => Stream, :Version => "1.4"
field :OC, :Type => Dictionary, :Version => "1.5"
field :Measure, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
field :PtData, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
def self.from_image_file(path, format = nil)
if path.respond_to?(:read)
fd = path
else
fd = File.open(File.expand_path(path), 'r').binmode
format ||= File.extname(path)
format.slice!(0) if format and format[0,1] == '.'
end
if ''.respond_to? :force_encoding
data = fd.read.force_encoding('binary') # 1.9
else
data = fd.read
end
fd.close
image = ImageXObject.new
raise ArgumentError, "Missing file format" if format.nil?
case format.downcase
when 'jpg', 'jpeg', 'jpe', 'jif', 'jfif', 'jfi'
image.setFilter :DCTDecode
image.rawdata = data
image
when 'jp2','jpx','j2k','jpf','jpm','mj2'
image.setFilter :JPXDecode
image.rawdata = data
image
when 'jb2', 'jbig', 'jbig2'
image.setFilter :JBIG2Decode
image.rawdata = data
image
else
raise NotImplementedError, "Unknown file format: '#{format}'"
end
end
#
# Converts an ImageXObject stream into an image file data.
# Output format depends on the stream encoding:
# * JPEG for DCTDecode
# * JPEG2000 for JPXDecode
# * JBIG2 for JBIG2Decode
# * PNG for everything else
#
# Returns an array of the form [ _format_, _data_ ]
#
def to_image_file
encoding = self.Filter
encoding = encoding[0] if encoding.is_a? ::Array
case (encoding && encoding.value)
when :DCTDecode then [ 'jpg', self.data ]
when :JBIG2Decode then [ 'jbig2', self.data ]
when :JPXDecode then [ 'jp2', self.data ]
else
raise InvalidColorError, "No colorspace specified" unless self.ColorSpace
case cs = self.ColorSpace.value
when Color::Space::DEVICE_GRAY
colortype = 0
components = 1
when Color::Space::DEVICE_RGB
colortype = 2
components = 3
when ::Array
cstype = cs[0].is_a?(Reference) ? cs[0].solve : cs[0]
case cstype.value
when :Indexed
colortype = 3
components = 3
csbase = cs[1].is_a?(Reference) ? cs[1].solve : cs[1]
lookup = cs[3].is_a?(Reference) ? cs[3].solve : cs[3]
when :ICCBased
iccprofile = cs[1].is_a?(Reference) ? cs[1].solve : cs[1]
raise InvalidColorError,
"Invalid ICC Profile parameter" unless iccprofile.is_a?(Stream)
case iccprofile.N
when 1
colortype = 0
components = 1
when 3
colortype = 2
components = 3
else
raise InvalidColorError,
"Invalid number of components in ICC profile: #{iccprofile.N}"
end
else
raise InvalidColorError, "Unsupported color space: #{self.ColorSpace}"
end
else
raise InvalidColorError, "Unsupported color space: #{self.ColorSpace}"
end
bpc = self.BitsPerComponent || 8
w,h = self.Width, self.Height
pixels = self.data
hdr = [137, 80, 78, 71, 13, 10, 26, 10].pack('C*')
chunks = []
chunks <<
[
'IHDR',
[
w, h,
bpc, colortype, 0, 0, 0
].pack("N2C5")
]
if self.Intents
intents =
case self.Intents.value
when Intents::PERCEPTUAL then 0
when Intents::RELATIVE then 1
when Intents::SATURATION then 2
when Intents::ABSOLUTE then 3
else
3
end
chunks <<
[
'sRGB',
[ intents ].pack('C')
]
chunks << [ 'gAMA', [ 45455 ].pack("N") ]
chunks <<
[
'cHRM',
[
31270,
32900,
64000,
33000,
30000,
60000,
15000,
6000
].pack("N8")
]
end
if colortype == 3
lookup =
case lookup
when Stream then lookup.data
when String then lookup.value
else
raise InvalidColorError, "Invalid indexed palette table"
end
raise InvalidColorError, "Invalid base color space" unless csbase
palette = ""
case csbase.value
when Color::Space::DEVICE_GRAY
lookup.each_byte do |g|
palette << Color.gray_to_rgb(g).pack("C3")
end
when Color::Space::DEVICE_RGB
palette << lookup[0, (lookup.size / 3) * 3]
when Color::Space::DEVICE_CMYK
(lookup.size / 4).times do |i|
cmyk = lookup[i * 4, 4].unpack("C4").map!{|c| c.to_f / 255}
palette << Color.cmyk_to_rgb(*cmyk).map!{|c| (c * 255).to_i}.pack("C3")
end
when ::Array
case csbase[0].solve.value
when :ICCBased
iccprofile = csbase[1].solve
raise InvalidColorError,
"Invalid ICC Profile parameter" unless iccprofile.is_a?(Stream)
case iccprofile.N
when 1
lookup.each_byte do |g|
palette << Color.gray_to_rgb(g).pack("C3")
end
when 3
palette << lookup[0, (lookup.size / 3) * 3]
else
raise InvalidColorError,
"Invalid number of components in ICC profile: #{iccprofile.N}"
end
else
raise InvalidColorError, "Unsupported color space: #{csbase}"
end
else
raise InvalidColorError, "Unsupported color space: #{csbase}"
end
if iccprofile
chunks <<
[
'iCCP',
'ICC Profile' + "\x00\x00" + Zlib::Deflate.deflate(iccprofile.data, Zlib::BEST_COMPRESSION)
]
end
chunks <<
[
'PLTE',
palette
]
bpr = w
else
if iccprofile
chunks <<
[
'iCCP',
'ICC Profile' + "\x00\x00" + Zlib::Deflate.deflate(iccprofile.data, Zlib::BEST_COMPRESSION)
]
end
bpr = (bpc >> 3) * components * w
end
require 'zlib'
nrows = pixels.size / bpr
nrows.times do |irow|
pixels.insert(irow * bpr + irow, "\x00")
end
chunks <<
[
'IDAT',
Zlib::Deflate.deflate(pixels, Zlib::BEST_COMPRESSION)
]
if self.Metadata.is_a?(Stream)
chunks <<
[
'tEXt',
"XML:com.adobe.xmp" + "\x00" + self.Metadata.data
]
end
chunks << [ 'IEND', '' ]
[ 'png',
hdr + chunks.map!{ |chk|
[ chk[1].size, chk[0], chk[1], Zlib.crc32(chk[0] + chk[1]) ].pack("NA4A*N")
}.join
]
end
end
end
class ReferenceDictionary < Dictionary
include StandardObject
field :F, :Type => Dictionary, :Required => true
field :Page, :Type => [Integer, String], :Required => true
field :ID, :Tyoe => Array
end
end
end
origami-pdf-1.2.7/lib/origami/graphics/instruction.rb 0000644 0001750 0001750 00000005545 12101464040 023035 0 ustar terceiro terceiro =begin
= File
graphics/instruction.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class InvalidPDFInstructionError < Exception ; end
class PDF::Instruction
attr_reader :operator
attr_accessor :operands
@@regexp = Regexp.new('([^ \\t\\r\\n\\0\\[\\]<>()%\\/]+)')
@insns = Hash.new(:operands => [], :render => lambda{})
def initialize(operator, *operands)
@operator = operator
@operands = operands.map!{|arg| arg.is_a?(Origami::Object) ? arg.value : arg}
if self.class.has_op?(operator)
opdef = self.class.get_operands(operator)
if not opdef.include?('*') and opdef.size != operands.size
raise InvalidPDFInstructionError,
"Numbers of operands mismatch for #{operator}: #{operands.inspect}"
end
end
end
def render(canvas)
self.class.get_render_proc(@operator)[canvas, *@operands]
self
end
def to_s
"#{operands.map{|op| op.to_o.to_s}.join(' ')}#{' ' unless operands.empty?}#{operator}\n"
end
class << self
def insn(operator, *operands, &render_proc)
@insns[operator] = {}
@insns[operator][:operands] = operands
@insns[operator][:render] = render_proc || lambda{}
end
def has_op?(operator)
@insns.has_key? operator
end
def get_render_proc(operator)
@insns[operator][:render]
end
def get_operands(operator)
@insns[operator][:operands]
end
def parse(stream)
operands = []
while type = Object.typeof(stream, true)
operands.push type.parse(stream)
end
if not stream.eos?
if stream.scan(@@regexp).nil?
raise InvalidPDFInstructionError,
"Operator: #{(stream.peek(10) + '...').inspect}"
end
operator = stream[1]
PDF::Instruction.new(operator, *operands)
else
if not operands.empty?
raise InvalidPDFInstructionError,
"No operator given for operands: #{operands.join}"
end
end
end
end
end
end
origami-pdf-1.2.7/lib/origami/graphics/state.rb 0000644 0001750 0001750 00000013226 12101464040 021567 0 ustar terceiro terceiro =begin
= File
graphics/state.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'matrix'
module Origami
module Graphics
class GraphicsStateError < Exception #:nodoc:
end
class State
#
# Device-independent parameters.
#
attr_accessor :ctm
attr_accessor :clipping_path
attr_accessor :stroking_colorspace, :nonstroking_colorspace, :stroking_color, :nonstroking_color
attr_accessor :text_state
attr_accessor :line_width, :line_cap, :line_join, :miter_limit, :dash_pattern
attr_accessor :rendering_intent
attr_accessor :stroke_adjustment
attr_accessor :blend_mode, :soft_mask, :alpha_constant, :alpha_source
attr_reader :current_path
def initialize
@stack = []
@current_path = []
@text_state = Text::State.new
self.reset
end
def reset
@ctm = Matrix.identity(3)
@clipping_path = nil
@stroking_colorspace = @nonstroking_colorspace = Color::Space::DEVICE_GRAY
@stroking_color = @nonstroking_color = [ 0.0 ] #black
@text_state.reset
@line_width = 1.0
@line_cap = LineCapStyle::BUTT_CAP
@line_join = LineJoinStyle::MITER_JOIN
@miter_limit = 10.0
@dash_pattern = DashPattern.new([], 0)
@rendering_intent = Color::Intent::RELATIVE
@stroke_adjustment = false
@blend_mode = Color::BlendMode::NORMAL
@soft_mask = :None
@alpha_constant = 1.0
@alpha_source = false
end
def save
context =
[
@ctm, @clipping_path,
@stroking_colorspace, @nonstroking_colorspace,
@stroking_color, @nonstroking_color,
@text_state, @line_width, @line_cap, @line_join,
@miter_limit, @dash_pattern, @rendering_intent,
@stroke_adjustment,
@blend_mode, @soft_mask, @alpha_constant, @alpha_source
]
@stack.push(context)
end
def restore
raise GraphicsStateError, "Cannot restore context : empty stack" if @stack.empty?
@ctm, @clipping_path,
@stroking_colorspace, @nonstroking_colorspace,
@stroking_color, @nonstroking_color,
@text_state, @line_width, @line_cap, @line_join,
@miter_limit, @dash_pattern, @rendering_intent,
@stroke_adjustment,
@blend_mode, @soft_mask, @alpha_constant, @alpha_source = @stack.pop
end
end
#
# Generic Graphic state
# 4.3.4 Graphics State Parameter Dictionaries p219
#
class ExtGState < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :ExtGState, :Required => true
field :LW, :Type => Integer, :Version => "1.3"
field :LC, :Type => Integer, :Version => "1.3"
field :LJ, :Type => Integer, :Version => "1.3"
field :ML, :Type => Number, :Version => "1.3"
field :D, :Type => Array, :Version => "1.3"
field :RI, :Type => Name, :Version => "1.3"
field :OP, :Type => Boolean
field :op, :Type => Boolean, :Version => "1.3"
field :OPM, :Type => Number, :Version => "1.3"
field :Font, :Type => Array, :Version => "1.3"
field :BG, :Type => Object
field :BG2, :Type => Object, :Version => "1.3"
field :UCR, :Type => Object
field :UCR2, :Type => Object, :Version => "1.3"
field :TR, :Type => Object
field :TR2, :Type => Object, :Version => "1.3"
field :HT, :Type => [ Dictionary, Name, Stream ]
field :FL, :Type => Number, :Version => "1.3"
field :SM, :Type => Number, :Version => "1.3"
field :SA, :Type => Boolean
field :BM, :Type => [ Name, Array ], :Version => "1.4"
field :SMask, :Type => [ Dictionary, Array ], :Version => "1.4"
field :CA, :Type => Number
field :ca, :Type => Number, :Version => "1.4"
field :AIS, :Type => Boolean, :Version => "1.4"
field :TK, :Type => Boolean, :Version => "1.4"
end # class ExtGState
end #module Graphics
class PDF::Instruction
insn 'q' do |canvas| canvas.gs.save; canvas.gs.reset end
insn 'Q' do |canvas| canvas.gs.restore end
insn 'w', Real do |canvas, lw| canvas.gs.line_width = lw end
insn 'J', Real do |canvas, lc| canvas.gs.line_cap = lc end
insn 'j', Real do |canvas, lj| canvas.gs.line_join = lj end
insn 'M', Real do |canvas, ml| canvas.gs.miter_limit = ml end
insn 'd', Array, Integer do |canvas, array, phase|
canvas.gs.dash_pattern = Graphics::DashPattern.new array, phase
end
insn 'ri', Name do |canvas, ri| canvas.gs.rendering_intent = ri end
end
end
origami-pdf-1.2.7/lib/origami/graphics/colors.rb 0000644 0001750 0001750 00000014064 12101464040 021751 0 ustar terceiro terceiro =begin
= File
graphics/colors.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
begin
require 'color'
rescue LoadError
end
module Origami
module Graphics
class InvalidColorError < Exception; end
module Color
module Intent
ABSOLUTE = :AbsoluteColorimetric
RELATIVE = :RelativeColorimetric
SATURATION = :Saturation
PERCEPTUAL = :Perceptual
end
module BlendMode
NORMAL = :Normal
COMPATIBLE = :Compatible
MULTIPLY = :Multiply
SCREEN = :Screen
OVERLAY = :Overlay
DARKEN = :Darken
LIGHTEN = :Lighten
COLORDODGE = :ColorDodge
COLORBURN = :ColorBurn
HARDLIGHT = :HardLight
SOFTLIGHt = :SoftLight
DIFFERENCE = :Difference
EXCLUSION = :Exclusion
end
module Space
DEVICE_GRAY = :DeviceGray
DEVICE_RGB = :DeviceRGB
DEVICE_CMYK = :DeviceCMYK
end
def self.cmyk_to_rgb(c, m, y, k)
r = 1 - (( c * ( 1 - k ) + k ))
g = 1 - (( m * ( 1 - k ) + k ))
b = 1 - (( y * ( 1 - k ) + k ))
[ r, g, b ]
end
def self.gray_to_rgb(g)
[ g, g, g ]
end
#
# Class representing an embedded ICC Profile stream.
#
class ICCProfile < Stream
field :N, :Type => Integer, :Required => true, :Version => '1.3'
field :Alternate, :Type => [ Name, Array ]
field :Range, :Type => Array
field :Metadata, :Type => Stream, :Version => '1.4'
end
class GrayScale
attr_accessor :g
def initialize(g)
@g = g
end
end
class RGB
attr_accessor :r,:g,:b
def initialize(r,g,b)
@r,@g,@b = r,g,b
end
end
class CMYK
attr_accessor :c,:m,:y,:k
def initialize(c,m,y,k)
@c,@m,@y,@k = c,m,y,k
end
end
def Color.to_a(color)
return color if color.is_a?(::Array)
if (color.respond_to? :r and color.respond_to? :g and color.respond_to? :b)
r = (color.respond_to?(:r) ? color.r : color[0]).to_f / 255
g = (color.respond_to?(:g) ? color.g : color[1]).to_f / 255
b = (color.respond_to?(:b) ? color.b : color[2]).to_f / 255
return [r, g, b]
elsif (color.respond_to? :c and color.respond_to? :m and color.respond_to? :y and color.respond_to? :k)
c = (color.respond_to?(:c) ? color.c : color[0]).to_f
m = (color.respond_to?(:m) ? color.m : color[1]).to_f
y = (color.respond_to?(:y) ? color.y : color[2]).to_f
k = (color.respond_to?(:k) ? color.k : color[3]).to_f
return [c,m,y,k]
elsif color.respond_to?:g or (0.0..1.0) === color
g = color.respond_to?(:g) ? color.g : color
return [ g ]
else
raise TypeError, "Invalid color : #{color}"
end
end
end
end
class PDF::Instruction
insn 'CS', Name do |canvas, cs| canvas.gs.stroking_colorspace = cs end
insn 'cs', Name do |canvas, cs| canvas.gs.nonstroking_colorspace = cs end
insn 'SC', '*' do |canvas, *c| canvas.gs.stroking_color = c end
insn 'sc', '*' do |canvas, *c| canvas.gs.nonstroking_color = c end
insn 'G', Real do |canvas, c|
unless (0..1).include? c
raise Graphics::InvalidColorError,
"Not a valid color for DeviceGray: #{c}"
end
canvas.gs.stroking_colorspace = Graphics::Color::Space::DEVICE_GRAY
canvas.gs.stroking_color = [ c ]
end
insn 'g', Real do |canvas, c|
unless (0..1).include? c
raise Graphics::InvalidColorError,
"Not a valid color for DeviceGray: #{c}"
end
canvas.gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_GRAY
canvas.gs.nonstroking_color = [ c ]
end
insn 'RG', Real, Real, Real do |canvas, r,g,b|
c = [ r, g, b ]
unless c.all? {|b| (0..1).include? b}
raise Graphics::InvalidColorError,
"Not a valid color for DeviceRGB: #{c.inspect}"
end
canvas.gs.stroking_colorspace = Graphics::Color::Space::DEVICE_RGB
canvas.gs.stroking_color = c
end
insn 'rg', Real, Real, Real do |canvas, r,g,b|
c = [ r, g, b ]
unless c.all? {|b| (0..1).include? b}
raise Graphics::InvalidColorError,
"Not a valid color for DeviceRGB: #{c.inspect}"
end
canvas.gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_RGB
canvas.gs.nonstroking_color = c
end
insn 'K', Real, Real, Real, Real do |canvas, c,m,y,k|
c = [ c, m, y, k ]
unless c.all? {|b| (0..1).include? b}
raise Graphics::InvalidColorError,
"Not a valid color for DeviceCMYK: #{c.inspect}"
end
canvas.gs.stroking_colorspace = Graphics::Color::Space::DEVICE_CMYK
canvas.gs.stroking_color = c
end
insn 'k', Real, Real, Real, Real do |canvas, c,m,y,k|
c = [ c, m, y, k ]
unless c.all? {|b| (0..1).include? b}
raise Graphics::InvalidColorError,
"Not a valid color for DeviceCMYK: #{c.inspect}"
end
canvas.gs.nonstroking_colorspace = Graphics::Color::Space::DEVICE_CMYK
canvas.gs.nonstroking_color = c
end
end
end # module Origami
origami-pdf-1.2.7/lib/origami/graphics/patterns.rb 0000644 0001750 0001750 00000016577 12131012465 022326 0 ustar terceiro terceiro =begin
= File
graphics/patterns.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Graphics
module Pattern
module Type
TILING = 1
SHADING = 2
end
def self.included(receiver)
receiver.field :Type, :Type => Name, :Default => :Pattern
receiver.field :PatternType, :Type => Integer, :Required => true
end
class Tiling < ContentStream
include Pattern
include ResourcesHolder
module PaintType
COLOURED = 1
UNCOLOURED = 2
end
module Type
CONSTANT_SPACING = 1
NO_DISTORTION = 2
CONSTANT_SPACING_AND_FASTER_TILING = 3
end
field :PatternType, :Type => Integer, :Default => Pattern::Type::TILING, :Required => true
field :PaintType, :Type => Integer, :Required => true
field :TilingType, :Type => Integer, :Required => true
field :BBox, :Type => Array, :Required => true
field :XStep, :Type => Number, :Required => true
field :YStep, :Type => Number, :Required => true
field :Resources, :Type => Resources, :Required => true
field :Matrix, :Type => Array, :Default => [ 1, 0, 0, 1, 0, 0 ]
end
class Shading < Dictionary
include StandardObject
include Pattern
module Type
FUNCTIONBASED = 1
AXIAL = 2
RADIAL = 3
FREEFORM_TRIANGLE_MESH = 4
LATTICEFORM_TRIANGLE_MESH = 5
COONS_PATCH_MESH = 6
TENSORPRODUCT_PATCH_MESH = 7
end
field :PatternType, :Type => Integer, :Default => Pattern::Type::SHADING, :Required => true
field :Shading, :Type => [ Dictionary, Stream ], :Required => true
field :Matrix, :Type => Array, :Default => [ 1, 0, 0, 1, 0, 0 ]
field :ExtGState, :Type => Dictionary
module ShadingObject
def self.included(receiver)
receiver.field :ShadingType, :Type => Integer, :Required => true
receiver.field :ColorSpace, :Type => [ Name, Array ], :Required => true
receiver.field :Background, :Type => Array
receiver.field :BBox, :Type => Array
receiver.field :AntiAlias, :Type => Boolean, :Default => false
end
end
class FunctionBased < Dictionary
include StandardObject
include ShadingObject
field :ShadingType, :Type => Integer, :Default => Shading::Type::FUNCTIONBASED, :Required => true
field :Domain, :Type => Array, :Default => [ 0.0, 1.0, 0.0, 1.0 ]
field :Matrix, :Type => Array, :Default => [ 1, 0, 0, 1, 0, 0 ]
field :Function, :Type => [ Dictionary, Stream ], :Required => true
end
class Axial < Dictionary
include StandardObject
include ShadingObject
field :ShadingType, :Type => Integer, :Default => Shading::Type::AXIAL, :Required => true
field :Coords, :Type => Array, :Required => true
field :Domain, :Type => Array, :Default => [ 0.0, 1.0 ]
field :Function, :Type => [ Dictionary, Stream ], :Required => true
field :Extend, :Type => Array, :Default => [ false, false ]
end
class Radial < Dictionary
include StandardObject
include ShadingObject
field :ShadingType, :Type => Integer, :Default => Shading::Type::RADIAL, :Required => true
field :Coords, :Type => Array, :Required => true
field :Domain, :Type => Array, :Default => [ 0.0, 1.0 ]
field :Function, :Type => [ Dictionary, Stream ], :Required => true
field :Extend, :Type => Array, :Default => [ false, false ]
end
class FreeFormTriangleMesh < Stream
include ShadingObject
field :ShadingType, :Type => Integer, :Default => Shading::Type::FREEFORM_TRIANGLE_MESH, :Required => true
field :BitsPerCoordinate, :Type => Integer, :Required => true
field :BitsPerComponent, :Type => Integer, :Required => true
field :BitsPerFlag, :Type => Integer, :Required => true
field :Decode, :Type => Array, :Required => true
field :Function, :Type => [ Dictionary, Stream ]
end
class LatticeFormTriangleMesh < Stream
include ShadingObject
field :ShadingType, :Type => Integer, :Default => Shading::Type::LATTICEFORM_TRIANGLE_MESH, :Required => true
field :BitsPerCoordinate, :Type => Integer, :Required => true
field :BitsPerComponent, :Type => Integer, :Required => true
field :VerticesPerRow, :Type => Integer, :Required => true
field :Decode, :Type => Array, :Required => true
field :Function, :Type => [ Dictionary, Stream ]
end
class CoonsPathMesh < Stream
include ShadingObject
field :ShadingType, :Type => Integer, :Default => Shading::Type::COONS_PATCH_MESH, :Required => true
field :BitsPerCoordinate, :Type => Integer, :Required => true
field :BitsPerComponent, :Type => Integer, :Required => true
field :BitsPerFlag, :Type => Integer, :Required => true
field :Decode, :Type => Array, :Required => true
field :Function, :Type => [ Dictionary, Stream ]
end
class TensorProductPatchMesh < Stream
include ShadingObject
field :ShadingType, :Type => Integer, :Default => Shading::Type::TENSORPRODUCT_PATCH_MESH, :Required => true
field :BitsPerCoordinate, :Type => Integer, :Required => true
field :BitsPerComponent, :Type => Integer, :Required => true
field :BitsPerFlag, :Type => Integer, :Required => true
field :Decode, :Type => Array, :Required => true
field :Function, :Type => [ Dictionary, Stream ]
end
end
end
end
class PDF::Instruction
insn 'sh', Name
end
end
origami-pdf-1.2.7/lib/origami/graphics/text.rb 0000644 0001750 0001750 00000015560 12101464040 021436 0 ustar terceiro terceiro =begin
= File
graphics/text.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Text
OPERATORS =
[
'Tc', 'Tw', 'Tz', 'TL', 'Tf', 'Tr', 'Ts', # Text state
'BT', 'ET', # Text objects
'Td', 'TD', 'Tm', 'T*', # Positioning
'Tj', "'", '"', 'TJ' # Showing
]
module Rendering
FILL = 0
STROKE = 1
FILL_AND_STROKE = 2
INVISIBLE = 3
FILL_AND_CLIP = 4
STROKE_AND_CLIP = 5
FILL_AND_STROKE_AND_CLIP = 6
CLIP = 7
end
class TextStateError < Exception #:nodoc:
end
class State
attr_accessor :char_spacing, :word_spacing, :scaling, :leading
attr_accessor :font, :font_size
attr_accessor :rendering_mode
attr_accessor :text_rise, :text_knockout
attr_accessor :text_matrix, :text_line_matrix, :text_rendering_matrix
def initialize
self.reset
end
def reset
@char_spacing = 0
@word_spacing = 0
@scaling = 100
@leading = 0
@font = nil
@font_size = nil
@rendering_mode = Rendering::FILL
@text_rise = 0
@text_knockout = true
#
# Text objects
#
@text_object = false
@text_matrix =
@text_line_matrix =
@text_rendering_matrix = nil
end
def is_in_text_object?
@text_object
end
def begin_text_object
if is_in_text_object?
raise TextStateError,
"Cannot start a text object within an existing text object."
end
@text_object = true
@text_matrix =
@text_line_matrix =
@text_rendering_matrix = Matrix.identity(3)
end
def end_text_object
unless is_in_text_object?
raise TextStateError,
"Cannot end text object : no previous text object has begun."
end
@text_object = false
@text_matrix =
@text_line_matrix =
@text_rendering_matrix = nil
end
end #class State
end #module Text
class PDF::Instruction
# Text instructions definitions
insn 'Tc', Real do |canvas, cS| canvas.gs.text_state.char_spacing = cS end
insn 'Tw', Real do |canvas, wS| canvas.gs.text_state.word_spacing = wS end
insn 'Tz', Real do |canvas, s| canvas.gs.text_state.scaling = s end
insn 'TL', Real do |canvas, l| canvas.gs.text_state.leading = l end
insn 'Tf', Name, Real do |canvas, font, size|
canvas.gs.text_state.font = font
canvas.gs.text_state.font_size = size
end
insn 'Tr', Integer do |canvas, r| canvas.gs.text_state.rendering_mode = r end
insn 'Ts', Real do |canvas, s| canvas.gs.text_state.text_rise = s end
insn 'BT' do |canvas| canvas.gs.text_state.begin_text_object end
insn 'ET' do |canvas| canvas.gs.text_state.end_text_object end
insn 'Td', Real, Real do |canvas, tx, ty|
unless canvas.gs.text_state.is_in_text_object?
raise TextStateError,
"Must be in a text object to use operator : Td"
end
canvas.gs.text_state.text_matrix =
canvas.gs.text_state.text_line_matrix =
Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * canvas.gs.text_state.text_line_matrix
end
insn 'TD', Real, Real do |canvas, tx, ty|
unless canvas.gs.text_state.is_in_text_object?
raise TextStateError,
"Must be in a text object to use operator : TD"
end
canvas.gs.text_state.leading = -ty
canvas.gs.text_state.text_matrix =
canvas.gs.text_state.text_line_matrix =
Matrix.rows([[1,0,0],[0,1,0],[tx,ty,1]]) * canvas.gs.text_state.text_line_matrix
end
insn 'Tm', Real, Real, Real, Real, Real, Real do |canvas, a,b,c,d,e,f,g|
unless canvas.gs.text_state.is_in_text_object?
raise TextStateError,
"Must be in a text object to use operator : Tm"
end
canvas.gs.text_state.text_matrix =
canvas.gs.text_state.text_line_matrix =
Matrix.rows([[a,b,0],[c,d,0],[e,f,1]])
end
insn 'T*' do |canvas|
unless canvas.gs.text_state.is_in_text_object?
raise TextStateError,
"Must be in a text object to use operator : T*"
end
tx, ty = 0, -canvas.gs.text_state.leading
canvas.gs.text_state.text_matrix =
canvas.gs.text_state.text_line_matrix =
Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * canvas.gs.text_state.text_line_matrix
end
insn 'Tj', String do |canvas, s|
unless canvas.gs.text_state.is_in_text_object?
raise TextStateError,
"Must be in a text object to use operator : Tj"
end
canvas.write_text(s)
end
insn "'", String do |canvas, s|
unless canvas.gs.text_state.is_in_text_object?
raise TextStateError,
"Must be in a text object to use operator : '"
end
tx, ty = 0, -canvas.gs.text_state.leading
canvas.gs.text_state.text_matrix =
canvas.gs.text_state.text_line_matrix =
Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * canvas.gs.text_state.text_line_matrix
canvas.write_text(s)
end
insn '"', Real, Real, String do |canvas, w, c, s|
unless canvas.gs.text_state.is_in_text_object?
raise TextStateError,
"Must be in a text object to use operator : \""
end
canvas.gs.text_state.word_spacing = w
canvas.gs.text_state.char_spacing = c
tx, ty = 0, -gs.text_state.leading
canvas.gs.text_state.text_matrix =
canvas.gs.text_state.text_line_matrix =
Matrix.rows([[1,0,0],[0,1,0],[tx, ty, 1]]) * canvas.gs.text_state.text_line_matrix
canvas.write_text(s)
end
insn 'TJ', Array do |canvas, arr|
arr.each do |g|
case g
when Fixnum,Float then
# XXX: handle this in text space ?
when ::String then
canvas.write_text(g)
else
raise InvalidPDFInstructionError,
"Invalid component type `#{g.class}` in TJ operand"
end
end
end
end
end
origami-pdf-1.2.7/lib/origami/graphics/path.rb 0000644 0001750 0001750 00000010130 12101464040 021372 0 ustar terceiro terceiro =begin
= File
graphics/path.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Graphics
module LineCapStyle
BUTT_CAP = 0
ROUND_CAP = 1
PROJECTING_SQUARE_CAP = 2
end
module LineJoinStyle
MITER_JOIN = 0
ROUND_JOIN = 1
BEVEL_JOIN = 2
end
class DashPattern
attr_accessor :array, :phase
def initialize(array, phase = 0)
@array = array
@phase = phase
end
def eql?(dash) #:nodoc
dash.array == @array and dash.phase == @phase
end
def hash #:nodoc:
[ @array, @phase ].hash
end
end
class InvalidPathError < Exception; end
class Path
module Segment
attr_accessor :from, :to
def initialize(from, to)
@from, @to = from, to
end
end
class Line
include Segment
end
attr_accessor :current_point
attr_reader :segments
def initialize
@segments = []
@current_point = nil
@closed = false
end
def is_closed?
@closed
end
def close!
from = @current_point
to = @segments.first.from
@segments << Line.new(from, to)
@segments.freeze
@closed = true
end
def add_segment(seg)
raise GraphicsStateError, "Cannot modify closed subpath" if is_closed?
@segments << seg
@current_point = seg.to
end
end
end
class PDF::Instruction
insn 'm', Real, Real do |canvas, x,y|
canvas.gs.current_path << (subpath = Graphics::Path.new)
subpath.current_point = [x,y]
end
insn 'l', Real, Real do |canvas, x,y|
if canvas.gs.current_path.empty?
raise InvalidPathError, "No current point is defined"
end
subpath = canvas.gs.current_path.last
from = subpath.current_point
to = [x,y]
subpath.add_segment(Graphics::Path::Line.new(from, to))
end
insn 'h' do |canvas|
unless canvas.gs.current_path.empty?
subpath = canvas.gs.current_path.last
subpath.close! unless subpath.is_closed?
end
end
insn 're', Real, Real, Real, Real do |canvas, x,y,width,height|
tx = x + width
ty = y + height
canvas.gs.current_path << (subpath = Graphics::Path.new)
subpath.segments << Graphics::Path::Line.new([x,y], [tx,y])
subpath.segments << Graphics::Path::Line.new([tx,y], [tx, ty])
subpath.segments << Graphics::Path::Line.new([tx, ty], [x, ty])
subpath.close!
end
insn 'S' do |canvas|
canvas.stroke_path
end
insn 's' do |canvas|
canvas.gs.current_path.last.close!
canvas.stroke_path
end
insn 'f' do |canvas|
canvas.fill_path
end
insn 'F' do |canvas|
canvas.fill_path
end
insn 'f*' do |canvas|
canvas.fill_path
end
insn 'B' do |canvas|
canvas.fill_path
canvas.stroke_path
end
insn 'B*' do |canvas|
canvas.fill_path
canvas.stroke_path
end
insn 'b' do |canvas|
canvas.gs.current_path.last.close!
canvas.fill_path
canvas.stroke_path
end
insn 'b*' do |canvas|
canvas.gs.current_path.last.close!
canvas.fill_path
canvas.stroke_path
end
insn 'n'
end
end
origami-pdf-1.2.7/lib/origami/array.rb 0000644 0001750 0001750 00000012236 12142214376 017777 0 ustar terceiro terceiro =begin
= File
array.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume DelugrÈ
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class InvalidArrayObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing an Array Object.
# Arrays contain a set of Object.
#
class Array < ::Array
include Origami::Object
TOKENS = %w{ [ ] } #:nodoc:
@@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first) + WHITESPACES)
@@regexp_close = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.last) + WHITESPACES)
attr_reader :strings_cache, :names_cache, :xref_cache
#
# Creates a new PDF Array Object.
# _data_:: An array of objects.
#
def initialize(data = [])
raise TypeError, "Expected type Array, received #{data.class}." unless data.is_a?(::Array)
super()
@strings_cache = []
@names_cache = []
@xref_cache = {}
i = 0
while i < data.size
case val = data[i].to_o
when String then @strings_cache.push(val)
when Name then @names_cache.push(val)
when Reference then
(@xref_cache[val] ||= []).push(self)
when Dictionary,Array then
@strings_cache.concat(val.strings_cache)
@names_cache.concat(val.names_cache)
@xref_cache.update(val.xref_cache) do |ref, cache1, cache2|
cache1.concat(cache2)
end
val.strings_cache.clear
val.names_cache.clear
val.xref_cache.clear
end
self[i] = val
i = i + 1
end
end
def pre_build
self.map!{|obj| obj.to_o}
super
end
def self.parse(stream, parser = nil) #:nodoc:
data = []
offset = stream.pos
if not stream.skip(@@regexp_open)
raise InvalidArrayObjectError, "No token '#{TOKENS.first}' found"
end
while stream.skip(@@regexp_close).nil? do
type = Object.typeof(stream)
if type.nil?
raise InvalidArrayObjectError, "Bad embedded object format"
end
value = type.parse(stream, parser)
data << value
end
array = Array.new(data)
array.file_offset = offset
array
end
#
# Converts self into a Ruby array.
#
def to_a
super.map { |item|
item.is_a?(Origami::Object) ? item.value : item
}
end
def to_s #:nodoc:
content = "#{TOKENS.first} "
self.each { |entry|
content << entry.to_o.to_s + ' '
}
content << TOKENS.last
super(content)
end
def +(other)
a = Origami::Array.new(self.to_a + other.to_a, is_indirect?)
a.no, a.generation = @no, @generation
return a
end
def <<(item)
obj = item.to_o
obj.parent = self unless obj.is_indirect?
super(obj)
end
def []=(key,val)
key, val = key.to_o, val.to_o
super(key.to_o,val.to_o)
val.parent = self unless val.is_indirect? or val.parent.equal?(self)
val
end
alias value to_a
def copy
copy = self.class.new
self.each do |obj|
copy << obj.copy
end
copy.parent = @parent
copy.no, copy.generation = @no, @generation
copy.set_indirect(true) if is_indirect?
copy.set_pdf(@pdf) if is_indirect?
copy
end
def self.native_type ; Origami::Array end
end
#
# Class representing a location on a page or a bounding box.
#
class Rectangle < Array
class << self
def [](coords)
corners =
if [ :llx, :lly, :urx, :ury ].all? {|p| coords.include?(p)}
coords.values_at(:llx, :lly, :urx, :ury)
elsif [ :width, :height ].all? {|p| coords.include?(p)}
width, height = coords.values_at(:width, :height)
x = coords.values_at(:x).first || 0
y = coords.values_at(:y).first || 0
[ x, y, x+width, y+height ]
else
raise ArgumentError, "Bad arguments for #{self.class}: #{coords.inspect}"
end
unless corners.all? { |corner| corner.is_a?(Numeric) }
raise TypeError, "All coords must be numbers"
end
Rectangle.new(*corners)
end
end
def initialize(lowerleftx, lowerlefty, upperrightx, upperrighty)
super([ lowerleftx, lowerlefty, upperrightx, upperrighty ])
end
end
end
origami-pdf-1.2.7/lib/origami/page.rb 0000644 0001750 0001750 00000046713 12146177126 017611 0 ustar terceiro terceiro =begin
= File
page.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
#
# Appends a page or list of pages to the end of the page tree.
#
def append_page(page = Page.new, *more)
raise InvalidPDFError, "Invalid page tree" if not self.Catalog or not self.Catalog.Pages or not self.Catalog.Pages.is_a?(PageTreeNode)
pages = [ page ].concat(more).map! do |pg|
if pg.pdf and pg.pdf != self
# Page from another document must be exported.
pg.export
else
pg
end
end
treeroot = self.Catalog.Pages
treeroot.Kids ||= [] #:nodoc:
treeroot.Kids.concat(pages)
treeroot.Count = treeroot.Kids.length
pages.each do |page|
page.Parent = treeroot
end
self
end
#
# Inserts a page at position _index_ into the document.
#
def insert_page(index, page)
raise InvalidPDFError, "Invalid page tree" if not self.Catalog or not self.Catalog.Pages or not self.Catalog.Pages.is_a?(PageTreeNode)
# Page from another document must be exported.
page = page.export if page.pdf and page.pdf != self
self.Catalog.Pages.insert_page(index, page)
self
end
#
# Returns an array of Page
#
def pages
raise InvalidPDFError, "Invalid page tree" if not self.Catalog or not self.Catalog.Pages or not self.Catalog.Pages.is_a?(PageTreeNode)
self.Catalog.Pages.children
end
#
# Iterate through each page, returns self.
#
def each_page(&b)
raise InvalidPDFError, "Invalid page tree" if not self.Catalog or not self.Catalog.Pages or not self.Catalog.Pages.is_a?(PageTreeNode)
self.Catalog.Pages.each_page(&b)
self
end
#
# Get the n-th Page object.
#
def get_page(n)
raise InvalidPDFError, "Invalid page tree" if not self.Catalog or not self.Catalog.Pages or not self.Catalog.Pages.is_a?(PageTreeNode)
self.Catalog.Pages.get_page(n)
end
#
# Lookup page in the page name directory.
#
def get_page_by_name(name)
resolve_name Names::Root::PAGES, name
end
#
# Calls block for each named page.
#
def each_named_page(&b)
each_name(Names::Root::PAGES, &b)
end
end
module ResourcesHolder
def add_extgstate(extgstate, name = nil)
add_resource(Resources::EXTGSTATE, extgstate, name)
end
def add_colorspace(colorspace, name = nil)
add_resource(Resources::COLORSPACE, colorspace, name)
end
def add_pattern(pattern, name = nil)
add_resource(Resources::PATTERN, pattern, name)
end
def add_shading(shading, name = nil)
add_resource(Resources::SHADING, shading, name)
end
def add_xobject(xobject, name = nil)
add_resource(Resources::XOBJECT, xobject, name)
end
def add_font(font, name = nil)
add_resource(Resources::FONT, font, name)
end
def add_properties(properties, name = nil)
add_resource(Resources::PROPERTIES, properties, name)
end
def add_resource(type, rsrc, name = nil)
return existing if not name and existing = ls_resources(type).key(rsrc)
name = new_id(type) unless name
target = self.is_a?(Resources) ? self : (self.Resources ||= Resources.new)
rsrc_dict = target.send(type) || (target[type] = Dictionary.new)
rsrc_dict[name] = rsrc
name
end
def ls_resources(type)
target = self.is_a?(Resources) ? self : (self.Resources ||= Resources.new)
rsrc = {}
(target.send(type) || {}).each_pair do |name, obj|
rsrc[name.value] = obj.solve
end
rsrc
end
def extgstates; ls_resources(Resources::EXTGSTATE) end
def colorspaces; ls_resources(Resources::COLORSPACE) end
def patterns; ls_resources(Resources::PATTERN) end
def shadings; ls_resources(Resources::SHADING) end
def xobjects; ls_resources(Resources::XOBJECT) end
def fonts; ls_resources(Resources::FONT) end
def properties; ls_resources(Resources::PROPERTIES) end
def resources;
self.extgstates.
merge self.colorspaces.
merge self.patterns.
merge self.shadings.
merge self.xobjects.
merge self.fonts.
merge self.properties
end
private
def new_id(type, prefix = nil) #:nodoc:
prefix ||=
{
Resources::EXTGSTATE => 'ExtG',
Resources::COLORSPACE => 'CS',
Resources::PATTERN => 'P',
Resources::SHADING => 'Sh',
Resources::XOBJECT => 'Im',
Resources::FONT => 'F',
Resources::PROPERTIES => 'Pr'
}[type]
rsrc = ls_resources(type)
n = '1'
while rsrc.include?((prefix + n).to_sym)
n.next!
end
(prefix + n).to_sym
end
def new_extgstate_id; new_id(Resources::EXTGSTATE) end
def new_colorspace_id; new_id(Resources::COLORSPACE) end
def new_pattern_id; new_id(Resources::PATTERN) end
def new_shading_id; new_id(Resources::SHADING) end
def new_xobject_id; new_id(Resources::XOBJECT) end
def new_font_id; new_name(Resources::FONT) end
def new_properties_id; new_name(Resources::PROPERTIES) end
end
#
# Class representing a Resources Dictionary for a Page.
#
class Resources < Dictionary
include StandardObject
include ResourcesHolder
EXTGSTATE = :ExtGState
COLORSPACE = :ColorSpace
PATTERN = :Pattern
SHADING = :Shading
XOBJECT = :XObject
FONT = :Font
PROPERTIES = :Properties
field EXTGSTATE, :Type => Dictionary
field COLORSPACE, :Type => Dictionary
field PATTERN, :Type => Dictionary
field SHADING, :Type => Dictionary, :Version => "1.3"
field XOBJECT, :Type => Dictionary
field FONT, :Type => Dictionary
field :ProcSet, :Type => Array
field PROPERTIES, :Type => Dictionary, :Version => "1.2"
def pre_build
add_font(Font::Type1::Standard::Helvetica.new.pre_build) unless self.Font
super
end
end
#
# Class representing a node in a Page tree.
#
class PageTreeNode < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Pages, :Required => true
field :Parent, :Type => Dictionary
field :Kids, :Type => Array, :Default => [], :Required => true
field :Count, :Type => Integer, :Default => 0, :Required => true
def initialize(hash = {})
self.Count = 0
self.Kids = []
super(hash)
set_indirect(true)
end
def pre_build #:nodoc:
self.Count = self.children.length
super
end
def insert_page(index, page)
if index > self.Count
raise IndexError, "Invalid index for page tree"
end
count = 0
kids = self.Kids
kids.length.times { |n|
if count == index
kids.insert(n, page)
self.Count = self.Count + 1
page.Parent = self
return self
else
node = kids[n].is_a?(Reference) ? kids[n].solve : kids[n]
case node
when Page
count = count + 1
next
when PageTreeNode
if count + node.Count > index
node.insert_page(index - count, page)
self.Count = self.Count + 1
return self
else
count = count + node.Count
next
end
end
end
}
if count == index
self << page
else
raise IndexError, "An error occured while inserting page"
end
self
end
#
# Returns an array of Page inheriting this tree node.
#
def children
pageset = []
unless self.Count.nil?
[ self.Count.value, self.Kids.length ].min.times do |n|
node = self.Kids[n].is_a?(Reference) ? self.Kids[n].solve : self.Kids[n]
case node
when PageTreeNode then pageset.concat(node.children)
when Page then pageset << node
end
end
end
pageset
end
#
# Iterate through each page of that node.
#
def each_page(&b)
unless self.Count.nil?
[ self.Count.value, self.Kids.length ].min.times do |n|
node = self.Kids[n].is_a?(Reference) ? self.Kids[n].solve : self.Kids[n]
case node
when PageTreeNode then node.each_page(&b)
when Page then b.call(node)
end
end
end
end
#
# Get the n-th Page object in this node, starting from 1.
#
def get_page(n)
raise IndexError, "Page numbers are referenced starting from 1" if n < 1
decount = n
loop do
[ self.Count.value, self.Kids.length ].min.times do |i|
node = self.Kids[i].is_a?(Reference) ? self.Kids[i].solve : self.Kids[i]
case node
when Page
decount = decount - 1
return node if decount == 0
when PageTreeNode
nchilds = [ node.Count.value, node.Kids.length ].min
if nchilds >= decount
return node.get_page(decount)
else
decount -= nchilds
end
end
end
end
end
def << (pageset)
pageset = [pageset] unless pageset.is_a?(::Array)
raise TypeError, "Cannot add anything but Page and PageTreeNode to this node" unless pageset.all? { |item| item.is_a?(Page) or item.is_a?(PageTreeNode) }
self.Kids ||= Array.new
self.Kids.concat(pageset)
self.Count = self.Kids.length
pageset.each do |node|
node.Parent = self
end
end
end
# Forward declaration.
class ContentStream < Stream; end
#
# Class representing a Page in the PDF document.
#
class Page < Dictionary
include StandardObject
include ResourcesHolder
module Format
A0 = Rectangle[:width => 2384, :height => 3370]
A1 = Rectangle[:width => 1684, :height => 2384]
A2 = Rectangle[:width => 1191, :height => 1684]
A3 = Rectangle[:width => 842, :height => 1191]
A4 = Rectangle[:width => 595, :height => 842]
A5 = Rectangle[:width => 420, :height => 595]
A6 = Rectangle[:width => 298, :height => 420]
A7 = Rectangle[:width => 210, :height => 298]
A8 = Rectangle[:width => 147, :height => 210]
A9 = Rectangle[:width => 105, :height => 147]
A10 = Rectangle[:width => 74, :height => 105]
B0 = Rectangle[:width => 2836, :height => 4008]
B1 = Rectangle[:width => 2004, :height => 2835]
B2 = Rectangle[:width => 1417, :height => 2004]
B3 = Rectangle[:width => 1001, :height => 1417]
B4 = Rectangle[:width => 709, :height => 1001]
B5 = Rectangle[:width => 499, :height => 709]
B6 = Rectangle[:width => 354, :height => 499]
B7 = Rectangle[:width => 249, :height => 354]
B8 = Rectangle[:width => 176, :height => 249]
B9 = Rectangle[:width => 125, :height => 176]
B10 = Rectangle[:width => 88, :height => 125]
end
field :Type, :Type => Name, :Default => :Page, :Required => true
field :Parent, :Type => Dictionary, :Required => true
field :LastModified, :Type => String, :Version => "1.3"
field :Resources, :Type => Resources, :Required => true
field :MediaBox, :Type => Array, :Default => Format::A4, :Required => true
field :CropBox, :Type => Array
field :BleedBox, :Type => Array, :Version => "1.3"
field :TrimBox, :Type => Array, :Version => "1.3"
field :ArtBox, :Type => Array, :Version => "1.3"
field :BoxColorInfo, :Type => Dictionary, :Version => "1.4"
field :Contents, :Type => [ ContentStream, Array ]
field :Rotate, :Type => Integer, :Default => 0
field :Group, :Type => Dictionary, :Version => "1.4"
field :Thumb, :Type => Stream
field :B, :Type => Array, :Version => "1.1"
field :Dur, :Type => Integer, :Version => "1.1"
field :Trans, :Type => Dictionary, :Version => "1.1"
field :Annots, :Type => Array
field :AA, :Type => Dictionary, :Version => "1.2"
field :Metadata, :Type => Stream, :Version => "1.4"
field :PieceInfo, :Type => Dictionary, :Version => "1.2"
field :StructParents, :Type => Integer, :Version => "1.3"
field :ID, :Type => String
field :PZ, :Type => Number
field :SeparationInfo, :Type => Dictionary, :Version => "1.3"
field :Tabs, :Type => Name, :Version => "1.5"
field :TemplateAssociated, :Type => Name, :Version => "1.5"
field :PresSteps, :Type => Dictionary, :Version => "1.5"
field :UserUnit, :Type => Number, :Default => 1.0, :Version => "1.6"
field :VP, :Type => Dictionary, :Version => "1.6"
def initialize(hash = {})
super(hash)
set_indirect(true)
end
def pre_build
self.Resources = Resources.new.pre_build unless self.has_key?(:Resources)
super
end
#
# Add an Annotation to the Page.
#
def add_annot(*annotations)
unless annotations.all?{|annot| annot.is_a?(Annotation) or annot.is_a?(Reference)}
raise TypeError, "Only Annotation objects must be passed."
end
self.Annots ||= []
annotations.each do |annot|
annot.solve[:P] = self if is_indirect?
self.Annots << annot
end
end
#
# Iterate through each Annotation of the Page.
#
def each_annot(&b)
annots = self.Annots
return unless annots.is_a?(Array)
annots.each do |annot|
b.call(annot.solve)
end
end
#
# Returns the array of Annotation objects of the Page.
#
def annotations
annots = self.Annots
return [] unless annots.is_a?(Array)
annots.map{|annot| annot.solve}
end
#
# Embed a SWF Flash application in the page.
#
def add_flash_application(swfspec, params = {})
options =
{
:windowed => false,
:transparent => false,
:navigation_pane => false,
:toolbar => false,
:pass_context_click => false,
:activation => Annotation::RichMedia::Activation::PAGE_OPEN,
:deactivation => Annotation::RichMedia::Deactivation::PAGE_CLOSE,
:flash_vars => nil
}
options.update(params)
annot = create_richmedia(:Flash, swfspec, options)
add_annot(annot)
annot
end
#
# Will execute an action when the page is opened.
#
def onOpen(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= PageAdditionalActions.new
self.AA.O = action
self
end
#
# Will execute an action when the page is closed.
#
def onClose(action)
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.AA ||= PageAdditionalActions.new
self.AA.C = action
self
end
#
# Will execute an action when navigating forward from this page.
#
def onNavigateForward(action) #:nodoc:
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.PresSteps ||= NavigationNode.new
self.PresSteps.NA = action
self
end
#
# Will execute an action when navigating backward from this page.
#
def onNavigateBackward(action) #:nodoc:
unless action.is_a?(Action) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
self.PresSteps ||= NavigationNode.new
self.PresSteps.PA = action
self
end
private
def create_richmedia(type, content, params) #:nodoc:
content.set_indirect(true)
richmedia = Annotation::RichMedia.new.set_indirect(true)
rminstance = Annotation::RichMedia::Instance.new.set_indirect(true)
rmparams = rminstance.Params = Annotation::RichMedia::Parameters.new
rmparams.Binding = Annotation::RichMedia::Parameters::Binding::BACKGROUND
rmparams.FlashVars = params[:flash_vars]
rminstance.Asset = content
rmconfig = Annotation::RichMedia::Configuration.new.set_indirect(true)
rmconfig.Instances = [ rminstance ]
rmconfig.Subtype = type
rmcontent = richmedia.RichMediaContent = Annotation::RichMedia::Content.new.set_indirect(true)
rmcontent.Assets = NameTreeNode.new
rmcontent.Assets.Names = NameLeaf.new(content.F.value => content)
rmcontent.Configurations = [ rmconfig ]
rmsettings = richmedia.RichMediaSettings = Annotation::RichMedia::Settings.new
rmactivation = rmsettings.Activation = Annotation::RichMedia::Activation.new
rmactivation.Condition = params[:activation]
rmactivation.Configuration = rmconfig
rmactivation.Animation = Annotation::RichMedia::Animation.new(:PlayCount => -1, :Subtype => :Linear, :Speed => 1.0)
rmpres = rmactivation.Presentation = Annotation::RichMedia::Presentation.new
rmpres.Style = Annotation::RichMedia::Presentation::WINDOWED if params[:windowed]
rmpres.Transparent = params[:transparent]
rmpres.NavigationPane = params[:navigation_pane]
rmpres.Toolbar = params[:toolbar]
rmpres.PassContextClick = params[:pass_context_click]
rmdeactivation = rmsettings.Deactivation = Annotation::RichMedia::Deactivation.new
rmdeactivation.Condition = params[:deactivation]
richmedia
end
end
#
# Class representing additional actions which can be associated to a Page.
#
class PageAdditionalActions < Dictionary
include StandardObject
field :O, :Type => Dictionary, :Version => "1.2" # Page Open
field :C, :Type => Dictionary, :Version => "1.2" # Page Close
end
#
# Class representing a navigation node associated to a Page.
#
class NavigationNode < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :NavNode
field :NA, :Type => Dictionary # Next action
field :PA, :Type => Dictionary # Prev action
field :Next, :Type => Dictionary
field :Prev, :Type => Dictionary
field :Dur, :Type => Number
end
end
origami-pdf-1.2.7/lib/origami/outputintents.rb 0000644 0001750 0001750 00000005523 12101464040 021615 0 ustar terceiro terceiro =begin
= File
outputintents.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class OutputIntent < Dictionary
include StandardObject
module Intent
PDFX = :GTS_PDFX
PDFA1 = :GTS_PDFA1
PDFE1 = :GTS_PDFE1
end
field :Type, :Type => Name, :Default => :OutputIntent
field :S, :Type => Name, :Version => '1.4', :Required => true
field :OutputCondition, :Type => String
field :OutputConditionIdentifier, :Type => String
field :RegistryName, :Type => String
field :Info, :Type => String
field :DestOutputProfile, :Type => Stream
end
class PDF
def is_a_pdfa1?
self.Catalog.OutputIntents.is_a?(Array) and
self.Catalog.OutputIntents.any?{|intent|
intent = intent.solve;
intent.S == OutputIntent::Intent::PDFA1
} and
self.has_metadata? and (
doc = REXML::Document.new self.Catalog.Metadata.data;
REXML::XPath.match(doc, "*/*/rdf:Description[@xmlns:pdfaid]").any? {|desc|
desc.elements["pdfaid:conformance"].text == "A" and
desc.elements["pdfaid:part"].text == "1"
}
)
end
private
def intents_as_pdfa1
unless self.is_a_pdfa1?
self.Catalog.OutputIntents ||= []
self.Catalog.OutputIntents << self.insert(
OutputIntent.new(
:Type => :OutputIntent,
:S => OutputIntent::Intent::PDFA1,
:OutputConditionIdentifier => "RGB"
)
)
metadata = self.create_metadata
doc = REXML::Document.new(metadata.data)
desc = REXML::Element.new 'rdf:Description'
desc.add_attribute 'rdf:about', ''
desc.add_attribute 'xmlns:pdfaid', 'http://www.aiim.org/pdfa/ns/id/'
desc.add REXML::Element.new('pdfaid:conformance').add_text('A')
desc.add REXML::Element.new('pdfaid:part').add_text('1')
doc.elements["*/rdf:RDF"].add desc
xml = ""; doc.write(xml, 3)
metadata.data = xml
end
end
end
end
origami-pdf-1.2.7/lib/origami/filters/ 0000755 0001750 0001750 00000000000 12427006355 020002 5 ustar terceiro terceiro origami-pdf-1.2.7/lib/origami/filters/crypt.rb 0000644 0001750 0001750 00000002302 12101464040 021451 0 ustar terceiro terceiro =begin
= File
filters/crypt.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Filter
#
# Class representing a Crypt Filter.
# TODO.
#
class Crypt
include Filter
class DecodeParms < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Crypt
field :Name, :Type => Name, :Default => :Identity
end
end
end
end
origami-pdf-1.2.7/lib/origami/filters/dct.rb 0000644 0001750 0001750 00000002765 12101464040 021077 0 ustar terceiro terceiro =begin
= File
filters/dct.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Filter
#
# Class representing a Filter used to encode and decode data with DCT (JPEG) compression algorithm.
#
class DCT
include Filter
class DecodeParms < Dictionary
include StandardObject
field :ColorTransform, :Type => Integer
end
def initialize(parameters = {})
super(DecodeParms.new(parameters))
end
def encode(stream)
stream
end
#
# DCTDecode implies that data is a JPEG image container.
# Just returns the raw JPEG image as is.
#
def decode(stream)
stream
end
end
end
end
origami-pdf-1.2.7/lib/origami/filters/ccitt.rb 0000644 0001750 0001750 00000045447 12142214376 021451 0 ustar terceiro terceiro =begin
= File
filters/ccitt.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Filter
class InvalidCCITTFaxDataError < InvalidFilterDataError #:nodoc:
end
class CCITTFaxFilterError < Exception #:nodoc:
end
#
# Class representing a Filter used to encode and decode data with CCITT-facsimile compression algorithm.
#
class CCITTFax
include Filter
class DecodeParms < Dictionary
include StandardObject
field :K, :Type => Integer, :Default => 0
field :EndOfLine, :Type => Boolean, :Default => false
field :EncodedByteAlign, :Type => Boolean, :Default => false
field :Columns, :Type => Integer, :Default => 1728
field :Rows, :Type => Integer, :Default => 0
field :EndOfBlock, :Type => Boolean, :Default => true
field :BlackIs1, :Type => Boolean, :Default => false
field :DamagedRowsBeforeError, :Type => :Integer, :Default => 0
end
def self.codeword(str) #:nodoc:
[ str.to_i(2), str.length ]
end
EOL = codeword('000000000001')
RTC = codeword('000000000001' * 6)
WHITE_TERMINAL_ENCODE_TABLE =
{
0 => codeword('00110101'),
1 => codeword('000111'),
2 => codeword('0111'),
3 => codeword('1000'),
4 => codeword('1011'),
5 => codeword('1100'),
6 => codeword('1110'),
7 => codeword('1111'),
8 => codeword('10011'),
9 => codeword('10100'),
10 => codeword('00111'),
11 => codeword('01000'),
12 => codeword('001000'),
13 => codeword('000011'),
14 => codeword('110100'),
15 => codeword('110101'),
16 => codeword('101010'),
17 => codeword('101011'),
18 => codeword('0100111'),
19 => codeword('0001100'),
20 => codeword('0001000'),
21 => codeword('0010111'),
22 => codeword('0000011'),
23 => codeword('0000100'),
24 => codeword('0101000'),
25 => codeword('0101011'),
26 => codeword('0010011'),
27 => codeword('0100100'),
28 => codeword('0011000'),
29 => codeword('00000010'),
30 => codeword('00000011'),
31 => codeword('00011010'),
32 => codeword('00011011'),
33 => codeword('00010010'),
34 => codeword('00010011'),
35 => codeword('00010100'),
36 => codeword('00010101'),
37 => codeword('00010110'),
38 => codeword('00010111'),
39 => codeword('00101000'),
40 => codeword('00101001'),
41 => codeword('00101010'),
42 => codeword('00101011'),
43 => codeword('00101100'),
44 => codeword('00101101'),
45 => codeword('00000100'),
46 => codeword('00000101'),
47 => codeword('00001010'),
48 => codeword('00001011'),
49 => codeword('01010010'),
50 => codeword('01010011'),
51 => codeword('01010100'),
52 => codeword('01010101'),
53 => codeword('00100100'),
54 => codeword('00100101'),
55 => codeword('01011000'),
56 => codeword('01011001'),
57 => codeword('01011010'),
58 => codeword('01011011'),
59 => codeword('01001010'),
60 => codeword('01001011'),
61 => codeword('00110010'),
62 => codeword('00110011'),
63 => codeword('00110100')
}
WHITE_TERMINAL_DECODE_TABLE = WHITE_TERMINAL_ENCODE_TABLE.invert
BLACK_TERMINAL_ENCODE_TABLE =
{
0 => codeword('0000110111'),
1 => codeword('010'),
2 => codeword('11'),
3 => codeword('10'),
4 => codeword('011'),
5 => codeword('0011'),
6 => codeword('0010'),
7 => codeword('00011'),
8 => codeword('000101'),
9 => codeword('000100'),
10 => codeword('0000100'),
11 => codeword('0000101'),
12 => codeword('0000111'),
13 => codeword('00000100'),
14 => codeword('00000111'),
15 => codeword('000011000'),
16 => codeword('0000010111'),
17 => codeword('0000011000'),
18 => codeword('0000001000'),
19 => codeword('00001100111'),
20 => codeword('00001101000'),
21 => codeword('00001101100'),
22 => codeword('00000110111'),
23 => codeword('00000101000'),
24 => codeword('00000010111'),
25 => codeword('00000011000'),
26 => codeword('000011001010'),
27 => codeword('000011001011'),
28 => codeword('000011001100'),
29 => codeword('000011001101'),
30 => codeword('000001101000'),
31 => codeword('000001101001'),
32 => codeword('000001101010'),
33 => codeword('000001101011'),
34 => codeword('000011010010'),
35 => codeword('000011010011'),
36 => codeword('000011010100'),
37 => codeword('000011010101'),
38 => codeword('000011010110'),
39 => codeword('000011010111'),
40 => codeword('000001101100'),
41 => codeword('000001101101'),
42 => codeword('000011011010'),
43 => codeword('000011011011'),
44 => codeword('000001010100'),
45 => codeword('000001010101'),
46 => codeword('000001010110'),
47 => codeword('000001010111'),
48 => codeword('000001100100'),
49 => codeword('000001100101'),
50 => codeword('000001010010'),
51 => codeword('000001010011'),
52 => codeword('000000100100'),
53 => codeword('000000110111'),
54 => codeword('000000111000'),
55 => codeword('000000100111'),
56 => codeword('000000101000'),
57 => codeword('000001011000'),
58 => codeword('000001011001'),
59 => codeword('000000101011'),
60 => codeword('000000101100'),
61 => codeword('000001011010'),
62 => codeword('000001100110'),
63 => codeword('000001100111')
}
BLACK_TERMINAL_DECODE_TABLE = BLACK_TERMINAL_ENCODE_TABLE.invert
WHITE_CONFIGURATION_ENCODE_TABLE =
{
64 => codeword('11011'),
128 => codeword('10010'),
192 => codeword('010111'),
256 => codeword('0110111'),
320 => codeword('00110110'),
384 => codeword('00110111'),
448 => codeword('01100100'),
512 => codeword('01100101'),
576 => codeword('01101000'),
640 => codeword('01100111'),
704 => codeword('011001100'),
768 => codeword('011001101'),
832 => codeword('011010010'),
896 => codeword('011010011'),
960 => codeword('011010100'),
1024 => codeword('011010101'),
1088 => codeword('011010110'),
1152 => codeword('011010111'),
1216 => codeword('011011000'),
1280 => codeword('011011001'),
1344 => codeword('011011010'),
1408 => codeword('011011011'),
1472 => codeword('010011000'),
1536 => codeword('010011001'),
1600 => codeword('010011010'),
1664 => codeword('011000'),
1728 => codeword('010011011'),
1792 => codeword('00000001000'),
1856 => codeword('00000001100'),
1920 => codeword('00000001001'),
1984 => codeword('000000010010'),
2048 => codeword('000000010011'),
2112 => codeword('000000010100'),
2176 => codeword('000000010101'),
2240 => codeword('000000010110'),
2340 => codeword('000000010111'),
2368 => codeword('000000011100'),
2432 => codeword('000000011101'),
2496 => codeword('000000011110'),
2560 => codeword('000000011111')
}
WHITE_CONFIGURATION_DECODE_TABLE = WHITE_CONFIGURATION_ENCODE_TABLE.invert
BLACK_CONFIGURATION_ENCODE_TABLE =
{
64 => codeword('0000001111'),
128 => codeword('000011001000'),
192 => codeword('000011001001'),
256 => codeword('000001011011'),
320 => codeword('000000110011'),
384 => codeword('000000110100'),
448 => codeword('000000110101'),
512 => codeword('0000001101100'),
576 => codeword('0000001101101'),
640 => codeword('0000001001010'),
704 => codeword('0000001001011'),
768 => codeword('0000001001100'),
832 => codeword('0000001001101'),
896 => codeword('0000001110010'),
960 => codeword('0000001110011'),
1024 => codeword('0000001110100'),
1088 => codeword('0000001110101'),
1152 => codeword('0000001110110'),
1216 => codeword('0000001110111'),
1280 => codeword('0000001010010'),
1344 => codeword('0000001010011'),
1408 => codeword('0000001010100'),
1472 => codeword('0000001010101'),
1536 => codeword('0000001011010'),
1600 => codeword('0000001011011'),
1664 => codeword('0000001100100'),
1728 => codeword('0000001100101'),
1792 => codeword('00000001000'),
1856 => codeword('00000001100'),
1920 => codeword('00000001001'),
1984 => codeword('000000010010'),
2048 => codeword('000000010011'),
2112 => codeword('000000010100'),
2176 => codeword('000000010101'),
2240 => codeword('000000010110'),
2340 => codeword('000000010111'),
2368 => codeword('000000011100'),
2432 => codeword('000000011101'),
2496 => codeword('000000011110'),
2560 => codeword('000000011111')
}
BLACK_CONFIGURATION_DECODE_TABLE = BLACK_CONFIGURATION_ENCODE_TABLE.invert
#
# Creates a new CCITT Fax Filter.
#
def initialize(parameters = {})
super(DecodeParms.new(parameters))
end
#
# Encodes data using CCITT-facsimile compression method.
#
def encode(stream)
mode = @params.has_key?(:K) ? @params.K.value : 0
unless mode.is_a?(::Integer) and mode <= 0
raise NotImplementedError, "CCITT encoding scheme not supported"
end
columns = @params.has_key?(:Columns) ? @params.Columns.value : (stream.size << 3)
unless columns.is_a?(::Integer) and columns > 0 #and columns % 8 == 0
raise CCITTFaxFilterError, "Invalid value for parameter `Columns'"
end
if stream.size % (columns >> 3) != 0
raise CCITTFaxFilterError, "Data size is not a multiple of image width"
end
colors = (@params.BlackIs1 == true) ? [0,1] : [1,0]
white, _black = colors
bitr = Utils::BitReader.new(stream)
bitw = Utils::BitWriter.new
# Group 4 requires an imaginary white line
if mode < 0
prev_line = Utils::BitWriter.new
write_bit_range(prev_line, white, columns)
prev_line = Utils::BitReader.new(prev_line.final.to_s)
end
until bitr.eod?
case
when mode == 0
encode_one_dimensional_line(bitr, bitw, columns, colors)
when mode < 0
encode_two_dimensional_line(bitr, bitw, columns, colors, prev_line)
end
end
# Emit return-to-control code
bitw.write(*RTC)
bitw.final.to_s
end
#
# Decodes data using CCITT-facsimile compression method.
#
def decode(stream)
mode = @params.has_key?(:K) ? @params.K.value : 0
unless mode.is_a?(::Integer) and mode <= 0
raise NotImplementedError, "CCITT encoding scheme not supported"
end
columns = @params.has_key?(:Columns) ? @params.Columns.value : 1728
unless columns.is_a?(::Integer) and columns > 0 #and columns % 8 == 0
raise CCITTFaxFilterError, "Invalid value for parameter `Columns'"
end
colors = (@params.BlackIs1 == true) ? [0,1] : [1,0]
white, _black = colors
params =
{
:is_aligned? => (@params.EncodedByteAlign == true),
:has_eob? => (@params.EndOfBlock.nil? or @params.EndOfBlock == true),
:has_eol? => (@params.EndOfLine == true)
}
unless params[:has_eob?]
unless @params.has_key?(:Rows) and @params.Rows.is_a?(::Integer) and @params.Rows.value > 0
raise CCITTFaxFilterError, "Invalid value for parameter `Rows'"
end
rows = @params.Rows.to_i
end
bitr = Utils::BitReader.new(stream)
bitw = Utils::BitWriter.new
# Group 4 requires an imaginary white line
if mode < 0
prev_line = Utils::BitWriter.new
write_bit_range(prev_line, white, columns)
prev_line = Utils::BitReader.new(prev_line.final.to_s)
end
until bitr.eod? or rows == 0
# realign the read line on a 8-bit boundary if required
if params[:is_aligned?] and bitr.pos % 8 != 0
bitr.pos += 8 - (bitr.pos % 8)
end
# received return-to-control code
if params[:has_eob?] and bitr.peek(RTC[1]) == RTC[0]
bitr.pos += RTC[1]
break
end
# checking for the presence of EOL
if bitr.peek(EOL[1]) != EOL[0]
raise InvalidCCITTFaxDataError.new(
"No end-of-line pattern found (at bit pos #{bitr.pos}/#{bitr.size}})",
bitw.final.to_s
) if params[:has_eol?]
else
bitr.pos += EOL[1]
end
case
when mode == 0
decode_one_dimensional_line(bitr, bitw, columns, colors)
when mode < 0
decode_two_dimensional_line(bitr, bitw, columns, colors, prev_line)
end
rows -= 1 unless params[:has_eob?]
end
bitw.final.to_s
end
private
def encode_one_dimensional_line(input, output, columns, colors) #:nodoc:
output.write(*EOL)
scan_len = 0
white, black = colors
current_color = white
# Process each bit in line.
begin
if input.read(1) == current_color
scan_len += 1
else
if current_color == white
put_white_bits(output, scan_len)
else
put_black_bits(output, scan_len)
end
current_color ^= 1
scan_len = 1
end
end while input.pos % columns != 0
if current_color == white
put_white_bits(output, scan_len)
else
put_black_bits(output, scan_len)
end
# Align encoded lign on a 8-bit boundary.
if @params.EncodedByteAlign == true and output.pos % 8 != 0
output.write(0, 8 - (output.pos % 8))
end
end
def encode_two_dimensional_line(input, output, columns, colors, prev_line) #:nodoc:
raise NotImplementedError, "CCITT two-dimensional encoding scheme not supported."
white, black = colors
current_color = white
end
def decode_one_dimensional_line(input, output, columns, colors) #:nodoc:
white, black = colors
current_color = white
line_length = 0
while line_length < columns
if current_color == white
bit_length = get_white_bits(input)
else
bit_length = get_black_bits(input)
end
raise InvalidCCITTFaxDataError.new(
"Unfinished line (at bit pos #{input.pos}/#{input.size}})",
output.final.to_s
) if bit_length.nil?
line_length += bit_length
raise InvalidCCITTFaxDataError.new(
"Line is too long (at bit pos #{input.pos}/#{input.size}})",
output.final.to_s
) if line_length > columns
write_bit_range(output, current_color, bit_length)
current_color ^= 1
end
end
def decode_two_dimensional_line(input, output, columns, colors, prev_line) #:nodoc:
raise NotImplementedError, "CCITT two-dimensional decoding scheme not supported."
end
def get_white_bits(bitr) #:nodoc:
get_color_bits(bitr, WHITE_CONFIGURATION_DECODE_TABLE, WHITE_TERMINAL_DECODE_TABLE)
end
def get_black_bits(bitr) #:nodoc:
get_color_bits(bitr, BLACK_CONFIGURATION_DECODE_TABLE, BLACK_TERMINAL_DECODE_TABLE)
end
def get_color_bits(bitr, config_words, term_words) #:nodoc:
bits = 0
check_conf = true
while check_conf
check_conf = false
(2..13).each do |length|
codeword = bitr.peek(length)
config_value = config_words[[codeword, length]]
if config_value
bitr.pos += length
bits += config_value
check_conf = true if config_value == 2560
break
end
end
end
(2..13).each do |length|
codeword = bitr.peek(length)
term_value = term_words[[codeword, length]]
if term_value
bitr.pos += length
bits += term_value
return bits
end
end
nil
end
def lookup_bits(table, codeword, length)
table.rassoc [codeword, length]
end
def put_white_bits(bitw, length) #:nodoc:
put_color_bits(bitw, length, WHITE_CONFIGURATION_ENCODE_TABLE, WHITE_TERMINAL_ENCODE_TABLE)
end
def put_black_bits(bitw, length) #:nodoc:
put_color_bits(bitw, length, BLACK_CONFIGURATION_ENCODE_TABLE, BLACK_TERMINAL_ENCODE_TABLE)
end
def put_color_bits(bitw, length, config_words, term_words) #:nodoc:
while length > 2559
bitw.write(*config_words[2560])
length -= 2560
end
if length > 63
conf_length = (length >> 6) << 6
bitw.write(*config_words[conf_length])
length -= conf_length
end
bitw.write(*term_words[length])
end
def write_bit_range(bitw, bit_value, length) #:nodoc:
bitw.write((bit_value << length) - bit_value, length)
end
end
CCF = CCITTFax
end
end
origami-pdf-1.2.7/lib/origami/filters/jpx.rb 0000644 0001750 0001750 00000002511 12101464040 021113 0 ustar terceiro terceiro =begin
= File
filters/jpx.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Filter
#
# Class representing a Filter used to encode and decode data with JPX compression algorithm.
#
class JPX
include Filter
#
# Not supported.
#
def encode(stream)
raise NotImplementedError, "#{self.class} is not yet supported"
end
#
# Not supported.
#
def decode(stream)
raise NotImplementedError, "#{self.class} is not yet supported"
end
end
end
end
origami-pdf-1.2.7/lib/origami/filters/ascii.rb 0000644 0001750 0001750 00000012063 12101464040 021405 0 ustar terceiro terceiro =begin
= File
filters/ascii.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Filter
class InvalidASCIIHexStringError < InvalidFilterDataError #:nodoc:
end
#
# Class representing a filter used to encode and decode data written into hexadecimal.
#
class ASCIIHex
include Filter
EOD = ">" #:nodoc:
#
# Encodes given data into upcase hexadecimal representation.
# _stream_:: The data to encode.
#
def encode(stream)
stream.unpack("H2" * stream.size).join.upcase
end
#
# Decodes given data writen into upcase hexadecimal representation.
# _string_:: The data to decode.
#
def decode(string)
input = string.include?(?>) ? string[0..string.index(?>) - 1] : string
digits = input.delete(" \f\t\r\n\0").split(/(..)/).delete_if{|digit| digit.empty?}
if not digits.all? { |d| d =~ /[a-fA-F0-9]{1,2}/ }
raise InvalidASCIIHexStringError, input
end
digits.pack("H2" * digits.size)
end
end
AHx = ASCIIHex
class InvalidASCII85StringError < InvalidFilterDataError #:nodoc:
end
#
# Class representing a filter used to encode and decode data written in base85 encoding.
#
class ASCII85
include Filter
EOD = "~>" #:nodoc:
#
# Encodes given data into base85.
# _stream_:: The data to encode.
#
def encode(stream)
i = 0
code = ""
input = stream.dup
while i < input.size do
if input.length - i < 4
addend = 4 - (input.length - i)
input << "\0" * addend
else
addend = 0
end
inblock = (input[i].ord * 256**3 + input[i+1].ord * 256**2 + input[i+2].ord * 256 + input[i+3].ord)
outblock = ""
5.times do |p|
c = inblock / 85 ** (4 - p)
outblock << ("!"[0].ord + c).chr
inblock -= c * 85 ** (4 - p)
end
outblock = "z" if outblock == "!!!!!" and addend == 0
if addend != 0
outblock = outblock[0,(4 - addend) + 1]
end
code << outblock
i = i + 4
end
code
end
#
# Decodes the given data encoded in base85.
# _string_:: The data to decode.
#
def decode(string)
input = (string.include?(EOD) ? string[0..string.index(EOD) - 1] : string).delete(" \f\t\r\n\0")
i = 0
result = ""
while i < input.size do
outblock = ""
if input[i].ord == "z"[0].ord
inblock = 0
codelen = 1
else
inblock = 0
codelen = 5
if input.length - i < 5
raise InvalidASCII85StringError.new("Invalid length", result) if input.length - i == 1
addend = 5 - (input.length - i)
input << "u" * addend
else
addend = 0
end
# Checking if this string is in base85
5.times do |j|
if input[i+j].ord > "u"[0].ord or input[i+j].ord < "!"[0].ord
raise InvalidASCII85StringError.new(
"Invalid character sequence: #{input[i,5].inspect}",
result
)
else
inblock += (input[i+j].ord - "!"[0].ord) * 85 ** (4 - j)
end
end
raise InvalidASCII85StringError.new(
"Invalid value (#{inblock}) for block #{input[i,5].inspect}",
result
) if inblock >= 2**32
end
4.times do |p|
c = inblock / 256 ** (3 - p)
outblock << c.chr
inblock -= c * 256 ** (3 - p)
end
if addend != 0
outblock = outblock[0, 4 - addend]
end
result << outblock
i = i + codelen
end
result
end
end
A85 = ASCII85
end
end
origami-pdf-1.2.7/lib/origami/filters/jbig2.rb 0000644 0001750 0001750 00000003062 12101464040 021311 0 ustar terceiro terceiro =begin
= File
filters/jbig2.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Filter
#
# Class representing a Filter used to encode and decode data with JBIG2 compression algorithm.
#
class JBIG2
include Filter
class DecodeParms < Dictionary
include StandardObject
field :JBIG2Globals, :Type => Stream
end
def initialize(parameters = {})
super(DecodeParms.new(parameters))
end
#
# Not supported.
#
def encode(stream)
raise NotImplementedError, "#{self.class} is not yet supported"
end
#
# Not supported.
#
def decode(stream)
raise NotImplementedError, "#{self.class} is not yet supported"
end
end
end
end
origami-pdf-1.2.7/lib/origami/filters/lzw.rb 0000644 0001750 0001750 00000013235 12101464040 021133 0 ustar terceiro terceiro =begin
= File
filters/lzw.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/filters/predictors'
module Origami
module Filter
class InvalidLZWDataError < InvalidFilterDataError #:nodoc:
end
#
# Class representing a filter used to encode and decode data with LZW compression algorithm.
#
class LZW
include Filter
class DecodeParms < Dictionary
include StandardObject
field :Predictor, :Type => Integer, :Default => 1
field :Colors, :Type => Integer, :Default => 1
field :BitsPerComponent, :Type => Integer, :Default => 8
field :Columns, :Type => Integer, :Default => 1
field :EarlyChange, :Type => Integer, :Default => 1
end
EOD = 257 #:nodoc:
CLEARTABLE = 256 #:nodoc:
#
# Creates a new LZW Filter.
# _parameters_:: A hash of filter options (ignored).
#
def initialize(parameters = {})
super(DecodeParms.new(parameters))
end
#
# Encodes given data using LZW compression method.
# _stream_:: The data to encode.
#
def encode(string)
if @params.Predictor.is_a?(Integer)
colors = @params.Colors.is_a?(Integer) ? @params.Colors.to_i : 1
bpc = @params.BitsPerComponent.is_a?(Integer) ? @params.BitsPerComponent.to_i : 8
columns = @params.Columns.is_a?(Integer) ? @params.Columns.to_i : 1
string = Predictor.do_pre_prediction(string, @params.Predictor.to_i, colors, bpc, columns)
end
codesize = 9
result = Utils::BitWriter.new
result.write(CLEARTABLE, codesize)
table = clear({})
s = ''
string.each_byte do |byte|
char = byte.chr
case table.size
when 512 then codesize = 10
when 1024 then codesize = 11
when 2048 then codesize = 12
when 4096
result.write(CLEARTABLE, codesize)
codesize = 9
clear table
redo
end
it = s + char
if table.has_key?(it)
s = it
else
result.write(table[s], codesize)
table[it] = table.size
s = char
end
end
result.write(table[s], codesize)
result.write(EOD, codesize)
result.final.to_s
end
#
# Decodes given data using LZW compression method.
# _stream_:: The data to decode.
#
def decode(string)
result = ""
bstring = Utils::BitReader.new(string)
codesize = 9
table = clear(Hash.new)
prevbyte = nil
until bstring.eod? do
byte = bstring.read(codesize)
case table.size
when 510 then codesize = 10
when 1022 then codesize = 11
when 2046 then codesize = 12
when 4095
if byte != CLEARTABLE
then
raise InvalidLZWDataError.new(
"LZW table is full and no clear flag was set (codeword #{byte.to_s(2).rjust(codesize,'0')} at bit #{bstring.pos - codesize}/#{bstring.size})",
result
)
end
end
if byte == CLEARTABLE
codesize = 9
code = EOD
clear table
prevbyte = nil
redo
elsif byte == EOD
break
else
if prevbyte.nil?
prevbyte = byte
result << table.key(byte)
redo
else
raise InvalidLZWDataError.new(
"No entry for codeword #{prevbyte.to_s(2).rjust(codesize,'0')}.",
result
) unless table.key(prevbyte)
if table.has_value?(byte)
entry = table.key(byte)
else
entry = table.key(prevbyte)
entry += entry[0,1]
end
result << entry
table[table.key(prevbyte) + entry[0,1]] = table.size
prevbyte = byte
end
end
end
if @params.Predictor.is_a?(Integer)
colors = @params.Colors.is_a?(Integer) ? @params.Colors.to_i : 1
bpc = @params.BitsPerComponent.is_a?(Integer) ? @params.BitsPerComponent.to_i : 8
columns = @params.Columns.is_a?(Integer) ? @params.Columns.to_i : 1
result = Predictor.do_post_prediction(result, @params.Predictor.to_i, colors, bpc, columns)
end
result
end
private
def clear(table) #:nodoc:
table.clear
256.times do |i|
table[i.chr] = i
end
table[CLEARTABLE] = CLEARTABLE
table[EOD] = EOD
table
end
end
end
end
origami-pdf-1.2.7/lib/origami/filters/predictors.rb 0000644 0001750 0001750 00000016621 12101464040 022477 0 ustar terceiro terceiro =begin
= File
filters/predictors.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Filter
class PredictorError < Exception #:nodoc:
end
module Predictor
NONE = 1
TIFF = 2
PNG_NONE = 10
PNG_SUB = 11
PNG_UP = 12
PNG_AVERAGE = 13
PNG_PAETH = 14
PNG_OPTIMUM = 15
def self.do_pre_prediction(data, predictor = NONE, colors = 1, bpc = 8, columns = 1)
return data if predictor == NONE
unless (1..4) === colors.to_i
raise PredictorError, "Colors must be between 1 and 4"
end
unless [1,2,4,8,16].include?(bpc.to_i)
raise PredictorError, "BitsPerComponent must be in 1, 2, 4, 8 or 16"
end
# components per line
nvals = columns * colors
# bytes per pixel
bpp = (colors * bpc + 7) >> 3
# bytes per row
bpr = (nvals * bpc + 7) >> 3
unless data.size % bpr == 0
raise PredictorError, "Invalid data size #{data.size}, should be multiple of bpr=#{bpr}"
end
if predictor == TIFF
do_tiff_pre_prediction(data, colors, bpc, columns)
elsif predictor >= 10 # PNG
do_png_pre_prediction(data, predictor, bpp, bpr)
else
raise PredictorError, "Unknown predictor : #{predictor}"
end
end
def self.do_post_prediction(data, predictor = NONE, colors = 1, bpc = 8, columns = 1)
return data if predictor == NONE
unless (1..4) === colors
raise PredictorError, "Colors must be between 1 and 4"
end
unless [1,2,4,8,16].include?(bpc)
raise PredictorError, "BitsPerComponent must be in 1, 2, 4, 8 or 16"
end
# components per line
nvals = columns * colors
# bytes per pixel
bpp = (colors * bpc + 7) >> 3
# bytes per row
bpr = ((nvals * bpc + 7) >> 3) + 1
if predictor == TIFF
do_tiff_post_prediction(data, colors, bpc, columns)
elsif predictor >= 10 # PNG
do_png_post_prediction(data, bpp, bpr)
else
raise PredictorError, "Unknown predictor : #{predictor}"
end
end
def self.do_png_post_prediction(data, bpp, bpr)
result = ""
uprow = "\0" * bpr
thisrow = "\0" * bpr
nrows = (data.size + bpr - 1) / bpr
nrows.times do |irow|
line = data[irow * bpr, bpr]
predictor = 10 + line[0].ord
line[0] = "\0"
for i in (1..line.size-1)
up = uprow[i].ord
if bpp > i
left = upleft = 0
else
left = line[i-bpp].ord
upleft = uprow[i-bpp].ord
end
case predictor
when PNG_NONE
thisrow = line
when PNG_SUB
thisrow[i] = ((line[i].ord + left) & 0xFF).chr
when PNG_UP
thisrow[i] = ((line[i].ord + up) & 0xFF).chr
when PNG_AVERAGE
thisrow[i] = ((line[i].ord + ((left + up) / 2)) & 0xFF).chr
when PNG_PAETH
p = left + up - upleft
pa, pb, pc = (p - left).abs, (p - up).abs, (p - upleft).abs
thisrow[i] = ((line[i].ord +
case [ pa, pb, pc ].min
when pa then left
when pb then up
when pc then upleft
end
) & 0xFF).chr
else
puts "Unknown PNG predictor : #{predictor}"
thisrow = line
end
end
result << thisrow[1..-1]
uprow = thisrow
end
result
end
def self.do_png_pre_prediction(data, predictor, bpp, bpr)
result = ""
nrows = data.size / bpr
line = "\0" + data[-bpr, bpr]
(nrows-1).downto(0) do |irow|
uprow =
if irow == 0
"\0" * (bpr+1)
else
"\0" + data[(irow-1)*bpr,bpr]
end
bpr.downto(1) do |i|
up = uprow[i].ord
left = line[i-bpp].ord
upleft = uprow[i-bpp].ord
case predictor
when PNG_SUB
line[i] = ((line[i].ord - left) & 0xFF).chr
when PNG_UP
line[i] = ((line[i].ord - up) & 0xFF).chr
when PNG_AVERAGE
line[i] = ((line[i].ord - ((left + up) / 2)) & 0xFF).chr
when PNG_PAETH
p = left + up - upleft
pa, pb, pc = (p - left).abs, (p - up).abs, (p - upleft).abs
line[i] = ((line[i].ord -
case [ pa, pb, pc ].min
when pa then left
when pb then up
when pc then upleft
end
) & 0xFF).chr
when PNG_NONE
else
raise PredictorError, "Unsupported PNG predictor : #{predictor}"
end
end
line[0] = (predictor - 10).chr
result = line + result
line = uprow
end
result
end
def self.do_tiff_post_prediction(data, colors, bpc, columns) #:nodoc:
bpr = (colors * bpc * columns + 7) >> 3
nrows = data.size / bpr
bitmask = (1 << bpc) - 1
result = Utils::BitWriter.new
nrows.times do |irow|
line = Utils::BitReader.new(data[irow * bpr, bpr])
pixel = ::Array.new(colors, 0)
columns.times do
diffpixel = ::Array.new(colors) { line.read(bpc) }
pixel = pixel.zip(diffpixel).map!{|c, diff| (c + diff) & bitmask}
pixel.each do |c|
result.write(c, bpc)
end
end
result.final
end
result.final.to_s
end
def self.do_tiff_pre_prediction(data, colors, bpc, columns) #:nodoc:
bpr = (colors * bpc * columns + 7) >> 3
nrows = data.size / bpr
bitmask = (1 << bpc) - 1
result = Utils::BitWriter.new
nrows.times do |irow|
line = Utils::BitReader.new(data[irow * bpr, bpr])
diffpixel = ::Array.new(colors, 0)
columns.times do
pixel = ::Array.new(colors) { line.read(bpc) }
diffpixel = diffpixel.zip(pixel).map!{|diff, c| (c - diff) & bitmask}
diffpixel.each do |c|
result.write(c, bpc)
end
end
result.final
end
result.final.to_s
end
end
end
end
origami-pdf-1.2.7/lib/origami/filters/flate.rb 0000644 0001750 0001750 00000006532 12133245664 021432 0 ustar terceiro terceiro =begin
= File
filters/flate.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'zlib'
require 'origami/filters/predictors'
module Origami
module Filter
class InvalidFlateDataError < InvalidFilterDataError; end #:nodoc:
#
# Class representing a Filter used to encode and decode data with zlib/Flate compression algorithm.
#
class Flate
include Filter
EOD = 257 #:nodoc:
class DecodeParms < Dictionary
include StandardObject
field :Predictor, :Type => Integer, :Default => 1
field :Colors, :Type => Integer, :Default => 1
field :BitsPerComponent, :Type => Integer, :Default => 8
field :Columns, :Type => Integer, :Default => 1
end
#
# Create a new Flate Filter.
# _parameters_:: A hash of filter options (ignored).
#
def initialize(parameters = {})
super(DecodeParms.new(parameters))
end
#
# Encodes data using zlib/Deflate compression method.
# _stream_:: The data to encode.
#
def encode(stream)
if @params.Predictor.is_a?(Integer)
colors = @params.Colors.is_a?(Integer) ? @params.Colors.to_i : 1
bpc = @params.BitsPerComponent.is_a?(Integer) ? @params.BitsPerComponent.to_i : 8
columns = @params.Columns.is_a?(Integer) ? @params.Columns.to_i : 1
stream = Predictor.do_pre_prediction(stream, @params.Predictor.to_i, colors, bpc, columns)
end
Zlib::Deflate.deflate(stream, Zlib::BEST_COMPRESSION)
end
#
# Decodes data using zlib/Inflate decompression method.
# _stream_:: The data to decode.
#
def decode(stream)
zlib_stream = Zlib::Inflate.new
begin
uncompressed = zlib_stream.inflate(stream)
rescue Zlib::DataError => zlib_except
uncompressed = zlib_stream.flush_next_out
unless Origami::OPTIONS[:ignore_zlib_errors]
raise InvalidFlateDataError.new(zlib_except.message, uncompressed)
end
end
if @params.Predictor.is_a?(Integer)
colors = @params.Colors.is_a?(Integer) ? @params.Colors.to_i : 1
bpc = @params.BitsPerComponent.is_a?(Integer) ? @params.BitsPerComponent.to_i : 8
columns = @params.Columns.is_a?(Integer) ? @params.Columns.to_i : 1
uncompressed = Predictor.do_post_prediction(uncompressed, @params.Predictor.to_i, colors, bpc, columns)
end
uncompressed
end
end
Fl = Flate
end
end
origami-pdf-1.2.7/lib/origami/filters/runlength.rb 0000644 0001750 0001750 00000005442 12101464040 022326 0 ustar terceiro terceiro =begin
= File
filters/runlength.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
module Filter
class InvalidRunLengthDataError < InvalidFilterDataError #:nodoc:
end
#
# Class representing a Filter used to encode and decode data using RLE compression algorithm.
#
class RunLength
include Filter
EOD = 128 #:nodoc:
#
# Encodes data using RLE compression method.
# _stream_:: The data to encode.
#
def encode(stream)
result = ""
i = 0
while i < stream.size
#
# How many identical bytes coming?
#
length = 1
while i+1 < stream.size and length < EOD and stream[i] == stream[i+1]
length = length + 1
i = i + 1
end
#
# If more than 1, then compress them.
#
if length > 1
result << (257 - length).chr << stream[i,1]
#
# Otherwise how many different bytes to copy ?
#
else
j = i
while j+1 < stream.size and (j - i + 1) < EOD and stream[j] != stream[j+1]
j = j + 1
end
length = j - i
result << length.chr << stream[i, length+1]
i = j
end
i = i + 1
end
result << EOD.chr
end
#
# Decodes data using RLE decompression method.
# _stream_:: The data to decode.
#
def decode(stream)
raise InvalidRunLengthDataError, "No end marker" unless stream.include?(EOD.chr)
i = 0
result = ""
until stream[i].ord == EOD do
length = stream[i].ord
if length < EOD
result << stream[i + 1, length + 1]
i = i + length + 2
else
result << stream[i + 1,1] * (257 - length)
i = i + 2
end
end
result
end
end
RL = RunLength
end
end
origami-pdf-1.2.7/lib/origami/signature.rb 0000644 0001750 0001750 00000050262 12140716050 020655 0 ustar terceiro terceiro =begin
= File
signature.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
begin
require 'openssl' if Origami::OPTIONS[:use_openssl]
rescue LoadError
Origami::OPTIONS[:use_openssl] = false
end
require 'digest/sha1'
module Origami
class PDF
class SignatureError < Exception #:nodoc:
end
#
# Verify a document signature.
# Options:
# _:trusted_: an array of trusted X509 certificates.
# If no argument is passed, embedded certificates are treated as trusted.
#
def verify(options = {})
unless Origami::OPTIONS[:use_openssl]
fail "OpenSSL is not present or has been disabled."
end
params =
{
:trusted => []
}.update(options)
digsig = self.signature
unless digsig[:Contents].is_a?(String)
raise SignatureError, "Invalid digital signature contents"
end
store = OpenSSL::X509::Store.new
params[:trusted].each do |ca| store.add_cert(ca) end
flags = 0
flags |= OpenSSL::PKCS7::NOVERIFY if params[:trusted].empty?
stream = StringScanner.new(self.original_data)
stream.pos = digsig[:Contents].file_offset
Object.typeof(stream).parse(stream)
endofsig_offset = stream.pos
stream.terminate
s1,l1,s2,l2 = digsig.ByteRange
if s1.value != 0 or
(s2.value + l2.value) != self.original_data.size or
(s1.value + l1.value) != digsig[:Contents].file_offset or
s2.value != endofsig_offset
raise SignatureError, "Invalid signature byte range"
end
data = self.original_data[s1,l1] + self.original_data[s2,l2]
case digsig.SubFilter.value.to_s
when 'adbe.pkcs7.detached'
flags |= OpenSSL::PKCS7::DETACHED
p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
raise SignatureError, "Not a PKCS7 detached signature" unless p7.detached?
p7.verify([], store, data, flags)
when 'adbe.pkcs7.sha1'
p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
p7.verify([], store, nil, flags) and p7.data == Digest::SHA1.digest(data)
else
raise NotImplementedError, "Unsupported method #{digsig.SubFilter}"
end
end
#
# Sign the document with the given key and x509 certificate.
# _certificate_:: The X509 certificate containing the public key.
# _key_:: The private key associated with the certificate.
# _ca_:: Optional CA certificates used to sign the user certificate.
#
def sign(certificate, key, options = {})
unless Origami::OPTIONS[:use_openssl]
fail "OpenSSL is not present or has been disabled."
end
params =
{
:method => "adbe.pkcs7.detached",
:ca => [],
:annotation => nil,
:location => nil,
:contact => nil,
:reason => nil
}.update(options)
unless certificate.is_a?(OpenSSL::X509::Certificate)
raise TypeError, "A OpenSSL::X509::Certificate object must be passed."
end
unless key.is_a?(OpenSSL::PKey::RSA)
raise TypeError, "A OpenSSL::PKey::RSA object must be passed."
end
ca = params[:ca]
unless ca.is_a?(::Array)
raise TypeError, "Expected an Array of CA certificate."
end
annotation = params[:annotation]
unless annotation.nil? or annotation.is_a?(Annotation::Widget::Signature)
raise TypeError, "Expected a Annotation::Widget::Signature object."
end
case params[:method]
when 'adbe.pkcs7.detached'
signfield_size = lambda{|crt,key,ca|
datatest = "abcdefghijklmnopqrstuvwxyz"
OpenSSL::PKCS7.sign(
crt,
key,
datatest,
ca,
OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
).to_der.size + 128
}
when 'adbe.pkcs7.sha1'
signfield_size = lambda{|crt,key,ca|
datatest = "abcdefghijklmnopqrstuvwxyz"
OpenSSL::PKCS7.sign(
crt,
key,
Digest::SHA1.digest(datatest),
ca,
OpenSSL::PKCS7::BINARY
).to_der.size + 128
}
when 'adbe.x509.rsa_sha1'
signfield_size = lambda{|crt,key,ca|
datatest = "abcdefghijklmnopqrstuvwxyz"
key.private_encrypt(
Digest::SHA1.digest(datatest)
).size + 128
}
raise NotImplementedError, "Unsupported method #{params[:method].inspect}"
else
raise NotImplementedError, "Unsupported method #{params[:method].inspect}"
end
digsig = Signature::DigitalSignature.new.set_indirect(true)
if annotation.nil?
annotation = Annotation::Widget::Signature.new
annotation.Rect = Rectangle[:llx => 0.0, :lly => 0.0, :urx => 0.0, :ury => 0.0]
end
annotation.V = digsig
add_fields(annotation)
self.Catalog.AcroForm.SigFlags =
InteractiveForm::SigFlags::SIGNATURESEXIST | InteractiveForm::SigFlags::APPENDONLY
digsig.Type = :Sig #:nodoc:
digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, ca]) #:nodoc:
digsig.Filter = Name.new("Adobe.PPKMS") #:nodoc:
digsig.SubFilter = Name.new(params[:method]) #:nodoc:
digsig.ByteRange = [0, 0, 0, 0] #:nodoc:
digsig.Location = HexaString.new(params[:location]) if params[:location]
digsig.ContactInfo = HexaString.new(params[:contact]) if params[:contact]
digsig.Reason = HexaString.new(params[:reason]) if params[:reason]
if params[:method] == 'adbe.x509.rsa_sha1'
digsig.Cert =
if ca.empty?
HexaString.new(certificate.to_der)
else
[ HexaString.new(certificate.to_der) ] + ca.map{ |crt| HexaString.new(crt.to_der) }
end
end
#
# Flattening the PDF to get file view.
#
compile
#
# Creating an empty Xref table to compute signature byte range.
#
rebuild_dummy_xrefs
sigoffset = get_object_offset(digsig.no, digsig.generation) + digsig.sigOffset
digsig.ByteRange[0] = 0
digsig.ByteRange[1] = sigoffset
digsig.ByteRange[2] = sigoffset + digsig.Contents.size
digsig.ByteRange[3] = filesize - digsig.ByteRange[2] until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
# From that point the file size remains constant
#
# Correct Xrefs variations caused by ByteRange modifications.
#
rebuildxrefs
filedata = output()
signable_data = filedata[digsig.ByteRange[0],digsig.ByteRange[1]] + filedata[digsig.ByteRange[2],digsig.ByteRange[3]]
signature =
case params[:method]
when 'adbe.pkcs7.detached'
OpenSSL::PKCS7.sign(
certificate,
key,
signable_data,
ca,
OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
).to_der
when 'adbe.pkcs7.sha1'
OpenSSL::PKCS7.sign(
certificate,
key,
Digest::SHA1.digest(signable_data),
ca,
OpenSSL::PKCS7::BINARY
).to_der
when 'adbe.x509.rsa_sha1'
key.private_encrypt(Digest::SHA1.digest(signable_data))
end
digsig.Contents[0, signature.size] = signature
#
# No more modification are allowed after signing.
#
self.freeze
end
#
# Returns whether the document contains a digital signature.
#
def is_signed?
begin
self.Catalog.AcroForm.is_a?(Dictionary) and
self.Catalog.AcroForm.has_key?(:SigFlags) and
(self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::SIGNATURESEXIST != 0)
rescue InvalidReferenceError
false
end
end
#
# Enable the document Usage Rights.
# _rights_:: list of rights defined in UsageRights::Rights
#
def enable_usage_rights(cert, pkey, *rights)
unless Origami::OPTIONS[:use_openssl]
fail "OpenSSL is not present or has been disabled."
end
signfield_size = lambda{|crt, key, ca|
datatest = "abcdefghijklmnopqrstuvwxyz"
OpenSSL::PKCS7.sign(crt, key, datatest, ca, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der.size + 128
}
#
# Load key pair
#
key = pkey.is_a?(OpenSSL::PKey::RSA) ? pkey : OpenSSL::PKey::RSA.new(pkey)
certificate = cert.is_a?(OpenSSL::X509::Certificate) ? cert : OpenSSL::X509::Certificate.new(cert)
#
# Forge digital signature dictionary
#
digsig = Signature::DigitalSignature.new.set_indirect(true)
self.Catalog.AcroForm ||= InteractiveForm.new
#self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::APPENDONLY
digsig.Type = :Sig #:nodoc:
digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, []]) #:nodoc:
digsig.Filter = Name.new("Adobe.PPKLite") #:nodoc:
digsig.Name = "ARE Acrobat Product v8.0 P23 0002337" #:nodoc:
digsig.SubFilter = Name.new("adbe.pkcs7.detached") #:nodoc:
digsig.ByteRange = [0, 0, 0, 0] #:nodoc:
sigref = Signature::Reference.new #:nodoc:
sigref.Type = :SigRef #:nodoc:
sigref.TransformMethod = :UR3 #:nodoc:
sigref.Data = self.Catalog
sigref.TransformParams = UsageRights::TransformParams.new
sigref.TransformParams.P = true #:nodoc:
sigref.TransformParams.Type = :TransformParams #:nodoc:
sigref.TransformParams.V = UsageRights::TransformParams::VERSION
rights.each do |right|
sigref.TransformParams[right.first] ||= []
sigref.TransformParams[right.first].concat(right[1..-1])
end
digsig.Reference = [ sigref ]
self.Catalog.Perms ||= Perms.new
self.Catalog.Perms.UR3 = digsig
#
# Flattening the PDF to get file view.
#
compile
#
# Creating an empty Xref table to compute signature byte range.
#
rebuild_dummy_xrefs
sigoffset = get_object_offset(digsig.no, digsig.generation) + digsig.sigOffset
digsig.ByteRange[0] = 0
digsig.ByteRange[1] = sigoffset
digsig.ByteRange[2] = sigoffset + digsig.Contents.size
digsig.ByteRange[3] = filesize - digsig.ByteRange[2] until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
# From that point the file size remains constant
#
# Correct Xrefs variations caused by ByteRange modifications.
#
rebuildxrefs
filedata = output()
signable_data = filedata[digsig.ByteRange[0],digsig.ByteRange[1]] + filedata[digsig.ByteRange[2],digsig.ByteRange[3]]
signature = OpenSSL::PKCS7.sign(certificate, key, signable_data, [], OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
digsig.Contents[0, signature.size] = signature
#
# No more modification are allowed after signing.
#
self.freeze
end
def has_usage_rights?
not self.Catalog.Perms.nil? and (not self.Catalog.Perms.has_key?(:UR3) or not self.Catalog.Perms.has_key?(:UR))
end
def signature
raise SignatureError, "Not a signed document" unless self.is_signed?
self.each_field do |field|
if field.FT == :Sig and field.V.is_a?(Dictionary)
return field.V
end
end
raise SignatureError, "Cannot find digital signature"
end
end
class Perms < Dictionary
include StandardObject
field :DocMDP, :Type => Dictionary
field :UR, :Type => Dictionary
field :UR3, :Type => Dictionary, :Version => "1.6"
end
module Signature
#
# Class representing a digital signature.
#
class DigitalSignature < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Sig
field :Filter, :Type => Name, :Default => "Adobe.PPKMS".to_sym, :Required => true
field :SubFilter, :Type => Name
field :Contents, :Type => String, :Required => true
field :Cert, :Type => [ Array, String ]
field :ByteRange, :Type => Array
field :Reference, :Type => Array, :Version => "1.5"
field :Changes, :Type => Array
field :Name, :Type => String
field :M, :Type => String
field :Location, :Type => String
field :Reason, :Type => String
field :ContactInfo, :Type => String
field :R, :Type => Integer
field :V, :Type => Integer, :Default => 0, :Version => "1.5"
field :Prop_Build, :Type => Dictionary, :Version => "1.5"
field :Prop_AuthTime, :Type => Integer, :Version => "1.5"
field :Prop_AuthType, :Type => Name, :Version => "1.5"
def pre_build #:nodoc:
self.M = Origami::Date.now
self.Prop_Build ||= BuildProperties.new.pre_build
super
end
def to_s(dummy_param = nil) #:nodoc:
indent = 1
pairs = self.to_a
content = TOKENS.first + EOL
pairs.sort_by{ |k| k.to_s }.reverse.each do |pair|
key, value = pair[0].to_o, pair[1].to_o
content << "\t" * indent + key.to_s + " " + (value.is_a?(Dictionary) ? value.to_s(indent + 1) : value.to_s) + EOL
end
content << "\t" * (indent - 1) + TOKENS.last
output(content)
end
def sigOffset #:nodoc:
base = 1
pairs = self.to_a
content = "#{no} #{generation} obj" + EOL + TOKENS.first + EOL
pairs.sort_by{ |k| k.to_s }.reverse.each do |pair|
key, value = pair[0].to_o, pair[1].to_o
if key == :Contents
content << "\t" * base + key.to_s + " "
return content.size
else
content << "\t" * base + key.to_s + " " + (value.is_a?(Dictionary) ? value.to_s(base+1) : value.to_s) + EOL
end
end
nil
end
end
#
# Class representing a signature which can be embedded in DigitalSignature dictionary.
# It must be a direct object.
#
class Reference < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :SigRef
field :TransformMethod, :Type => Name, :Default => :DocMDP, :Required => true
field :TransformParams, :Type => Dictionary
field :Data, :Type => Object
field :DigestMethod, :Type => Name, :Default => :MD5
field :DigestValue, :Type => String
field :DigestLocation, :Type => Array
def initialize(hash = {})
set_indirect(false)
super(hash)
end
end
class BuildProperties < Dictionary
include StandardObject
field :Filter, :Type => Dictionary, :Version => "1.5"
field :PubSec, :Type => Dictionary, :Version => "1.5"
field :App, :Type => Dictionary, :Version => "1.5"
field :SigQ, :Type => Dictionary, :Version => "1.7"
def initialize(hash = {})
set_indirect(false)
super(hash)
end
def pre_build #:nodoc:
self.Filter ||= BuildData.new
self.Filter.Name ||= Name.new("Adobe.PPKMS")
self.Filter.R ||= 0x2001D
self.Filter.Date ||= Time.now.to_s
self.SigQ ||= SigQData.new
self.SigQ.Preview ||= false
self.SigQ.R ||= 0x2001D
self.PubSec ||= BuildData.new
self.PubSec.NonEFontNoWarn ||= false
self.PubSec.Date ||= Time.now.to_s
self.PubSec.R ||= 0x2001D
self.App ||= AppData.new
self.App.TrustedMode ||= false
self.App.OS ||= [ :Win ]
self.App.R ||= 0x70000
self.App.Name ||= Name.new("Exchange-Pro")
super
end
end
class BuildData < Dictionary
include StandardObject
field :Name, :Type => Name, :Version => "1.5"
field :Date, :Type => String, :Version => "1.5"
field :R, :Type => Number, :Version => "1.5"
field :PreRelease, :Type => Boolean, :Default => false, :Version => "1.5"
field :OS, :Type => Array, :Version => "1.5"
field :NonEFontNoWarn, :Type => Boolean, :Version => "1.5"
field :TrustedMode, :Type => Boolean, :Version => "1.5"
field :V, :Type => Number, :Version => "1.5"
def initialize(hash = {})
set_indirect(false)
super(hash)
end
end
class AppData < BuildData
field :REx, :Type => String, :Version => "1.6"
end
class SigQData < BuildData
field :Preview, :Type => Boolean, :Default => false, :Version => "1.7"
end
end
module UsageRights
module Rights
DOCUMENT_FULLSAVE = [:Document, :FullSave]
DOCUMENT_ALL = DOCUMENT_FULLSAVE
ANNOTS_CREATE = [:Annots, :Create]
ANNOTS_DELETE = [:Annots, :Delete]
ANNOTS_MODIFY = [:Annots, :Modify]
ANNOTS_COPY = [:Annots, :Copy]
ANNOTS_IMPORT = [:Annots, :Import]
ANNOTS_EXPORT = [:Annots, :Export]
ANNOTS_ONLINE = [:Annots, :Online]
ANNOTS_SUMMARYVIEW = [:Annots, :SummaryView]
ANNOTS_ALL = [ :Annots, :Create, :Modify, :Copy, :Import, :Export, :Online, :SummaryView ]
FORM_FILLIN = [:Form, :FillIn]
FORM_IMPORT = [:Form, :Import]
FORM_EXPORT = [:Form, :Export]
FORM_SUBMITSTANDALONE = [:Form, :SubmitStandAlone]
FORM_SPAWNTEMPLATE = [:Form, :SpawnTemplate]
FORM_BARCODEPLAINTEXT = [:Form, :BarcodePlaintext]
FORM_ONLINE = [:Form, :Online]
FORM_ALL = [:Form, :FillIn, :Import, :Export, :SubmitStandAlone, :SpawnTemplate, :BarcodePlaintext, :Online]
FORMEX_BARCODEPLAINTEXT = [:FormEx, :BarcodePlaintext]
FORMEX_ALL = FORMEX_BARCODEPLAINTEXT
SIGNATURE_MODIFY = [:Signature, :Modify]
SIGNATURE_ALL = SIGNATURE_MODIFY
EF_CREATE = [:EF, :Create]
EF_DELETE = [:EF, :Delete]
EF_MODIFY = [:EF, :Modify]
EF_IMPORT = [:EF, :Import]
EF_ALL = [:EF, :Create, :Delete, :Modify, :Import]
ALL = [ DOCUMENT_ALL, ANNOTS_ALL, FORM_ALL, SIGNATURE_ALL, EF_ALL ]
end
class TransformParams < Dictionary
include StandardObject
VERSION = Name.new("2.2")
field :Type, :Type => Name, :Default => :TransformParams
field :Document, :Type => Array
field :Msg, :Type => String
field :V, :Type => Name, :Default => VERSION
field :Annots, :Type => Array
field :Form, :Type => Array
field :FormEx, :Type => Array
field :Signature, :Type => Array
field :EF, :Type => Array, :Version => "1.6"
field :P, :Type => Boolean, :Default => false, :Version => "1.6"
def initialize(hash = {})
set_indirect(false)
super(hash)
end
end
end
end
origami-pdf-1.2.7/lib/origami/stream.rb 0000644 0001750 0001750 00000036434 12155654760 020173 0 ustar terceiro terceiro =begin
= File
stream.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'strscan'
module Origami
class InvalidStreamObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing a PDF Stream Object.
# Streams can be used to hold any kind of data, especially binary data.
#
class Stream
include Origami::Object
include StandardObject
TOKENS = [ "stream" + WHITECHARS_NORET + "\\r?\\n", "endstream" ] #:nodoc:
@@regexp_open = Regexp.new(WHITESPACES + TOKENS.first)
@@regexp_close = Regexp.new(TOKENS.last)
@@cast_fingerprints = {}
#
# Actually only 5 first ones are implemented, other ones are mainly about image data processing (JPEG, JPEG2000 ... )
#
@@defined_filters =
[
:ASCIIHexDecode,
:ASCII85Decode,
:LZWDecode,
:FlateDecode,
:RunLengthDecode,
# TODO
:CCITTFaxDecode,
:JBIG2Decode,
:DCTDecode,
:JPXDecode,
# abbrev
:AHx, # ASCIIHexDecode
:A85, # ASCII85Decode
:LZW, # LZWDecode
:Fl, # FlateDecode
:RL, # RunLengthDecode
:CCF, # CCITTFaxDecode
:DCT, # DCTDecode
]
attr_accessor :dictionary
field :Length, :Type => Integer, :Required => true
field :Filter, :Type => [ Name, Array ]
field :DecodeParms, :Type => [ Dictionary, Array ]
field :F, :Type => Dictionary, :Version => "1.2"
field :FFilter, :Type => [ Name, Array ], :Version => "1.2"
field :FDecodeParms, :Type => [ Dictionary, Array ], :Version => "1.2"
field :DL, :Type => Integer, :Version => "1.5"
#
# Creates a new PDF Stream.
# _data_:: The Stream uncompressed data.
# _dictionary_:: A hash representing the Stream attributes.
#
def initialize(data = "", dictionary = {})
super()
set_indirect(true)
@dictionary, @data = Dictionary.new(dictionary), data
@dictionary.parent = self
end
def pre_build
encode!
super
end
def post_build
self.Length = @rawdata.length
super
end
def method_missing(field, *args) #:nodoc:
if field.to_s[-1,1] == '='
self[field.to_s[0..-2].to_sym] = args.first
else
obj = self[field];
obj.is_a?(Reference) ? obj.solve : obj
end
end
def self.parse(stream, parser = nil) #:nodoc:
dictionary = Dictionary.parse(stream, parser)
return dictionary if not stream.skip(@@regexp_open)
length = dictionary[:Length]
if not length.is_a?(Integer)
rawdata = stream.scan_until(@@regexp_close)
if rawdata.nil?
raise InvalidStreamObjectError,
"Stream shall end with a 'endstream' statement"
end
else
length = length.value
rawdata = stream.peek(length)
stream.pos += length
if not ( unmatched = stream.scan_until(@@regexp_close) )
raise InvalidStreamObjectError,
"Stream shall end with a 'endstream' statement"
end
rawdata << unmatched
end
stm =
if Origami::OPTIONS[:enable_type_guessing]
self.guess_type(dictionary).new('', dictionary.to_h)
else
Stream.new('', dictionary.to_h)
end
rawdata.chomp!(TOKENS.last)
if rawdata[-1,1] == "\n"
if rawdata[-2,1] == "\r"
rawdata = rawdata[0, rawdata.size - 2]
else
rawdata = rawdata[0, rawdata.size - 1]
end
end
#rawdata.chomp! if length.is_a?(Integer) and length < rawdata.length
stm.rawdata = rawdata
stm.file_offset = dictionary.file_offset
stm
end
def self.add_type_info(typeclass, key, value) #:nodoc:
if not @@cast_fingerprints.has_key?(typeclass) and typeclass.superclass != Stream and
@@cast_fingerprints.has_key?(typeclass.superclass)
@@cast_fingerprints[typeclass] = @@cast_fingerprints[typeclass.superclass].dup
end
@@cast_fingerprints[typeclass] ||= {}
@@cast_fingerprints[typeclass][key.to_o] = value.to_o
end
def self.guess_type(hash) #:nodoc:
best_type = Stream
@@cast_fingerprints.each_pair do |typeclass, keys|
best_type = typeclass if keys.all? { |k,v|
hash.has_key?(k) and hash[k] == v
} and typeclass < best_type
end
best_type
end
def set_predictor(predictor, colors = 1, bitspercomponent = 8, columns = 1)
filters = self.Filter
filters = [ filters ] unless filters.is_a?(::Array)
if not filters.include?(:FlateDecode) and not filters.include?(:LZWDecode)
raise InvalidStreamObjectError, 'Predictor functions can only be used with Flate or LZW filters'
end
layer = filters.index(:FlateDecode) or filters.index(:LZWDecode)
params = Filter::LZW::DecodeParms.new
params[:Predictor] = predictor
params[:Colors] = colors if colors != 1
params[:BitsPerComponent] = bitspercomponent if bitspercomponent != 8
params[:Columns] = columns if columns != 1
set_decode_params(layer, params)
self
end
def cast_to(type)
super(type)
cast = type.new("", self.dictionary.to_h)
cast.rawdata = @rawdata.dup
cast.no, cast.generation = self.no, self.generation
cast.set_indirect(true)
cast.set_pdf(self.pdf)
cast.file_offset = self.file_offset
cast
end
def value #:nodoc:
self
end
#
# Returns the uncompressed stream content.
#
def data
self.decode! if @data.nil?
@data
end
#
# Sets the uncompressed stream content.
# _str_:: The new uncompressed data.
#
def data=(str)
@rawdata = nil
@data = str
end
#
# Returns the raw compressed stream content.
#
def rawdata
self.encode! if @rawdata.nil?
@rawdata
end
#
# Sets the raw compressed stream content.
# _str_:: the new raw data.
#
def rawdata=(str)
@rawdata = str
@data = nil
end
#
# Uncompress the stream data.
#
def decode!
self.decrypt! if self.is_a?(Encryption::EncryptedStream)
unless is_decoded?
filters = self.Filter
if filters.nil?
@data = @rawdata.dup
else
case filters
when Array, Name then
dparams = self.DecodeParms || []
dparams = [ dparams ] unless dparams.is_a?(::Array)
filters = [ filters ] unless filters.is_a?(::Array)
@data = @rawdata.dup
@data.freeze
filters.length.times do |layer|
params = dparams[layer].is_a?(Dictionary) ? dparams[layer] : {}
filter = filters[layer]
begin
@data = decode_data(@data, filter, params)
rescue Filter::InvalidFilterDataError => e
@data = e.decoded_data if e.decoded_data
raise InvalidStreamObjectError,
"Error while decoding stream #{self.reference}\n\t-> [#{e.class}] #{e.message}"
end
end
else
raise InvalidStreamObjectError, "Invalid Filter type parameter"
end
end
end
self
end
#
# Compress the stream data.
#
def encode!
unless is_encoded?
filters = self.Filter
if filters.nil?
@rawdata = @data.dup
else
case filters
when Array, Name then
dparams = self.DecodeParms || []
dparams = [ dparams ] unless dparams.is_a?(::Array)
filters = [ filters ] unless filters.is_a?(::Array)
@rawdata = @data.dup
(filters.length - 1).downto(0) do |layer|
params = dparams[layer].is_a?(Dictionary) ? dparams[layer] : {}
filter = filters[layer]
@rawdata = encode_data(@rawdata, filter, params)
end
else
raise InvalidStreamObjectError, "Invalid filter type parameter"
end
end
self.Length = @rawdata.length
end
self
end
def to_s(indent = 1) #:nodoc:
content = ""
content << @dictionary.to_s(indent)
content << "stream" + EOL
content << self.rawdata
content << EOL << TOKENS.last
super(content)
end
def [](key) #:nodoc:
@dictionary[key]
end
def []=(key,val) #:nodoc:
@dictionary[key] = val
end
def each_key(&b) #:nodoc:
@dictionary.each_key(&b)
end
def self.native_type ; Stream end
private
def is_decoded? #:nodoc:
not @data.nil?
end
def is_encoded? #:nodoc:
not @rawdata.nil?
end
def set_decode_params(layer, params) #:nodoc:
dparms = self.DecodeParms
unless dparms.is_a? ::Array
@dictionary[:DecodeParms] = dparms = []
end
if layer > dparms.length - 1
dparms.concat(::Array.new(layer - dparms.length + 1, Null.new))
end
dparms[layer] = params
@dictionary[:DecodeParms] = dparms.first if dparms.length == 1
self
end
def decode_data(data, filter, params) #:nodoc:
unless @@defined_filters.include?(filter.value)
raise InvalidStreamObjectError, "Unknown filter : #{filter}"
end
Origami::Filter.const_get(filter.value.to_s.sub(/Decode$/,"")).decode(data, params)
end
def encode_data(data, filter, params) #:nodoc:
unless @@defined_filters.include?(filter.value)
raise InvalidStreamObjectError, "Unknown filter : #{filter}"
end
encoded = Origami::Filter.const_get(filter.value.to_s.sub(/Decode$/,"")).encode(data, params)
if filter.value == :ASCIIHexDecode or filter.value == :ASCII85Decode
encoded << Origami::Filter.const_get(filter.value.to_s.sub(/Decode$/,""))::EOD
end
encoded
end
end
#
# Class representing an external Stream.
#
class ExternalStream < Stream
def initialize(filespec, hash = {})
hash[:F] = filespec
super('', hash)
end
end
class InvalidObjectStreamObjectError < InvalidStreamObjectError #:nodoc:
end
#
# Class representing a Stream containing other Objects.
#
class ObjectStream < Stream
include Enumerable
NUM = 0 #:nodoc:
OBJ = 1 #:nodoc:
field :Type, :Type => Name, :Default => :ObjStm, :Required => true, :Version => "1.5"
field :N, :Type => Integer, :Required => true
field :First, :Type => Integer, :Required => true
field :Extends, :Type => Stream
#
# Creates a new Object Stream.
# _dictionary_:: A hash of attributes to set to the Stream.
# _rawdata_:: The Stream data.
#
def initialize(rawdata = "", dictionary = {})
@objects = nil
super(rawdata, dictionary)
end
def pre_build #:nodoc:
load! if @objects.nil?
prolog = ""
data = ""
objoff = 0
@objects.to_a.sort.each do |num,obj|
obj.set_indirect(false)
obj.objstm_offset = objoff
prolog << "#{num} #{objoff} "
objdata = "#{obj.to_s} "
objoff += objdata.size
data << objdata
obj.set_indirect(true)
obj.no = num
end
self.data = prolog + data
@dictionary[:N] = @objects.size
@dictionary[:First] = prolog.size
super
end
#
# Adds a new Object to this Stream.
# _object_:: The Object to append.
#
def <<(object)
unless object.generation == 0
raise InvalidObjectError, "Cannot store an object with generation > 0 in an ObjectStream"
end
if object.is_a?(Stream)
raise InvalidObjectError, "Cannot store a Stream in an ObjectStream"
end
load! if @objects.nil?
object.no, object.generation = @pdf.alloc_new_object_number if object.no == 0
object.set_indirect(true) # object is indirect
object.parent = self # set this stream as the parent
object.set_pdf(@pdf) # indirect objects need pdf information
@objects[object.no] = object
Reference.new(object.no, 0)
end
alias :insert :<<
#
# Deletes Object _no_.
#
def delete(no)
load! if @objects.nil?
@objects.delete(no)
end
#
# Returns the index of Object _no_.
#
def index(no)
ind = 0
@objects.to_a.sort.each { |num, obj|
return ind if num == no
ind = ind + 1
}
nil
end
#
# Returns a given decompressed object contained in the Stream.
# _no_:: The Object number.
#
def extract(no)
load! if @objects.nil?
@objects[no]
end
#
# Returns a given decompressed object by index.
# _index_:: The Object index in the ObjectStream.
#
def extract_by_index(index)
load! if @objects.nil?
@objects.to_a.sort[index]
end
#
# Returns whether a specific object is contained in this stream.
# _no_:: The Object number.
#
def include?(no)
load! if @objects.nil?
@objects.include?(no)
end
#
# Iterates over each object in the stream.
#
def each(&b)
load! if @objects.nil?
@objects.values.each(&b)
end
#
# Returns the array of inner objects.
#
def objects
load! if @objects.nil?
@objects.values
end
private
def load! #:nodoc:
decode!
data = StringScanner.new(@data)
nums = []
offsets = []
@dictionary[:N].to_i.times do
nums << Integer.parse(data).to_i
offsets << Integer.parse(data)
end
@objects = {}
nums.size.times do |i|
type = Object.typeof(data)
raise InvalidObjectStreamObjectError,
"Bad embedded object format in object stream" if type.nil?
embeddedobj = type.parse(data)
embeddedobj.set_indirect(true) # object is indirect
embeddedobj.no = nums[i] # object number
embeddedobj.parent = self # set this stream as the parent
embeddedobj.set_pdf(@pdf) # indirect objects need pdf information
embeddedobj.objstm_offset = offsets[i]
@objects[nums[i]] = embeddedobj
end
end
end
end
origami-pdf-1.2.7/lib/origami/catalog.rb 0000644 0001750 0001750 00000036724 12142214376 020303 0 ustar terceiro terceiro =begin
= File
catalog.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume DelugrÈ
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class PDF
#
# Sets PDF extension level and version. Only supported values are "1.7" and 3.
#
def set_extension_level(version, level)
exts = (self.Catalog.Extensions ||= Extensions.new)
exts[:ADBE] = DeveloperExtension.new
exts[:ADBE].BaseVersion = Name.new(version)
exts[:ADBE].ExtensionLevel = level
self
end
#
# Returns the current Catalog Dictionary.
#
def Catalog
cat = get_doc_attr(:Root)
case cat
when Catalog then
cat
when Dictionary then
casted = Catalog.new(cat)
casted.no, casted.generation = cat.no, cat.generation
casted.set_indirect(true)
casted.set_pdf(self)
casted
else
raise InvalidPDFError, "Broken catalog"
end
end
#
# Sets the current Catalog Dictionary.
#
def Catalog=(cat)
#unless cat.is_a?(Catalog)
# raise TypeError, "Expected type Catalog, received #{cat.class}"
#end
cat = Catalog.new(cat) unless cat.is_a? Catalog
if @revisions.last.trailer.Root
delete_object(@revisions.last.trailer[:Root])
end
@revisions.last.trailer.Root = self << cat
end
#
# Sets an action to run on document opening.
# _action_:: An Action Object.
#
def onDocumentOpen(action)
unless action.is_a?(Action) or action.is_a?(Destination) or action.is_a?(Reference)
raise TypeError, "An Action object must be passed."
end
unless self.Catalog
raise InvalidPDFError, "A catalog object must exist to add this action."
end
self.Catalog.OpenAction = action
self
end
#
# Sets an action to run on document closing.
# _action_:: A JavaScript Action Object.
#
def onDocumentClose(action)
unless action.is_a?(Action::JavaScript) or action.is_a?(Reference)
raise TypeError, "An Action::JavaScript object must be passed."
end
unless self.Catalog
raise InvalidPDFError, "A catalog object must exist to add this action."
end
self.Catalog.AA ||= CatalogAdditionalActions.new
self.Catalog.AA.WC = action
self
end
#
# Sets an action to run on document printing.
# _action_:: A JavaScript Action Object.
#
def onDocumentPrint(action)
unless action.is_a?(Action::JavaScript) or action.is_a?(Reference)
raise TypeError, "An Action::JavaScript object must be passed."
end
unless self.Catalog
raise InvalidPDFError, "A catalog object must exist to add this action."
end
self.Catalog.AA ||= CatalogAdditionalActions.new
self.Catalog.AA.WP = action
end
#
# Registers an object into a specific Names root dictionary.
# _root_:: The root dictionary (see Names::Root)
# _name_:: The value name.
# _value_:: The value to associate with this name.
#
def register(root, name, value)
self.Catalog.Names ||= Names.new
value.set_indirect(true) unless value.is_a? Reference
namesroot = self.Catalog.Names[root]
if namesroot.nil?
names = NameTreeNode.new(:Names => []).set_indirect(true)
self.Catalog.Names[root] = names
names.Names << name << value
else
namesroot.solve[:Names] << name << value
end
end
def each_name(root, &b)
namesroot = get_names_root(root)
return if namesroot.nil?
each_name_from_node(namesroot, [], &b)
self
end
#
# Retrieve the corresponding value associated with _name_ in
# the specified _root_ name directory, or nil if the value does
# not exist.
#
def resolve_name(root, name)
namesroot = get_names_root(root)
return nil if namesroot.nil?
resolve_name_from_node(namesroot, name)
end
#
# Returns a Hash of all names under specified _root_ name directory.
# Returns nil if the directory does not exist.
#
def ls_names(root)
namesroot = get_names_root(root)
return {} if namesroot.nil?
names = names_from_node(namesroot)
if names.length % 2 != 0
return InvalidNameTreeError, "Odd number of elements"
end
Hash[*names]
end
private
def names_from_node(node, browsed_nodes = []) #:nodoc:
children = []
unless browsed_nodes.any? {|browsed| browsed.equal?(node)}
browsed_nodes.push(node)
if node.has_key?(:Names) # leaf node
children.concat(node.Names)
elsif node.has_key?(:Kids) # intermediate node
node.Kids.each do |kid|
children.concat(names_from_node(kid.solve, browsed_nodes))
end
end
end
children
end
def resolve_name_from_node(node, name, browsed_nodes = []) #:nodoc:
unless browsed_nodes.any? {|browsed| browsed.equal?(node)}
browsed_nodes.push(node)
if node.has_key?(:Names) # leaf node
limits = node.Limits
if limits
min, max = limits[0].value, limits[1].value
if (min..max) === name.to_str
names = Hash[*node.Names]
target = names[name]
return target && target.solve
end
else
names = Hash[*node.Names]
target = names[name]
return target && target.solve
end
elsif node.has_key?(:Kids) # intermediate node
node.Kids.each do |kid|
kid = kid.solve
limits = kid.Limits
min, max = limits[0].value, limits[1].value
if (min..max) === name.to_str
return resolve_name_from_node(kid, name, browsed_nodes)
end
end
end
end
end
def each_name_from_node(node, browsed_nodes = [], &b) #:nodoc:
if node.has_key?(:Names) # leaf node
names = Hash[*node.Names]
names.each_pair do |name, value|
b.call(name, value.solve)
end
elsif node.has_key?(:Kids) # intermediate node
node.Kids.each do |kid|
each_name_from_node(kid.solve, browsed_nodes, &b)
end
end
end
def get_names_root(root) #:nodoc:
namedirs = self.Catalog.Names
return nil if namedirs.nil? or namedirs[root].nil?
namedirs[root].solve
end
end
module PageLayout #:nodoc:
SINGLE = :SinglePage
ONE_COLUMN = :OneColumn
TWO_COLUMN_LEFT = :TwoColumnLeft
TWO_COLUMN_RIGHT = :TwoColumnRight
TWO_PAGE_LEFT = :TwoPageLeft
TWO_PAGE_RIGHT = :TwoPageRight
end
module PageMode #:nodoc:
NONE = :UseNone
OUTLINES = :UseOutlines
THUMBS = :UseThumbs
FULLSCREEN = :FullScreen
OPTIONAL_CONTENT = :UseOC
ATTACHMENTS = :UseAttachments
end
#
# Class representing additional actions which can be associated with a Catalog.
#
class CatalogAdditionalActions < Dictionary
include StandardObject
field :WC, :Type => Dictionary, :Version => "1.4"
field :WS, :Type => Dictionary, :Version => "1.4"
field :DS, :Type => Dictionary, :Version => "1.4"
field :WP, :Type => Dictionary, :Version => "1.4"
field :DP, :Type => Dictionary, :Version => "1.4"
end
class InvalidNameTreeError < Exception #:nodoc:
end
#
# Class representing the Names Dictionary of a PDF file.
#
class Names < Dictionary
include StandardObject
#
# Defines constants for Names tree root entries.
#
module Root
DESTS = :Dests
AP = :AP
JAVASCRIPT = :JavaScript
PAGES = :Pages
TEMPLATES = :Templates
IDS = :IDS
URLS = :URLS
EMBEDDEDFILES = :EmbeddedFiles
ALTERNATEPRESENTATIONS = :AlternatePresentations
RENDITIONS = :Renditions
XFARESOURCES = :XFAResources
end
field Root::DESTS, :Type => Dictionary, :Version => "1.2"
field Root::AP, :Type => Dictionary, :Version => "1.3"
field Root::JAVASCRIPT, :Type => Dictionary, :Version => "1.3"
field Root::PAGES, :Type => Dictionary, :Version => "1.3"
field Root::TEMPLATES, :Type => Dictionary, :Version => "1.3"
field Root::IDS, :Type => Dictionary, :Version => "1.3"
field Root::URLS, :Type => Dictionary, :Version => "1.3"
field Root::EMBEDDEDFILES, :Type => Dictionary, :Version => "1.4"
field Root::ALTERNATEPRESENTATIONS, :Type => Dictionary, :Version => "1.4"
field Root::RENDITIONS, :Type => Dictionary, :Version => "1.5"
field Root::XFARESOURCES, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
end
#
# Class representing a node in a Name tree.
#
class NameTreeNode < Dictionary
include StandardObject
field :Kids, :Type => Array
field :Names, :Type => Array
field :Limits, :Type => Array
end
#
# Class representing a leaf in a Name tree.
#
class NameLeaf < Origami::Array
#
# Creates a new leaf in a Name tree.
# _hash_:: A hash of couples, associating a Name with an Reference.
#
def initialize(hash = {})
names = []
hash.each_pair do |k,v|
names << k.to_o << v.to_o
end
super(names)
end
end
#
# Class representing the ViewerPreferences Dictionary of a PDF.
# This dictionary modifies the way the UI looks when the file is opened in a viewer.
#
class ViewerPreferences < Dictionary
include StandardObject
field :HideToolbar, :Type => Boolean, :Default => false
field :HideMenubar, :Type => Boolean, :Default => false
field :HideWindowUI, :Type => Boolean, :Default => false
field :FitWindow, :Type => Boolean, :Default => false
field :CenterWindow, :Type => Boolean, :Default => false
field :DisplayDocTitle, :Type => Boolean, :Default => false, :Version => "1.4"
field :NonFullScreenPageMode, :Type => Name, :Default => :UseNone
field :Direction, :Type => Name, :Default => :L2R
field :ViewArea, :Type => Name, :Default => :CropBox, :Version => "1.4"
field :ViewClip, :Type => Name, :Default => :CropBox, :Version => "1.4"
field :PrintArea, :Type => Name, :Default => :CropBox, :Version => "1.4"
field :PrintClip, :Type => Name, :Default => :CropBox, :Version => "1.4"
field :PrintScaling, :Type => Name, :Default => :AppDefault, :Version => "1.6"
field :Duplex, :Type => Name, :Default => :Simplex, :Version => "1.7"
field :PickTrayByPDFSize, :Type => Boolean, :Version => "1.7"
field :PrintPageRange, :Type => Array, :Version => "1.7"
field :NumCopies, :Type => Integer, :Version => "1.7"
field :Enforce, :Type => Array, :Version => "1.7", :ExtensionLevel => 3
end
class Requirement < Dictionary
include StandardObject
class Handler < Dictionary
include StandardObject
module Type
JS = :JS
NOOP = :NoOp
end
field :Type, :Type => Name, :Default => :ReqHandler
field :S, :Type => Name, :Default => Type::NOOP, :Required => true
field :Script, :Type => ByteString
end
field :Type, :Type => Name, :Default => :Requirement
field :S, :Type => Name, :Default => :EnableJavaScripts, :Version => "1.7", :Required => true
field :RH, :Type => Array
end
#
# Class representing an extension Dictionary.
#
class Extensions < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Extensions
end
#
# Class representing a developer extension.
#
class DeveloperExtension < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :DeveloperExtensions
field :BaseVersion, :Type => Name, :Required => true
field :ExtensionLevel, :Type => Integer, :Required => true
end
#
# Class representing the Catalog Dictionary of a PDF file.
#
class Catalog < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :Catalog, :Required => true
field :Version, :Type => Name, :Version => "1.4"
field :Pages, :Type => Dictionary, :Required => true
field :PageLabels, :Type => Dictionary, :Version => "1.3"
field :Names, :Type => Dictionary, :Version => "1.2"
field :Dests, :Type => Dictionary, :Version => "1.1"
field :ViewerPreferences, :Type => ViewerPreferences, :Version => "1.2"
field :PageLayout, :Type => Name, :Default => PageLayout::SINGLE
field :PageMode, :Type => Name, :Default => PageMode::NONE
field :Outlines, :Type => Dictionary
field :Threads, :Type => Array, :Version => "1.1"
field :OpenAction, :Type => [ Array, Dictionary ], :Version => "1.1"
field :AA, :Type => Dictionary, :Version => "1.4"
field :URI, :Type => Dictionary, :Version => "1.1"
field :AcroForm, :Type => Dictionary, :Version => "1.2"
field :Metadata, :Type => Stream, :Version => "1.4"
field :StructTreeRoot, :Type => Dictionary, :Version => "1.3"
field :MarkInfo, :Type => Dictionary, :Version => "1.4"
field :Lang, :Type => String, :Version => "1.4"
field :SpiderInfo, :Type => Dictionary, :Version => "1.3"
field :OutputIntents, :Type => Array, :Version => "1.4"
field :PieceInfo, :Type => Dictionary, :Version => "1.4"
field :OCProperties, :Type => Dictionary, :Version => "1.5"
field :Perms, :Type => Dictionary, :Version => "1.5"
field :Legal, :Type => Dictionary, :Version => "1.5"
field :Requirements, :Type => Array, :Version => "1.7"
field :Collection, :Type => Dictionary, :Version => "1.7"
field :NeedsRendering, :Type => Boolean, :Version => "1.7", :Default => false
field :Extensions, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
def initialize(hash = {})
set_indirect(true)
super(hash)
end
end
end
origami-pdf-1.2.7/lib/origami/3d.rb 0000644 0001750 0001750 00000021277 12101464040 017162 0 ustar terceiro terceiro =begin
= File
3d.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
class Annotation
#
# 3D Artwork annotation.
#
class Artwork3D < Annotation
field :Subtype, :Type => Name, :Default => :"3D", :Version => "1.6", :Required => true
field :"3DD", :Type => [ Dictionary, Stream ], :Required => true
field :"3DV", :Type => Object
field :"3DA", :Type => Dictionary
field :"3DI", :Type => Boolean, :Default => true
field :"3DB", :Type => Array
class Activation < Dictionary
include StandardObject
module Events
PAGE_OPEN = :PO
PAGE_CLOSE = :PC
PAGE_VISIBLE = :PV
PAGE_INVISIBLE = :PI
USER_ACTIVATE = :XA
USER_DEACTIVATE = :XD
end
module State
UNINSTANCIATED = :U
INSTANCIATED = :I
LIVE = :L
end
field :A, :Type => Name, :Default => Events::USER_ACTIVATE
field :AIS, :Type => Name, :Default => State::LIVE
field :D, :Type => Name, :Default => Events::PAGE_INVISIBLE
field :DIS, :Type => Name, :Default => State::UNINSTANCIATED
field :TB, :Type => Boolean, :Version => "1.7", :Default => true
field :NP, :Type => Boolean, :Version => "1.7", :Default => false
end
end
end
class U3DStream < Stream
include StandardObject
field :Type, :Type => Name, :Default => :"3D"
field :Subtype, :Type => Name, :Default => :U3D, :Required => true
field :VA, :Type => Dictionary
field :DV, :Type => Object
field :Resources, :Type => Dictionary
field :OnInstantiate, :Type => Stream
field :AN, :Type => Dictionary
def onInstantiate(action)
self[:OnInstantiate] = action
end
end
class AnimationStyle3D < Dictionary
include StandardObject
module Styles
NONE = :None
LINEAR = :Linear
OSCILLATING = :Oscillating
end
field :Type, :Type => Name, :Default => "3DAnimationStyle"
field :Subtype, :Type => Name, :Default => Styles::NONE
field :PC, :Type => Integer, :Default => 0
field :TM, :Type => Number, :Default => 1
end
class Reference3D < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :"3DRef"
field :"3DD", :Type => Stream
end
class View3D < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :"3DView"
field :XN, :Type => ByteString, :Required => true
field :IN, :Type => ByteString
field :MS, :Type => Name
field :C2W, :Type => Array
field :U3DPath, :Type => [ ByteString, Array ]
field :CO, :Type => Number
field :P, :Type => Dictionary
field :O, :Type => Stream
field :BG, :Type => Dictionary
field :RM, :Type => Dictionary, :Version => "1.7"
field :LS, :Type => Dictionary, :Version => "1.7"
field :SA, :Type => Array, :Version => "1.7"
field :NA, :Type => Array, :Version => "1.7"
field :NR, :Type => Boolean, :Version => "1.7", :Default => false
end
class Projection3D < Dictionary
include StandardObject
ORTHOGRAPHIC = :O
PERSPECTIVE = :P
module ClippingStyles
EXPLICIT_NEARFAR = :XNF
AUTOMATIC_NEARFAR = :ANF
end
module Scaling
WIDTH = :W
HEIGHT = :H
MINIMUM = :Min
MAXIMUM = :Max
ABSOLUTE = :Absolute
end
field :Subtype, :Type => Name, :Default => ORTHOGRAPHIC
field :CS, :Type => Name, :Default => ClippingStyles::AUTOMATIC_NEARFAR
field :F, :Type => Number
field :N, :Type => Number
field :FOV, :Type => Number
field :PS, :Type => [ Number, Name ], :Default => Scaling::WIDTH
field :OS, :Type => Number, :Default => 1
field :OB, :Type => Name, :Version => "1.7", :Default => Scaling::ABSOLUTE
end
class Background3D < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :"3DBG"
field :Subtype, :Type => Name, :Default => :SC
field :CS, :Type => [ Name, Array ], :Default => Graphics::Color::Space::DEVICE_RGB
field :C, :Type => Object, :Default => [ 1, 1, 1 ]
field :EA, :Type => Boolean, :Default => false
end
class RenderMode3D < Dictionary
include StandardObject
module Modes
SOLID = :Solid
SOLID_WIREFRAME = :SolidWireFrame
TRANSPARENT = :Transparent
TRANSPARENT_WIREFRAME = :TransparentWireFrame
BOUNDINGBOX = :BoundingBox
TRANSPARENT_BOUNDINGBOX = :TransparentBoundingBox
TRANSPARENT_BOUNDINGBOX_OUTLINE = :TransparentBoundingBoxOutline
WIREFRAME = :WireFrame
SHADED_WIREFRAME = :ShadedWireFrame
HIDDEN_WIREFRAME = :HiddenWireFrame
VERTICES = :Vertices
SHADED_VERTICES = :ShadedVertices
ILLUSTRATION = :Illustration
SOLID_OUTLINE = :SolidOutline
SHADED_ILLUSTRATION = :ShadedIllustration
end
field :Type, :Type => Name, :Default => :"3DRenderMode"
field :Subtype, :Type => Name, :Required => true, :Version => "1.7"
field :AC, :Type => Array, :Default => [ Graphics::Color::Space::DEVICE_RGB, 0, 0, 0]
field :BG, :Type => [ Name, Array ], :Default => :BG
field :O, :Type => Number, :Default => 0.5
field :CV, :Type => Number, :Default => 45
end
class LightingScheme3D < Dictionary
include StandardObject
module Styles
ARTWORK = :Artwork
NONE = :None
WHITE = :White
DAY = :Day
NIGHT = :Night
HARD = :Hard
PRIMARY = :Primary
BLUE = :Blue
RED = :Red
CUBE = :Cube
CAD = :CAD
HEADLAMP = :HeadLamp
end
field :Type, :Type => Name, :Default => :"3DLightingScheme"
field :Subtype, :Type => Name, :Version => "1.7", :Required => true
end
class CrossSection3D < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :"3DCrossSection"
field :C, :Type => Array, :Default => [ 0, 0, 0 ]
field :O, :Type => Array, :Version => "1.7", :Default => [ Null.new, 0, 0 ], :Required => true
field :PO, :Type => Number, :Default => 0.5
field :PC, :Type => Array, :Default => [ Graphics::Color::Space::DEVICE_RGB, 1, 1, 1 ]
field :IV, :Type => Boolean, :Default => false
field :IC, :Type => Array, :Default => [ Graphics::Color::Space::DEVICE_RGB, 0, 1 ,0]
end
class Node3D < Dictionary
include StandardObject
field :Type, :Type => Name, :Default => :"3DNode"
field :N, :Type => ByteString, :Version => "1.7", :Required => true
field :O, :Type => Number
field :V, :Type => Boolean
field :M, :Type => Array
end
end
origami-pdf-1.2.7/lib/origami/obfuscation.rb 0000644 0001750 0001750 00000011743 11645333134 021200 0 ustar terceiro terceiro module Origami
module Obfuscator
WHITECHARS = [ " ", "\t", "\r", "\n", "\0" ]
OBJECTS = [ Array, Boolean, Dictionary, Integer, Name, Null, Stream, String, Real, Reference ]
MAX_INT = 0xFFFFFFFF
PRINTABLE = ("!".."9").to_a + (':'..'Z').to_a + ('['..'z').to_a + ('{'..'~').to_a
FILTERS = [ :FlateDecode, :RunLengthDecode, :LZWDecode, :ASCIIHexDecode, :ASCII85Decode ]
def self.junk_spaces(max_size = 3)
length = rand(max_size) + 1
::Array.new(length) { WHITECHARS[rand(WHITECHARS.size)] }.join
end
def self.junk_comment(max_size = 15)
length = rand(max_size) + 1
junk_comment = ::Array.new(length) {
byte = rand(256).chr until (not byte.nil? and byte != "\n" and byte != "\r"); byte
}.join
"%#{junk_comment}#{EOL}"
end
def self.junk_object(type = nil)
if type.nil?
type = OBJECTS[rand(OBJECTS.size)]
end
unless type.include?(Origami::Object)
raise TypeError, "Not a valid object type"
end
Obfuscator.send("junk_#{type.to_s.split('::').last.downcase}")
end
def self.junk_array(max_size = 5)
length = rand(max_size) + 1
::Array.new(length) {
obj = Obfuscator.junk_object until (not obj.nil? and not obj.is_a?(Stream)) ; obj
}.to_o
end
def self.junk_boolean
Boolean.new(rand(2).zero?)
end
def self.junk_dictionary(max_size = 5)
length = rand(max_size) + 1
hash = Hash.new
length.times do
obj = Obfuscator.junk_object
hash[Obfuscator.junk_name] = obj unless obj.is_a?(Stream)
end
hash.to_o
end
def self.junk_integer(max = MAX_INT)
Integer.new(rand(max + 1))
end
def self.junk_name(max_size = 8)
length = rand(max_size) + 1
Name.new(::Array.new(length) { PRINTABLE[rand(PRINTABLE.size)] }.join)
end
def self.junk_null
Null.new
end
def self.junk_stream(max_data_size = 200)
chainlen = rand(2) + 1
chain = ::Array.new(chainlen) { FILTERS[rand(FILTERS.size)] }
length = rand(max_data_size) + 1
junk_data = ::Array.new(length) { rand(256).chr }.join
stm = Stream.new
stm.dictionary = Obfuscator.junk_dictionary(5)
stm.setFilter(chain)
stm.data = junk_data
stm
end
def self.junk_string(max_size = 10)
length = rand(max_size) + 1
strtype = (rand(2).zero?) ? ByteString : HexaString
strtype.new(::Array.new(length) { PRINTABLE[rand(PRINTABLE.size)] }.join)
end
def self.junk_real
Real.new(rand * rand(MAX_INT + 1))
end
def self.junk_reference(max_no = 300, max_gen = 1)
no = rand(max_no) + 1
gen = rand(max_gen)
Reference.new(no, gen)
end
end
class Dictionary
def to_obfuscated_str
content = TOKENS.first + Obfuscator.junk_spaces
self.each_pair { |key, value|
content << Obfuscator.junk_spaces +
key.to_obfuscated_str + Obfuscator.junk_spaces +
value.to_obfuscated_str + Obfuscator.junk_spaces
}
content << TOKENS.last
super(content)
end
end
module Object
alias :to_obfuscated_str :to_s
end
class Array
def to_obfuscated_str
content = TOKENS.first + Obfuscator.junk_spaces
self.each { |entry|
content << entry.to_o.to_obfuscated_str + Obfuscator.junk_spaces
}
content << TOKENS.last
super(content)
end
end
class Null
alias :to_obfuscated_str :to_s
end
class Boolean
alias :to_obfuscated_str :to_s
end
class Integer
alias :to_obfuscated_str :to_s
end
class Real
alias :to_obfuscated_str :to_s
end
class Reference
def to_obfuscated_str
refstr = refno.to_s + Obfuscator.junk_spaces + refgen.to_s + Obfuscator.junk_spaces + "R"
super(refstr)
end
end
class ByteString
def to_obfuscated_str
to_s
end
end
class HexaString
def to_obfuscated_str
to_s
end
end
class Name
def to_obfuscated_str(prop = 2)
name = @value.dup
forbiddenchars = [ " ","#","\t","\r","\n","\0","[","]","<",">","(",")","%","/","\\" ]
name.gsub!(/./) do |c|
if rand(prop) == 0 or forbiddenchars.include?(c)
hexchar = c[0].to_s(base=16)
hexchar = "0" + hexchar if hexchar.length < 2
'#' + hexchar
else
c
end
end
super(TOKENS.first + name)
end
end
class Stream
def to_obfuscated_str
content = ""
content << @dictionary.to_obfuscated_str
content << "stream" + EOL
content << self.rawdata
content << EOL << TOKENS.last
super(content)
end
end
class Trailer
def to_obfuscated_str
content = ""
if self.has_dictionary?
content << TOKENS.first << EOL << @dictionary.to_obfuscated_str << EOL
end
content << XREF_TOKEN << EOL << @startxref.to_s << EOL << TOKENS.last << EOL
content
end
end
end
origami-pdf-1.2.7/lib/origami/string.rb 0000644 0001750 0001750 00000031370 12142214376 020167 0 ustar terceiro terceiro =begin
= File
string.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
module Origami
#
# Module common to String objects.
#
module String
module Encoding
class EncodingError < Exception #:nodoc:
end
module PDFDocEncoding
CHARMAP =
[
"\x00\x00", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd",
"\xff\xfd", "\x00\x09", "\x00\x0a", "\xff\xfd", "\x00\x0c", "\x00\x0d", "\xff\xfd", "\xff\xfd",
"\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd", "\xff\xfd",
"\x02\xd8", "\x02\xc7", "\x02\xc6", "\x02\xd9", "\x02\xdd", "\x02\xdb", "\x02\xda", "\x02\xdc",
"\x00\x20", "\x00\x21", "\x00\x22", "\x00\x23", "\x00\x24", "\x00\x25", "\x00\x26", "\x00\x27",
"\x00\x28", "\x00\x29", "\x00\x2a", "\x00\x2b", "\x00\x2c", "\x00\x2d", "\x00\x2e", "\x00\x2f",
"\x00\x30", "\x00\x31", "\x00\x32", "\x00\x33", "\x00\x34", "\x00\x35", "\x00\x36", "\x00\x37",
"\x00\x38", "\x00\x39", "\x00\x3a", "\x00\x3b", "\x00\x3c", "\x00\x3d", "\x00\x3e", "\x00\x3f",
"\x00\x40", "\x00\x41", "\x00\x42", "\x00\x43", "\x00\x44", "\x00\x45", "\x00\x46", "\x00\x47",
"\x00\x48", "\x00\x49", "\x00\x4a", "\x00\x4b", "\x00\x4c", "\x00\x4d", "\x00\x4e", "\x00\x4f",
"\x00\x50", "\x00\x51", "\x00\x52", "\x00\x53", "\x00\x54", "\x00\x55", "\x00\x56", "\x00\x57",
"\x00\x58", "\x00\x59", "\x00\x5a", "\x00\x5b", "\x00\x5c", "\x00\x5d", "\x00\x5e", "\x00\x5f",
"\x00\x60", "\x00\x61", "\x00\x62", "\x00\x63", "\x00\x64", "\x00\x65", "\x00\x66", "\x00\x67",
"\x00\x68", "\x00\x69", "\x00\x6a", "\x00\x6b", "\x00\x6c", "\x00\x6d", "\x00\x6e", "\x00\x6f",
"\x00\x70", "\x00\x71", "\x00\x72", "\x00\x73", "\x00\x74", "\x00\x75", "\x00\x76", "\x00\x77",
"\x00\x78", "\x00\x79", "\x00\x7a", "\x00\x7b", "\x00\x7c", "\x00\x7d", "\x00\x7e", "\xff\xfd",
"\x20\x22", "\x20\x20", "\x20\x21", "\x20\x26", "\x20\x14", "\x20\x13", "\x01\x92", "\x20\x44",
"\x20\x39", "\x20\x3a", "\x22\x12", "\x20\x30", "\x20\x1e", "\x20\x1c", "\x20\x1d", "\x20\x18",
"\x20\x19", "\x20\x1a", "\x21\x22", "\xfb\x01", "\xfb\x02", "\x01\x41", "\x01\x52", "\x01\x60",
"\x01\x78", "\x01\x7d", "\x01\x31", "\x01\x42", "\x01\x53", "\x01\x61", "\x01\x7e", "\xff\xfd",
"\x20\xac", "\x00\xa1", "\x00\xa2", "\x00\xa3", "\x00\xa4", "\x00\xa5", "\x00\xa6", "\x00\xa7",
"\x00\xa8", "\x00\xa9", "\x00\xaa", "\x00\xab", "\x00\xac", "\xff\xfd", "\x00\xae", "\x00\xaf",
"\x00\xb0", "\x00\xb1", "\x00\xb2", "\x00\xb3", "\x00\xb4", "\x00\xb5", "\x00\xb6", "\x00\xb7",
"\x00\xb8", "\x00\xb9", "\x00\xba", "\x00\xbb", "\x00\xbc", "\x00\xbd", "\x00\xbe", "\x00\xbf",
"\x00\xc0", "\x00\xc1", "\x00\xc2", "\x00\xc3", "\x00\xc4", "\x00\xc5", "\x00\xc6", "\x00\xc7",
"\x00\xc8", "\x00\xc9", "\x00\xca", "\x00\xcb", "\x00\xcc", "\x00\xcd", "\x00\xce", "\x00\xcf",
"\x00\xd0", "\x00\xd1", "\x00\xd2", "\x00\xd3", "\x00\xd4", "\x00\xd5", "\x00\xd6", "\x00\xd7",
"\x00\xd8", "\x00\xd9", "\x00\xda", "\x00\xdb", "\x00\xdc", "\x00\xdd", "\x00\xde", "\x00\xdf",
"\x00\xe0", "\x00\xe1", "\x00\xe2", "\x00\xe3", "\x00\xe4", "\x00\xe5", "\x00\xe6", "\x00\xe7",
"\x00\xe8", "\x00\xe9", "\x00\xea", "\x00\xeb", "\x00\xec", "\x00\xed", "\x00\xee", "\x00\xef",
"\x00\xf0", "\x00\xf1", "\x00\xf2", "\x00\xf3", "\x00\xf4", "\x00\xf5", "\x00\xf6", "\x00\xf7",
"\x00\xf8", "\x00\xf9", "\x00\xfa", "\x00\xfb", "\x00\xfc", "\x00\xfd", "\x00\xfe", "\x00\xff"
]
def PDFDocEncoding.to_utf16be(pdfdocstr)
utf16bestr = "#{UTF16BE::MAGIC}"
pdfdocstr.each_byte do |byte|
utf16bestr << CHARMAP[byte]
end
utf16bestr
end
def PDFDocEncoding.to_pdfdoc(str)
str
end
end
module UTF16BE
MAGIC = "\xFE\xFF"
def UTF16BE.to_utf16be(str)
str
end
def UTF16BE.to_pdfdoc(str)
pdfdoc = []
i = 2
while i < str.size
char = PDFDocEncoding::CHARMAP.index(str[i,2])
raise EncodingError, "Can't convert UTF16-BE character to PDFDocEncoding" if char.nil?
pdfdoc << char
i = i + 2
end
pdfdoc.pack("C*")
end
end
end
module ClassMethods #:nodoc:all
def native_type; Origami::String end
end
def self.included(receiver) #:nodoc:
receiver.extend(ClassMethods)
end
def self.native_type; Origami::String end #:nodoc:
include Origami::Object
attr_accessor :encoding
def initialize(str) #:nodoc:
infer_encoding
super(str)
end
#
# Convert String object to an UTF8 encoded Ruby string.
#
def to_utf8
infer_encoding
if RUBY_VERSION < '1.9'
require 'iconv'
i = Iconv.new("UTF-8", "UTF-16")
utf8str = i.iconv(self.encoding.to_utf16be(self.value))
i.close
else
utf8str = self.encoding.to_utf16be(self.value).encode("utf-8", "utf-16")
end
utf8str
end
#
# Convert String object to an UTF16-BE encoded Ruby string.
#
def to_utf16be
infer_encoding
self.encoding.to_utf16be(self.value)
end
#
# Convert String object to a PDFDocEncoding encoded Ruby string.
#
def to_pdfdoc
infer_encoding
self.encoding.to_pdfdoc(self.value)
end
def infer_encoding #:nodoc:
@encoding =
if self.value[0,2] == Encoding::UTF16BE::MAGIC
Encoding::UTF16BE
else
Encoding::PDFDocEncoding
end
end
end
class InvalidHexaStringObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing an hexadecimal-writen String Object.
#
class HexaString < ::String
include String
TOKENS = %w{ < > } #:nodoc:
@@regexp_open = Regexp.new(WHITESPACES + TOKENS.first)
@@regexp_close = Regexp.new(TOKENS.last)
#
# Creates a new PDF hexadecimal String.
# _str_:: The string value.
#
def initialize(str = "")
unless str.is_a?(::String)
raise TypeError, "Expected type String, received #{str.class}."
end
super(str)
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
if stream.skip(@@regexp_open).nil?
raise InvalidHexaStringObjectError, "Hexadecimal string shall start with a '#{TOKENS.first}' token"
end
hexa = stream.scan_until(@@regexp_close)
if hexa.nil?
raise InvalidHexaStringObjectError, "Hexadecimal string shall end with a '#{TOKENS.last}' token"
end
decoded = Filter::ASCIIHex.decode(hexa.chomp!(TOKENS.last))
hexastr = HexaString.new(decoded)
hexastr.file_offset = offset
hexastr
end
def to_s #:nodoc:
super(TOKENS.first + Filter::ASCIIHex.encode(to_str) + TOKENS.last)
end
#
# Converts self to ByteString
#
def to_raw
ByteString.new(self.value)
end
def value
self.decrypt! if self.is_a?(Encryption::EncryptedString) and not @decrypted
to_str
end
end
class InvalidByteStringObjectError < InvalidObjectError #:nodoc:
end
#
# Class representing an ASCII String Object.
#
class ByteString < ::String
include String
TOKENS = %w{ ( ) } #:nodoc:
@@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first))
@@regexp_close = Regexp.new(Regexp.escape(TOKENS.last))
#
# Creates a new PDF String.
# _str_:: The string value.
#
def initialize(str = "")
unless str.is_a?(::String)
raise TypeError, "Expected type String, received #{str.class}."
end
super(str)
end
def self.parse(stream, parser = nil) #:nodoc:
offset = stream.pos
if not stream.skip(@@regexp_open)
raise InvalidByteStringObjectError, "No literal string start token found"
end
result = ""
depth = 0
while depth != 0 or stream.peek(1) != TOKENS.last do
if stream.eos?
raise InvalidByteStringObjectError, "Non-terminated string"
end
c = stream.get_byte
case c
when "\\"
if stream.match?(/\d{1,3}/)
oct = stream.peek(3).oct.chr
stream.pos += 3
result << oct
elsif stream.match?(/((\r?\n)|(\r\n?))/)
stream.skip(/((\r?\n)|(\r\n?))/)
next
else
flag = stream.get_byte
case flag
when "n" then result << "\n"
when "r" then result << "\r"
when "t" then result << "\t"
when "b" then result << "\b"
when "f" then result << "\f"
when "(" then result << "("
when ")" then result << ")"
when "\\" then result << "\\"
when "\r"
if str.peek(1) == "\n" then stream.pos += 1 end
when "\n"
else
result << flag
end
end
when "(" then
depth = depth + 1
result << c
when ")" then
depth = depth - 1
result << c
else
result << c
end
end
if not stream.skip(@@regexp_close)
raise InvalidByteStringObjectError, "Byte string shall be terminated with '#{TOKENS.last}'"
end
bytestr = ByteString.new(result)
bytestr.file_offset
bytestr
end
def expand #:nodoc:
extended = self.gsub("\\", "\\\\\\\\")
extended.gsub!(/\)/, "\\)")
extended.gsub!("\n", "\\n")
extended.gsub!("\r", "\\r")
extended.gsub!(/\(/, "\\(")
extended
end
def to_s #:nodoc:
super(TOKENS.first + self.expand + TOKENS.last)
end
#
# Converts self to HexaString
#
def to_hex
HexaString.new(self.value)
end
def value
self.decrypt! if self.is_a?(Encryption::EncryptedString) and not @decrypted
to_str
end
end
#
# Class representing a Date string.
# _Not used_
# _Not tested_
#
class Date < ByteString #:nodoc:
REGEXP_TOKEN = "(D:)?(\\d{4})(\\d{2})?(\\d{2})?(\\d{2})?(\\d{2})?(\\d{2})?(?:([\\+-Z])(?:(\\d{2})')?(?:(\\d{2})')?)?"
def initialize(year, month = nil, day = nil, hour = nil, minute = nil, second = nil, ut_sign = nil, ut_hours = nil, ut_min = nil)
year_str = '%04d' % year
month_str = month.nil? ? '01' : '%02d' % month
day_str = day.nil? ? '01' : '%02d' % day
hour_str = '%02d' % hour
minute_str = '%02d' % minute
second_str = '%02d' % second
date_str = "D:#{year_str}#{month_str}#{day_str}#{hour_str}#{minute_str}#{second_str}"
date_str << "#{ut_sign}#{'%02d' % ut_hours}'#{'%02d' % ut_min}" unless ut_sign.nil?
super(date_str)
end
def self.parse(stream, parser = nil) #:nodoc:
dateReg = Regexp.new(REGEXP_TOKEN)
raise InvalidDate if stream.scan(dateReg).nil?
year = stream[2].to_i
month = stream[3] and stream[3].to_i
day = stream[4] and stream[4].to_i
hour = stream[5] and stream[5].to_i
min = stream[6] and stream[6].to_i
sec = stream[7] and stream[7].to_i
ut_sign = stream[8]
ut_hours = stream[9] and stream[9].to_i
ut_min = stream[10] and stream[10].to_i
Origami::Date.new(year, month, day, hour, min, sec, ut_sign, ut_hours, ut_min)
end
#
# Returns current Date String in UTC time.
#
def self.now
now = Time.now.getutc
year = now.strftime("%Y").to_i
month = now.strftime("%m").to_i
day = now.strftime("%d").to_i
hour = now.strftime("%H").to_i
min = now.strftime("%M").to_i
sec = now.strftime("%S").to_i
Origami::Date.new(year, month, day, hour, min, sec, 'Z', 0, 0)
end
end
end
origami-pdf-1.2.7/lib/origami/graphics.rb 0000644 0001750 0001750 00000002143 12101464040 020443 0 ustar terceiro terceiro =begin
= File
graphics.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'origami/graphics/instruction'
require 'origami/graphics/colors'
require 'origami/graphics/path'
require 'origami/graphics/xobject'
require 'origami/graphics/patterns'
require 'origami/graphics/text'
require 'origami/graphics/state'
require 'origami/graphics/render'
origami-pdf-1.2.7/bin/ 0000755 0001750 0001750 00000000000 12427006355 014705 5 ustar terceiro terceiro origami-pdf-1.2.7/bin/pdfcocoon 0000755 0001750 0001750 00000004630 11645333134 016607 0 ustar terceiro terceiro #!/usr/bin/env ruby
=begin
= Author:
Guillaume Delugré
= Info:
Embeds and PDF document into a trojan PDF document.
= License:
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
begin
require 'origami'
rescue LoadError
ORIGAMIDIR = "#{File.dirname(__FILE__)}/../lib"
$: << ORIGAMIDIR
require 'origami'
end
include Origami
require 'optparse'
class OptParser
BANNER = <] [-o ]
Embeds and PDF document into a trojan PDF document.
Bug reports or feature requests at: http://origami-pdf.googlecode.com/
Options:
USAGE
def self.parser(options)
OptionParser.new do |opts|
opts.banner = BANNER
opts.on("-o", "--output FILE", "Output PDF file (stdout by default)") do |o|
options[:output] = o
end
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
end
end
def self.parse(args)
options =
{
:output => STDOUT,
}
self.parser(options).parse!(args)
options
end
end
begin
@options = OptParser.parse(ARGV)
target = (ARGV.empty?) ? STDIN : ARGV.shift
EMBEDDEDNAME = "#{::Array.new(5){ rand(26) + 97}}.pdf"
pdf = PDF.new
objstm = ObjectStream.new.setFilter(:FlateDecode)
pdf.insert(objstm)
pagetree = PageTreeNode.new.insert_page(0, page = Page.new)
pdf.Catalog.Pages = objstm.insert(pagetree)
objstm.insert(page)
file = objstm.insert(pdf.attach_file(target, :Register => false))
pdf.Catalog.Names = objstm.insert(
Names.new.setEmbeddedFiles(NameTreeNode.new.setNames([ EMBEDDEDNAME, file ]))
)
page.onOpen Action::GoToE.new(EMBEDDEDNAME, Destination::GlobalFit.new(0))
pdf.save(@options[:output], :noindent => true)
rescue SystemExit
rescue Exception => e
STDERR.puts "#{e.class}: #{e.message}"
exit 1
end
origami-pdf-1.2.7/bin/pdfexplode 0000755 0001750 0001750 00000015005 12133261600 016754 0 ustar terceiro terceiro #!/usr/bin/env ruby
=begin
= Author:
Guillaume Delugré
= Info:
Explodes a PDF into separate documents.
= License:
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
begin
require 'origami'
rescue LoadError
ORIGAMIDIR = "#{File.dirname(__FILE__)}/../lib"
$: << ORIGAMIDIR
require 'origami'
end
include Origami
require 'optparse'
require 'rexml/document'
class OptParser
BANNER = < [-r ] [-t pages|rsrc] [-d ]
Explodes a document into separate documents.
Bug reports or feature requests at: http://origami-pdf.googlecode.com/
Options:
USAGE
def self.parser(options)
OptionParser.new do |opts|
opts.banner = BANNER
opts.on("-d", "--output-dir DIR", "Output directory.") do |d|
options[:output_dir] = d
end
opts.on("-r", "--range PAGES", "Page range (e.g: 2-, 1-3, 5). Default to '-'.") do |r|
range =
if r.index('-').nil?
page = r.to_i
Range.new(page-1, page-1)
else
from, to = r.split('-').map{|bound| bound.to_i}
from ||= 1
to ||= 0
Range.new(from-1, to-1)
end
options[:page_range] = range
end
opts.on("-t", "--type TYPE", "Split by type. Can be 'pages' or 'rsrc'. Default to 'pages'.") do |t|
options[:split_by] = t
end
opts.on_tail("-h", "--help", "Show this message.") do
puts opts
exit
end
end
end
def self.parse(args)
options =
{
:page_range => (0..-1),
:split_by => 'pages'
}
self.parser(options).parse!(args)
options
end
end
begin
@options = OptParser.parse(ARGV)
if ARGV.empty?
STDERR.puts "Error: No filename was specified. #{$0} --help for details."
exit 1
else
target = ARGV.shift
end
if @options[:output_dir].nil?
@options[:output_dir] = "#{File.join(File.dirname(target), File.basename(target,'.pdf'))}.explode"
end
Origami::OPTIONS[:ignore_bad_references] = true
OUTPUT_DIR = @options[:output_dir]
Dir::mkdir(OUTPUT_DIR) unless File.directory?(OUTPUT_DIR)
def split_by_rsrc(n, page, type)
all_rsrc = page.resources
type_rsrc = page.ls_resources(type)
other_rsrc = all_rsrc.keys - type_rsrc.keys
unless type_rsrc.empty?
# Keep only specified resource type.
output_file = File.join(OUTPUT_DIR, "page_#{n}_keeponly_#{type}.pdf")
PDF.write(output_file) do |pdf|
reduced = page.copy
# New resource dictionary with only matching resources.
reduced.Resources = Resources.new(type => type_rsrc)
# Remove mention of other resources.
reduced.Contents.data = reduced.Contents.data.lines.to_a.
delete_if {|line| other_rsrc.any?{|rsrc| line =~ /#{rsrc}/}}.join
STDERR.puts "Creating #{output_file}..."
pdf.append_page(reduced)
end
# Remove all specified resource type.
output_file = File.join(OUTPUT_DIR, "page_#{n}_excluded_#{type}.pdf")
PDF.write(output_file) do |pdf|
reduced = page.copy
# New resource dictionary with no resource of specified type.
reduced.Resources = reduced.Resources.copy
reduced.Resources.delete(type)
# Remove mention this resource type.
reduced.Contents.data = reduced.Contents.data.lines.to_a.
delete_if {|line| type_rsrc.keys.any?{|rsrc| line =~ /#{rsrc}/}}.join
STDERR.puts "Creating #{output_file}..."
pdf.append_page(reduced)
end
# Now treating each resource object separately.
type_rsrc.each_pair do |name, rsrc|
anyother_rsrc = all_rsrc.keys - [ name ]
# Keey only specified resource object.
output_file = File.join(OUTPUT_DIR, "page_#{n}_keeponly_#{type}_#{name}.pdf")
PDF.write(output_file) do |pdf|
reduced = page.copy
# New resource dictionary with only specified resource object.
reduced.Resources = Resources.new(type => {name => rsrc})
# Remove mention of all other resources.
reduced.Contents.data = reduced.Contents.data.lines.to_a.
delete_if {|line| anyother_rsrc.any?{|rsrc| line =~ /#{rsrc}/}}.join
STDERR.puts "Creating #{output_file}..."
pdf.append_page(reduced)
end
# Remove only specified resource object.
output_file = File.join(OUTPUT_DIR, "page_#{n}_excluded_#{type}_#{name}.pdf")
PDF.write(output_file) do |pdf|
reduced = page.copy
# New resource dictionary with only specified resource object.
reduced.Resources = reduced.Resources.copy
reduced.Resources[type] = reduced.Resources.send(type).copy
reduced.Resources[type].delete(name)
# Remove mention of this resource only.
reduced.Contents.data = reduced.Contents.data.lines.to_a.
delete_if {|line| line =~ /#{name}/}.join
STDERR.puts "Creating #{output_file}..."
pdf.append_page(reduced)
end
end
end
end
params =
{
:verbosity => Parser::VERBOSE_QUIET,
}
pdf = PDF.read(target, params)
i = @options[:page_range].first + 1
pdf.pages[@options[:page_range]].each do |page|
case @options[:split_by]
when 'pages'
output_file = File.join(OUTPUT_DIR, "page_#{i}.pdf")
PDF.write(output_file) do |pdf|
STDERR.puts "Creating #{output_file}..."
pdf.append_page(page)
end
when 'rsrc'
[ Resources::EXTGSTATE,
Resources::COLORSPACE,
Resources::PATTERN,
Resources::SHADING,
Resources::XOBJECT,
Resources::FONT,
Resources::PROPERTIES
].each { |type| split_by_rsrc(i, page, type) }
else
raise ArgumentError, "Unknown split option: #{@options[:split_by]}"
end
i += 1
end
rescue SystemExit
rescue Exception => e
STDERR.puts "#{e.class}: #{e.message} #{e.backtrace}"
exit 1
end
origami-pdf-1.2.7/bin/config/ 0000755 0001750 0001750 00000000000 12427006355 016152 5 ustar terceiro terceiro origami-pdf-1.2.7/bin/config/pdfcop.conf.yml 0000644 0001750 0001750 00000011457 11567212342 021103 0 ustar terceiro terceiro ---
POLICY_NONE:
#
# General features.
#
allowParserErrors: true
allowAttachments: true
allowEncryption: true
allowFormCalc: true
allowJSAtOpening: true
allowJS: true
allowAcroForms: true
allowXFAForms: true
#
# Page annotations.
#
allowAnnotations: true
allow3DAnnotation: true
allowFileAttachmentAnnotation: true
allowMovieAnnotation: true
allowRichMediaAnnotation: true
allowScreenAnnotation: true
allowSoundAnnotation: true
#
# PDF Actions.
#
allowChainedActions: true
allowOpenAction: true
allowGoTo3DAction: true
allowGoToAction: true
allowGoToEAction: true
allowGoToRAction: true
allowImportDataAction: true
allowJSAction: true
allowLaunchAction: true
allowMovieAction: true
allowNamedAction: true
allowRenditionAction: true
allowRichMediaAction: true
allowSoundAction: true
allowSubmitFormAction: true
allowURIAction: true
#
# Stream filters.
#
allowASCII85Filter: true
allowASCIIHexFilter: true
allowCCITTFaxFilter: true
allowCryptFilter: true
allowDCTFilter: true
allowFlateFilter: true
allowJBIG2Filter: true
allowJPXFilter: true
allowLZWFilter: true
allowRunLengthFilter: true
POLICY_STANDARD:
#
# General features.
#
allowParserErrors: false
allowAttachments: false
allowAcroForms: true
allowEncryption: true
allowFormCalc: true
allowJS: true
allowJSAtOpening: false
allowXFAForms: true
#
# Page annotations.
#
allowAnnotations: true
allow3DAnnotation: false
allowFileAttachmentAnnotation: false
allowMovieAnnotation: false
allowRichMediaAnnotation: false
allowScreenAnnotation: false
allowSoundAnnotation: false
#
# PDF Actions.
#
allowChainedActions: true
allowOpenAction: true
allowGoTo3DAction: false
allowGoToAction: true
allowGoToEAction: false
allowGoToRAction: false
allowImportDataAction: false
allowJSAction: true
allowLaunchAction: false
allowMovieAction: false
allowNamedAction: false
allowRenditionAction: false
allowRichMediaAction: false
allowSoundAction: false
allowSubmitFormAction: true
allowURIAction: true
#
# Stream filters.
#
allowASCII85Filter: false
allowASCIIHexFilter: false
allowCCITTFaxFilter: true
allowCryptFilter: true
allowDCTFilter: true
allowFlateFilter: true
allowJBIG2Filter: false
allowJPXFilter: false
allowLZWFilter: false
allowRunLengthFilter: false
POLICY_STRONG:
#
# General features.
#
allowParserErrors: false
allowAttachments: false
allowAcroForms: false
allowEncryption: true
allowFormCalc: true
allowJS: false
allowJSAtOpening: false
allowXFAForms: false
#
# Page annotations.
#
allowAnnotations: true
allow3DAnnotation: false
allowFileAttachmentAnnotation: false
allowMovieAnnotation: false
allowRichMediaAnnotation: false
allowScreenAnnotation: false
allowSoundAnnotation: false
#
# PDF Actions.
#
allowChainedActions: false
allowOpenAction: true
allowGoTo3DAction: false
allowGoToAction: true
allowGoToEAction: false
allowGoToRAction: false
allowImportDataAction: false
allowJSAction: false
allowLaunchAction: false
allowMovieAction: false
allowNamedAction: false
allowRenditionAction: false
allowRichMediaAction: false
allowSoundAction: false
allowSubmitFormAction: false
allowURIAction: true
#
# Stream filters.
#
allowASCII85Filter: false
allowASCIIHexFilter: false
allowCCITTFaxFilter: false
allowCryptFilter: true
allowDCTFilter: true
allowFlateFilter: true
allowJBIG2Filter: false
allowJPXFilter: false
allowLZWFilter: false
allowRunLengthFilter: false
POLICY_PARANOID:
#
# General features.
#
allowParserErrors: false
allowAttachments: false
allowAcroForms: false
allowEncryption: false
allowFormCalc: false
allowJS: false
allowJSAtOpening: false
allowXFAForms: false
#
# Page annotations.
#
allowAnnotations: true
allow3DAnnotation: false
allowFileAttachmentAnnotation: false
allowMovieAnnotation: false
allowRichMediaAnnotation: false
allowScreenAnnotation: false
allowSoundAnnotation: false
#
# PDF Actions.
#
allowChainedActions: false
allowOpenAction: false
allowGoTo3DAction: false
allowGoToAction: true
allowGoToEAction: false
allowGoToRAction: false
allowImportDataAction: false
allowJSAction: false
allowLaunchAction: false
allowMovieAction: false
allowNamedAction: false
allowRenditionAction: false
allowRichMediaAction: false
allowSoundAction: false
allowSubmitFormAction: false
allowURIAction: false
#
# Stream filters.
#
allowASCII85Filter: false
allowASCIIHexFilter: false
allowCCITTFaxFilter: false
allowCryptFilter: false
allowDCTFilter: true
allowFlateFilter: true
allowJBIG2Filter: false
allowJPXFilter: false
allowLZWFilter: false
allowRunLengthFilter: false
origami-pdf-1.2.7/bin/pdfcop 0000755 0001750 0001750 00000031455 11645333134 016115 0 ustar terceiro terceiro #!/usr/bin/env ruby
=begin
= Author:
Guillaume Delugré
= Info:
This is a PDF document filtering engine using Origami.
Security policies are based on a white list of PDF features.
Default policies details can be found in the default configuration file.
You can also add your own policy and activate it using the -p switch.
= License:
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
begin
require 'origami'
rescue LoadError
ORIGAMIDIR = "#{File.dirname(__FILE__)}/../lib"
$: << ORIGAMIDIR
require 'origami'
end
include Origami
require 'optparse'
require 'yaml'
require 'rexml/document'
require 'digest/md5'
DEFAULT_CONFIG_FILE = "#{File.dirname(__FILE__)}/config/pdfcop.conf.yml"
DEFAULT_POLICY = "standard"
SECURITY_POLICIES = {}
def load_config_file(path)
SECURITY_POLICIES.update(Hash.new(false).update YAML.load(File.read(path)))
end
class OptParser
BANNER = <
The PDF filtering engine. Scans PDF documents for malicious structures.
Bug reports or feature requests at: http://origami-pdf.googlecode.com/
Options:
USAGE
def self.parse(args)
options = {:colors => true}
opts = OptionParser.new do |opts|
opts.banner = BANNER
opts.on("-o", "--output LOG_FILE", "Output log file (default STDOUT)") do |o|
options[:output_log] = o
end
opts.on("-c", "--config CONFIG_FILE", "Load security policies from given configuration file") do |cf|
options[:config_file] = cf
end
opts.on("-p", "--policy POLICY_NAME", "Specify applied policy. Predefined policies: 'none', 'standard', 'strong', 'paranoid'") do |p|
options[:policy] = p
end
opts.on("-n", "--no-color", "Suppress colored output") do
options[:colors] = false
end
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
end
opts.parse!(args)
options
end
end
@options = OptParser.parse(ARGV)
if @options.has_key?(:output_log)
LOGGER = File.open(@options[:output_log], "a+")
else
LOGGER = STDOUT
end
if not @options.has_key?(:policy)
@options[:policy] = DEFAULT_POLICY
end
load_config_file(@options[:config_file] || DEFAULT_CONFIG_FILE)
unless SECURITY_POLICIES.has_key?("POLICY_#{@options[:policy].upcase}")
STDERR.puts "Undeclared policy `#{@options[:policy]}'"
exit(1)
end
if ARGV.empty?
STDERR.puts "Error: No filename was specified. #{$0} --help for details."
exit 1
else
TARGET = ARGV.shift
end
def log(str, color = Console::Colors::GREY)
if @options[:colors]
Console.colorprint("[#{Time.now}] ", Console::Colors::CYAN, LOGGER)
Console.colorprint(str, color, LOGGER)
else
LOGGER.print("[#{Time.now}] #{str}")
end
LOGGER.puts
end
def reject(cause)
log("Document rejected by policy `#{@options[:policy]}', caused by #{cause.inspect}.", Console::Colors::RED)
exit(1)
end
def check_rights(*required_rights)
current_rights = SECURITY_POLICIES["POLICY_#{@options[:policy].upcase}"]
reject(required_rights) if required_rights.any?{|right| current_rights[right.to_s] == false}
end
def analyze_xfa_forms(xfa)
case xfa
when Array then
xml = ""
i = 0
xfa.each do |packet|
if i % 2 == 1
xml << packet.solve.data
end
i = i + 1
end
when Stream then
xml = xfa.data
else
reject("Malformed XFA dictionary")
end
xfadoc = REXML::Document.new(xml)
REXML::XPath.match(xfadoc, "//script").each do |script|
case script.attributes["contentType"]
when "application/x-formcalc" then
check_rights(:allowFormCalc)
else
check_rights(:allowJS)
end
end
end
def analyze_annotation(annot, level = 0)
check_rights(:allowAnnotations)
if annot.is_a?(Dictionary) and annot.has_key?(:Subtype)
case annot[:Subtype].solve.value
when :FileAttachment then
check_rights(:allowAttachments, :allowFileAttachmentAnnotation)
when :Sound then
check_rights(:allowSoundAnnotation)
when :Movie then
check_rights(:allowMovieAnnotation)
when :Screen then
check_rights(:allowScreenAnnotation)
when :Widget then
check_rights(:allowAcroforms)
when :"3D" then
check_rights(:allow3DAnnotation)
# 3D annotation might pull in JavaScript for real-time driven behavior.
if annot.has_key?(:"3DD")
dd = annot[:"3DD"].solve
u3dstream = nil
case dd
when Stream then
u3dstream = dd
when Dictionary then
u3dstream = dd[:"3DD"]
end
if u3dstream and u3dstream.has_field?(:OnInstantiate)
check_rights(:allowJS)
if annot.has_key?(:"3DA") # is 3d view instantiated automatically?
u3dactiv = annot[:"3DA"].solve
check_rights(:allowJSAtOpening) if u3dactiv.is_a?(Dictionary) and (u3dactiv[:A] == :PO or u3dactiv[:A] == :PV)
end
end
end
when :RichMedia then
check_rights(:allowRichMediaAnnotation)
end
end
end
def analyze_page(page, level = 0)
section_prefix = " " * 2 * level + ">" * (level + 1)
log(section_prefix + " Inspecting page...")
text_prefix = " " * 2 * (level + 1) + "." * (level + 1)
if page.is_a?(Dictionary)
#
# Checking page additional actions.
#
if page.has_key?(:AA)
if page.AA.is_a?(Dictionary)
log(text_prefix + " Page has an action dictionary.")
aa = PageAdditionalActions.new(page.AA); aa.parent = page.AA.parent
analyze_action(aa.O, true, level + 1) if aa.has_key?(:O)
analyze_action(aa.C, false, level + 1) if aa.has_key?(:C)
end
end
#
# Looking for page annotations.
#
page.each_annot do |annot|
analyze_annotation(annot, level + 1)
end
end
end
def analyze_action(action, triggered_at_opening, level = 0)
section_prefix = " " * 2 * level + ">" * (level + 1)
log(section_prefix + " Inspecting action...")
text_prefix = " " * 2 * (level + 1) + "." * (level + 1)
if action.is_a?(Dictionary)
log(text_prefix + " Found #{action[:S]} action.")
type = action[:S].is_a?(Reference) ? action[:S].solve : action[:S]
case type.value
when :JavaScript
check_rights(:allowJS)
check_rights(:allowJSAtOpening) if triggered_at_opening
when :Launch
check_rights(:allowLaunchAction)
when :Named
check_rights(:allowNamedAction)
when :GoTo
check_rights(:allowGoToAction)
dest = action[:D].is_a?(Reference) ? action[:D].solve : action[:D]
if dest.is_a?(Array) and dest.length > 0 and dest.first.is_a?(Reference)
dest_page = dest.first.solve
if dest_page.is_a?(Page)
log(text_prefix + " Destination page found.")
analyze_page(dest_page, level + 1)
end
end
when :GoToE
check_rights(:allowAttachments,:allowGoToEAction)
when :GoToR
check_rights(:allowGoToRAction)
when :Thread
check_rights(:allowGoToRAction) if action.has_key?(:F)
when :URI
check_rights(:allowURIAction)
when :SubmitForm
check_rights(:allowAcroForms,:allowSubmitFormAction)
when :ImportData
check_rights(:allowAcroForms,:allowImportDataAction)
when :Rendition
check_rights(:allowScreenAnnotation,:allowRenditionAction)
when :Sound
check_rights(:allowSoundAnnotation,:allowSoundAction)
when :Movie
check_rights(:allowMovieAnnotation,:allowMovieAction)
when :RichMediaExecute
check_rights(:allowRichMediaAnnotation,:allowRichMediaAction)
when :GoTo3DView
check_rights(:allow3DAnnotation,:allowGoTo3DAction)
end
if action.has_key?(:Next)
log(text_prefix + "This action is chained to another action!")
check_rights(:allowChainedActions)
analyze_action(action.Next)
end
elsif action.is_a?(Array)
dest = action
if dest.length > 0 and dest.first.is_a?(Reference)
dest_page = dest.first.solve
if dest_page.is_a?(Page)
log(text_prefix + " Destination page found.")
check_rights(:allowGoToAction)
analyze_page(dest_page, level + 1)
end
end
end
end
begin
log("PDFcop is running on target `#{TARGET}', policy = `#{@options[:policy]}'", Console::Colors::GREEN)
log(" File size: #{File.size(TARGET)} bytes", Console::Colors::MAGENTA)
log(" MD5: #{Digest::MD5.hexdigest(File.read(TARGET))}", Console::Colors::MAGENTA)
@pdf = PDF.read(TARGET,
:verbosity => Parser::VERBOSE_QUIET,
:ignore_errors => SECURITY_POLICIES["POLICY_#{@options[:policy].upcase}"]['allowParserErrors']
)
log("> Inspecting document structure...", Console::Colors::YELLOW)
if @pdf.is_encrypted?
log(" . Encryption = YES")
check_rights(:allowEncryption)
end
log("> Inspecting document catalog...", Console::Colors::YELLOW)
catalog = @pdf.Catalog
reject("Invalid document catalog") unless catalog.is_a?(Catalog)
if catalog.has_key?(:OpenAction)
log(" . OpenAction entry = YES")
check_rights(:allowOpenAction)
action = catalog.OpenAction
analyze_action(action, true, 1)
end
if catalog.has_key?(:AA)
if catalog.AA.is_a?(Dictionary)
aa = CatalogAdditionalActions.new(catalog.AA); aa.parent = catalog;
log(" . Additional actions dictionary = YES")
analyze_action(aa.WC, false, 1) if aa.has_key?(:WC)
analyze_action(aa.WS, false, 1) if aa.has_key?(:WS)
analyze_action(aa.DS, false, 1) if aa.has_key?(:DS)
analyze_action(aa.WP, false, 1) if aa.has_key?(:WP)
analyze_action(aa.DP, false, 1) if aa.has_key?(:DP)
end
end
if catalog.has_key?(:AcroForm)
acroform = catalog.AcroForm
if acroform.is_a?(Dictionary)
log(" . AcroForm = YES")
check_rights(:allowAcroForms)
if acroform.has_key?(:XFA)
log(" . XFA = YES")
check_rights(:allowXFAForms)
analyze_xfa_forms(acroform[:XFA].solve)
end
end
end
log("> Inspecting JavaScript names directory...", Console::Colors::YELLOW)
unless @pdf.ls_names(Names::Root::JAVASCRIPT).empty?
check_rights(:allowJS)
check_rights(:allowJSAtOpening)
end
log("> Inspecting attachment names directory...", Console::Colors::YELLOW)
unless @pdf.ls_names(Names::Root::EMBEDDEDFILES).empty?
check_rights(:allowAttachments)
end
log("> Inspecting document pages...", Console::Colors::YELLOW)
@pdf.each_page do |page|
analyze_page(page, 1)
end
log("> Inspecting document streams...", Console::Colors::YELLOW)
@pdf.indirect_objects.find_all{|obj| obj.is_a?(Stream)}.each do |stream|
if stream.dictionary.has_key?(:Filter)
filters = stream.Filter
filters = [ filters ] if filters.is_a?(Name)
if filters.is_a?(Array)
filters.each do |filter|
case filter.value
when :ASCIIHexDecode
check_rights(:allowASCIIHexFilter)
when :ASCII85Decode
check_rights(:allowASCII85Filter)
when :LZWDecode
check_rights(:allowLZWFilter)
when :FlateDecode
check_rights(:allowFlateDecode)
when :RunLengthDecode
check_rights(:allowRunLengthFilter)
when :CCITTFaxDecode
check_rights(:allowCCITTFaxFilter)
when :JBIG2Decode
check_rights(:allowJBIG2Filter)
when :DCTDecode
check_rights(:allowDCTFilter)
when :JPXDecode
check_rights(:allowJPXFilter)
when :Crypt
check_rights(:allowCryptFilter)
end
end
end
end
end
#
# TODO: Detect JS at opening in XFA (check event tag)
# Check image encoding in XFA ?
# Only allow valid signed documents ?
# Recursively scan attached files.
# On-the-fly injection of prerun JS code to hook vulnerable methods (dynamic exploit detection) ???
# ...
#
log("Document accepted by policy `#{@options[:policy]}'.", Console::Colors::GREEN)
rescue SystemExit
rescue Exception => e
log("An error occured during analysis : #{e.class} (#{e.message})")
reject("Analysis failure")
ensure
LOGGER.close
end
origami-pdf-1.2.7/bin/shell/ 0000755 0001750 0001750 00000000000 12427006355 016014 5 ustar terceiro terceiro origami-pdf-1.2.7/bin/shell/.irbrc 0000644 0001750 0001750 00000005174 11645333134 017124 0 ustar terceiro terceiro begin
require 'origami'
rescue LoadError
ORIGAMIDIR = "#{File.dirname(__FILE__)}/../../lib"
$: << ORIGAMIDIR
require 'origami'
end
include Origami
require 'console.rb'
require 'readline'
OPENSSL_SUPPORT = (defined?(OpenSSL).nil?) ? 'no' : 'yes'
JAVASCRIPT_SUPPORT = (defined?(PDF::JavaScript::Engine).nil?) ? 'no' : 'yes'
DEFAULT_BANNER = "Welcome to the PDF shell (Origami release #{Origami::VERSION}) [OpenSSL: #{OPENSSL_SUPPORT}, JavaScript: #{JAVASCRIPT_SUPPORT}]\n\n"
def set_completion
completionProc = proc { |input|
bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
validClasses = Origami.constants.reject do |name|
obj = Origami.const_get(name)
(not obj.is_a?(Module) and not obj.is_a?(Class)) or obj <= Exception
end
case input
# # Classes
# when /^([A-Z][^:\.\(]*)$/
# classname = $1
# candidates = validClasses
# return candidates.grep(/^#{classname}/)
#
# # Methods
# when /^([^:.\(]*)\.([^:.]*)$/
# classname = $1
# method = Regexp.quote($2)
# candidates = []
# if validClasses.include? $1
# begin
# candidates = eval("Origami::#{classname}.methods", bind)
# rescue Exception
# candidates = []
# end
# return candidates.grep(/^#{method}/).collect{|e| classname + "." + e}
# else
# begin
# var = $1.dup
# classname = eval("#{classname}.class", bind).to_s
# if validClasses.include?(classname.split("::").last)
# candidates = eval("#{classname}.public_instance_methods", bind)
# end
# rescue Exception => e
# candidates = []
# end
# return candidates.grep(/^#{method}/).collect{|e| var + "." + e}
# end
#
# Mod/class
when /^(.*)::$/
begin
space = eval("Origami::#{$1}", bind)
rescue Exception
return []
end
return space.constants.reject{|const| space.const_get(const) <= Exception}
when /^(.*).$/
begin
space = eval($1, bind)
rescue
return []
end
return space.public_methods
end
}
if Readline.respond_to?("basic_word_break_characters=")
Readline.basic_word_break_characters= " \t\n\"\\'`><=;|&{("
end
Readline.completion_append_character = nil
Readline.completion_proc = completionProc
end
def set_prompt
IRB.conf[:PROMPT][:PDFSH] = {
:PROMPT_C => "?>> ",
:RETURN => "%s\n",
:PROMPT_I => ">>> ",
:PROMPT_N => ">>> ",
:PROMPT_S => nil
}
IRB.conf[:PROMPT_MODE] = :PDFSH
end
Console.colorprint(DEFAULT_BANNER, Console::Colors::GREEN)
#set_completion
set_prompt
origami-pdf-1.2.7/bin/shell/hexdump.rb 0000644 0001750 0001750 00000004176 11571243672 020027 0 ustar terceiro terceiro =begin
= File
hexdump.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2009 Guillaume Delugr
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Origami. If not, see .
=end
if RUBY_VERSION < '1.9'
class Fixnum
def ord; self; end
end
end
class String #:nodoc:
def hexdump(bytesperline = 16, upcase = true, offsets = true, delta = 0)
dump = ""
counter = 0
while counter < length
offset = sprintf("%010X", counter + delta)
linelen = (counter < length - bytesperline) ? bytesperline : (length - counter)
bytes = ""
linelen.times do |i|
byte = self[counter + i].ord.to_s(base=16)
if byte.size < 2 then byte.insert(0, "0") end
bytes << byte
bytes << " " unless i == bytesperline - 1
end
ascii = self[counter, linelen].ascii_print
if upcase
offset.upcase!
bytes.upcase!
end
if RUBY_PLATFORM =~ /win32/
dump << "#{offset if offsets} #{bytes.to_s.ljust(bytesperline * 3 - 1)} #{ascii}\n"
else
dump << "#{Console.colorize(offset, Console::Colors::YELLOW) if offsets} #{Console.colorize(bytes.to_s.ljust(bytesperline * 3 - 1), Console::Colors::BRIGHT_GREY)} #{ascii}\n"
end
counter += bytesperline
end
dump
end
def ascii_print
printable = ""
self.each_byte { |c|
if c >= ' '[0].ord && c <= '~'[0].ord then printable << c else printable << '.' end
}
return printable
end
end
origami-pdf-1.2.7/bin/shell/console.rb 0000644 0001750 0001750 00000010202 11755740231 017777 0 ustar terceiro terceiro =begin
= File
console.rb
= Info
This file is part of Origami, PDF manipulation framework for Ruby
Copyright (C) 2010 Guillaume Delugré
All right reserved.
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
require 'hexdump'
if RUBY_VERSION < '1.9'
def Kernel.spawn(cmd)
fork do
exec(cmd)
end
end
end
module Origami
module Object
def inspect
to_s
end
end
unless RUBY_VERSION < '1.9'
require 'tempfile'
class Stream
def edit(editor = ENV['EDITOR'])
tmpfile = Tempfile.new("origami")
tmpfile.write(self.data)
tmpfile.close
Process.wait Kernel.spawn "#{editor} #{tmpfile.path}"
self.data = File.read(tmpfile.path)
tmpfile.unlink
true
end
def inspect
self.data.hexdump
end
end
class Page
def edit
self.Contents.edit
end
end
end
class PDF
if defined?(PDF::JavaScript::Engine)
class JavaScript::Engine
def shell
while (print 'js> '; line = gets)
begin
puts exec(line)
rescue V8::JSError => e
puts "Error: #{e.message}"
end
end
end
end
end
class Revision
def to_s
Console.colorprint("---------- Body ----------\n", Console::Colors::WHITE, true)
@body.each_value { |obj|
Console.colorprint("#{obj.reference.to_s.rjust(8,' ')}".ljust(10), Console::Colors::MAGENTA)
Console.colorprint("#{obj.type}\n", Console::Colors::YELLOW)
}
#colorprint("---------- Xrefs -----------\n", Colors::BRIGHT_WHITE, true)
#set_fg_color(Colors::BLUE, true) {
# if not @xreftable
# puts " [x] No xref table found."
# else
# @xreftable.to_s.each_line { |line|
# puts " " + line
# }
# end
#}
Console.colorprint("---------- Trailer ---------\n", Console::Colors::WHITE, true)
if not @trailer.dictionary
Console.set_fg_color(Console::Colors::BLUE, true) {
puts " [x] No trailer found."
}
else
@trailer.dictionary.each_pair { |entry, value|
Console.colorprint(" [*] ", Console::Colors::MAGENTA)
Console.colorprint("#{entry.to_s}: ", Console::Colors::YELLOW)
Console.colorprint("#{value.to_s}\n", Console::Colors::RED)
}
Console.colorprint(" [+] ", Console::Colors::MAGENTA)
Console.colorprint("startxref: ", Console::Colors::YELLOW)
Console.colorprint("#{@trailer.startxref}\n", Console::Colors::RED)
end
end
def inspect
to_s
end
end
def to_s
puts
Console.colorprint("---------- Header ----------\n", Console::Colors::WHITE, true)
Console.colorprint(" [+] ", Console::Colors::MAGENTA)
Console.colorprint("Major version: ", Console::Colors::YELLOW)
Console.colorprint("#{@header.majorversion}\n", Console::Colors::RED)
Console.colorprint(" [+] ", Console::Colors::MAGENTA)
Console.colorprint("Minor version: ", Console::Colors::YELLOW)
Console.colorprint("#{@header.minorversion}\n", Console::Colors::RED)
@revisions.each { |revision|
revision.to_s
}
puts
end
def inspect
to_s
end
end
end
origami-pdf-1.2.7/bin/gui/ 0000755 0001750 0001750 00000000000 12427006355 015471 5 ustar terceiro terceiro origami-pdf-1.2.7/bin/gui/gtkhex.rb 0000644 0001750 0001750 00000106135 12101464040 017302 0 ustar terceiro terceiro =begin
= File
gtkhex.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
This work has been derived from the GHex project. Thanks to them.
Original implementation: Jaka Mocnik
=end
require 'gtk2'
module Gtk
class HexEditor < Fixed
module View
HEX = 1
ASCII = 2
end
module Group
BYTE = 1
WORD = 2
LONG = 4
end
class Highlight
attr_accessor :start, :end
attr_accessor :start_line, :end_line
attr_accessor :style
attr_accessor :min_select
attr_accessor :valid
end
class AutoHighlight
attr_accessor :search_view
attr_accessor :search_string
attr_accessor :search_len
attr_accessor :color
attr_accessor :view_min
attr_accessor :view_max
attr_accessor :highlights
end
DEFAULT_FONT = "Monospace 12"
DEFAULT_CPL = 32
DEFAULT_LINES = 16
DISPLAY_BORDER = 4
SCROLL_TIMEOUT = 100
type_register
@@primary = Clipboard.get(Gdk::Selection::PRIMARY)
@@clipboard = Clipboard.get(Gdk::Selection::CLIPBOARD)
def initialize(data = '')
super()
@data = data
if RUBY_VERSION >= '1.9'
@data.force_encoding('binary')
end
@scroll_timeout = -1
@disp_buffer = ""
@starting_offset = 0
@xdisp_width = @adisp_width = 200
@xdisp_gc = @adisp_gc = nil
@active_view = View::HEX
@group_type = Group::BYTE
@lines = @vis_lines = @top_line = @cpl = 0
@cursor_pos = 0
@lower_nibble = false
@cursor_shown = false
@button = 0
@insert = false
@selecting = false
@selection = Highlight.new
@selection.start = @selection.end = 0
@selection.style = nil
@selection.min_select = 1
@selection.valid = false
@highlights = [ @selection ]
@auto_highlight = nil
@disp_font_metrics = load_font DEFAULT_FONT
@font_desc = Pango::FontDescription.new DEFAULT_FONT
@char_width = get_max_char_width(@disp_font_metrics)
@char_height = Pango.pixels(@disp_font_metrics.ascent) + Pango.pixels(@disp_font_metrics.descent) + 2
self.can_focus = true
self.events = Gdk::Event::KEY_PRESS_MASK
self.border_width = DISPLAY_BORDER
mouse_handler = lambda do |widget, event|
if event.event_type == Gdk::Event::BUTTON_RELEASE and event.button == 1
if @scroll_timeout
GLib::Source.remove @scroll_timeout
@scroll_timeout = nil
@scroll_dir = 0
end
@selecting = false
Gtk.grab_remove(widget)
@button = 0
elsif event.event_type == Gdk::Event::BUTTON_PRESS and event.button == 1
self.grab_focus unless self.has_focus?
Gtk.grab_add(widget)
@button = event.button
focus_view = (widget == @xdisp) ? View::HEX : View::ASCII
if @active_view == focus_view
if @active_view == View::HEX
hex_to_pointer(event.x, event.y)
else
ascii_to_pointer(event.x, event.y)
end
unless @selecting
@selecting = true
set_selection(@cursor_pos, @cursor_pos)
end
else
hide_cursor
@active_view = focus_view
show_cursor
end
elsif event.event_type == Gdk::Event::BUTTON_PRESS and event.button == 2
# TODO
else
@button = 0
end
end
@xdisp = DrawingArea.new
@xdisp.modify_font @font_desc
@xlayout = @xdisp.create_pango_layout('')
@xdisp.events =
Gdk::Event::EXPOSURE_MASK |
Gdk::Event::BUTTON_PRESS_MASK |
Gdk::Event::BUTTON_RELEASE_MASK |
Gdk::Event::BUTTON_MOTION_MASK |
Gdk::Event::SCROLL_MASK
@xdisp.signal_connect 'realize' do
@xdisp_gc = Gdk::GC.new(@xdisp.window)
@xdisp_gc.set_exposures(true)
end
@xdisp.signal_connect 'expose_event' do |xdisp, event|
imin = (event.area.y / @char_height).to_i
imax = ((event.area.y + event.area.height) / @char_height).to_i
imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0
imax = [ imax, @vis_lines ].min
render_hex_lines(imin, imax)
end
@xdisp.signal_connect 'scroll_event' do |xdisp, event|
@scrollbar.event(event)
end
@xdisp.signal_connect 'button_press_event' do |xdisp, event|
mouse_handler[xdisp, event]
end
@xdisp.signal_connect 'button_release_event' do |xdisp, event|
mouse_handler[xdisp, event]
end
@xdisp.signal_connect 'motion_notify_event' do |xdisp, event|
w, x, y, m = xdisp.window.pointer
if y < 0
@scroll_dir = -1
elsif y >= xdisp.allocation.height
@scroll_dir = 1
else
@scroll_dir = 0
end
if @scroll_dir != 0
if @scroll_timeout == nil
@scroll_timeout = GLib::Timeout.add(SCROLL_TIMEOUT) {
if @scroll_dir < 0
set_cursor([ 0, @cursor_pos - @cpl ].max)
elsif @scroll_dir > 0
set_cursor([ @data.size - 1, @cursor_pos + @cpl ].min)
end
true
}
next
end
else
if @scroll_timeout != nil
GLib::Source.remove @scroll_timeout
@scroll_timeout = nil
end
end
next if event.window != xdisp.window
hex_to_pointer(x,y) if @active_view == View::HEX and @button == 1
end
put @xdisp, 0, 0
@xdisp.show
@adisp = DrawingArea.new
@adisp.modify_font @font_desc
@alayout = @adisp.create_pango_layout('')
@adisp.events =
Gdk::Event::EXPOSURE_MASK |
Gdk::Event::BUTTON_PRESS_MASK |
Gdk::Event::BUTTON_RELEASE_MASK |
Gdk::Event::BUTTON_MOTION_MASK |
Gdk::Event::SCROLL_MASK
@adisp.signal_connect 'realize' do
@adisp_gc = Gdk::GC.new(@adisp.window)
@adisp_gc.set_exposures(true)
end
@adisp.signal_connect 'expose_event' do |adisp, event|
imin = (event.area.y / @char_height).to_i
imax = ((event.area.y + event.area.height) / @char_height).to_i
imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0
imax = [ imax, @vis_lines ].min
render_ascii_lines(imin, imax)
end
@adisp.signal_connect 'scroll_event' do |adisp, event|
@scrollbar.event(event)
end
@adisp.signal_connect 'button_press_event' do |adisp, event|
mouse_handler[adisp, event]
end
@adisp.signal_connect 'button_release_event' do |adisp, event|
mouse_handler[adisp, event]
end
@adisp.signal_connect 'motion_notify_event' do |adisp, event|
w, x, y, m = adisp.window.pointer
if y < 0
@scroll_dir = -1
elsif y >= adisp.allocation.height
@scroll_dir = 1
else
@scroll_dir = 0
end
if @scroll_dir != 0
if @scroll_timeout == nil
@scroll_timeout = GLib::Timeout.add(SCROLL_TIMEOUT) {
if @scroll_dir < 0
set_cursor([ 0, @cursor_pos - @cpl ].max)
elsif @scroll_dir > 0
set_cursor([ @data.size - 1, @cursor_pos + @cpl ].min)
end
true
}
next
end
else
if @scroll_timeout != nil
GLib::Source.remove @scroll_timeout
@scroll_timeout = nil
end
end
next if event.window != adisp.window
ascii_to_pointer(x,y) if @active_view == View::ASCII and @button == 1
end
put @adisp, 0, 0
@adisp.show
@adj = Gtk::Adjustment.new(0, 0, 0, 0, 0, 0)
@scrollbar = Gtk::VScrollbar.new(@adj)
@adj.signal_connect 'value_changed' do |adj|
unless @xdisp_gc.nil? or @adisp_gc.nil? or not @xdisp.drawable? or not @adisp.drawable?
source_min = (adj.value.to_i - @top_line) * @char_height
source_max = source_min + @xdisp.allocation.height
dest_min = 0
dest_max = @xdisp.allocation.height
rect = Gdk::Rectangle.new(0, 0, 0, 0)
@top_line = adj.value.to_i
if source_min < 0
rect.y = 0
rect.height = -source_min
rect_height = [ rect.height, @xdisp.allocation.height ].min
source_min = 0
dest_min = rect.height
else
rect.y = 2 * @xdisp.allocation.height - source_max
rect.y = 0 if rect.y < 0
rect.height = @xdisp.allocation.height - rect.y
source_max = @xdisp.allocation.height
dest_max = rect.y
end
if source_min != source_max
@xdisp.window.draw_drawable(
@xdisp_gc,
@xdisp.window,
0, source_min,
0, dest_min,
@xdisp.allocation.width,
source_max - source_min
)
@adisp.window.draw_drawable(
@adisp_gc,
@adisp.window,
0, source_min,
0, dest_min,
@adisp.allocation.width,
source_max - source_min
)
if @offsets
if @offsets_gc.nil?
@offsets_gc = Gdk::GC.new(@offsets.window)
@offsets_gc.set_exposures(true)
end
@offsets.window.draw_drawable(
@offsets_gc,
@offsets.window,
0, source_min,
0, dest_min,
@offsets.allocation.width,
source_max - source_min
)
end
# TODO update_all_auto_highlights(true, true)
invalidate_all_highlights
rect.width = @xdisp.allocation.width
@xdisp.window.invalidate(rect, false)
rect.width = @adisp.allocation.width
@adisp.window.invalidate(rect, false)
if @offsets
rect.width = @offsets.allocation.width
@offsets.window.invalidate(rect, false)
end
end
end
end
put @scrollbar, 0, 0
@scrollbar.show
end
def set_selection(s, e)
e = [ e, @data.size ].min
@@primary.clear if @selection.start != @selection.end
os, oe = [ @selection.start, @selection.end ].sort
@selection.start = [ 0, s ].max
@selection.start = [ @selection.start, @data.size ].min
@selection.end = [ e, @data.size ].min
invalidate_highlight(@selection)
ns, ne = [ @selection.start, @selection.end ].sort
if ns != os and ne != oe
bytes_changed([ns, os].min, [ne, oe].max)
elsif ne != oe
bytes_changed(*[ne, oe].sort)
elsif ns != os
bytes_changed(*[ns, os].sort)
end
if @selection.start != @selection.end
if @active_view == View::HEX
brk_len = 2 * @cpl + @cpl / @group_type
format_xblock(s,e)
(@disp_buffer.size / brk_len + 1).times do |i| @disp_buffer.insert(i * (brk_len + 1), $/) end
else
brk_len = @cpl
format_ablock(s,e)
end
@@primary.set_text(@disp_buffer)
end
end
def get_selection
[ @selection.start, @selection.end ].sort
end
def clear_selection
set_selection(0, 0)
end
def cursor
@cursor_pos
end
def set_cursor(index)
return if index < 0 or index > @data.size
old_pos = @cursor_pos
index -= 1 if @insert and index == @data.size
index = [ 0, index ].max
hide_cursor
@cursor_pos = index
return if @cpl == 0
y = index / @cpl
if y >= @top_line + @vis_lines
@adj.value = [ y - @vis_lines + 1, @lines - @vis_lines ].min
@adj.value = [ 0, @adj.value ].max
@adj.signal_emit 'value_changed'
elsif y < @top_line
@adj.value = y
@adj.signal_emit 'value_changed'
end
@lower_nibble = false if index == @data.size
if @selecting
set_selection(@selection.start, @cursor_pos)
bytes_changed(*[@cursor_pos, old_pos].sort)
else# @selection.start != @selection.end
s, e = [@selection.start, @selection.end].sort
@selection.end = @selection.start = @cursor_pos
bytes_changed(s, e)
end
self.signal_emit 'cursor_moved'
bytes_changed(old_pos, old_pos)
show_cursor
end
def set_cursor_xy(x, y)
pos = y.to_i * @cpl + x.to_i
return if y < 0 or y >= @lines or x < 0 or x >= @cpl or pos > @data.size
set_cursor(pos)
end
def set_cursor_on_lower_nibble(bool)
if @selecting
bytes_changed(@cursor_pos, @cursor_pos)
@lower_nibble = bool
elsif @selection.start != @selection.end
s, e = [ @selection.start, @selection.end ].sort
@selection.start = @selection.end = 0
bytes_changed(s, e)
@lower_nibble = bool
else
hide_cursor
@lower_nibble = bool
show_cursor
end
end
def set_group_type(type)
hide_cursor
@group_type = type
recalc_displays(self.allocation.width, self.allocation.height)
self.queue_resize
show_cursor
end
def show_offsets(bool)
return unless @show_offsets ^ bool
@show_offsets = bool
if bool
show_offsets_widget
else
hide_offsets_widget
end
end
def set_font(fontname)
@font_desc = Pango::FontDescription.new(fontname)
@disp_font_metrics = load_font(fontname)
@xdisp.modify_font(@font_desc) if @xdisp
@adisp.modify_font(@font_desc) if @adisp
@offsets.modify_font(@font_desc) if @offsets
@char_width = get_max_char_width(@disp_font_metrics)
@char_height = Pango.pixels(@disp_font_metrics.ascent) + Pango.pixels(@disp_font_metrics.descent) + 2
recalc_displays(self.allocation.width, self.allocation.height)
redraw_widget
end
def set_data(data)
prev_data_size = @data.size
@data = data.dup
recalc_displays(self.allocation.width, self.allocation.height)
set_cursor 0
bytes_changed(0, [ prev_data_size, @data.size ].max)
redraw_widget
end
def validate_highlight(hl)
unless hl.valid
hl.start_line = [ hl.start, hl.end ].min / @cpl - @top_line
hl.end_line = [ hl.start, hl.end ].max / @cpl - @top_line
hl.valid = true
end
end
def invalidate_highlight(hl)
hl.valid = false
end
def invalidate_all_highlights
@highlights.each do |hl| invalidate_highlight(hl) end
end
private
signal_new(
'data_changed',
GLib::Signal::RUN_FIRST,
nil,
nil,
String
)
signal_new(
'cursor_moved',
GLib::Signal::RUN_FIRST,
nil,
nil
)
def signal_do_cursor_moved
end
def signal_do_data_changed(data)
# TODO
end
def signal_do_realize
super
self.window.set_back_pixmap(nil, true)
end
def signal_do_size_allocate(alloc)
hide_cursor
recalc_displays(alloc.width, alloc.height)
self.set_allocation(alloc.x, alloc.y, alloc.width, alloc.height)
self.window.move_resize(
alloc.x, alloc.y,
alloc.width, alloc.height
) if self.realized?
bw = self.border_width
xt = widget_get_xt
yt = widget_get_yt
my_alloc = Gtk::Allocation.new(0, 0, 0, 0)
my_alloc.x = bw + xt
my_alloc.y = bw + yt
my_alloc.height = [ alloc.height - 2*bw - 2*yt, 1 ].max
if @show_offsets
my_alloc.width = 8 * @char_width
@offsets.size_allocate(my_alloc)
@offsets.queue_draw
my_alloc.x += 2*xt + my_alloc.width
end
my_alloc.width = @xdisp_width
@xdisp.size_allocate(my_alloc)
my_alloc.x = alloc.width - bw - @scrollbar.requisition[0]
my_alloc.y = bw
my_alloc.width = @scrollbar.requisition[0]
my_alloc.height = [ alloc.height - 2*bw, 1 ].max
@scrollbar.size_allocate(my_alloc)
my_alloc.x -= @adisp_width + xt
my_alloc.y = bw + yt
my_alloc.width = @adisp_width
my_alloc.height = [ alloc.height - 2*bw - 2*yt, 1 ].max
@adisp.size_allocate(my_alloc)
show_cursor
end
def signal_do_size_request(req)
sb_width, sb_height = @scrollbar.size_request
bw = self.border_width
xt, yt = widget_get_xt, widget_get_yt
width = 4*xt + 2*bw + sb_width +
@char_width*(DEFAULT_CPL + (DEFAULT_CPL-1)/@group_type)
width += 2*xt + 8*@char_width if @show_offsets
height = DEFAULT_LINES * @char_height + 2*yt + 2*bw
req[0] = width
req[1] = height
end
def signal_do_expose_event(event)
draw_shadow(event.area)
super(event)
true
end
def signal_do_key_press_event(event)
old_cp = @cursor_pos
hide_cursor
@selecting = (event.state & Gdk::Window::SHIFT_MASK) != 0
ret = true
case event.keyval
when Gdk::Keyval::GDK_KP_Tab, Gdk::Keyval::GDK_Tab
@active_view = (@active_view == View::HEX) ? View::ASCII : View::HEX
when Gdk::Keyval::GDK_Up
set_cursor(@cursor_pos - @cpl)
when Gdk::Keyval::GDK_Down
set_cursor(@cursor_pos + @cpl)
when Gdk::Keyval::GDK_Page_Up
set_cursor([0, @cursor_pos - @vis_lines * @cpl].max)
when Gdk::Keyval::GDK_Page_Down
set_cursor([@cursor_pos + @vis_lines * @cpl, @data.size].min)
when Gdk::Keyval::GDK_Left
if @active_view == View::HEX
if @selecting
set_cursor(@cursor_pos - 1)
else
@lower_nibble ^= 1
set_cursor(@cursor_pos - 1) if @lower_nibble
end
else
set_cursor(@cursor_pos - 1)
end
when Gdk::Keyval::GDK_Right
if @active_view == View::HEX
if @selecting
set_cursor(@cursor_pos + 1)
else
@lower_nibble ^= 1
set_cursor(@cursor_pos + 1) unless @lower_nibble
end
else
set_cursor(@cursor_pos + 1)
end
when Gdk::Keyval::GDK_c, Gdk::Keyval::GDK_C
if event.state & Gdk::Window::CONTROL_MASK != 0
s,e = @selection.start, @selection.end + 1
if @active_view == View::HEX
brk_len = 2 * @cpl + @cpl / @group_type
format_xblock(s,e)
(@disp_buffer.size / brk_len + 1).times do |i| @disp_buffer.insert(i * (brk_len + 1), $/) end
else
brk_len = @cpl
format_ablock(s,e)
end
@@clipboard.set_text(@disp_buffer)
end
else
ret = false
end
show_cursor
ret
end
def hex_to_pointer(mx, my)
cy = @top_line + my.to_i / @char_height
cx = x = 0
while cx < 2 * @cpl
x += @char_width
if x > mx
set_cursor_xy(cx / 2, cy)
set_cursor_on_lower_nibble(cx % 2 != 0)
cx = 2 * @cpl
end
cx += 1
x += @char_width if ( cx % (2 * @group_type) == 0 )
end
end
def ascii_to_pointer(mx, my)
cx = mx / @char_width
cy = @top_line + my / @char_height
set_cursor_xy(cx, cy)
end
def load_font(fontname)
desc = Pango::FontDescription.new(fontname)
context = Gdk::Pango.context
context.set_language(Gtk.default_language)
font = context.load_font(desc)
font.metrics(context.language)
end
def draw_shadow(area)
bw = self.border_width
x = bw
xt = widget_get_xt
if @show_offsets
self.style.paint_shadow(
self.window,
Gtk::STATE_NORMAL, Gtk::SHADOW_IN,
nil, self, nil,
bw, bw,
8*@char_width + 2*xt, self.allocation.height - 2*bw
)
x += 8*@char_width + 2*xt
end
self.style.paint_shadow(
self.window,
Gtk::STATE_NORMAL, Gtk::SHADOW_IN,
nil, self, nil,
x, bw,
@xdisp_width + 2*xt, self.allocation.height - 2*bw
)
self.style.paint_shadow(
self.window,
Gtk::STATE_NORMAL, Gtk::SHADOW_IN,
nil, self, nil,
self.allocation.width - bw - @adisp_width - @scrollbar.requisition[0] - 2*xt, bw,
@adisp_width + 2*xt, self.allocation.height - 2*bw
)
end
def redraw_widget
return unless self.realized?
self.window.invalidate(nil, false)
end
def widget_get_xt
self.style.xthickness
end
def widget_get_yt
self.style.ythickness
end
def recalc_displays(width, height)
old_cpl = @cpl
w, h = @scrollbar.size_request
@xdisp_width = 1
@adisp_width = 1
total_width = width - 2 * self.border_width - 4 * widget_get_xt - w
total_width -= 2 * widget_get_xt + 8 * @char_width if @show_offsets
total_cpl = total_width / @char_width
if total_cpl == 0 or total_width < 0
@cpl = @lines = @vis_lines = 0
return
end
@cpl = 0
begin
break if @cpl % @group_type == 0 and total_cpl < @group_type * 3
@cpl += 1
total_cpl -= 3
total_cpl -= 1 if @cpl % @group_type == 0
end while total_cpl > 0
return if @cpl == 0
if @data.empty?
@lines = 1
else
@lines = @data.size / @cpl
@lines += 1 if @data.size % @cpl != 0
end
@vis_lines = (height - 2*self.border_width - 2*widget_get_yt).to_i / @char_height.to_i
@adisp_width = @cpl * @char_width + 1
xcpl = @cpl * 2 + (@cpl - 1) / @group_type
@xdisp_width = xcpl * @char_width + 1
@disp_buffer = ''
@adj.value = [@top_line * old_cpl / @cpl, @lines - @vis_lines].min
@adj.value = [ 0, @adj.value ].max
if @cursor_pos / @cpl < @adj.value or @cursor_pos / @cpl > @adj.value + @vis_lines - 1
@adj.value = [ @cursor_pos / @cpl, @lines - @vis_lines ].min
@adj.value = [ 0, @adj.value ].max
end
@adj.lower = 0
@adj.upper = @lines
@adj.step_increment = 1
@adj.page_increment = @vis_lines - 1
@adj.page_size = @vis_lines
@adj.signal_emit 'changed'
@adj.signal_emit 'value_changed'
end
def get_max_char_width(metrics)
layout = self.create_pango_layout('')
layout.set_font_description(@font_desc)
char_widths = [ 0 ]
(1..100).each do |i|
logical_rect = Pango::Rectangle.new(0, 0, 0, 0)
if is_displayable(i.chr)
layout.set_text(i.chr)
logical_rect = layout.pixel_extents[1]
end
char_widths << logical_rect.width
end
char_widths[48..122].max
end
def show_cursor
unless @cursor_shown
if @xdisp_gc and @adisp_gc and @xdisp.realized? and @adisp.realized?
render_xc
render_ac
end
@cursor_shown = true
end
end
def hide_cursor
if @cursor_shown
if @xdisp_gc and @adisp_gc and @xdisp.realized? and @adisp.realized?
render_byte(@cursor_pos)
end
@cursor_shown = false
end
end
def show_offsets_widget
@offsets = DrawingArea.new
@offsets.modify_font @font_desc
@olayout = @offsets.create_pango_layout('')
@offsets.events = Gdk::Event::EXPOSURE_MASK
@offsets.signal_connect 'expose_event' do |offsets, event|
imin = (event.area.y / @char_height).to_i
imax = ((event.area.y + event.area.height) / @char_height).to_i
imax += 1 if (event.area.y + event.area.height).to_i % @char_height != 0
imax = [ imax, @vis_lines ].min
render_offsets(imin, imax)
end
put @offsets, 0, 0
@offsets.show
end
def hide_offsets_widget
if @offsets
self.remove(@offsets)
@offsets = @offsets_gc = nil
end
end
def is_displayable(c)
if RUBY_VERSION < '1.9'
c = c[0]
else
c = c.ord
end
c >= 0x20 and c < 0x7f
end
def bytes_changed(s, e)
start_line = s / @cpl - @top_line
end_line = e / @cpl - @top_line
return if end_line < 0 or start_line > @vis_lines
start_line = [ 0, start_line ].max
render_hex_lines(start_line, end_line)
render_ascii_lines(start_line, end_line)
render_offsets(start_line, end_line) if @show_offsets
end
def render_hex_highlights(cursor_line)
xcpl = @cpl * 2 + @cpl / @group_type
@highlights.each do |hl|
next if (hl.start - hl.end).abs < hl.min_select
validate_highlight(hl)
s, e = [ hl.start, hl.end ].sort
sl, el = hl.start_line, hl.end_line
hl.style.attach(@xdisp.window) if hl.style
state = (@active_view == View::HEX) ? Gtk::STATE_SELECTED : Gtk::STATE_INSENSITIVE
if cursor_line == sl
cursor_off = 2 * (s % @cpl) + (s % @cpl) / @group_type
if cursor_line == el
len = 2 * (e % @cpl + 1) + (e % @cpl) / @group_type
else
len = xcpl
end
len -= cursor_off
(hl.style || self.style).paint_flat_box(
@xdisp.window,
state, Gtk::SHADOW_NONE,
nil, @xdisp, '',
cursor_off * @char_width, cursor_line * @char_height,
len * @char_width, @char_height
) if len > 0
elsif cursor_line == el
cursor_off = 2 * (e % @cpl + 1) + (e % @cpl) / @group_type
(hl.style || self.style).paint_flat_box(
@xdisp.window,
state, Gtk::SHADOW_NONE,
nil, @xdisp, '',
0, cursor_line * @char_height,
cursor_off * @char_width, @char_height
) if cursor_off > 0
elsif cursor_line > sl and cursor_line < el
(hl.style || self.style).paint_flat_box(
@xdisp.window,
state, Gtk::SHADOW_NONE,
nil, @xdisp, '',
0, cursor_line * @char_height,
xcpl * @char_width, @char_height
)
end
hl.style.attach(@adisp.window) if hl.style
end
end
def render_hex_lines(imin, imax)
return unless self.realized? and @cpl != 0
cursor_line = @cursor_pos / @cpl - @top_line
@xdisp_gc.set_foreground(self.style.base(Gtk::STATE_NORMAL))
@xdisp.window.draw_rectangle(
@xdisp_gc,
true,
0,
imin * @char_height,
@xdisp.allocation.width,
(imax - imin + 1) * @char_height
)
imax = [ imax, @vis_lines, @lines ].min
@xdisp_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
frm_len = format_xblock((@top_line+imin) * @cpl, [(@top_line+imax+1) * @cpl, @data.size].min)
tmp = nil
xcpl = @cpl*2 + @cpl/@group_type
(imin..imax).each do |i|
return unless (tmp = frm_len - ((i - imin) * xcpl)) > 0
render_hex_highlights(i)
text = @disp_buffer[(i-imin) * xcpl, [xcpl, tmp].min]
@xlayout.set_text(text)
@xdisp.window.draw_layout(@xdisp_gc, 0, i * @char_height, @xlayout)
end
render_xc if cursor_line >= imin and cursor_line <= imax and @cursor_shown
end
def render_ascii_highlights(cursor_line)
@highlights.each do |hl|
next if (hl.start - hl.end).abs < hl.min_select
validate_highlight(hl)
s, e = [ hl.start, hl.end ].sort
sl, el = hl.start_line, hl.end_line
hl.style.attach(@adisp.window) if hl.style
state = (@active_view == View::ASCII) ? Gtk::STATE_SELECTED : Gtk::STATE_INSENSITIVE
if cursor_line == sl
cursor_off = s % @cpl
len =
if cursor_line == el
e - s + 1
else
@cpl - cursor_off
end
(hl.style || self.style).paint_flat_box(
@adisp.window,
state, Gtk::SHADOW_NONE,
nil, @adisp, '',
cursor_off * @char_width, cursor_line * @char_height,
len * @char_width, @char_height
) if len > 0
elsif cursor_line == el
cursor_off = e % @cpl + 1
(hl.style || self.style).paint_flat_box(
@adisp.window,
state, Gtk::SHADOW_NONE,
nil, @adisp, '',
0, cursor_line * @char_height,
cursor_off * @char_width, @char_height
) if cursor_off > 0
elsif cursor_line > sl and cursor_line < el
(hl.style || self.style).paint_flat_box(
@adisp.window,
state, Gtk::SHADOW_NONE,
nil, @adisp, '',
0, cursor_line * @char_height,
@cpl * @char_width, @char_height
)
end
hl.style.attach(@adisp.window) if hl.style
end
end
def render_ascii_lines(imin, imax)
return unless self.realized? and @cpl != 0
cursor_line = @cursor_pos / @cpl - @top_line
@adisp_gc.set_foreground(self.style.base(Gtk::STATE_NORMAL))
@adisp.window.draw_rectangle(
@adisp_gc,
true,
0,
imin * @char_height,
@adisp.allocation.width,
(imax - imin + 1) * @char_height
)
imax = [ imax, @vis_lines, @lines ].min
@adisp_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
frm_len = format_ablock((@top_line+imin) * @cpl, [(@top_line+imax+1) * @cpl, @data.size].min)
tmp = nil
(imin..imax).each do |i|
return unless (tmp = frm_len - ((i - imin) * @cpl)) > 0
render_ascii_highlights(i)
text = @disp_buffer[(i-imin) * @cpl, [@cpl, tmp].min]
@alayout.set_text(text)
@adisp.window.draw_layout(@adisp_gc, 0, i * @char_height, @alayout)
end
render_ac if cursor_line >= imin and cursor_line <= imax and @cursor_shown
end
def render_offsets(imin, imax)
return unless self.realized?
unless @offsets_gc
@offsets_gc = Gdk::GC.new(@offsets.window)
@offsets_gc.set_exposures(true)
end
@offsets_gc.set_foreground(self.style.base(Gtk::STATE_INSENSITIVE))
@offsets.window.draw_rectangle(
@offsets_gc,
true,
0, imin * @char_height,
@offsets.allocation.width, (imax - imin + 1) * @char_height
)
imax = [ imax, @vis_lines, @lines - @top_line - 1 ].min
@offsets_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
(imin..imax).each do |i|
text = "%08x" % ((@top_line + i) * @cpl + @starting_offset)
@olayout.set_text(text)
@offsets.window.draw_layout(@offsets_gc, 0, i * @char_height, @olayout)
end
end
def render_byte(pos)
return unless @xdisp_gc and @adisp_gc and @xdisp.realized? and @adisp.realized?
return unless (coords = get_xcoords(pos))
cx, cy = coords
c = format_xbyte(pos)
@xdisp_gc.set_foreground(self.style.base(Gtk::STATE_NORMAL))
@xdisp.window.draw_rectangle(
@xdisp_gc,
true,
cx, cy,
2 * @char_width, @char_height
)
if pos < @data.size
@xdisp_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
@xlayout.set_text(c)
@xdisp.window.draw_layout(@xdisp_gc, cx, cy, @xlayout)
end
return unless (coords = get_acoords(pos))
cx, cy = coords
@adisp_gc.set_foreground(self.style.base(Gtk::STATE_NORMAL))
@adisp.window.draw_rectangle(
@adisp_gc,
true,
cx, cy,
@char_width, @char_height
)
if pos < @data.size
@adisp_gc.set_foreground(self.style.text(Gtk::STATE_NORMAL))
c = get_byte(pos)
c = '.' unless is_displayable(c)
@alayout.set_text(c)
@adisp.window.draw_layout(@adisp_gc, cx, cy, @alayout)
end
end
def render_xc
return unless @xdisp.realized?
if coords = get_xcoords(@cursor_pos)
cx, cy = coords
c = format_xbyte(@cursor_pos)
if @lower_nibble
cx += @char_width
c = c[1,1]
else
c = c[0,1]
end
@xdisp_gc.set_foreground(self.style.base(Gtk::STATE_ACTIVE))
@xdisp.window.draw_rectangle(
@xdisp_gc,
(@active_view == View::HEX),
cx, cy,
@char_width,
@char_height - 1
)
@xdisp_gc.set_foreground(self.style.text(Gtk::STATE_ACTIVE))
@xlayout.set_text(c)
@xdisp.window.draw_layout(@xdisp_gc, cx, cy, @xlayout)
end
end
def render_ac
return unless @adisp.realized?
if coords = get_acoords(@cursor_pos)
cx, cy = coords
c = get_byte(@cursor_pos)
c = '.' unless is_displayable(c)
@adisp_gc.set_foreground(self.style.base(Gtk::STATE_ACTIVE))
@adisp.window.draw_rectangle(
@adisp_gc,
(@active_view == View::ASCII),
cx, cy,
@char_width,
@char_height - 1
)
@adisp_gc.set_foreground(self.style.text(Gtk::STATE_ACTIVE))
@alayout.set_text(c)
@adisp.window.draw_layout(@adisp_gc, cx, cy, @alayout)
end
end
def get_xcoords(pos)
return nil if @cpl == 0
cy = pos / @cpl - @top_line
return nil if cy < 0
cx = 2 * (pos % @cpl)
spaces = (pos % @cpl) / @group_type
cx *= @char_width
cy *= @char_height
spaces *= @char_width
[cx + spaces, cy]
end
def get_acoords(pos)
return nil if @cpl == 0
cy = pos / @cpl - @top_line
return nil if cy < 0
cy *= @char_height
cx = @char_width * (pos % @cpl)
[cx, cy]
end
def format_xblock(s, e)
@disp_buffer = ''
(s+1..e).each do |i|
@disp_buffer << get_byte(i - 1).unpack('H2')[0]
@disp_buffer << ' ' if i % @group_type == 0
end
@disp_buffer.size
end
def format_ablock(s, e)
@disp_buffer = ''
(s..e-1).each do |i|
c = get_byte(i)
c = '.' unless is_displayable(c)
@disp_buffer << c
end
@disp_buffer.size
end
def get_byte(offset)
if offset >= 0 and offset < @data.size
@data[offset, 1]
else
0.chr
end
end
def format_xbyte(pos)
get_byte(pos).unpack('H2')[0]
end
end
end
__END__
hexedit = Gtk::HexEditor.new(File.read '/bin/cat')
hexedit.show_offsets(true)
hexedit.set_cursor 2
hexedit.set_cursor_on_lower_nibble true
hexedit.set_font 'Terminus 12'
hexedit.set_group_type Gtk::HexEditor::Group::LONG
window = Gtk::Window.new
window.add(hexedit)
window.show_all
Gtk.main
origami-pdf-1.2.7/bin/gui/treeview.rb 0000644 0001750 0001750 00000030113 12130767505 017650 0 ustar terceiro terceiro =begin
= File
treeview.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
module PDFWalker
class Walker < Window
private
def create_treeview
@treeview = PDFTree.new(self).set_headers_visible(false)
colcontent = Gtk::TreeViewColumn.new("Names",
Gtk::CellRendererText.new.set_foreground_set(true).set_background_set(true),
:text => PDFTree::TEXTCOL,
:weight => PDFTree::WEIGHTCOL,
:style => PDFTree::STYLECOL,
:foreground => PDFTree::FGCOL,
:background => PDFTree::BGCOL
)
@treeview.append_column(colcontent)
end
end
class PDFTree < TreeView
include Popable
OBJCOL = 0
TEXTCOL = 1
WEIGHTCOL = 2
STYLECOL = 3
FGCOL = 4
BGCOL = 5
@@appearance = Hash.new(:Weight => Pango::WEIGHT_NORMAL, :Style => Pango::STYLE_NORMAL)
attr_reader :parent
def initialize(parent)
@parent = parent
reset_appearance
@treestore = TreeStore.new(Object::Object, String, Pango::FontDescription::Weight, Pango::FontDescription::Style, String, String)
super(@treestore)
signal_connect('cursor-changed') {
iter = selection.selected
if iter
obj = @treestore.get_value(iter, OBJCOL)
if obj.is_a?(Stream) and iter.n_children == 1
# Processing with an XRef or Object Stream
if obj.is_a?(ObjectStream)
obj.each { |embeddedobj|
load_object(iter, embeddedobj)
}
elsif obj.is_a?(XRefStream)
obj.each { |xref|
load_xrefstm(iter, xref)
}
end
end
parent.hexview.load(obj)
parent.objectview.load(obj)
end
}
signal_connect('row-activated') { |tree, path, column|
if selection.selected
obj = @treestore.get_value(selection.selected, OBJCOL)
if row_expanded?(path)
collapse_row(path)
else
expand_row(path, false)
end
goto(obj) if obj.is_a?(Origami::Reference)
end
}
add_events(Gdk::Event::BUTTON_PRESS_MASK)
signal_connect('button_press_event') { |widget, event|
if event.button == 3 && parent.opened
path = get_path(event.x,event.y).first
set_cursor(path, nil, false)
obj = @treestore.get_value(@treestore.get_iter(path), OBJCOL)
popup_menu(obj, event, path)
end
}
end
def clear
@treestore.clear
end
def goto(obj)
if obj.is_a?(TreePath)
set_cursor(obj, nil, false)
else
if obj.is_a?(Name) and obj.parent.is_a?(Dictionary) and obj.parent.has_key?(obj)
obj = obj.parent[obj]
elsif obj.is_a?(Reference)
obj =
begin
obj.solve
rescue InvalidReferenceError
@parent.error("Object not found : #{obj}")
return
end
end
@treestore.each { |model, path, iter|
current_obj = @treestore.get_value(iter, OBJCOL)
if current_obj.is_a?(ObjectStream) and obj.parent.equal?(current_obj)
current_obj.each { |embeddedobj|
load_object(iter, embeddedobj)
}
next
end
if obj.equal?(current_obj)
expand_to_path(path) unless row_expanded?(path)
if cursor.first then @parent.explorer_history << cursor.first end
set_cursor(path, nil, false)
return
end
}
@parent.error("Object not found : #{obj}")
end
end
def highlight(obj, color)
if obj.is_a?(Name) and obj.parent.is_a?(Dictionary) and obj.parent.has_key?(obj)
obj = obj.parent[obj]
end
@treestore.each { |model, path, iter|
current_obj = @treestore.get_value(iter, OBJCOL)
if obj.equal?(current_obj)
@treestore.set_value(iter, BGCOL, color)
expand_to_path(path) unless row_expanded?(path)
return
end
}
@parent.error("Object not found : #{obj}")
end
def load(pdf)
if pdf
self.clear
begin
#
# Create root entry
#
root = @treestore.append(nil)
@treestore.set_value(root, OBJCOL, pdf)
set_node(root, :Filename, @parent.filename)
#
# Create header entry
#
header = @treestore.append(root)
@treestore.set_value(header, OBJCOL, pdf.header)
set_node(header, :Header, "Header (version #{pdf.header.majorversion}.#{pdf.header.minorversion})")
no = 1
pdf.revisions.each { |revision|
load_revision(root, no, revision)
no = no + 1
}
set_model(@treestore)
ensure
expand(@treestore.iter_first, 3)
set_cursor(@treestore.iter_first.path, nil, false)
end
end
end
private
def expand(row, depth)
if row and depth != 0
loop do
expand_row(row.path, false)
expand(row.first_child, depth - 1)
break if not row.next!
end
end
end
def load_revision(root, no, revision)
revroot = @treestore.append(root)
@treestore.set_value(revroot, OBJCOL, revision)
set_node(revroot, :Revision, "Revision #{no}")
load_body(revroot, revision.body.values)
load_xrefs(revroot, revision.xreftable)
load_trailer(revroot, revision.trailer)
end
def load_body(rev, body)
bodyroot = @treestore.append(rev)
@treestore.set_value(bodyroot, OBJCOL, body)
set_node(bodyroot, :Body, "Body")
body.sort_by{|obj| obj.file_offset}.each { |object|
begin
load_object(bodyroot, object)
rescue Exception => e
msg = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n")}"
#@parent.error(msg)
next
end
}
end
def load_object(container, object, name = nil)
obj = @treestore.append(container)
@treestore.set_value(obj, OBJCOL, object)
type = object.native_type.to_s.split('::').last.to_sym
if name.nil?
name =
case object
when Origami::String
'"' + object.to_utf8 + '"'
when Origami::Number, Name
object.value.to_s
else
object.type.to_s
end
end
set_node(obj, type, name)
if object.is_a? Origami::Array
object.each { |subobject|
load_object(obj, subobject)
}
elsif object.is_a? Origami::Dictionary
object.each_key { |subkey|
load_object(obj, object[subkey.value], subkey.value.to_s)
}
elsif object.is_a? Origami::Stream
load_object(obj, object.dictionary, "Stream Dictionary")
end
end
def load_xrefstm(stm, embxref)
xref = @treestore.append(stm)
@treestore.set_value(xref, OBJCOL, embxref)
if embxref.is_a?(XRef)
set_node(xref, :XRef, embxref.to_s.chomp)
else
set_node(xref, :XRef, "xref to ObjectStream #{embxref.objstmno}, object index #{embxref.index}")
end
end
def load_xrefs(rev, table)
if table
section = @treestore.append(rev)
@treestore.set_value(section, OBJCOL, table)
set_node(section, :XRefSection, "XRef section")
table.each { |subtable|
subsection = @treestore.append(section)
@treestore.set_value(subsection, OBJCOL, subtable)
set_node(subsection, :XRefSubSection, "#{subtable.range.begin} #{subtable.range.end - subtable.range.begin + 1}")
subtable.each { |entry|
xref = @treestore.append(subsection)
@treestore.set_value(xref, OBJCOL, entry)
set_node(xref, :XRef, entry.to_s.chomp)
}
}
end
end
def load_trailer(rev, trailer)
trailerroot = @treestore.append(rev)
@treestore.set_value(trailerroot, OBJCOL, trailer)
set_node(trailerroot, :Trailer, "Trailer")
unless trailer.dictionary.nil?
load_object(trailerroot, trailer.dictionary)
end
end
def reset_appearance
@@appearance[:Filename] = {:Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:Header] = {:Color => "darkgreen", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:Revision] = {:Color => "blue", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:Body] = {:Color => "purple", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:XRefSection] = {:Color => "purple", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:XRefSubSection] = {:Color => "brown", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:XRef] = {:Color => "gray20", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:Trailer] = {:Color => "purple", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:StartXref] = {:Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:String] = {:Color => "red", :Weight => Pango::WEIGHT_NORMAL, :Style => Pango::STYLE_ITALIC}
@@appearance[:Name] = {:Color => "gray", :Weight => Pango::WEIGHT_NORMAL, :Style => Pango::STYLE_ITALIC}
@@appearance[:Number] = {:Color => "orange", :Weight => Pango::WEIGHT_NORMAL, :Style => Pango::STYLE_NORMAL}
@@appearance[:Dictionary] = {:Color => "brown", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:Stream] = {:Color => "darkcyan", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:StreamData] = {:Color => "darkcyan", :Weight => Pango::WEIGHT_NORMAL, :Style => Pango::STYLE_OBLIQUE}
@@appearance[:Array] = {:Color => "darkgreen", :Weight => Pango::WEIGHT_BOLD, :Style => Pango::STYLE_NORMAL}
@@appearance[:Reference] = {:Weight => Pango::WEIGHT_NORMAL, :Style => Pango::STYLE_OBLIQUE}
@@appearance[:Boolean] = {:Color => "deeppink", :Weight => Pango::WEIGHT_NORMAL, :Style => Pango::STYLE_NORMAL}
end
def get_object_appearance(type)
@@appearance[type]
end
def set_node(node, type, text)
@treestore.set_value(node, TEXTCOL, text)
app = get_object_appearance(type)
@treestore.set_value(node, WEIGHTCOL, app[:Weight])
@treestore.set_value(node, STYLECOL, app[:Style])
@treestore.set_value(node, FGCOL, app[:Color])
end
end
end
origami-pdf-1.2.7/bin/gui/properties.rb 0000644 0001750 0001750 00000011214 12101464040 020175 0 ustar terceiro terceiro =begin
= File
properties.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
require 'digest/md5'
module PDFWalker
class Walker < Window
def display_file_properties
if @opened
prop = Properties.new(self, @opened)
end
end
class Properties < Dialog
@@acrobat_versions =
{
1.0 => "1.x",
1.1 => "2.x",
1.2 => "3.x",
1.3 => "4.x",
1.4 => "5.x",
1.5 => "6.x",
1.6 => "7.x",
1.7 => "8.x / 9.x / 10.x"
}
def initialize(parent, pdf)
super("Document properties", parent, Dialog::MODAL, [Stock::CLOSE, Dialog::RESPONSE_NONE])
docframe = Frame.new(" File properties ")
stat = File.stat(parent.filename)
if RUBY_VERSION < '1.9'
require 'iconv'
i = Iconv.new("UTF-8//IGNORE//TRANSLIT", "ISO-8859-1")
creation_date = i.iconv(stat.ctime.to_s)
last_modified = i.iconv(stat.mtime.to_s)
fd = File.open(parent.filename, 'rb')
md5sum = Digest::MD5.hexdigest(fd.read)
fd.close
i.close
else
creation_date = stat.ctime.to_s.encode("utf-8", :invalid => :replace, :undef => :replace)
last_modified = stat.mtime.to_s.encode("utf-8", :invalid => :replace, :undef => :replace)
md5sum = Digest::MD5.hexdigest(File.binread(parent.filename))
end
labels =
[
[ "Filename:", parent.filename ],
[ "File size:", "#{File.size(parent.filename)} bytes" ],
[ "MD5:", md5sum ],
[ "Read-only:", "#{not stat.writable?}" ],
[ "Creation date:", creation_date ],
[ "Last modified:", last_modified ]
]
doctable = Table.new(labels.size + 1, 3)
row = 0
labels.each do |name, value|
doctable.attach(Label.new(name).set_alignment(1,0), 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
doctable.attach(Label.new(value).set_alignment(0,0), 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
row = row.succ
end
docframe.border_width = 5
docframe.shadow_type = Gtk::SHADOW_IN
docframe.add(doctable)
pdfframe = Frame.new(" PDF properties ")
labels =
[
[ "Version:", "#{pdf.header.to_f} (Acrobat #{ if pdf.header.to_f >= 1.0 and pdf.header.to_f <= 1.7 then @@acrobat_versions[pdf.header.to_f] else "unknown version" end})" ],
[ "Number of revisions:", "#{pdf.revisions.size}" ],
[ "Number of indirect objects:", "#{pdf.indirect_objects.size}" ],
[ "Number of pages:", "#{pdf.pages.size}" ],
[ "Is linearized:", "#{pdf.is_linearized?}" ],
[ "Is encrypted:", "#{pdf.is_encrypted?}" ],
[ "Is signed:", "#{pdf.is_signed?}" ],
[ "Has usage rights:", "#{pdf.has_usage_rights?}"],
[ "Contains Acroform:", "#{pdf.has_form?}" ],
#[ "Contains XFA forms:", "#{pdf.has_xfa_forms?}" ]
[ "Has document information:", "#{pdf.has_document_info?}" ],
[ "Has metadata:", "#{pdf.has_metadata?}" ]
]
pdftable = Table.new(labels.size + 1, 3)
row = 0
labels.each do |name, value|
pdftable.attach(Label.new(name).set_alignment(1,0), 0, 1, row, row + 1, Gtk::FILL, Gtk::SHRINK, 4, 4)
pdftable.attach(Label.new(value).set_alignment(0,0), 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
row = row.succ
end
pdfframe.border_width = 5
pdfframe.shadow_type = Gtk::SHADOW_IN
pdfframe.add(pdftable)
vbox.add(docframe)
vbox.add(pdfframe)
signal_connect('response') { destroy }
show_all
end
end
end
end
origami-pdf-1.2.7/bin/gui/file.rb 0000644 0001750 0001750 00000026545 12101464040 016735 0 ustar terceiro terceiro =begin
= File
file.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugr
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
require 'origami'
include Origami
module PDFWalker
class Walker < Window
attr_reader :opened
attr_reader :explore_history
def close
@opened = nil
@filename = ''
@explorer_history.clear
@treeview.clear
@objectview.clear
@hexview.clear
[
@file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
@document_menu_search,
@document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
@document_menu_properties, @document_menu_sign, @document_menu_ur
].each do |menu|
menu.sensitive = false
end
@statusbar.pop(@main_context)
GC.start
end
def open(filename = nil)
dialog = Gtk::FileChooserDialog.new("Open PDF File",
self,
FileChooser::ACTION_OPEN,
nil,
[Stock::CANCEL, Dialog::RESPONSE_CANCEL],
[Stock::OPEN, Dialog::RESPONSE_ACCEPT])
last_file = @config.recent_files.first
unless last_file.nil?
last_folder = last_file[0..last_file.size - File.basename(last_file).size - 1]
dialog.set_current_folder(last_folder) if File.directory?(last_folder)
end
dialog.filter = FileFilter.new.add_pattern("*.acrodata").add_pattern("*.pdf").add_pattern("*.fdf")
if filename or dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
create_progressbar
filename ||= dialog.filename
dialog.destroy
begin
if @help_menu_profile.active?
require 'ruby-prof'
RubyProf.start
end
target = parsefile(filename)
if @help_menu_profile.active?
result = RubyProf.stop
txtprinter = RubyProf::FlatPrinter.new(result)
htmlprinter = RubyProf::GraphHtmlPrinter.new(result)
txtprinter.print(File.new("#{@config.profile_output_dir}/#{File.basename(filename)}.log", "w"))
htmlprinter.print(File.new("#{@config.profile_output_dir}/#{File.basename(filename)}.log.html", "w"))
end
if target
close if @opened
@opened = target
@filename = filename
@config.last_opened_file(filename)
@config.save
update_recent_menu
@last_search_result = []
@last_search =
{
:expr => "",
:regexp => false,
:type => :body
}
self.reload
[
@file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
@document_menu_search,
@document_menu_gotocatalog, @document_menu_gotopage, @document_menu_gotorev, @document_menu_gotoobj,
@document_menu_properties, @document_menu_sign, @document_menu_ur
].each do |menu|
menu.sensitive = true
end
@explorer_history.clear
@statusbar.push(@main_context, "Viewing #{filename}")
if @opened.is_a?(PDF)
pagemenu = Menu.new
@document_menu_gotopage.remove_submenu
page_index = 1
@opened.pages.each do |page|
pagemenu.append(item = MenuItem.new(page_index.to_s).show)
item.signal_connect("activate") do @treeview.goto(page) end
page_index = page_index + 1
end
@document_menu_gotopage.set_submenu(pagemenu)
revmenu = Menu.new
@document_menu_gotorev.remove_submenu
rev_index = 1
@opened.revisions.each do |rev|
revmenu.append(item = MenuItem.new(rev_index.to_s).show)
item.signal_connect("activate") do @treeview.goto(rev) end
rev_index = rev_index + 1
end
@document_menu_gotorev.set_submenu(revmenu)
goto_catalog
end
end
rescue Exception => e
error("Error while parsing file.\n#{e} (#{e.class})\n" + e.backtrace.join("\n"))
end
close_progressbar
self.activate_focus
else
dialog.destroy
end
end
def deserialize
dialog = Gtk::FileChooserDialog.new("Open dump file",
self,
FileChooser::ACTION_OPEN,
nil,
[Stock::CANCEL, Dialog::RESPONSE_CANCEL],
[Stock::OPEN, Dialog::RESPONSE_ACCEPT])
dialog.current_folder = "#{Dir.pwd}/dumps"
dialog.filter = FileFilter.new.add_pattern("*.gz")
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
if @opened then close end
filename = dialog.filename
begin
@opened = PDF.deserialize(filename)
self.reload
[ @file_menu_close, @file_menu_saveas, @file_menu_serialize, @file_menu_refresh,
@document_menu_properties, @document_menu_sign, @document_menu_ur ].each do |menu|
menu.sensitive = true
end
@explorer_history.clear
@statusbar.push(@main_context, "Viewing dump of #{filename}")
rescue Exception => e
error("This file cannot be loaded.\n#{e} (#{e.class})")
end
end
dialog.destroy
end
def serialize
dialog = Gtk::FileChooserDialog.new("Save dump file",
self,
Gtk::FileChooser::ACTION_SAVE,
nil,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
)
dialog.do_overwrite_confirmation = true
dialog.current_folder = "#{Dir.pwd}/dumps"
dialog.current_name = "#{File.basename(@filename)}.dmp.gz"
dialog.filter = FileFilter.new.add_pattern("*.gz")
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
@opened.serialize(dialog.filename)
end
dialog.destroy
end
def save_data(caption, data, filename = "")
dialog = Gtk::FileChooserDialog.new(caption,
self,
Gtk::FileChooser::ACTION_SAVE,
nil,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
)
dialog.do_overwrite_confirmation = true
dialog.current_name = File.basename(filename)
dialog.filter = FileFilter.new.add_pattern("*.*")
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
fd = File.open(dialog.filename, "w").binmode
fd << data
fd.close
end
dialog.destroy
end
def save
dialog = Gtk::FileChooserDialog.new("Save PDF file",
self,
Gtk::FileChooser::ACTION_SAVE,
nil,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
)
dialog.filter = FileFilter.new.add_pattern("*.acrodata").add_pattern("*.pdf").add_pattern("*.fdf")
folder = @filename[0..@filename.size - File.basename(@filename).size - 1]
dialog.set_current_folder(folder)
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
@opened.save(dialog.filename)
end
dialog.destroy
end
def save_dot
dialog = Gtk::FileChooserDialog.new("Save dot file",
self,
Gtk::FileChooser::ACTION_SAVE,
nil,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
)
dialog.filter = FileFilter.new.add_pattern("*.dot")
folder = @filename[0..@filename.size - File.basename(@filename).size - 1]
dialog.set_current_folder(folder)
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
@opened.export_to_graph(dialog.filename)
end
dialog.destroy
end
def save_graphml
dialog = Gtk::FileChooserDialog.new("Save GraphML file",
self,
Gtk::FileChooser::ACTION_SAVE,
nil,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
)
dialog.filter = FileFilter.new.add_pattern("*.graphml")
folder = @filename[0..@filename.size - File.basename(@filename).size - 1]
dialog.set_current_folder(folder)
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
@opened.export_to_graphml(dialog.filename)
end
dialog.destroy
end
private
def parsefile(filename)
update_bar = lambda { |obj|
if @progressbar then @progressbar.pulse end
while (Gtk.events_pending?) do Gtk.main_iteration end
}
prompt_passwd = lambda {
passwd = ""
dialog = Gtk::Dialog.new(
"This document is encrypted",
nil,
Gtk::Dialog::MODAL,
[ Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK ],
[ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ]
)
dialog.set_default_response(Gtk::Dialog::RESPONSE_OK)
label = Gtk::Label.new("Please enter password:")
entry = Gtk::Entry.new
entry.signal_connect('activate') {
dialog.response(Gtk::Dialog::RESPONSE_OK)
}
dialog.vbox.add(label)
dialog.vbox.add(entry)
dialog.show_all
dialog.run do |response|
if response == Gtk::Dialog::RESPONSE_OK
passwd = entry.text
end
end
dialog.destroy
return passwd
}
PDF.read(filename,
:verbosity => Parser::VERBOSE_INSANE,
:ignoreerrors => false,
:callback => update_bar,
:prompt_password => prompt_passwd
)
end
def create_progressbar
@progresswin = Dialog.new("Parsing file...", self, Dialog::MODAL)
@progresswin.vbox.add(@progressbar = ProgressBar.new.set_pulse_step(0.05))
@progresswin.show_all
end
def close_progressbar
@progresswin.close
end
end
end
origami-pdf-1.2.7/bin/gui/walker.rb 0000644 0001750 0001750 00000016117 12101464040 017275 0 ustar terceiro terceiro #!/usr/bin/env ruby
=begin
= File
walker.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
begin
require 'gtk2'
rescue LoadError
abort('Error: you need to install ruby-gtk2 to run this application')
end
include Gtk
begin
require 'origami'
rescue LoadError
ORIGAMIDIR = "#{File.dirname(__FILE__)}/../../lib"
$: << ORIGAMIDIR
require 'origami'
end
require 'gui/menu'
require 'gui/about'
require 'gui/file'
require 'gui/hexview'
require 'gui/treeview'
require 'gui/textview'
require 'gui/imgview'
require 'gui/config'
require 'gui/properties'
require 'gui/xrefs'
require 'gui/signing'
module PDFWalker #:nodoc:all
class Walker < Window
attr_reader :treeview, :hexview, :objectview
attr_reader :explorer_history
attr_reader :config
attr_reader :filename
def self.start(file = nil)
Gtk.init
Walker.new(file)
Gtk.main
end
def initialize(target_file = nil)
super("PDF Walker")
@config = Walker::Config.new
@last_search_result = []
@last_search =
{
:expr => "",
:regexp => false,
:type => :body
}
@explorer_history = Array.new
signal_connect('destroy') {
@config.save
Gtk.main_quit
}
add_events(Gdk::Event::KEY_RELEASE_MASK)
signal_connect('key_release_event') { |w, event|
if event.keyval == Gdk::Keyval::GDK_F1 then about
elsif event.keyval == Gdk::Keyval::GDK_Escape && @opened && ! @explorer_history.empty?
@treeview.goto(@explorer_history.pop)
end
}
create_menus
create_treeview
create_hexview
create_objectview
create_panels
create_statusbar
@vbox = VBox.new
@vbox.pack_start(@menu, false, false)
@vbox.pack_start(@hpaned)
@vbox.pack_end(@statusbar, false, false)
add @vbox
set_default_size(self.screen.width * 0.5, self.screen.height * 0.5)
#maximize
show_all
open(target_file)
end
def error(msg)
dialog = Gtk::MessageDialog.new(self,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::ERROR,
Gtk::MessageDialog::BUTTONS_CLOSE,
msg)
dialog.run
dialog.destroy
end
def reload
@treeview.load(@opened) if @opened
end
def search
dialog = Gtk::Dialog.new("Search...",
self,
Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
[Gtk::Stock::FIND, Gtk::Dialog::RESPONSE_OK],
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL]
)
entry = Gtk::Entry.new
entry.signal_connect('activate') { dialog.response(Gtk::Dialog::RESPONSE_OK) }
entry.text = @last_search[:expr]
button_bydata = Gtk::RadioButton.new("In object body")
button_byname = Gtk::RadioButton.new(button_bydata, "In object name")
button_regexp = Gtk::CheckButton.new("Regular expression")
button_bydata.set_active(true) if @last_search[:type] == :body
button_byname.set_active(true) if @last_search[:type] == :name
button_regexp.set_active(@last_search[:regexp])
hbox = HBox.new
hbox.pack_start Gtk::Label.new("Search for expression ")
hbox.pack_start entry
dialog.vbox.pack_start(hbox)
dialog.vbox.pack_start(button_bydata)
dialog.vbox.pack_start(button_byname)
dialog.vbox.pack_end(button_regexp)
dialog.signal_connect('response') do |dlg, response|
if response == Gtk::Dialog::RESPONSE_OK
search =
{
:expr => entry.text,
:regexp => button_regexp.active?,
:type => button_byname.active? ? :name : :body
}
if search == @last_search
@last_search_result.push @last_search_result.shift
results = @last_search_result
else
expr = search[:regexp] ? Regexp.new(search[:expr]) : search[:expr]
results =
if search[:type] == :body
@opened.grep(expr)
else
@opened.ls(expr)
end
@last_search = search
end
if results.empty?
error("No result found.")
else
if results != @last_search_result
@last_search_result.each do |obj| @treeview.highlight(obj, nil) end
results.each do |obj| @treeview.highlight(obj, "lightpink") end
@last_search_result = results
end
@treeview.goto(results.first)
end
else
dialog.destroy
end
end
dialog.show_all
end
def goto_catalog
@treeview.goto(@opened.Catalog.reference)
end
def goto_object
dialog = Gtk::Dialog.new("Jump to object...",
self,
Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK],
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL]
)
entry = Gtk::Entry.new
entry.signal_connect('activate') { dialog.response(Gtk::Dialog::RESPONSE_OK) }
dialog.vbox.pack_start Gtk::Label.new("Object number: ")
dialog.vbox.pack_start entry
dialog.show_all
no = 0
dialog.run do |response|
if response == Gtk::Dialog::RESPONSE_OK
no = entry.text.to_i
end
dialog.destroy
end
if no > 0
obj = @opened[no]
if obj.nil?
error("Object #{no} not found.")
else
@treeview.goto(obj)
end
end
end
private
def create_panels
@hpaned = HPaned.new
@treepanel = ScrolledWindow.new.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
@treepanel.add @treeview
@vpaned = VPaned.new
@vpaned.pack1(@objectview, true, false)
@vpaned.pack2(@hexview, true, false)
@hpaned.pack1(@treepanel, true, false)
@hpaned.pack2(@vpaned, true, false)
end
def create_statusbar
@statusbar = Statusbar.new
@main_context = @statusbar.get_context_id 'Main'
@statusbar.push(@main_context, 'No file selected')
end
end
end
if __FILE__ == $0
PDFWalker::Walker.start
end
origami-pdf-1.2.7/bin/gui/xrefs.rb 0000644 0001750 0001750 00000004015 12101464040 017131 0 ustar terceiro terceiro =begin
= File
xrefs.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
require 'origami'
include Origami
module PDFWalker
class Walker < Window
def show_xrefs(target)
XrefsDialog.new(self, target)
end
end
class XrefsDialog < Dialog
OBJCOL = 0
TEXTCOL = 1
def initialize(parent, target)
super("Xrefs to #{target.reference}", parent, Dialog::MODAL, [Stock::CLOSE, Dialog::RESPONSE_NONE])
@parent = parent
@list = ListStore.new(Object, String)
@view = TreeView.new(@list)
column = Gtk::TreeViewColumn.new("Objects", Gtk::CellRendererText.new, :text => TEXTCOL)
@view.append_column(column)
target.xrefs.each { |obj|
str = obj.class.to_s
iter = @list.append
@list.set_value(iter, OBJCOL, obj)
@list.set_value(iter, TEXTCOL, str)
}
@view.signal_connect("row_activated") { |tree, path, column|
if @view.selection.selected
from = @list.get_value(@view.selection.selected, OBJCOL)
@parent.treeview.goto(from)
end
}
scroll = ScrolledWindow.new.set_policy(POLICY_NEVER, POLICY_AUTOMATIC)
scroll.add(@view)
vbox.add(scroll)
signal_connect('response') { destroy }
show_all
end
end
end
origami-pdf-1.2.7/bin/gui/config.rb 0000644 0001750 0001750 00000006107 12133245664 017271 0 ustar terceiro terceiro =begin
= File
config.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
require 'yaml'
module PDFWalker
class Walker < Window
class Config
DEFAULT_CONFIG_FILE = "#{File.expand_path("~")}/.pdfwalker.conf.yml"
DEFAULT_CONFIG =
{
"Debug" =>
{
"Profiling" => false,
"ProfilingOutputDir" => "prof",
"Verbosity" => Parser::VERBOSE_INSANE,
"IgnoreFileHeader" => true
},
"UI" =>
{
"LastOpenedDocuments" => []
}
}
NLOG_RECENT_FILES = 5
def initialize(configfile = DEFAULT_CONFIG_FILE)
begin
@conf = YAML.load(File.open(configfile)) or DEFAULT_CONFIG
rescue Exception
ensure
@filename = configfile
set_missing_values
end
end
def last_opened_file(filepath)
@conf["UI"]['LastOpenedDocuments'].push(filepath).uniq!
@conf["UI"]['LastOpenedDocuments'].delete_at(0) while @conf["UI"]['LastOpenedDocuments'].size > NLOG_RECENT_FILES
save
end
def recent_files(n = NLOG_RECENT_FILES)
@conf["UI"]['LastOpenedDocuments'].last(n).reverse
end
def set_profiling(bool)
@conf["Debug"]['Profiling'] = bool
save
end
def profile?
@conf["Debug"]['Profiling']
end
def profile_output_dir
@conf["Debug"]['ProfilingOutputDir']
end
def set_ignore_header(bool)
@conf["Debug"]['IgnoreFileHeader'] = bool
save
end
def ignore_header?
@conf["Debug"]['IgnoreFileHeader']
end
def set_verbosity(level)
@conf["Debug"]['Verbosity'] = level
save
end
def verbosity
@conf["Debug"]['Verbosity']
end
def save
File.open(@filename, "w").write(@conf.to_yaml)
end
private
def set_missing_values
@conf ||= {}
DEFAULT_CONFIG.each_key do |cat|
@conf[cat] = {} unless @conf.include?(cat)
DEFAULT_CONFIG[cat].each_pair do |key, value|
@conf[cat][key] = value unless @conf[cat].include?(key)
end
end
end
end
end
end
origami-pdf-1.2.7/bin/gui/signing.rb 0000644 0001750 0001750 00000053331 12101464040 017445 0 ustar terceiro terceiro =begin
= File
signing.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
module PDFWalker
class Walker < Window
def display_signing_wizard
if @opened
SignWizard.new(self, @opened)
end
end
def display_usage_rights_wizard
if @opened
UsageRightsWizard.new(self, @opened)
end
end
class UsageRightsWizard < Assistant
def initialize(parent, pdf)
super()
@parent = parent
@pkey, @cert = nil, nil
create_intro_page
create_rights_selection_page
create_termination_page
signal_connect('delete_event') { self.destroy }
signal_connect('cancel') { self.destroy }
signal_connect('close') { self.destroy }
signal_connect('apply') {
rights = []
rights << UsageRights::Rights::DOCUMENT_FULLSAVE if @document_fullsave.active?
rights << UsageRights::Rights::ANNOTS_CREATE if @annots_create.active?
rights << UsageRights::Rights::ANNOTS_DELETE if @annots_delete.active?
rights << UsageRights::Rights::ANNOTS_MODIFY if @annots_modify.active?
rights << UsageRights::Rights::ANNOTS_COPY if @annots_copy.active?
rights << UsageRights::Rights::ANNOTS_IMPORT if @annots_import.active?
rights << UsageRights::Rights::ANNOTS_EXPORT if @annots_export.active?
rights << UsageRights::Rights::ANNOTS_ONLINE if @annots_online.active?
rights << UsageRights::Rights::ANNOTS_SUMMARYVIEW if @annots_sumview.active?
rights << UsageRights::Rights::FORM_FILLIN if @form_fillin.active?
rights << UsageRights::Rights::FORM_IMPORT if @form_import.active?
rights << UsageRights::Rights::FORM_EXPORT if @form_export.active?
rights << UsageRights::Rights::FORM_SUBMITSTANDALONE if @form_submit.active?
rights << UsageRights::Rights::FORM_SPAWNTEMPLATE if @form_spawntemplate.active?
rights << UsageRights::Rights::FORM_BARCODEPLAINTEXT if @form_barcode.active?
rights << UsageRights::Rights::FORM_ONLINE if @form_online.active?
rights << UsageRights::Rights::SIGNATURE_MODIFY if @signature_modify.active?
rights << UsageRights::Rights::EF_CREATE if @ef_create.active?
rights << UsageRights::Rights::EF_DELETE if @ef_delete.active?
rights << UsageRights::Rights::EF_MODIFY if @ef_modify.active?
rights << UsageRights::Rights::EF_IMPORT if @ef_import.active?
begin
pdf.enable_usage_rights(*rights)
set_page_title(@lastpage, "Usage Rights have been enabled")
@msg_status.text = "Usage Rights have been enabled for the current document.\n You should consider saving it now."
@parent.reload
rescue Exception => e
puts e
puts e.backtrace
set_page_title(@lastpage, "Usage Rights have not been enabled")
@msg_status.text = "An error occured during the signature process."
end
}
show_all
end
private
def create_intro_page
intro = < e
puts e
puts e.backtrace
set_page_title(@lastpage, "Document has not been signed")
@msg_status.text = "An error occured during the signature process."
end
}
show_all
end
private
def create_intro_page
intro = < e
puts e.backtrace
error = MessageDialog.new(@parent,
Dialog::MODAL,
Gtk::MessageDialog::ERROR,
Gtk::MessageDialog::BUTTONS_CLOSE,
"Error loading file '#{File.basename(dialog.filename)}'")
error.run
error.destroy
@pkey, @cert, @ca = nil, nil, []
@p12filename.text = ""
set_page_complete(page, false)
end
end
dialog.destroy
end
vbox = VBox.new(false, 5)
hbox = HBox.new(false, 5)
vbox.pack_start(hbox, true, false, 10)
@p12filename = Entry.new.set_editable(false).set_sensitive(false)
choosebtn = Button.new(Gtk::Stock::OPEN)
choosebtn.signal_connect('clicked') { open_file_dialog(vbox) }
hbox.pack_start(@p12filename, true, true, 5)
hbox.pack_start(choosebtn, false, false, 5)
append_page(vbox)
set_page_title(vbox, "Import a PKCS12 container")
set_page_type(vbox, Assistant::PAGE_CONTENT)
end
def create_keypair_import_page
def open_pkey_dialog(page)
dialog = FileChooserDialog.new("Choose a private RSA key",
@parent,
FileChooser::ACTION_OPEN,
nil,
[Stock::CANCEL, Dialog::RESPONSE_CANCEL],
[Stock::OPEN, Dialog::RESPONSE_ACCEPT])
filter = FileFilter.new
filter.add_pattern("*.key")
filter.add_pattern("*.pem")
filter.add_pattern("*.der")
dialog.set_filter(filter)
if dialog.run == Dialog::RESPONSE_ACCEPT
begin
@pkey = OpenSSL::PKey::RSA.new(File.open(dialog.filename, 'r').binmode.read)
@pkeyfilename.set_text(dialog.filename)
if @cert then set_page_complete(page, true) end
rescue Exception => e
puts e.backtrace
error = MessageDialog.new(@parent,
Dialog::MODAL,
Gtk::MessageDialog::ERROR,
Gtk::MessageDialog::BUTTONS_CLOSE,
"Error loading file '#{File.basename(dialog.filename)}'")
error.run
error.destroy
@pkey = nil
@pkeyfilename.text = ""
set_page_complete(page, false)
ensure
@ca = [] # Shall be added to the GUI
end
end
dialog.destroy
end
def open_cert_dialog(page)
dialog = FileChooserDialog.new("Choose a private RSA key",
@parent,
FileChooser::ACTION_OPEN,
nil,
[Stock::CANCEL, Dialog::RESPONSE_CANCEL],
[Stock::OPEN, Dialog::RESPONSE_ACCEPT])
filter = FileFilter.new
filter.add_pattern("*.crt")
filter.add_pattern("*.cer")
filter.add_pattern("*.pem")
filter.add_pattern("*.der")
dialog.set_filter(filter)
if dialog.run == Dialog::RESPONSE_ACCEPT
begin
@cert = OpenSSL::X509::Certificate.new(File.open(dialog.filename, 'r').binmode.read)
@certfilename.set_text(dialog.filename)
if @pkey then set_page_complete(page, true) end
rescue Exception => e
puts e.backtrace
error = MessageDialog.new(@parent,
Dialog::MODAL,
Gtk::MessageDialog::ERROR,
Gtk::MessageDialog::BUTTONS_CLOSE,
"Error loading file '#{File.basename(dialog.filename)}'")
error.run
error.destroy
@cert = nil
@certfilename.text = ""
set_page_complete(page, false)
ensure
@ca = [] # Shall be added to the GUI
end
end
dialog.destroy
end
labels =
[
[ "Private RSA key:", @pkeyfilename = Entry.new, pkeychoosebtn = Button.new(Gtk::Stock::OPEN) ],
[ "Public certificate:", @certfilename = Entry.new, certchoosebtn = Button.new(Gtk::Stock::OPEN) ]
]
row = 0
table = Table.new(2, 3)
labels.each do |lbl, entry, btn|
entry.editable = entry.sensitive = false
table.attach(Label.new(lbl).set_alignment(1,0), 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
table.attach(entry, 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
table.attach(btn, 2, 3, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
row = row.succ
end
pkeychoosebtn.signal_connect('clicked') { open_pkey_dialog(table) }
certchoosebtn.signal_connect('clicked') { open_cert_dialog(table) }
append_page(table)
set_page_title(table, "Import a public/private key pair")
set_page_type(table, Assistant::PAGE_CONTENT)
end
def create_signature_info_page
vbox = VBox.new(false, 5)
lbl = Label.new("Here are a few optional information you can add with your signature.")
vbox.pack_start(lbl, true, true, 0)
labels =
[
[ "Location:", @location = Entry.new ],
[ "Contact:", @email = Entry.new ],
[ "Reason:", @reason = Entry.new ]
]
row = 0
table = Table.new(4, 3)
labels.each do |label|
table.attach(Label.new(label[0]).set_alignment(1,0), 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
table.attach(label[1], 1, 2, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 4, 4)
row = row.succ
end
vbox.pack_start(table, true, true, 0)
append_page(vbox)
set_page_title(vbox, "Fill in signature details")
set_page_type(vbox, Assistant::PAGE_CONFIRM)
set_page_complete(vbox, true)
end
def create_termination_page
@lastpage = VBox.new(false, 5)
@msg_status = Label.new
@lastpage.pack_start(@msg_status, true, true, 0)
append_page(@lastpage)
set_page_title(@lastpage, "Document has not been signed")
set_page_type(@lastpage, Assistant::PAGE_SUMMARY)
end
end
end
end
origami-pdf-1.2.7/bin/gui/imgview.rb 0000644 0001750 0001750 00000003304 12130621136 017454 0 ustar terceiro terceiro =begin
= File
imgview.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
module PDFWalker
class ImgViewer < Window
attr_reader :image
def initialize
super()
set_title "Image view"
set_decorated false
set_resizable false
add_events(Gdk::Event::KEY_RELEASE_MASK)
signal_connect('key_release_event') { |w, event|
destroy if event.keyval == Gdk::Keyval::GDK_Escape
}
end
def show_raw_img(data, w, h, bpc, bpr)
set_default_size w,h
pixbuf = Gdk::Pixbuf.new data,
Gdk::Pixbuf::ColorSpace::RGB, false, bpc,
w, h,
bpr
@image = Gtk::Image.new(pixbuf)
add @image
show_all
end
def show_compressed_img(data)
loader = Gdk::PixbufLoader.new
loader.last_write data
pixbuf = loader.pixbuf
set_default_size pixbuf.width, pixbuf.height
@image = Gtk::Image.new(pixbuf)
add @image
show_all
end
end
end
origami-pdf-1.2.7/bin/gui/hexview.rb 0000644 0001750 0001750 00000003447 12101464040 017471 0 ustar terceiro terceiro =begin
= File
hexview.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
require 'gui/gtkhex'
module PDFWalker
class Walker < Window
private
def create_hexview
@hexview = DumpView.new(self)
end
class DumpView < ScrolledWindow
def initialize(parent)
@parent = parent
super()
set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
@current_obj = nil
@view = HexEditor.new
@view.show_offsets(true)
add_with_viewport @view
end
def clear
@view.set_data ''
end
def load(object)
return if @current_obj.equal?(object)
begin
self.clear
case object
when Origami::Stream
@view.set_data(object.data)
when Origami::String
@view.set_data(object.value)
end
@current_obj = object
rescue Exception => e
@parent.error("An error occured while loading this object.\n#{e} (#{e.class})")
end
end
end
end
end
origami-pdf-1.2.7/bin/gui/about.rb 0000644 0001750 0001750 00000002477 12101464040 017126 0 ustar terceiro terceiro =begin
= File
about.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
module PDFWalker
class Walker < Window
def about
AboutDialog.show(self,
{
:name => "PDF Walker",
:program_name => "PDF Walker",
:version => Origami::VERSION,
:copyright => "Copyright (C) 2010\nGuillaume Delugre \nAll right reserved.",
:comments => "A graphical PDF parser front-end",
:license => File.read("#{File.dirname(__FILE__)}/COPYING")
})
end
end
end
origami-pdf-1.2.7/bin/gui/COPYING 0000644 0001750 0001750 00000104513 11654506427 016536 0 ustar terceiro terceiro GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
origami-pdf-1.2.7/bin/gui/menu.rb 0000644 0001750 0001750 00000033630 12130767505 016771 0 ustar terceiro terceiro =begin
= File
menu.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
module PDFWalker
module Popable
@@menus = Hash.new([])
@@menus[:"PDF File"] =
[
{
:Name => Stock::SAVE_AS,
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
viewer.parent.save
}
},
{
:Name => "Serialize",
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
viewer.parent.serialize
}
},
{
:Name => :"---"
},
{
:Name => Stock::PROPERTIES,
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
viewer.parent.display_file_properties
}
},
{
:Name => :"---"
},
{
:Name => Stock::CLOSE,
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
viewer.parent.close
}
}
]
@@menus[:Reference] =
[
{
:Name => Stock::JUMP_TO,
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
viewer.row_activated(path, viewer.get_column(viewer.class::TEXTCOL))
}
}
]
@@menus[:Revision] =
[
{
:Name => "Save to this revision",
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
revstr = viewer.model.get_value(viewer.model.get_iter(path), viewer.class::TEXTCOL)
revstr.slice!(0, "Revision ".size)
revnum = revstr.to_i
dialog = Gtk::FileChooserDialog.new("Save PDF File",
viewer.parent,
Gtk::FileChooser::ACTION_SAVE,
nil,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
)
dialog.filter = FileFilter.new.add_pattern("*.pdf")
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
viewer.parent.opened.save_upto(revnum, dialog.filename)
end
dialog.destroy
}
}
]
@@menus[:Stream] =
[
{
:Name => "Dump encoded stream",
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
stream = viewer.model.get_value(viewer.model.get_iter(path), viewer.class::OBJCOL)
viewer.parent.save_data("Save stream to file", stream.rawdata)
}
},
{
:Name => "Dump decoded stream",
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
stream = viewer.model.get_value(viewer.model.get_iter(path), viewer.class::OBJCOL)
viewer.parent.save_data("Save stream to file", stream.data)
}
}
]
@@menus[:String] =
[
{
:Name => "Dump string",
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
string = viewer.model.get_value(viewer.model.get_iter(path), viewer.class::OBJCOL)
viewer.parent.save_data("Save string to file", string.value)
}
}
]
@@menus[:Image] = @@menus[:Stream] +
[
{
:Name => :"---"
},
{
:Name => "View image",
:Sensitive => true,
:Callback => lambda { |widget, viewer, path|
stm = viewer.model.get_value(viewer.model.get_iter(path), viewer.class::OBJCOL)
w,h = stm.Width, stm.Height
colors =
case stm.ColorSpace
when :DeviceGray.to_o then 1
when :DeviceRGB.to_o then 3
when :DeviceCMYK.to_o then 4
else
1
end
bpc = stm.BitsPerComponent || 8
bpr = (w * colors * bpc + 7) >> 3
data = stm.data
begin
imgview = ImgViewer.new
if stm.Filter == :DCTDecode or (stm.Filter.is_a?(Array) and stm.Filter[0] == :DCTDecode)
imgview.show_compressed_img data
else
imgview.show_raw_img data, w, h, bpc, bpr
end
rescue Exception => e
viewer.parent.error("#{e.class}: #{e.message}")
end
}
}
]
def popup_menu(obj, event, path)
menu = Menu.new
type = if obj.is_a?(Origami::Object)
if obj.is_a?(Graphics::ImageXObject)
:Image
else
obj.native_type.to_s.split("::").last.to_sym
end
else case obj
when Origami::PDF
:"PDF File"
when Origami::PDF::Revision, Origami::Adobe::PPKLite::Revision
:Revision
when ::Array
:Body
when Origami::PDF::Header, Origami::Adobe::PPKLite::Header
:Header
when Origami::Trailer
:Trailer
when Origami::XRef::Section
:XRefSection
when Origami::XRef::Subsection
:XRefSubsection
when Origami::XRef, Origami::XRefToCompressedObj
:XRef
else
:Unknown
end
end
title = obj.is_a?(Origami::Object) ? "Object : " : ""
title << type.to_s
menu.append(MenuItem.new(title).set_sensitive(false).modify_text(Gtk::STATE_INSENSITIVE, Gdk::Color.new(255,0,255)))
if obj.is_a?(Origami::Object)
if obj.is_indirect?
menu.append(MenuItem.new("Number : #{obj.no}; Generation : #{obj.generation}").set_sensitive(false))
menu.append(MenuItem.new("File offset : #{obj.file_offset}").set_sensitive(false))
xrefsproc = lambda { |widget,viewer,path|
ref = viewer.model.get_value(viewer.model.get_iter(path), viewer.class::OBJCOL)
viewer.parent.show_xrefs(ref)
}
getxrefs = MenuItem.new("Search references to this object").set_sensitive(true)
getxrefs.signal_connect("activate", self, path, &xrefsproc)
menu.append(getxrefs)
elsif not obj.parent.nil?
gotoproc = lambda { |widget,viewer,path|
dest = viewer.model.get_value(viewer.model.get_iter(path), viewer.class::OBJCOL).parent
viewer.goto(dest)
}
gotoparent = MenuItem.new("Goto Parent Object").set_sensitive(true)
gotoparent.signal_connect("activate", self, path, &gotoproc)
menu.append(gotoparent)
end
end
items = @@menus[type]
menu.append(SeparatorMenuItem.new) if not items.empty?
items.each { |item|
if item[:Name] == :"---"
entry = SeparatorMenuItem.new
else
if item[:Name].is_a?(String)
entry = MenuItem.new(item[:Name])
else entry = ImageMenuItem.new(item[:Name])
end
entry.set_sensitive(item[:Sensitive])
entry.signal_connect("activate", self, path, &item[:Callback])
end
menu.append(entry)
}
menu.show_all
menu.popup(nil, nil, event.button, event.time)
end
end
class Walker < Window
private
def create_menus
AccelMap.add_entry("/File/Open", Gdk::Keyval::GDK_O, Gdk::Window::CONTROL_MASK)
AccelMap.add_entry("/File/Refresh", Gdk::Keyval::GDK_R, Gdk::Window::CONTROL_MASK)
AccelMap.add_entry("/File/Close", Gdk::Keyval::GDK_W, Gdk::Window::CONTROL_MASK)
AccelMap.add_entry("/File/Save", Gdk::Keyval::GDK_S, Gdk::Window::CONTROL_MASK)
AccelMap.add_entry("/File/Quit", Gdk::Keyval::GDK_Q, Gdk::Window::CONTROL_MASK)
AccelMap.add_entry("/Document/Search", Gdk::Keyval::GDK_F, Gdk::Window::CONTROL_MASK)
@menu = MenuBar.new
####################################################
file_ag = Gtk::AccelGroup.new
@file_menu = Menu.new.set_accel_group(file_ag).set_accel_path("/File")
add_accel_group(file_ag)
@file_menu_open = ImageMenuItem.new(Stock::OPEN).set_accel_path("/File/Open")
@file_menu_recent = MenuItem.new("Last opened")
@file_menu_deserialize = MenuItem.new("Deserialize")
@file_menu_refresh = ImageMenuItem.new(Stock::REFRESH).set_sensitive(false).set_accel_path("/File/Refresh")
@file_menu_close = ImageMenuItem.new(Stock::CLOSE).set_sensitive(false).set_accel_path("/File/Close")
@file_menu_saveas = ImageMenuItem.new(Stock::SAVE_AS).set_sensitive(false)
@file_menu_serialize = MenuItem.new("Serialize").set_sensitive(false)
@file_menu_exit = ImageMenuItem.new(Stock::QUIT).set_accel_path("/File/Quit")
@export_menu = Menu.new
@export_pdf_menu = MenuItem.new("As reassembled PDF").set_accel_path("/File/Save")
@export_graph_menu = MenuItem.new("As GraphViz dot file")
@export_graphml_menu = MenuItem.new("As GraphML file")
@export_pdf_menu.signal_connect('activate') do save end
@export_graph_menu.signal_connect('activate') do save_dot end
@export_graphml_menu.signal_connect('activate') do save_graphml end
@export_menu.append(@export_pdf_menu)
@export_menu.append(@export_graph_menu)
@export_menu.append(@export_graphml_menu)
@file_menu_saveas.set_submenu(@export_menu)
@file_menu_open.signal_connect('activate') do open end
@file_menu_deserialize.signal_connect('activate') do deserialize end
@file_menu_refresh.signal_connect('activate') do open(@filename) end
@file_menu_close.signal_connect('activate') do close end
@file_menu_serialize.signal_connect('activate') do serialize end
@file_menu_exit.signal_connect('activate') do self.destroy end
update_recent_menu
@file_menu.append(@file_menu_open)
@file_menu.append(@file_menu_recent)
@file_menu.append(@file_menu_deserialize)
@file_menu.append(@file_menu_refresh)
@file_menu.append(@file_menu_close)
@file_menu.append(@file_menu_saveas)
@file_menu.append(@file_menu_serialize)
@file_menu.append(@file_menu_exit)
@menu.append(MenuItem.new('_File').set_submenu(@file_menu))
####################################################
doc_ag = Gtk::AccelGroup.new
@document_menu = Menu.new.set_accel_group(doc_ag)
add_accel_group(doc_ag)
@document_menu_search = ImageMenuItem.new(Stock::FIND).set_sensitive(false).set_accel_path("/Document/Search")
@document_menu_gotocatalog = MenuItem.new("Jump To Catalog").set_sensitive(false)
@document_menu_gotorev = MenuItem.new("Jump To Revision...").set_sensitive(false)
@document_menu_gotopage = MenuItem.new("Jump To Page...").set_sensitive(false)
@document_menu_gotoobj = MenuItem.new("Jump To Object...").set_sensitive(false)
@document_menu_properties = ImageMenuItem.new(Stock::PROPERTIES).set_sensitive(false)
@document_menu_sign = MenuItem.new("Sign the document").set_sensitive(false)
@document_menu_ur = MenuItem.new("Enable Usage Rights").set_sensitive(false)
@document_menu_search.signal_connect('activate') do search end
@document_menu_gotocatalog.signal_connect('activate') do goto_catalog end
@document_menu_gotoobj.signal_connect('activate') do goto_object end
@document_menu_properties.signal_connect('activate') do display_file_properties end
@document_menu_sign.signal_connect('activate') do display_signing_wizard end
@document_menu_ur.signal_connect('activate') do display_usage_rights_wizard end
@document_menu.append(@document_menu_search)
@document_menu.append(MenuItem.new)
@document_menu.append(@document_menu_gotocatalog)
@document_menu.append(@document_menu_gotorev)
@document_menu.append(@document_menu_gotopage)
@document_menu.append(@document_menu_gotoobj)
@document_menu.append(MenuItem.new)
@document_menu.append(@document_menu_sign)
@document_menu.append(@document_menu_ur)
@document_menu.append(@document_menu_properties)
@menu.append(MenuItem.new('_Document').set_submenu(@document_menu))
####################################################
@help_menu = Menu.new
@help_menu_profile = CheckMenuItem.new("Profiling (Debug purposes only)").set_active(@config.profile?)
@help_menu_profile.signal_connect('toggled') do @config.set_profiling(@help_menu_profile.active?) end
@help_menu_about = ImageMenuItem.new(Stock::ABOUT)
@help_menu_about.signal_connect('activate') do about end
@help_menu.append(@help_menu_profile)
@help_menu.append(@help_menu_about)
@menu.append(MenuItem.new('_Help').set_submenu(@help_menu))
####################################################
end
def update_recent_menu
@recent_menu = Menu.new
@config.recent_files.each { |file|
menu = MenuItem.new(file)
menu.signal_connect('activate') do open(file) end
@recent_menu.append(menu)
}
@file_menu_recent.set_submenu(@recent_menu)
@file_menu_recent.show_all
end
end
end
origami-pdf-1.2.7/bin/gui/textview.rb 0000644 0001750 0001750 00000005720 12101464040 017665 0 ustar terceiro terceiro =begin
= File
textview.rb
= Info
This file is part of PDF Walker, a graphical PDF file browser
Copyright (C) 2010 Guillaume Delugré
All right reserved.
PDF Walker is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PDF Walker is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with PDF Walker. If not, see .
=end
module PDFWalker
class Walker < Window
private
def create_objectview
@objectview = ObjectView.new(self)
end
class ObjectView < Notebook
attr_reader :parent
attr_reader :pdfpanel, :valuepanel
def initialize(parent)
@parent = parent
super()
@pdfbuffer = TextBuffer.new
@pdfview = TextView.new(@pdfbuffer).set_editable(false).set_cursor_visible(false).set_left_margin(5)
@pdfpanel = ScrolledWindow.new.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
@pdfpanel.add_with_viewport @pdfview
append_page(@pdfpanel, Label.new("PDF Code"))
@pdfbuffer.create_tag("Object",
:weight => Pango::WEIGHT_BOLD,
#:foreground => "darkblue",
:family => "Courier",
:scale => Pango::AttrScale::LARGE
)
end
def load(object)
begin
self.clear
pdftag = "Object"
if object.is_a?(Stream)
stm = "#{object.no} #{object.generation} obj\n"
stm << object.dictionary.to_s
#if object.rawdata.is_binary_data?
# stm << "stream\n[Binary data]\nendstream"
#else
# stm << "stream\n#{object.rawdata}endstream"
#end
@pdfbuffer.set_text(stm)
elsif not (object.is_a?(::Array) or object.is_a?(Array)) and
not object.is_a?(PDF) and not object.is_a?(Adobe::PPKLite) and
not object.is_a?(PDF::Revision) and not object.is_a?(Adobe::PPKLite::Revision) and
not object.is_a?(XRefToCompressedObj)
@pdfbuffer.set_text(object.to_s)
end
@pdfbuffer.apply_tag(pdftag, @pdfbuffer.start_iter, @pdfbuffer.end_iter)
rescue Exception => e
@parent.error("An error occured while loading this object.\n#{e} (#{e.class})")
end
end
def clear
@pdfbuffer.set_text("")
end
end
end
end
origami-pdf-1.2.7/bin/pdfmetadata 0000755 0001750 0001750 00000005455 11645333134 017115 0 ustar terceiro terceiro #!/usr/bin/env ruby
=begin
= Author:
Guillaume Delugré
= Info:
Prints out the metadata contained in a PDF document.
= License:
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
=end
begin
require 'origami'
rescue LoadError
ORIGAMIDIR = "#{File.dirname(__FILE__)}/../lib"
$: << ORIGAMIDIR
require 'origami'
end
include Origami
require 'optparse'
class OptParser
BANNER = <] [-i] [-x]
Prints out the metadata contained in a PDF document.
Bug reports or feature requests at: http://origami-pdf.googlecode.com/
Options:
USAGE
def self.parser(options)
OptionParser.new do |opts|
opts.banner = BANNER
opts.on("-i", "Extracts document info metadata") do |i|
options[:doc_info] = true
end
opts.on("-x", "Extracts XMP document metadata stream") do |i|
options[:doc_stream] = true
end
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
end
end
def self.parse(args)
options =
{
:output => STDOUT,
}
self.parser(options).parse!(args)
options
end
end
begin
@options = OptParser.parse(ARGV)
unless @options[:doc_info] or @options[:doc_stream]
@options[:doc_info] = @options[:doc_stream] = true
end
target = (ARGV.empty?) ? STDIN : ARGV.shift
params =
{
:verbosity => Parser::VERBOSE_QUIET,
}
pdf = PDF.read(target, params)
if @options[:doc_info]
if pdf.has_document_info?
Console.colorprint "[*] Document information dictionary:\n", Console::Colors::MAGENTA
docinfo = pdf.get_document_info
docinfo.each_pair do |name, item|
Console.colorprint name.value.to_s.ljust(20, ' '), Console::Colors::GREEN
puts ": #{item.solve.value}"
end
puts
end
end
if @options[:doc_stream]
if pdf.has_metadata?
Console.colorprint "[*] Metadata stream:\n", Console::Colors::MAGENTA
metadata = pdf.get_metadata
metadata.each_pair do |name, item|
Console.colorprint name.ljust(20, ' '), Console::Colors::GREEN
puts ": #{item}"
end
end
end
rescue SystemExit
rescue Exception => e
STDERR.puts "#{e.class}: #{e.message}"
exit 1
end
origami-pdf-1.2.7/bin/pdfwalker 0000755 0001750 0001750 00000000164 11756461012 016612 0 ustar terceiro terceiro #!/usr/bin/env ruby
$:.unshift "#{File.dirname(__FILE__)}"
require 'gui/walker'
PDFWalker::Walker.start(ARGV[0])
origami-pdf-1.2.7/bin/pdf2ruby 0000755 0001750 0001750 00000021210 12200424503 016350 0 ustar terceiro terceiro #!/usr/bin/env ruby
=begin
= Info
Convert a PDF document to an Origami script.
Experimental.
= License:
Origami is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Origami is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Origami. If not, see .
= Author
Guillaume Delugré
=end
require 'optparse'
require 'fileutils'
begin
ORIGAMIDIR = "#{File.dirname(__FILE__)}/../lib"
require 'origami'
rescue LoadError
$: << ORIGAMIDIR
require 'origami'
end
include Origami
@var_hash = {}
@code_hash = {}
@obj_route = []
@current_idx = nil
class OptParser
def self.parse(args)
options = {}
options[:verbose] =
options[:xstreams] = false
opts = OptionParser.new do |opts|
opts.banner = <
Convert a PDF document to an Origami script (experimental).
Options:
BANNER
opts.on("-v", "--verbose", "Verbose mode") do
options[:verbose] = true
end
opts.on("-x", "--extract-streams", "Extract PDF streams to separate files") do
options[:xstreams] = true
end
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
end
opts.parse!(args)
options
end
end
@options = OptParser.parse(ARGV)
if ARGV.empty?
STDERR.puts "Error: No filename was specified. #{$0} --help for details."
exit 1
else
TARGET = ARGV.shift
end
Origami::OPTIONS[:enable_type_guessing] = Origami::OPTIONS[:enable_type_propagation] = true
TARGET_DIR = File.basename(TARGET, '.pdf')
TARGET_FILE = "#{TARGET_DIR}/#{TARGET_DIR}.rb"
STREAM_DIR = "streams"
def objectToRuby(obj, inclevel = 0, internalname = nil, do_convert = false)
code = ""
code <<
case obj
when Origami::Null
"Null.new"
when Origami::Boolean, Origami::Number
obj.value.to_s
when Origami::String
"'#{obj.value.gsub("'","\\\\'")}'"
when Origami::Dictionary
customclass = nil
if obj.class != Origami::Dictionary
p = (obj.class == Origami::Encoding) ? 0 : 1
customclass = obj.class.to_s.split('::')[p..-1].join('::') # strip Origami prefix if there is no collision
end
dictionaryToRuby(obj, inclevel, internalname, customclass)
when Origami::Array
arrayToRuby(obj, inclevel, internalname)
when Origami::Stream
streamToRuby(obj, internalname)
when Origami::Name
nameToRuby(obj)
when Origami::Reference
referenceToRuby(obj, internalname)
else
raise RuntimeError, "Unknown object type: #{obj.class}"
end
case obj
when Origami::String, Origami::Dictionary, Origami::Array, Origami::Name
code << ".to_o" if do_convert
end
code
end
def referenceToRuby(ref, internalname)
varname = @var_hash[ref]
if varname.nil?
"nil"
elsif @obj_route[0..@current_idx].include?(varname)
@code_hash[varname] ||= {}
@code_hash[varname][:afterDecl] ||= []
@code_hash[varname][:afterDecl] << "#{internalname} = #{varname}"#.to_o.set_indirect(true)"
"nil"
else
@obj_route.push(varname) unless @obj_route.include?(varname)
varname
end
end
def nameToRuby(name)
code = ':'
valid = (name.value.to_s =~ /[+.:-]/).nil?
code << '"' unless valid
code << name.value.to_s
code << '"' unless valid
code
end
def arrayToRuby(arr, inclevel, internalname)
i = 0
code = "\n" + " " * inclevel + "["
arr.each do |obj|
subintname = "#{internalname}[#{i}]"
code << "#{objectToRuby(obj, inclevel + 1, subintname)}"
code << ", " unless i == arr.length - 1
i = i + 1
end
code << "]"
code
end
def dictionaryToRuby(dict, inclevel, internalname, customtype = nil)
i = 0
code = "\n" + " " * inclevel
if customtype
code << "#{customtype}.new(#{dictionaryToHashMap(dict, inclevel, internalname)}"
code << " " * inclevel + ")"
else
code << "{\n"
dict.each_pair do |key, val|
rubyname = nameToRuby(key)
subintname = "#{internalname}[#{rubyname}]"
if val.is_a?(Origami::Reference) and @var_hash[val] and @var_hash[val][0,3] == "obj"
oldname = @var_hash[val]
newname = (key.value.to_s.downcase + "_" + @var_hash[val][4..-1]).gsub('.','_')
if not @obj_route.include?(oldname)
@var_hash[val] = newname
@code_hash[newname] = @code_hash[oldname]
@code_hash.delete(oldname)
end
end
code << " " * (inclevel + 1) +
"#{rubyname} => #{objectToRuby(val, inclevel + 2, subintname)}"
code << ", " unless i == dict.length - 1
i = i + 1
code << "\n"
end
code << " " * inclevel + "}"
end
code
end
def dictionaryToHashMap(dict, inclevel, internalname)
i = 0
code = "\n"
dict.each_pair do |key, val|
rubyname = nameToRuby(key)
subintname = "#{internalname}[#{rubyname}]"
if val.is_a?(Origami::Reference) and @var_hash[val] and @var_hash[val][0,3] == "obj"
oldname = @var_hash[val]
newname = (key.value.to_s.downcase + "_" + @var_hash[val][4..-1]).gsub('.','_')
if not @obj_route.include?(oldname)
@var_hash[val] = newname
@code_hash[newname] = @code_hash[oldname]
@code_hash.delete(oldname)
end
end
code << " " * (inclevel + 1) +
"#{rubyname} => #{objectToRuby(val, inclevel + 2, subintname)}"
code << ", " unless i == dict.length - 1
i = i + 1
code << "\n"
end
code
end
def streamToRuby(stm, internalname)
dict = stm.dictionary.dup.delete_if{|k,v| k == :Length or k == :Filter}
code = "Stream.new("
if @options[:xstreams]
stmdir = "#{TARGET_DIR}/#{STREAM_DIR}"
Dir::mkdir(stmdir) unless File.directory? stmdir
stmfile = "#{stmdir}/stm_#{stm.reference.refno}.data"
File.open(stmfile, "w") do |stmfd|
stmfd.write stm.data
end
code << "File.read('#{STREAM_DIR}/stm_#{stm.reference.refno}.data')"
else
code << stm.data.inspect
end
code << ", #{dictionaryToHashMap(dict, 1, internalname)}" unless dict.empty?
code << ")"
if stm.dictionary.has_key? :Filter
code << ".setFilter(#{objectToRuby(stm.Filter, 1, internalname)})"
end
code
end
Console.colorprint "[*] ", Console::Colors::RED
puts "Loading document '#{TARGET}'"
verbosity = @options[:verbose] ? Parser::VERBOSE_INSANE : Parser::VERBOSE_QUIET
target = PDF.read(TARGET, :verbosity => verbosity)
Console.colorprint "[*] ", Console::Colors::RED
puts "Document successfully loaded into Origami"
Dir::mkdir(TARGET_DIR) unless File.directory? TARGET_DIR
fd = File.open(TARGET_FILE, 'w', 0700)
DOCREF = "pdf"
ORIGAMI_PATH = ORIGAMIDIR[0,1] == '/' ?
ORIGAMIDIR :
"../#{ORIGAMIDIR}"
fd.puts <