pdf_utils 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "pdf_utils"
5
+ gemspec.summary = "PdfUtils abstracts a lot of well working UNIX tools for PDF files"
6
+ gemspec.description = <<-DESC
7
+ Requires xpdf, pdftk, swftools/pdf2swf and imagemagick.
8
+ You can check their functionality by running `$ rake check_system_dependencies´.
9
+ DESC
10
+ gemspec.email = "l.rieder@gmail.com"
11
+ gemspec.homepage = "http://github.com/Overbryd/pdf_utils"
12
+ gemspec.authors = ["Lukas Rieder", "Andreas Korth"]
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: gem install jeweler"
17
+ end
18
+
19
+ require 'spec/rake/spectask'
20
+
21
+ desc "Run all examples"
22
+ Spec::Rake::SpecTask.new('spec') do |t|
23
+ t.spec_opts = ['--colour', '--format specdoc']
24
+ t.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ task :check_system_dependencies do
28
+ load File.join(File.dirname(__FILE__), 'script', 'check_system_dependencies')
29
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,29 @@
1
+ require 'tmpdir'
2
+
3
+ module InTmpdir
4
+
5
+ private
6
+
7
+ def in_tmpdir(dirname='tmp')
8
+ dirname = tmp_file_name(dirname)
9
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
10
+ begin
11
+ yield(dirname)
12
+ ensure
13
+ FileUtils.rm_rf(dirname) if File.exists?(dirname)
14
+ end
15
+ end
16
+
17
+ def with_tmpfile(filename='tmp')
18
+ filename = tmp_file_name(filename)
19
+ begin
20
+ yield(filename)
21
+ ensure
22
+ FileUtils.rm_rf(filename) if File.exists?(filename)
23
+ end
24
+ end
25
+
26
+ def tmp_file_name(name='tmp')
27
+ File.join(Dir.tmpdir, [$$.to_s(16), object_id.to_s(16), name].join('_'))
28
+ end
29
+ end
@@ -0,0 +1,175 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'prawn'
4
+ require 'pdf/reader'
5
+
6
+ require 'in_tempdir'
7
+ require 'pdf_utils/info'
8
+ require 'pdf_utils/color'
9
+
10
+ module PdfUtils
11
+
12
+ class << self
13
+
14
+ include InTmpdir
15
+
16
+ def objects(filename, filter={})
17
+ uncompress(filename) do |uncompressed|
18
+ PDF::Hash.new(uncompressed).values.select do |obj|
19
+ if obj.kind_of?(Hash)
20
+ if obj.has_key?(:Contents) && obj[:Contents].kind_of?(String)
21
+ obj[:Contents] = Iconv.conv('utf-8', 'utf-16', obj[:Contents])
22
+ end
23
+ filter.detect {|k, v| obj[k] == v }
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ def annotations(filename)
30
+ objects(filename, :Type => :Annot)
31
+ end
32
+
33
+ def info(filename)
34
+ PdfUtils::Info.new(filename)
35
+ end
36
+
37
+ def toc(filename)
38
+ toc = []
39
+ if meta_data = run_command(:pdftk, "#{filename} dump_data")
40
+ entry = nil
41
+ meta_data.scan(/^Bookmark(\w+): (.*)$/) do |(key, value)|
42
+ case key
43
+ when "Title"
44
+ entry = [value]
45
+ when "Level"
46
+ entry << value.to_i
47
+ when "PageNumber"
48
+ if value.to_i > 0
49
+ entry << value.to_i
50
+ toc << entry
51
+ end
52
+ end
53
+ end
54
+ end
55
+ toc
56
+ end
57
+
58
+ def to_text(filename, target = nil)
59
+ run_command(:pdftotext, "-layout -enc UTF-8 #{filename} #{target || '-'}", !!target)
60
+ end
61
+
62
+ def to_swf(source, target)
63
+ run_command(:pdf2swf, "-T 9 -f -s transparent -s detectspaces #{source} -o #{target}")
64
+ end
65
+
66
+ def uncompress(source, target=nil, &block)
67
+ target ||= tmp_file_name("uncompress")
68
+ run_command(:pdftk, "#{source} output #{target} uncompress")
69
+ if block_given?
70
+ begin
71
+ yield(target)
72
+ ensure
73
+ FileUtils.rm(target)
74
+ end
75
+ end
76
+ end
77
+
78
+ def slice(source, from_page, to_page, target)
79
+ run_command(:pdftk, "#{source} cat #{from_page}-#{to_page} output #{target}")
80
+ end
81
+
82
+ def slice!(source, from_page, to_page)
83
+ target = tmp_file_name("slice")
84
+ slice(source, from_page, to_page, target)
85
+ FileUtils.mv(target, source)
86
+ end
87
+
88
+ def burst(source, target)
89
+ run_command(:pdftk, "#{source} burst output #{target}")
90
+ end
91
+
92
+ def cat(source, target)
93
+ run_command(:pdftk, "#{source} cat output #{target}")
94
+ end
95
+
96
+ def watermark(source, target, options = {}, &block)
97
+ raise ArgumentError.new("No block given") unless block_given?
98
+ options[:page_size] ||= info(source).page_dimensions
99
+ with_tmpfile("watermark") do |watermarked|
100
+ Prawn::Document.generate(watermarked, options, &block)
101
+ run_command(:pdftk, "#{source} background #{watermarked} output #{target}")
102
+ end
103
+ end
104
+
105
+ def watermark!(source, options = {}, &block)
106
+ target = tmp_file_name("watermark_merge")
107
+ watermark(source, target, options, &block)
108
+ FileUtils.mv(target, source)
109
+ end
110
+
111
+ def annotate(source, annotations, target, options = {})
112
+ options[:page_size] ||= info(source).page_dimensions
113
+ with_tmpfile("annotate") do |annotated|
114
+ Prawn::Document.generate(annotated, options) do |pdf|
115
+ annotations.each do |annotation|
116
+ pdf.annotate(annotation)
117
+ end
118
+ end
119
+ run_command(:pdftk, "#{annotated} background #{source} output #{target}")
120
+ end
121
+ end
122
+
123
+ def annotate!(source, annotations, options = {})
124
+ target = tmp_file_name("annotate_merge")
125
+ annotate(source, annotations, target, options)
126
+ FileUtils.mv(target, source)
127
+ end
128
+
129
+ THUMBNAIL_DEFAULTS = { :density => 150, :size => '50%', :format => 'jpg', :quality => 85, :target => nil, :page => 1 }.freeze
130
+
131
+ def thumbnail(source, options = {})
132
+ options.assert_valid_keys(THUMBNAIL_DEFAULTS.keys)
133
+
134
+ options = THUMBNAIL_DEFAULTS.merge(options)
135
+ target = options[:target] || source.sub(/(\.\w+)?$/, ".#{options[:format]}")
136
+ source = "#{source}[#{options[:page].to_i-1}]"
137
+
138
+ run_command(:convert, [
139
+ '-density' , options[:density],
140
+ source,
141
+ '-colorspace' , :rgb,
142
+ '-thumbnail' , options[:size],
143
+ '-quality' , options[:quality],
144
+ target
145
+ ].join(' '))
146
+ return target
147
+ end
148
+
149
+ SNAPSHOT_DEFAULTS = { :density => 150, :compress => 'JPEG', :quality => 85 }.freeze
150
+
151
+ def snapshot(source, target, options = {})
152
+ options.assert_valid_keys(SNAPSHOT_DEFAULTS.keys + [:page])
153
+ page_number = (options.delete(:page) || 1).to_i
154
+ source = "#{source}[#{page_number-1}]"
155
+ options = SNAPSHOT_DEFAULTS.merge(options).map{ |opt,val| "-#{opt} #{val}" } << "#{source} #{target}"
156
+ run_command(:convert, options)
157
+ end
158
+
159
+ def snapshot!(source, options = {})
160
+ snapshot(source, source, options)
161
+ end
162
+
163
+ def run_command(command, args, reroute_errors = true)
164
+ command = [command, args]
165
+ command << '2>&1' if reroute_errors
166
+ command = command.join(' ')
167
+ output = `#{command}`
168
+ if $?.success?
169
+ output
170
+ else
171
+ raise RuntimeError.new("Command failed: #{command}\n#{output}")
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,27 @@
1
+ module PdfUtils
2
+ class Color
3
+ def initialize(*args)
4
+ if args.first.is_a?(String)
5
+ @r, @g, @b = args.first.scan(/../).map{ |rgb| rgb.to_i(16) }
6
+ elsif args.size == 3
7
+ if args.any? {|arg| arg.kind_of?(Float) }
8
+ @r, @g, @b = args.map {|v| (v * 255).to_i }
9
+ else
10
+ @r, @g, @b = args
11
+ end
12
+ end
13
+ end
14
+
15
+ def to_pdf
16
+ to_rgb.map{ |v| v / 255.0 }
17
+ end
18
+
19
+ def to_hex
20
+ to_rgb.inject(''){ |hex, v| hex << "%02x" % v }
21
+ end
22
+
23
+ def to_rgb
24
+ [@r, @g, @b]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,46 @@
1
+ module PdfUtils
2
+ class Info
3
+ attr_reader :pdf_version, :title, :author, :subject, :keywords, :creator, :producer, :tagged, :optimized, :encrypted
4
+ attr_reader :file_size, :mod_date, :creation_date, :pages, :page_size, :page_format
5
+ alias_method :tagged?, :tagged
6
+ alias_method :optimized?, :optimized
7
+ alias_method :encrypted?, :encrypted
8
+ alias_method :modification_date, :mod_date
9
+
10
+ def initialize(filename)
11
+ raise ArgumentError.new("File does not exist: #{filename}") unless File.exist?(filename)
12
+ info = {}
13
+ PdfUtils::run_command(:pdfinfo, filename).scan(/^([^:]+): +(.*?)?$/) do |property|
14
+ info.store(*property)
15
+ end
16
+ @pdf_version = info["PDF version"]
17
+ @title = info["Title"]
18
+ @subject = info["Subject"]
19
+ @keywords = info["Keywords"]
20
+ @author = info["Author"]
21
+ @creator = info["Creator"]
22
+ @producer = info["Producer"]
23
+ @pages = info["Pages"].to_i
24
+ @creation_date = Time.parse(info["CreationDate"]) rescue nil
25
+ @mod_date = Time.parse(info["ModDate"]) rescue nil
26
+ @page_size = info["Page size"] =~ /^([\d.]+) x ([\d.]+)/ && [$1.to_f, $2.to_f]
27
+ @page_format = info["Page size"] =~ /\(([^(]+)\)$/ && $1
28
+ @file_size = info["File size"] =~ /^(\d+) bytes$/ && $1.to_i
29
+ @optimized = info["Optimized"] == 'yes'
30
+ @tagged = info["Tagged"] == 'yes'
31
+ @encrypted = info["Encrypted"] == 'yes'
32
+ end
33
+
34
+ def page_dimensions
35
+ [page_width, page_height]
36
+ end
37
+
38
+ def page_width
39
+ @page_size[0]
40
+ end
41
+
42
+ def page_height
43
+ @page_size[1]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.join(File.dirname(__FILE__), '../lib'))
3
+ require 'pdf_utils'
4
+
5
+ tmpdir = File.join(Dir.tmpdir, "#{$$}-dependency_checks")
6
+ FileUtils.mkdir(tmpdir) rescue nil
7
+
8
+ root = File.join(File.dirname(__FILE__), '../')
9
+ checkfile = File.join(tmpdir, 'check.pdf')
10
+ FileUtils.cp(File.join(root, 'spec/fixtures/marketing.pdf'), checkfile)
11
+
12
+ def check(what, &block)
13
+ print "Checking #{what}... "
14
+ yield
15
+ if $?.success?
16
+ puts "passed."
17
+ else
18
+ puts "failed."
19
+ end
20
+ end
21
+
22
+ check 'pdf info' do
23
+ PdfUtils::info(checkfile)
24
+ end
25
+
26
+ check 'pdf table of contents' do
27
+ PdfUtils::toc(checkfile)
28
+ end
29
+
30
+ check 'pdf to text' do
31
+ PdfUtils::to_text(checkfile)
32
+ end
33
+
34
+ check 'pdf uncompress' do
35
+ PdfUtils::uncompress(checkfile)
36
+ end
37
+
38
+ check 'pdf to swf' do
39
+ PdfUtils::to_swf(checkfile, File.join(tmpdir, 'check.swf'))
40
+ end
41
+
42
+ check 'pdf slicing' do
43
+ PdfUtils::slice(checkfile, 1, 2, File.join(tmpdir, 'sliced.pdf'))
44
+ end
45
+
46
+ check 'pdf bursting' do
47
+ PdfUtils::burst(checkfile, File.join(tmpdir, 'burst-%04d.pdf'))
48
+ end
49
+
50
+ check 'pdf background' do
51
+ PdfUtils::watermark(checkfile, File.join(tmpdir, 'watermarked.pdf'), :margin => 0) do |page|
52
+ page.text 'I am watermarked'
53
+ end
54
+ end
55
+
56
+ check 'pdf to cover' do
57
+ PdfUtils::thumbnail(checkfile, :target => File.join(tmpdir, 'cover.jpg'))
58
+ end
59
+
60
+ check 'pdf to snapshot' do
61
+ PdfUtils::snapshot(checkfile, File.join(tmpdir, 'snapshot.pdf'))
62
+ end
Binary file
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe PdfUtils::Color do
4
+ it "should convert to pdf color" do
5
+ PdfUtils::Color.new(255, 255, 0).to_pdf.should eql([1.0, 1.0, 0.0])
6
+ end
7
+
8
+ it "should convert to hex color" do
9
+ PdfUtils::Color.new(0, 1.0, 0).to_hex.should eql('00ff00')
10
+ end
11
+
12
+ it "should convert to rgb values" do
13
+ PdfUtils::Color.new('ff0000').to_rgb.should eql([255, 0, 0])
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe PdfUtils::Info do
4
+ it "should extract meta information from the pdf" do
5
+ @info = PdfUtils::Info.new(fixture_file_path('marketing.pdf'))
6
+
7
+ @info.tagged .should eql(false)
8
+ @info.keywords .should eql("")
9
+ @info.page_size .should eql([595.0, 842.0])
10
+ @info.producer .should eql("Mac OS X 10.5.6 Quartz PDFContext")
11
+ @info.optimized .should eql(false)
12
+ @info.title .should eql("marketing")
13
+ @info.mod_date .should eql(Time.parse('2010/01/12 17:34:51 +0100'))
14
+ @info.creator .should eql("Preview")
15
+ @info.file_size .should eql(33179)
16
+ @info.pdf_version .should eql("1.4")
17
+ @info.creation_date .should eql(Time.parse('2009/02/04 13:42:55 +0100'))
18
+ @info.encrypted .should eql(false)
19
+ @info.author .should eql("Alexander Lang")
20
+ @info.page_format .should eql("A4")
21
+ @info.pages .should eql(3)
22
+ @info.subject .should eql("")
23
+ end
24
+ end
@@ -0,0 +1,129 @@
1
+ require 'spec_helper'
2
+
3
+ describe PdfUtils do
4
+
5
+ describe "toc" do
6
+
7
+ it "should return an array of toc entries" do
8
+ PdfUtils.should_receive(:run_command).with(:pdftk, "stub.pdf dump_data").and_return(
9
+ "BookmarkTitle: Content\nBookmarkLevel: 1\nBookmarkPageNumber: 3\nBookmarkTitle: Intro\nBookmarkLevel: 2\nBookmarkPageNumber: 4")
10
+ PdfUtils::toc('stub.pdf').should eql([["Content",1,3], ["Intro",2,4]])
11
+ end
12
+
13
+ it "should not add an toc entry with page number < 1" do
14
+ PdfUtils.stub(:run_command => "BookmarkTitle: Content\nBookmarkLevel: 1\nBookmarkPageNumber: 0")
15
+ PdfUtils::toc('stub.pdf').should be_empty
16
+ end
17
+
18
+ it "should be empty if the document has no toc data" do
19
+ PdfUtils.should_receive(:run_command).with(:pdftk, "stub.pdf dump_data").and_return(
20
+ "InfoKey: Creator\nInfoValue: The Pragmatic Bookshelf")
21
+ PdfUtils::toc('stub.pdf').entries.should be_empty
22
+ end
23
+ end
24
+
25
+ describe "slice" do
26
+
27
+ it "should slice into a new pdf" do
28
+ sliced_path = Tempfile.new('sliced').path
29
+ PdfUtils::slice(fixture_file_path('marketing.pdf'), 2, 3, sliced_path)
30
+ PdfUtils::info(sliced_path).pages.should eql(2)
31
+ end
32
+
33
+ it "should slice the given file" do
34
+ sliced_path = duplicate_fixture_file('marketing.pdf').path
35
+ PdfUtils::slice!(sliced_path, 2, 3)
36
+ PdfUtils::info(sliced_path).pages.should eql(2)
37
+ end
38
+
39
+ it "should raise an error if pdftk fails to slice the pdf" do
40
+ lambda {
41
+ PdfUtils::slice('does/not/exist.pdf', 2, 3, 'does/not/exist/either.pdf')
42
+ }.should raise_error(RuntimeError)
43
+ end
44
+ end
45
+
46
+ describe "watermark" do
47
+
48
+ before :each do
49
+ @pdf_path = fixture_file_path('marketing.pdf')
50
+ end
51
+
52
+ it "should watermark into a new pdf" do
53
+ watermarked_path = Tempfile.new('watermarked').path
54
+ PdfUtils::watermark(@pdf_path, watermarked_path) do |pdf|
55
+ pdf.text "WATERMARKED PDF", :align => :center, :size => 8
56
+ end
57
+ pdf_text = PdfUtils::to_text(watermarked_path)
58
+ pdf_text.should include('WATERMARKED PDF')
59
+ pdf_text.should include('Beltz Verlag Weinheim')
60
+ end
61
+
62
+ it "should watermark the given file" do
63
+ watermarked_path = duplicate_fixture_file('marketing.pdf').path
64
+ PdfUtils::watermark!(watermarked_path) do |pdf|
65
+ pdf.text "WATERMARKED PDF", :align => :center, :size => 8
66
+ end
67
+ pdf_text = PdfUtils::to_text(watermarked_path)
68
+ pdf_text.should include('WATERMARKED PDF')
69
+ pdf_text.should include('Beltz Verlag Weinheim')
70
+ end
71
+
72
+ it "should pass options to the watermark pdf" do
73
+ Prawn::Document.should_receive(:generate).with(anything, :page_size => [25, 25])
74
+ PdfUtils::watermark(@pdf_path, Tempfile.new('target').path, :page_size => [25, 25]) {}
75
+ end
76
+ end
77
+
78
+ describe "annotate" do
79
+
80
+ before :each do
81
+ @pdf_path = fixture_file_path('page.pdf')
82
+ @annotations = [{
83
+ :Type => :Annot,
84
+ :Subtype => :Text,
85
+ :Name => :Comment,
86
+ :Rect => [10, 10, 34, 34],
87
+ :Contents => 'Dies ist eine Notiz.',
88
+ :C => PdfUtils::Color.new('fdaa00').to_pdf,
89
+ :M => Time.now,
90
+ :F => 4
91
+ }]
92
+ end
93
+
94
+ it "should annotate into a new pdf" do
95
+ annotated_path = Tempfile.new('annotated').path
96
+ PdfUtils::annotate(@pdf_path, @annotations, annotated_path)
97
+ annotations = PdfUtils::annotations(annotated_path)
98
+ annotations.should have(1).item
99
+ annotations.first[:Contents].should eql('Dies ist eine Notiz.')
100
+ end
101
+ end
102
+
103
+ describe "thumbnail" do
104
+
105
+ it "should create a thumbnail of the given page" do
106
+ source = fixture_file_path('marketing.pdf')
107
+ target = File.join(Dir.tmpdir, 'marketing.png')
108
+ FileUtils.rm(target) if File.exists?(target)
109
+ PdfUtils::thumbnail(source, :page => '3', :target => target)
110
+ File.should be_exists(target)
111
+ end
112
+ end
113
+
114
+ describe "snapshot" do
115
+
116
+ it "should rasterize the given page" do
117
+ path = duplicate_fixture_file('marketing.pdf').path
118
+ original_info = PdfUtils::info(path)
119
+
120
+ PdfUtils::snapshot!(path, :page => 2)
121
+
122
+ info = PdfUtils::info(path)
123
+ info.pages.should eql(1)
124
+ info.page_width. should be_close(original_info.page_width , 1.0)
125
+ info.page_height.should be_close(original_info.page_height, 1.0)
126
+ info.page_format.should eql(original_info.page_format)
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,20 @@
1
+ require "rubygems"
2
+ require 'test/unit'
3
+ require "spec"
4
+ require 'tempfile'
5
+
6
+ require 'pdf_utils'
7
+
8
+ def fixture_file_path(file)
9
+ File.join(File.dirname(__FILE__), 'fixtures', file)
10
+ end
11
+
12
+ def fixture_file(file)
13
+ File.open(fixture_file_path(file))
14
+ end
15
+
16
+ def duplicate_fixture_file(file)
17
+ tempfile = Tempfile.new(File.basename(file))
18
+ FileUtils.cp_r(fixture_file_path(file), tempfile.path)
19
+ tempfile
20
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pdf_utils
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Lukas Rieder
13
+ - Andreas Korth
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-03 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: " Requires xpdf, pdftk, swftools/pdf2swf and imagemagick.\n You can check their functionality by running `$ rake check_system_dependencies\xC2\xB4.\n"
23
+ email: l.rieder@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - Rakefile
32
+ - VERSION
33
+ - lib/in_tempdir.rb
34
+ - lib/pdf_utils.rb
35
+ - lib/pdf_utils/color.rb
36
+ - lib/pdf_utils/info.rb
37
+ - script/check_system_dependencies
38
+ - spec/fixtures/marketing.pdf
39
+ - spec/fixtures/page.pdf
40
+ - spec/pdf_utils/color_spec.rb
41
+ - spec/pdf_utils/info_spec.rb
42
+ - spec/pdf_utils_spec.rb
43
+ - spec/spec_helper.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/Overbryd/pdf_utils
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.3.6
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: PdfUtils abstracts a lot of well working UNIX tools for PDF files
74
+ test_files:
75
+ - spec/pdf_utils/color_spec.rb
76
+ - spec/pdf_utils/info_spec.rb
77
+ - spec/pdf_utils_spec.rb
78
+ - spec/spec_helper.rb