pdf_editor 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/pdf_editor.rb +17 -0
- data/lib/pdf_editor/bundle.rb +77 -0
- data/lib/pdf_editor/merge.rb +47 -0
- data/lib/pdf_editor/mixins/errors.rb +14 -0
- data/lib/pdf_editor/mixins/pdf_runner.rb +11 -0
- data/lib/pdf_editor/mixins/prawn_dsl.rb +31 -0
- data/lib/pdf_editor/mixins/service.rb +57 -0
- data/lib/pdf_editor/remove_pages.rb +47 -0
- data/lib/pdf_editor/reorder.rb +19 -0
- data/lib/pdf_editor/resource.rb +59 -0
- data/lib/pdf_editor/rotate_page.rb +69 -0
- data/lib/pdf_editor/shuffle.rb +58 -0
- data/lib/pdf_editor/table_of_contents.rb +52 -0
- data/lib/pdf_editor/title_page.rb +38 -0
- data/lib/pdf_editor/version.rb +3 -0
- data/pdf_editor.gemspec +30 -0
- data/spec/bundle_spec.rb +158 -0
- data/spec/merge_spec.rb +63 -0
- data/spec/remove_pages_spec.rb +55 -0
- data/spec/reorder_spec.rb +7 -0
- data/spec/resource_spec.rb +129 -0
- data/spec/rotate_page_spec.rb +85 -0
- data/spec/shuffle_spec.rb +69 -0
- data/spec/support/docs/five_page.pdf +265 -0
- data/spec/support/docs/merge_1.pdf +69 -0
- data/spec/support/docs/merge_2.pdf +69 -0
- data/spec/support/docs/not_a_pdf.txt +1 -0
- data/spec/support/docs/two_page.pdf +98 -0
- data/spec/support/resource_helper.rb +19 -0
- data/spec/support/spec_helper.rb +12 -0
- data/spec/table_of_contents_spec.rb +15 -0
- data/spec/title_page_spec.rb +35 -0
- metadata +210 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require_relative 'resource'
|
|
2
|
+
require_relative 'mixins/service'
|
|
3
|
+
require_relative 'mixins/pdf_runner'
|
|
4
|
+
require_relative 'mixins/errors'
|
|
5
|
+
|
|
6
|
+
module PdfEditor
|
|
7
|
+
class RotatePage
|
|
8
|
+
include PdfEditor::Service
|
|
9
|
+
include PdfEditor::PdfRunner
|
|
10
|
+
include PdfEditor::Errors
|
|
11
|
+
|
|
12
|
+
ROTATIONS = {
|
|
13
|
+
left: 'left',
|
|
14
|
+
right: 'right',
|
|
15
|
+
flip: 'down'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
attr_reader :resource, :page, :rotate
|
|
19
|
+
|
|
20
|
+
def post_init
|
|
21
|
+
@resource = args.fetch(:resource, nil)
|
|
22
|
+
@page = args.fetch(:page, nil)
|
|
23
|
+
@rotate = ROTATIONS[args.fetch(:rotate, '')]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
if resource.nil? || page.nil? || rotate.nil?
|
|
28
|
+
raise Errors::ArgumentMissingError,
|
|
29
|
+
"resource: #{resource} \n page: #{page} \n rotate: #{rotate}"
|
|
30
|
+
end
|
|
31
|
+
PdfEditor::Resource.new(
|
|
32
|
+
create_tempfile {run_command}
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def run_command
|
|
39
|
+
read_from_io do
|
|
40
|
+
pdf_runner.cat(format_command)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get_page_count
|
|
45
|
+
page_count = resource.page_count
|
|
46
|
+
if page_count == 0 || page > page_count
|
|
47
|
+
raise Errors::PageRangeError, "Page #{page} does not exist. There are #{page_count} pages "
|
|
48
|
+
end
|
|
49
|
+
page_count
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def format_command
|
|
53
|
+
page_count = get_page_count
|
|
54
|
+
page_nums = Array(1..page_count)
|
|
55
|
+
|
|
56
|
+
before_page = page_nums[0, page_nums.index(page)]
|
|
57
|
+
|
|
58
|
+
first_page_after = page_nums.index(page+1) || page_nums.length
|
|
59
|
+
after_page = page_nums[first_page_after, page_nums.length]
|
|
60
|
+
|
|
61
|
+
result = []
|
|
62
|
+
result << {:pdf => resource.path, :start => before_page.first, :end => before_page.last} unless before_page.empty?
|
|
63
|
+
result << {:pdf => resource.path, :start => page, :end => page, :orientation => rotate}
|
|
64
|
+
result << {:pdf => resource.path, :start => after_page.first, :end => after_page.last} unless after_page.empty?
|
|
65
|
+
result
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require_relative 'resource'
|
|
2
|
+
require_relative 'mixins/service'
|
|
3
|
+
require_relative 'mixins/pdf_runner'
|
|
4
|
+
require_relative 'mixins/errors'
|
|
5
|
+
|
|
6
|
+
module PdfEditor
|
|
7
|
+
class Shuffle
|
|
8
|
+
include PdfEditor::Service
|
|
9
|
+
include PdfEditor::PdfRunner
|
|
10
|
+
include PdfEditor::Errors
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Page order does not require unique members and
|
|
14
|
+
# is not sorted. This is the base class for other
|
|
15
|
+
# shuffle operations.
|
|
16
|
+
#
|
|
17
|
+
|
|
18
|
+
attr_reader :resource, :page_order
|
|
19
|
+
|
|
20
|
+
def post_init
|
|
21
|
+
@resource = args[:resource]
|
|
22
|
+
@page_order = args.fetch(:page_order, [])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def call
|
|
26
|
+
if resource.nil?
|
|
27
|
+
raise Errors::ResourcesEmptyError, 'Must have a resource to shuffle'
|
|
28
|
+
end
|
|
29
|
+
if page_order.empty?
|
|
30
|
+
raise Errors::PageOrderInvalidError, 'Page order was invalid'
|
|
31
|
+
end
|
|
32
|
+
PdfEditor::Resource.new(
|
|
33
|
+
create_tempfile {run_command}
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def run_command
|
|
40
|
+
read_from_io do
|
|
41
|
+
pdf_runner.cat(format_command)
|
|
42
|
+
end
|
|
43
|
+
rescue ::ActivePdftk::CommandError => e
|
|
44
|
+
raise InvalidInputError, e.message
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def format_command
|
|
48
|
+
page_order.map do |page_num|
|
|
49
|
+
{
|
|
50
|
+
:pdf => resource.path,
|
|
51
|
+
:start => page_num,
|
|
52
|
+
:end => page_num
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require_relative 'resource'
|
|
2
|
+
require_relative 'mixins/service'
|
|
3
|
+
require_relative 'mixins/prawn_dsl'
|
|
4
|
+
|
|
5
|
+
module PdfEditor
|
|
6
|
+
class TableOfContents
|
|
7
|
+
include PdfEditor::Service
|
|
8
|
+
include PdfEditor::PrawnDSL
|
|
9
|
+
|
|
10
|
+
attr_reader :resources
|
|
11
|
+
|
|
12
|
+
def post_init
|
|
13
|
+
@resources = args.fetch(:resources, [])
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
if resources.empty?
|
|
18
|
+
raise PdfEditor::Errors::ResourcesEmptyError,
|
|
19
|
+
'You must have at least one resource to create a table of contents'
|
|
20
|
+
end
|
|
21
|
+
PdfEditor::Resource.new(
|
|
22
|
+
create_tempfile {create_pdf}
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def create_pdf
|
|
27
|
+
update_pdf do
|
|
28
|
+
header
|
|
29
|
+
body
|
|
30
|
+
end
|
|
31
|
+
to_pdf
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def header
|
|
35
|
+
move_down 20
|
|
36
|
+
text 'applicant_name', size: 40, align: :center
|
|
37
|
+
move_down 50
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def body
|
|
41
|
+
begin_page_count = 1 # TOC normally doesn't count itself as a page
|
|
42
|
+
|
|
43
|
+
resources.inject(begin_page_count) do |page_location, resource|
|
|
44
|
+
text "#{resource.name}........................#{page_location}"
|
|
45
|
+
move_down 10
|
|
46
|
+
|
|
47
|
+
page_location + resource.page_count
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require_relative 'resource'
|
|
2
|
+
require_relative 'mixins/service'
|
|
3
|
+
require_relative 'mixins/prawn_dsl'
|
|
4
|
+
|
|
5
|
+
module PdfEditor
|
|
6
|
+
class TitlePage
|
|
7
|
+
include PdfEditor::Service
|
|
8
|
+
include PdfEditor::PrawnDSL
|
|
9
|
+
include PdfEditor::Errors
|
|
10
|
+
|
|
11
|
+
attr_reader :title
|
|
12
|
+
|
|
13
|
+
def post_init
|
|
14
|
+
@title = args[:title]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
unless title
|
|
19
|
+
raise Errors::TitlePageTitleError, 'Title page requires a title to operate'
|
|
20
|
+
end
|
|
21
|
+
PdfEditor::Resource.new(
|
|
22
|
+
create_tempfile {create_pdf},
|
|
23
|
+
title
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def create_pdf
|
|
30
|
+
update_pdf do
|
|
31
|
+
move_down 30
|
|
32
|
+
text title, size: 40, align: :center
|
|
33
|
+
end
|
|
34
|
+
to_pdf
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
data/pdf_editor.gemspec
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'pdf_editor/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "pdf_editor"
|
|
8
|
+
spec.version = PdfEditor::VERSION
|
|
9
|
+
spec.authors = ["bradwheel"]
|
|
10
|
+
spec.email = ["bradley.m.wheel@gmail.com"]
|
|
11
|
+
spec.summary = %q{Edits Pdf's}
|
|
12
|
+
spec.description = %q{Edits Pdf's}
|
|
13
|
+
spec.homepage = ""
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_dependency 'prawn', '1.2.1'
|
|
22
|
+
spec.add_dependency 'pdf-reader', '1.3.3'
|
|
23
|
+
spec.add_dependency 'docsplit', '0.7.5'
|
|
24
|
+
|
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
|
26
|
+
spec.add_development_dependency 'rake'
|
|
27
|
+
spec.add_development_dependency 'rspec'
|
|
28
|
+
spec.add_development_dependency 'pdf-inspector'
|
|
29
|
+
spec.add_development_dependency 'pry'
|
|
30
|
+
end
|
data/spec/bundle_spec.rb
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
require 'support/spec_helper'
|
|
2
|
+
|
|
3
|
+
module PdfEditor
|
|
4
|
+
describe Bundle do
|
|
5
|
+
|
|
6
|
+
before do
|
|
7
|
+
@resource_1 = get_resource('five_page.pdf', 'Five Page Pdf')
|
|
8
|
+
@resource_2 = get_resource('two_page.pdf', 'Two Page Pdf')
|
|
9
|
+
@service = PdfEditor::Bundle.new(
|
|
10
|
+
resources: [@resource_1, @resource_2],
|
|
11
|
+
title: 'TOC',
|
|
12
|
+
with_toc: true,
|
|
13
|
+
with_title_pages: true
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe '#create_title_page' do
|
|
18
|
+
|
|
19
|
+
it 'returns a resource' do
|
|
20
|
+
ret = @service.send(:create_title_page, @resource_1)
|
|
21
|
+
expect(ret).to be_instance_of PdfEditor::Resource
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '#merge_resource_with_title_page' do
|
|
27
|
+
|
|
28
|
+
it 'creates a single resource with title page and original content' do
|
|
29
|
+
ret = @service.send(:merge_resource_with_title_page, @resource_1)
|
|
30
|
+
contents = []
|
|
31
|
+
ret.open_file do |f|
|
|
32
|
+
reader = ::PDF::Reader.new(f)
|
|
33
|
+
reader.pages.each {|page| contents << page.text}
|
|
34
|
+
end
|
|
35
|
+
expect(ret.page_count).to eq 6
|
|
36
|
+
expect(contents).to eq ['Five Page Pdf', 'Page 1', 'Page 2', 'Page 3', 'Page 4', 'Page 5']
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '#call' do
|
|
42
|
+
|
|
43
|
+
before do
|
|
44
|
+
@service.stub(:create_title_pages) { nil }
|
|
45
|
+
@service.stub(:create_table_of_contents) { nil }
|
|
46
|
+
@service.stub(:bundle_documents) { nil }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'calls #create_title_pages' do
|
|
50
|
+
expect(@service).to receive(:create_title_pages)
|
|
51
|
+
@service.call
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'calls #create_table_of_contents' do
|
|
55
|
+
expect(@service).to receive(:create_table_of_contents)
|
|
56
|
+
@service.call
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'calls #bundle_documents' do
|
|
60
|
+
expect(@service).to receive(:bundle_documents)
|
|
61
|
+
@service.call
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
describe '#create_title_pages' do
|
|
67
|
+
|
|
68
|
+
context 'when with_title_pages' do
|
|
69
|
+
|
|
70
|
+
it 'creates title pages' do
|
|
71
|
+
service = PdfEditor::Bundle.new(
|
|
72
|
+
resources: [@resource_1, @resource_2],
|
|
73
|
+
title: 'TOC',
|
|
74
|
+
with_toc: true,
|
|
75
|
+
with_title_pages: true
|
|
76
|
+
)
|
|
77
|
+
service.send(:create_title_pages)
|
|
78
|
+
ready_for_toc = service.send(:ready_for_toc)
|
|
79
|
+
page_count = ready_for_toc.inject(0) {|sum, resource| sum + resource.page_count}
|
|
80
|
+
expect(page_count).to eq 9
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context 'when not with_title_pages' do
|
|
86
|
+
|
|
87
|
+
it 'does not create title pages' do
|
|
88
|
+
service = PdfEditor::Bundle.new(
|
|
89
|
+
resources: [@resource_1, @resource_2],
|
|
90
|
+
title: 'TOC',
|
|
91
|
+
with_toc: true,
|
|
92
|
+
with_title_pages: false
|
|
93
|
+
)
|
|
94
|
+
service.send(:create_title_pages)
|
|
95
|
+
ready_for_toc = service.send(:ready_for_toc)
|
|
96
|
+
page_count = ready_for_toc.inject(0) {|sum, resource| sum + resource.page_count}
|
|
97
|
+
expect(page_count).to eq 7
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe '#create_table_of_contents' do
|
|
105
|
+
|
|
106
|
+
it 'does nothing if ready_for_toc is empty' do
|
|
107
|
+
service = PdfEditor::Bundle.new(
|
|
108
|
+
resources: [],
|
|
109
|
+
title: 'TOC',
|
|
110
|
+
with_toc: true,
|
|
111
|
+
with_title_pages: false
|
|
112
|
+
)
|
|
113
|
+
service.send(:create_table_of_contents)
|
|
114
|
+
expect(service.send(:table_of_contents)).to be_nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'does nothing if not with_toc' do
|
|
118
|
+
service = PdfEditor::Bundle.new(
|
|
119
|
+
resources: [@resource_1, @resource_2],
|
|
120
|
+
title: 'TOC',
|
|
121
|
+
with_toc: false,
|
|
122
|
+
with_title_pages: false
|
|
123
|
+
)
|
|
124
|
+
service.send(:create_table_of_contents)
|
|
125
|
+
expect(service.send(:table_of_contents)).to be_nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'sets table_of_contents to generated table_of_contents' do
|
|
129
|
+
service = PdfEditor::Bundle.new(
|
|
130
|
+
resources: [@resource_1, @resource_2],
|
|
131
|
+
title: 'TOC',
|
|
132
|
+
with_toc: true,
|
|
133
|
+
with_title_pages: true
|
|
134
|
+
)
|
|
135
|
+
service.stub(:ready_for_toc) {[@resource_1, @resource_2]}
|
|
136
|
+
service.send(:create_table_of_contents)
|
|
137
|
+
expect(service.send(:table_of_contents)).to be_instance_of PdfEditor::Resource
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
describe '#bundle_documents' do
|
|
143
|
+
|
|
144
|
+
it 'merges the table_of_contents with the contents' do
|
|
145
|
+
service = PdfEditor::Bundle.new(
|
|
146
|
+
resources: [@resource_1, @resource_2],
|
|
147
|
+
title: 'TOC',
|
|
148
|
+
with_toc: true,
|
|
149
|
+
with_title_pages: true
|
|
150
|
+
)
|
|
151
|
+
ret = service.call
|
|
152
|
+
expect(ret.page_count).to eq 10
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
end
|
data/spec/merge_spec.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'support/spec_helper'
|
|
2
|
+
|
|
3
|
+
module PdfEditor
|
|
4
|
+
describe Merge do
|
|
5
|
+
|
|
6
|
+
before do
|
|
7
|
+
support_dir = ::File.join(::File.dirname(__FILE__), 'support', 'docs')
|
|
8
|
+
|
|
9
|
+
['merge_1.pdf', 'merge_2.pdf'].each_with_index do |file, i|
|
|
10
|
+
|
|
11
|
+
::File.open(::File.join(support_dir, file), 'r') do |f|
|
|
12
|
+
|
|
13
|
+
::Tempfile.open(['', '.pdf']) do |tf|
|
|
14
|
+
tf.write f.read
|
|
15
|
+
instance_variable_set(:"@resource_#{i+1}", PdfEditor::Resource.new(tf))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
describe '#call' do
|
|
24
|
+
|
|
25
|
+
before do
|
|
26
|
+
@ret = PdfEditor::Merge.call(resources: [@resource_1, @resource_2], merged_name: 'Merged Name')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'returns a resource' do
|
|
30
|
+
expect(@ret).to be_instance_of PdfEditor::Resource
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'returns a merged document' do
|
|
34
|
+
contents = ''
|
|
35
|
+
@ret.open_file do |f|
|
|
36
|
+
PDF::Reader.new(f).pages.each {|page| contents << page.text}
|
|
37
|
+
end
|
|
38
|
+
expect(contents).to eq 'merge 1merge 2'
|
|
39
|
+
expect(@ret.page_count).to eq 2
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'raises an error if no resources are present' do
|
|
43
|
+
expect{PdfEditor::Merge.call}.to raise_error PdfEditor::Errors::ResourcesEmptyError
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'returned resource has merged_name if supplied' do
|
|
47
|
+
expect(@ret.name).to eq 'Merged Name'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe '#format_command' do
|
|
53
|
+
|
|
54
|
+
it 'creates the proper format for merge' do
|
|
55
|
+
expected_format = [{:pdf => @resource_1.path}, {:pdf => @resource_2.path}]
|
|
56
|
+
service = PdfEditor::Merge.new(resources: [@resource_1, @resource_2])
|
|
57
|
+
expect(service.send(:format_command)).to eq expected_format
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
end
|