Class: PDF::Toolkit
- Inherits:
-
Object
- Object
- PDF::Toolkit
- Extended by:
- Forwardable
- Includes:
- Enumerable
- Defined in:
- lib/pdf/toolkit.rb
Overview
PDF::Toolkit can be used as a simple class, or derived from and tweaked. The following two examples have identical results.
my_pdf = PDF::Toolkit.open("somefile.pdf")
my_pdf.updated_at = Time.now # ModDate
my_pdf["SomeAttribute"] = "Some value"
my_pdf.save!
class MyDocument < PDF::Toolkit
info_accessor :some_attribute
def before_save
self.updated_at = Time.now
end
end
my_pdf = MyDocument.open("somefile.pdf")
my_pdf.some_attribute = "Some value"
my_pdf.save!
Note the use of a before_save callback in the second example. This is the only supported callback unless you use the experimental #loot_active_record class method.
Requirements
PDF::Toolkit requires pdftk, which is available from www.accesspdf.com/pdftk. For full functionality, also install xpdf from www.foolabs.com/xpdf. ActiveSupport (from Ruby on Rails) is also required but this dependency may be removed in the future.
Limitations
Timestamps are written in UTF-16 by pdftk, which is not appropriately handled by pdfinfo.
pdftk requires the owner password, even for simply querying the document.
Defined Under Namespace
Classes: Error, ExecutionError, FileNotSaved
Constant Summary collapse
- PDF_TOOLKIT_VERSION =
"0.5.0"
Instance Attribute Summary collapse
-
#owner_password ⇒ Object
writeonly
Sets the attribute owner_password.
-
#pdf_ids ⇒ Object
readonly
Returns the value of attribute pdf_ids.
-
#permissions ⇒ Object
readonly
Returns the value of attribute permissions.
-
#user_password ⇒ Object
writeonly
Sets the attribute user_password.
Class Method Summary collapse
-
.human_attribute_name(arg) ⇒ Object
:nodoc:.
-
.info_accessor(accessor_name, info_key = nil) ⇒ Object
Add an accessor for a key.
-
.loot_active_record ⇒ Object
This method will
requireandincludevalidations, callbacks, and timestamping fromActiveRecord. -
.open(filename, input_password = nil) ⇒ Object
Create a new object associated with
filenameand read in the associated metadata. -
.pdftk(*args, &block) ⇒ Object
Invoke
pdftkwith the given arguments, plusdont_ask. -
.pdftotext(file, outfile = nil, &block) ⇒ Object
Invoke
pdftotext.
Instance Method Summary collapse
-
#[](key) ⇒ Object
Read a metadata attribute.
-
#[]=(key, value) ⇒ Object
Write a metadata attribute.
-
#delete(key) ⇒ Object
Remove the metadata attribute from the file.
-
#delete_if(&block) ⇒ Object
Remove metadata if the given block returns false.
-
#has_key?(value) ⇒ Boolean
(also: #key?)
True if the file has the given metadata attribute.
-
#initialize(filename, input_password = nil) ⇒ Toolkit
constructor
Like
open, only the attributes are lazily loaded. -
#merge!(hash) ⇒ Object
Add the specified attributes to the file.
-
#new_record? ⇒ Boolean
:nodoc:.
- #page_count ⇒ Object (also: #pages)
-
#path ⇒ Object
Path to the file.
-
#reject!(&block) ⇒ Object
Like
delete_if, only nil is returned if no attributes were removed. -
#reload ⇒ Object
Reload (or load) the file’s metadata.
-
#save ⇒ Object
Commit changes to the PDF.
-
#save! ⇒ Object
Like
save, only raise an exception if the operation fails. -
#save_as(filename) ⇒ Object
Save to a different file.
-
#save_as!(filename) ⇒ Object
Save to a different file.
-
#to_hash ⇒ Object
Create a hash from the file’s metadata.
-
#to_s ⇒ Object
:nodoc:.
-
#to_text(filename = nil, &block) ⇒ Object
Invoke
pdftotexton the file and return anIOobject for reading the results. - #update_attribute(key, value) ⇒ Object
-
#version ⇒ Object
Retrieve the file’s version as a symbol.
Constructor Details
#initialize(filename, input_password = nil) ⇒ Toolkit
Like open, only the attributes are lazily loaded. Under most circumstances, open is preferred.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/pdf/toolkit.rb', line 233 def initialize(filename,input_password = nil) @filename = if filename.respond_to?(:to_str) filename.to_str elsif filename.kind_of?(self.class) filename.instance_variable_get("@filename") elsif filename.respond_to?(:path) filename.path else filename end @input_password = input_password || default_input_password @owner_password = default_owner_password @user_password = default_user_password @permissions = || [] @new_info = {} callback(:after_initialize) if respond_to?(:after_initialize) && respond_to?(:callback) # reload end |
Instance Attribute Details
#owner_password=(value) ⇒ Object (writeonly)
Sets the attribute owner_password
253 254 255 |
# File 'lib/pdf/toolkit.rb', line 253 def owner_password=(value) @owner_password = value end |
#pdf_ids ⇒ Object (readonly)
Returns the value of attribute pdf_ids.
252 253 254 |
# File 'lib/pdf/toolkit.rb', line 252 def pdf_ids @pdf_ids end |
#permissions ⇒ Object (readonly)
Returns the value of attribute permissions.
252 253 254 |
# File 'lib/pdf/toolkit.rb', line 252 def @permissions end |
#user_password=(value) ⇒ Object (writeonly)
Sets the attribute user_password
253 254 255 |
# File 'lib/pdf/toolkit.rb', line 253 def user_password=(value) @user_password = value end |
Class Method Details
.human_attribute_name(arg) ⇒ Object
:nodoc:
160 161 162 |
# File 'lib/pdf/toolkit.rb', line 160 def human_attribute_name(arg) #:nodoc: defined? ActiveRecord::Base ? ActiveRecord::Base.human_attribute_name(arg) : arg.gsub(/_/,' ') end |
.info_accessor(accessor_name, info_key = nil) ⇒ Object
Add an accessor for a key. If the key is omitted, defaults to a camelized version of the accessor (foo_bar becomes FooBar). The example below illustrates the defaults.
class MyDocument < PDF::Toolkit
info_accessor :created_at, "CreationDate"
info_accessor :updated_at, "ModDate"
info_accessor :author
[:subject, :title, :keywords, :producer, :creator].each do |key|
info_accessor key
end
end
MyDocument.open("document.pdf").created_at
100 101 102 103 104 105 106 107 108 109 |
# File 'lib/pdf/toolkit.rb', line 100 def info_accessor(accessor_name, info_key = nil) info_key ||= camelize_key(accessor_name) read_inheritable_attribute(:info_accessors)[accessor_name] = info_key define_method accessor_name do self[info_key] end define_method "#{accessor_name}=" do |value| self[info_key] = value end end |
.loot_active_record ⇒ Object
This method will require and include validations, callbacks, and timestamping from ActiveRecord. Use at your own risk.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/pdf/toolkit.rb', line 135 def loot_active_record require 'active_support' require 'active_record' # require 'active_record/validations' # require 'active_record/callbacks' # require 'active_record/timestamp' unless defined? @@looted_active_record @@looted_active_record = true = (class <<self; self; end) alias_method :initialize_ar_hack, :initialize include ActiveRecord::Validations include ActiveRecord::Callbacks include ActiveRecord::Timestamp alias_method :initialize, :initialize_ar_hack cattr_accessor :record_timestamps # nil by default .send(:define_method,:default_timezone) do defined? ActiveRecord::Base ? ActiveRecord::Base.default_timezone : :local end end self end |
.open(filename, input_password = nil) ⇒ Object
225 226 227 228 229 |
# File 'lib/pdf/toolkit.rb', line 225 def self.open(filename,input_password = nil) object = new(filename,input_password) object.reload object end |
.pdftk(*args, &block) ⇒ Object
Invoke pdftk with the given arguments, plus dont_ask. If :mode or a block is given, IO::popen is called. Otherwise, Kernel#system is used.
result = PDF::Toolkit.pdftk(*%w(foo.pdf bar.pdf cat output baz.pdf))
io = PDF::Toolkit.pdftk("foo.pdf","dump_data","output","-",:mode => 'r')
PDF::Toolkit.pdftk("foo.pdf","dump_data","output","-") { |io| io.read }
118 119 120 121 122 123 124 |
# File 'lib/pdf/toolkit.rb', line 118 def pdftk(*args,&block) = args.last.is_a?(Hash) ? args.pop : {} args << "dont_ask" args << result = call_program(executables[:pdftk],*args,&block) return block_given? ? $?.success? : result end |
.pdftotext(file, outfile = nil, &block) ⇒ Object
Invoke pdftotext. If outfile is omitted, returns an IO object for the output.
128 129 130 131 |
# File 'lib/pdf/toolkit.rb', line 128 def pdftotext(file,outfile = nil,&block) call_program(executables[:pdftotext],file, outfile||"-",:mode => (outfile ? nil : 'r'),&block) end |
Instance Method Details
#[](key) ⇒ Object
Read a metadata attribute.
= my_pdf["Author"]
See info_accessor for an alternate syntax.
352 353 354 355 356 357 |
# File 'lib/pdf/toolkit.rb', line 352 def [](key) key = lookup_key(key) return @new_info[key.to_s] if @new_info.has_key?(key.to_s) ensure_loaded @info[key.to_s] end |
#[]=(key, value) ⇒ Object
Write a metadata attribute.
my_pdf["Author"] =
See info_accessor for an alternate syntax.
365 366 367 368 |
# File 'lib/pdf/toolkit.rb', line 365 def []=(key,value) key = lookup_key(key) @new_info[key.to_s] = value end |
#delete(key) ⇒ Object
Remove the metadata attribute from the file.
384 385 386 387 388 389 390 391 |
# File 'lib/pdf/toolkit.rb', line 384 def delete(key) key = lookup_key(key) if @info.has_key?(key) || !@pages @new_info[key] = nil else @new_info.delete(key) end end |
#delete_if(&block) ⇒ Object
Remove metadata if the given block returns false. The following would remove all timestamps.
my_pdf.delete_if {|key,value| value.kind_of?(Time)}
410 411 412 413 |
# File 'lib/pdf/toolkit.rb', line 410 def delete_if(&block) reject!(&block) self end |
#has_key?(value) ⇒ Boolean Also known as: key?
True if the file has the given metadata attribute.
376 377 378 379 380 |
# File 'lib/pdf/toolkit.rb', line 376 def has_key?(value) ensure_loaded value = lookup_key(value) (@info.has_key?(value) || @new_info.has_key?(value)) && !!(self[value]) end |
#merge!(hash) ⇒ Object
Add the specified attributes to the file. If symbols are given as keys, they are camelized.
my_pdf.merge!(“Author” => “Dave Thomas”, :title => “Programming Ruby”)
419 420 421 422 423 424 |
# File 'lib/pdf/toolkit.rb', line 419 def merge!(hash) hash.each do |k,v| @new_info[lookup_key(k)] = v end self end |
#new_record? ⇒ Boolean
:nodoc:
343 344 345 |
# File 'lib/pdf/toolkit.rb', line 343 def new_record? #:nodoc: !@new_filename.nil? end |
#page_count ⇒ Object Also known as: pages
255 256 257 258 |
# File 'lib/pdf/toolkit.rb', line 255 def page_count read_data unless @pages @pages end |
#path ⇒ Object
Path to the file.
263 264 265 |
# File 'lib/pdf/toolkit.rb', line 263 def path @new_filename || @filename end |
#reject!(&block) ⇒ Object
Like delete_if, only nil is returned if no attributes were removed.
394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/pdf/toolkit.rb', line 394 def reject!(&block) ensure_loaded ret = nil each do |key,value| if yield(key,value) ret = self delete(key) end end ret end |
#reload ⇒ Object
Reload (or load) the file’s metadata.
277 278 279 280 281 282 |
# File 'lib/pdf/toolkit.rb', line 277 def reload @new_info = {} read_data # run_callbacks_for(:after_load) self end |
#save ⇒ Object
Commit changes to the PDF. The return value is a boolean reflecting the success of the operation (This should always be true unless you’re utilizing #loot_active_record).
287 288 289 |
# File 'lib/pdf/toolkit.rb', line 287 def save create_or_update end |
#save! ⇒ Object
Like save, only raise an exception if the operation fails.
TODO: ensure no ActiveRecord::RecordInvalid errors make it through.
294 295 296 297 298 299 300 |
# File 'lib/pdf/toolkit.rb', line 294 def save! if save self else raise FileNotSaved end end |
#save_as(filename) ⇒ Object
Save to a different file. A new object is returned if the operation succeeded. Otherwise, nil is returned.
304 305 306 307 308 |
# File 'lib/pdf/toolkit.rb', line 304 def save_as(filename) dup.save_as!(filename) rescue FileNotSaved nil end |
#save_as!(filename) ⇒ Object
Save to a different file. The existing object is modified. An exception is raised if the operation fails.
312 313 314 315 316 317 318 |
# File 'lib/pdf/toolkit.rb', line 312 def save_as!(filename) @new_filename = filename save! self rescue ActiveRecord::RecordInvalid raise FileNotSaved end |
#to_hash ⇒ Object
Create a hash from the file’s metadata.
335 336 337 338 |
# File 'lib/pdf/toolkit.rb', line 335 def to_hash ensure_loaded @info.merge(@new_info).reject {|key,value| value.nil?} end |
#to_s ⇒ Object
:nodoc:
328 329 330 |
# File 'lib/pdf/toolkit.rb', line 328 def to_s #:nodoc: "#<#{self.class}:#{path}>" end |
#to_text(filename = nil, &block) ⇒ Object
Invoke pdftotext on the file and return an IO object for reading the results.
text = my_pdf.to_text.read
324 325 326 |
# File 'lib/pdf/toolkit.rb', line 324 def to_text(filename = nil,&block) self.class.send(:pdftotext,@filename,filename,&block) end |
#update_attribute(key, value) ⇒ Object
370 371 372 373 |
# File 'lib/pdf/toolkit.rb', line 370 def update_attribute(key,value) self[key] = value save end |
#version ⇒ Object
Retrieve the file’s version as a symbol.
my_pdf.version # => :"1.4"
270 271 272 273 274 |
# File 'lib/pdf/toolkit.rb', line 270 def version @version ||= File.open(@filename) do |io| io.read(8)[5..-1].to_sym end end |