jpdfer 0.3.1-java

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,14 @@
1
+ = jpdfer
2
+
3
+ JRuby gem to wrap iText, the PDF manipulation java libraries. Allows
4
+ you to:
5
+
6
+
7
+ * Inspect PDF forms
8
+ * Write to form fields
9
+ * Flatten PDF forms
10
+
11
+
12
+ = TODO
13
+
14
+ * Add checkbox field to simple_form.pdf
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,23 @@
1
+ module Jpdfer
2
+
3
+ class KeyStore
4
+ JavaFileInputStream = java.io.FileInputStream
5
+ JavaKeyStore = java.security.KeyStore
6
+ JavaString = java.lang.String
7
+
8
+ def initialize(keystore_path, _alias, password)
9
+ @alias, @password = _alias, password
10
+ @keystore = JavaKeyStore::getInstance(JavaKeyStore::getDefaultType())
11
+ @keystore.load(JavaFileInputStream.new(keystore_path), JavaString.new(@password).toCharArray)
12
+ end
13
+
14
+ def private_key
15
+ @keystore.getKey(@alias, java.lang.String.new(@password).toCharArray)
16
+ end
17
+
18
+ def certificate_chain
19
+ @keystore.getCertificateChain(@alias)
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,62 @@
1
+ module Jpdfer
2
+ include_package "com.itextpdf.text"
3
+
4
+ def self.dimension_array(page_size)
5
+ [page_size.width, page_size.height]
6
+ end
7
+
8
+ PAGE_SIZES = {
9
+ dimension_array(PageSize::_11X17) => :'11x17',
10
+ dimension_array(PageSize::A0) => :a0,
11
+ dimension_array(PageSize::A1) => :a1,
12
+ dimension_array(PageSize::A10) => :a10,
13
+ dimension_array(PageSize::A2) => :a2,
14
+ dimension_array(PageSize::A3) => :a3,
15
+ dimension_array(PageSize::A4) => :a4,
16
+ dimension_array(PageSize::A5) => :a5,
17
+ dimension_array(PageSize::A6) => :a6,
18
+ dimension_array(PageSize::A7) => :a7,
19
+ dimension_array(PageSize::A8) => :a8,
20
+ dimension_array(PageSize::A9) => :a9,
21
+ dimension_array(PageSize::ARCH_A) => :arch_a,
22
+ dimension_array(PageSize::ARCH_B) => :arch_b,
23
+ dimension_array(PageSize::ARCH_C) => :arch_c,
24
+ dimension_array(PageSize::ARCH_D) => :arch_d,
25
+ dimension_array(PageSize::ARCH_E) => :arch_e,
26
+ dimension_array(PageSize::B0) => :b0,
27
+ dimension_array(PageSize::B1) => :b1,
28
+ dimension_array(PageSize::B10) => :b10,
29
+ dimension_array(PageSize::B2) => :b2,
30
+ dimension_array(PageSize::B3) => :b3,
31
+ dimension_array(PageSize::B4) => :b4,
32
+ dimension_array(PageSize::B5) => :b5,
33
+ dimension_array(PageSize::B6) => :b6,
34
+ dimension_array(PageSize::B7) => :b7,
35
+ dimension_array(PageSize::B8) => :b8,
36
+ dimension_array(PageSize::B9) => :b9,
37
+ dimension_array(PageSize::CROWN_OCTAVO) => :crown_octavo,
38
+ dimension_array(PageSize::CROWN_QUARTO) => :crown_quarto,
39
+ dimension_array(PageSize::DEMY_OCTAVO) => :demy_octavo,
40
+ dimension_array(PageSize::DEMY_QUARTO) => :demy_quarto,
41
+ dimension_array(PageSize::EXECUTIVE) => :executive,
42
+ dimension_array(PageSize::FLSA) => :flsa,
43
+ dimension_array(PageSize::FLSE) => :flse,
44
+ dimension_array(PageSize::HALFLETTER) => :halfletter,
45
+ dimension_array(PageSize::ID_1) => :id_1,
46
+ dimension_array(PageSize::ID_2) => :id_2,
47
+ dimension_array(PageSize::ID_3) => :id_3,
48
+ dimension_array(PageSize::LARGE_CROWN_OCTAVO) => :large_crown_octavo,
49
+ dimension_array(PageSize::LARGE_CROWN_QUARTO) => :large_crown_quarto,
50
+ dimension_array(PageSize::LEDGER) => :ledger,
51
+ dimension_array(PageSize::LEGAL) => :legal,
52
+ dimension_array(PageSize::LETTER) => :letter,
53
+ dimension_array(PageSize::NOTE) => :note,
54
+ dimension_array(PageSize::PENGUIN_LARGE_PAPERBACK) => :penguin_large_paperback,
55
+ dimension_array(PageSize::PENGUIN_SMALL_PAPERBACK) => :penguin_small_paperback,
56
+ dimension_array(PageSize::POSTCARD) => :postcard,
57
+ dimension_array(PageSize::ROYAL_OCTAVO) => :royal_octavo,
58
+ dimension_array(PageSize::ROYAL_QUARTO) => :royal_quarto,
59
+ dimension_array(PageSize::SMALL_PAPERBACK) => :small_paperback,
60
+ dimension_array(PageSize::TABLOID) => :tabloid
61
+ }
62
+ end
data/lib/jpdfer/pdf.rb ADDED
@@ -0,0 +1,246 @@
1
+ require 'jpdfer/page_sizes'
2
+ # High-level/convenience wrapper class for a PDF document.
3
+
4
+ module Jpdfer
5
+
6
+ # PDF Document with a form that can be read, filled, and saved.
7
+ class Pdf
8
+ class NonexistentFieldError < Exception; end
9
+ class ReadOnlyError < Exception; end
10
+ include_class Java::com.itextpdf.text.Image
11
+ include_class Java::java.net.URL
12
+ include_package "com.itextpdf.text.pdf"
13
+ include_package "com.itextpdf.text.xml.xmp"
14
+
15
+ def self.create_flatten_fields_xml(fields)
16
+ schema = DublinCoreSchema.new
17
+ schema.addDescription(JSON({'jpdfer_flattened_fields' => fields}))
18
+ metaout = StringIO.new
19
+ xmpwriter = XmpWriter.new(metaout.to_outputstream)
20
+ xmpwriter.addRdfDescription(schema)
21
+ xmpwriter.close
22
+ metaout.string
23
+ end
24
+
25
+ def self.description_from_metadata_xml(metadata_string)
26
+ metadata_string.gsub!(/<\?.*?\?>/, '')
27
+ namespaces = {
28
+ "xmlns:x" => "adobe:ns:meta/",
29
+ "xmlns:rdf" => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
30
+ "xmlns:dc" => "http://purl.org/dc/elements/1.1/"
31
+ }
32
+ root_node = Nokogiri::XML.parse(metadata_string)
33
+ descriptions = root_node.xpath('.//dc:description//rdf:li/text()', namespaces)
34
+ descriptions.count > 0 ? descriptions.first.text : ""
35
+ end
36
+
37
+ def self.open(pdf_path, save_path, options={})
38
+ flatten = options.delete(:flatten)
39
+ pdf = self.new(pdf_path, options)
40
+ if block_given?
41
+ yield pdf
42
+ pdf.save_as(save_path, flatten)
43
+ else
44
+ pdf
45
+ end
46
+ end
47
+
48
+ # Currently the only option is :keystore
49
+ def initialize(path, options = {})
50
+ @data = File.read(path)
51
+ @output_buffer = StringIO.new
52
+ reader = PdfReader.new(@data.to_java_bytes)
53
+ @stamper = create_stamper(reader, options[:keystore])
54
+ @saved = false
55
+ end
56
+
57
+ # helper method for initialize not ment to be used publicly
58
+ def create_stamper(reader, keystore = nil)
59
+ if keystore
60
+ stamper = PdfStamper.createSignature(reader, @output_buffer.to_outputstream, 0)
61
+ key, certificate_chain = keystore.private_key, keystore.certificate_chain
62
+ # TODO: Should not always be self-signed
63
+ signature_type = Pdf::PdfSignatureAppearance::SELF_SIGNED
64
+ stamper.getSignatureAppearance.setCrypto(key, certificate_chain, nil, signature_type)
65
+ else
66
+ stamper = PdfStamper.new(reader, @output_buffer.to_outputstream)
67
+ end
68
+ stamper
69
+ end
70
+
71
+ # Writes PDF to +path+. If +flatten+ is true, also flattens the form
72
+ # so that the form is printed on the PDF document but the form is no
73
+ # longer editable.
74
+ #
75
+ # Once a this has been called the PDF becomes read-only and any
76
+ # subsequent calls to +save_as+, +set_field+, or +set_fields+
77
+ # will raise an ReadOnlyError.
78
+ #
79
+ # save_as returns *UNTESTED* if the PDF form is not valid
80
+ def save_as(path, flatten=false)
81
+ raise ReadOnlyError.new("Cannot save a previously saved pdf") if @saved
82
+ @saved = true
83
+ if flatten
84
+ metadata = self.class.create_flatten_fields_xml(fields)
85
+ @stamper.setXmpMetadata metadata.to_java_bytes
86
+ end
87
+ @stamper.setFormFlattening(flatten)
88
+ @stamper.close
89
+ File.open(path, 'wb') do |file|
90
+ file.write(@output_buffer.string)
91
+ end
92
+ end
93
+
94
+ # Returns the page size of the pdf as [width (Float), height (Float)]
95
+ def page_size
96
+ page_size = @stamper.reader.crop_box(1)
97
+ if @stamper.reader.page_rotation(1) % 180 == 0
98
+ [page_size.width, page_size.height]
99
+ else
100
+ [page_size.height, page_size.width]
101
+ end
102
+ end
103
+
104
+ # Returns the page type of the pdf or :unknown
105
+ # See Jpdfer::PAGES_SIZES
106
+ def page_type
107
+ PAGE_SIZES.fetch(page_size, :unknown)
108
+ end
109
+
110
+ # Returns fields defined in this PDF form and their values, if any.
111
+ # fields returns an empty hash if PDF document does not contain a form
112
+ def fields
113
+ form = @stamper.getAcroFields
114
+ h = {}
115
+ form.getFields.each_pair do |name, value|
116
+ h[name.to_sym] = form.getField(name)
117
+ end
118
+ h
119
+ end
120
+
121
+ # Returns value of named field.
122
+ #
123
+ # Raises Pdf::NonexistentFieldError if field does not exist.
124
+ # +name+:: Symbol name of field to retrieve
125
+ def get_field(name)
126
+ raise NonexistentFieldError.new("'#{name}' field does not exist in form") unless has_field?(name)
127
+ @stamper.getAcroFields.getField(name.to_s)
128
+ end
129
+
130
+ # Sets named field. set_field returns value set.
131
+ #
132
+ # Raises Pdf::NonexistentFieldError if field does not exist.
133
+ # +name+: Symbol naming the field to write
134
+ def set_field(name, value)
135
+ raise ReadOnlyError.new('Previously saved pdfs are read-only') if @saved
136
+ name = name.to_sym
137
+ raise NonexistentFieldError.new("'#{name}' field does not exist in form") unless has_field?(name)
138
+ @stamper.getAcroFields.setField(name.to_s, value.to_s)
139
+ value
140
+ end
141
+
142
+ # Sets many fields at once. Returns the hash of fields set (should
143
+ # always be equal to given set of fields).
144
+ #
145
+ # Raises Pdf::NonexistentFieldError if any field does not exist.
146
+ # +fields+:: A hash of :key => "value" pairs.
147
+ def set_fields(fields)
148
+ fields.each_pair do |name, value|
149
+ set_field(name, value)
150
+ end
151
+ fields
152
+ end
153
+
154
+ # true if field +name+ exists in form
155
+ #
156
+ # +name+:: Field name as Symbol (or String)
157
+ def has_field?(name)
158
+ fields.key?(name.to_sym)
159
+ end
160
+
161
+ # true if the receiving Pdf instance has a form
162
+ def has_form?
163
+ @stamper.getAcroFields.getFields.size > 0
164
+ end
165
+
166
+ # Returns field names and values that were written to a
167
+ # form in this pdf before flattening.
168
+ # Returns an empty hash if there are not any.
169
+ def flattened_fields
170
+ reader = @stamper.reader
171
+ metadata_string = String.from_java_bytes reader.getMetadata
172
+ description_text = self.class.description_from_metadata_xml(metadata_string)
173
+ begin
174
+ metadata = JSON(description_text)
175
+ _flattened_fields = metadata.key?('jpdfer_flattened_fields') ? metadata['jpdfer_flattened_fields'] : {}
176
+ rescue JSON::ParserError
177
+ _flattened_fields = {}
178
+ end
179
+ h = {}
180
+ _flattened_fields.each_pair do |name, value|
181
+ h[name.to_sym] = value
182
+ end
183
+ h
184
+ end
185
+
186
+ # true if the receiving Pdf instance was previously flattened with jpdfer
187
+ def has_flattened_fields?
188
+ flattened_fields.size > 0 ? true : false
189
+ end
190
+
191
+ # Returns the certification level of the pdf
192
+ def certification_level
193
+ case @stamper.reader.getCertificationLevel
194
+ when PdfSignatureAppearance::CERTIFIED_FORM_FILLING
195
+ level = :form_filling
196
+ when PdfSignatureAppearance::CERTIFIED_FORM_FILLING_AND_ANNOTATIONS
197
+ level = :form_filling_and_annotations
198
+ when PdfSignatureAppearance::CERTIFIED_NO_CHANGES_ALLOWED
199
+ level = :no_changes_allowed
200
+ when PdfSignatureAppearance::NOT_CERTIFIED
201
+ level = :not_certified
202
+ end
203
+ level
204
+ end
205
+
206
+ # Set the certification level on a pdf initialized with an optional keystore
207
+ #
208
+ # *level* must be one of :form_filling, :form_filling_and_annotations,
209
+ # :no_changes_allowed, :not_certified
210
+ def set_certification_level(level)
211
+ case level
212
+ when :form_filling
213
+ certification_level = PdfSignatureAppearance::CERTIFIED_FORM_FILLING
214
+ when :form_filling_and_annotations
215
+ certification_level = PdfSignatureAppearance::CERTIFIED_FORM_FILLING_AND_ANNOTATIONS
216
+ when :no_changes_allowed
217
+ certification_level = PdfSignatureAppearance::CERTIFIED_NO_CHANGES_ALLOWED
218
+ when :not_certified
219
+ level = PdfSignatureAppearance::NOT_CERTIFIED
220
+ end
221
+ @stamper.getSignatureAppearance.setCertificationLevel(certification_level)
222
+ end
223
+
224
+ # Sets the reason for the signature on the pdf
225
+ def set_signature_reason(reason)
226
+ @stamper.getSignatureAppearance.setReason(reason)
227
+ end
228
+
229
+ # Sets the location of the signature on the pdf
230
+ def set_signature_location(location)
231
+ @stamper.getSignatureAppearance.setLocation(location)
232
+ end
233
+
234
+ # Adds the image at +image_path+ to the given +page+, at coordinates +x+ and +y+
235
+ def add_image(image_path, page, x, y, scale=1.0)
236
+ raise ReadOnlyError.new('Previously saved pdfs are read-only') if @saved
237
+ canvas = @stamper.getOverContent(page)
238
+ image = Image.getInstance(image_path)
239
+ image.setAbsolutePosition(x, y)
240
+ image.scalePercent(scale * 100)
241
+ canvas.addImage(image, false)
242
+ end
243
+
244
+ end
245
+
246
+ end
@@ -0,0 +1,3 @@
1
+ module Jpdfer
2
+ VERSION = "0.3.1"
3
+ end
data/lib/jpdfer.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'java'
2
+ require 'stringio'
3
+ require 'json'
4
+ require 'nokogiri'
5
+
6
+ module Jpdfer
7
+ ROOT = File.expand_path('..', File.dirname(__FILE__))
8
+ end
9
+
10
+ require File.expand_path('jars/itextpdf-5.1.1.jar', Jpdfer::ROOT)
11
+ require File.expand_path('jars/bcprov-jdk16-146.jar', Jpdfer::ROOT)
12
+ require File.expand_path('jars/bctsp-jdk16-146.jar', Jpdfer::ROOT)
13
+ require File.expand_path('jars/bcmail-jdk16-146.jar', Jpdfer::ROOT)
14
+
15
+ require 'jpdfer/pdf'
16
+ require 'jpdfer/key_store'
17
+ require 'jpdfer/version'
@@ -0,0 +1,387 @@
1
+ require 'spec_helper'
2
+
3
+ module Jpdfer
4
+
5
+ describe "Pdf Acceptance" do
6
+ before(:each) do
7
+ @data_path = File.join(ROOT, 'spec', 'data')
8
+ @pdf_path = File.join(@data_path, 'simple_form.pdf')
9
+ @pdf = Pdf.new(@pdf_path)
10
+ @unfilled_fields = {
11
+ :important_field => '',
12
+ :unimportant_field => '',
13
+ :semiimportant_field => '',
14
+ :tuesday_field => '',
15
+ :must_not_be_left_blank_field => ''
16
+ }
17
+ @filled_fields = {
18
+ :important_field => "I am important",
19
+ :unimportant_field => 'I am unimportant',
20
+ :semiimportant_field => 'I am confused',
21
+ :tuesday_field => 'Is it Tuesday already?',
22
+ :must_not_be_left_blank_field => 'NOT BLANK'
23
+ }
24
+ end
25
+
26
+ describe '.new' do
27
+ it 'should create new pdf' do
28
+ @pdf.should_not be_nil
29
+ end
30
+
31
+ describe 'given missing file' do
32
+ it 'should raise Errno::ENOENT (File Not Found)' do
33
+ lambda { Pdf.new('derp.pdf') }.should raise_error(Errno::ENOENT)
34
+ end
35
+ end
36
+
37
+ describe 'given an optional keystore' do
38
+ describe 'when Pdf is configured' do
39
+ before(:each) do
40
+ @keystore = KeyStore.new(
41
+ File.join(@data_path, 'keystore.ks'),
42
+ 'jpdfer',
43
+ 'durrderp'
44
+ )
45
+ @pdf = Pdf.new(@pdf_path, :keystore => @keystore)
46
+ @signed_pdf_path = File.join(@data_path, 'new_signed.pdf')
47
+ end
48
+
49
+ after(:each) do
50
+ FileUtils.rm_f(@signed_pdf_path)
51
+ end
52
+
53
+ it 'should create a signed pdf if saved' do
54
+ @pdf.save_as(@signed_pdf_path)
55
+ File.open(@signed_pdf_path) do |file|
56
+ data = file.read
57
+ data['Scott Nielsen'].should == 'Scott Nielsen'
58
+ data['Saxton Horne'].should == 'Saxton Horne'
59
+ end
60
+ end
61
+
62
+ describe '#set_certification_level' do
63
+ it 'should have the given certification level when saved' do
64
+ @pdf.set_certification_level(:no_changes_allowed)
65
+ @pdf.save_as(@signed_pdf_path)
66
+ @pdf = Pdf.new(@signed_pdf_path)
67
+ @pdf.certification_level.should == :no_changes_allowed
68
+ end
69
+ end
70
+
71
+ # TODO: I would like to add this functionality but havn't researched the java
72
+ # KeyStore and encryption classes enough yet
73
+ #
74
+ # describe 'given a pdf signed with the private_key and certificate' do
75
+ # before(:each) do
76
+ # pdf_path = File.join(@data_path, 'simple_form_flattened_signed.pdf')
77
+ # @pdf = Pdf.new(pdf_path, :keystore => @keystore)
78
+ # end
79
+
80
+ # describe '#signed?' do
81
+ # it 'should be true' do
82
+ # @pdf.should be_signed
83
+ # end
84
+ # end
85
+ # end
86
+
87
+ # describe 'given a pdf not signed with the private_key and certificate' do
88
+ # before(:each) do
89
+ # pdf_path = File.join(@data_path, 'simple_form_signed_by_someone_else.pdf')
90
+ # @pdf = Pdf.new(pdf_path, :keystore => @keystore)
91
+ # end
92
+
93
+ # describe '#signed?' do
94
+ # it 'should be false' do
95
+ # @pdf.should_not be_signed
96
+ # end
97
+ # end
98
+ # end
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '.open' do
104
+ before(:each) do
105
+ @save_path = File.join(@data_path, 'new_pdf.pdf')
106
+ end
107
+
108
+ after(:each) do
109
+ FileUtils.rm_f(@save_path)
110
+ end
111
+
112
+ it 'should instaniate a new pdf a pass it to the block and then save it' do
113
+ Pdf.open(@pdf_path, @save_path) do |pdf|
114
+ pdf.set_fields(@filled_fields)
115
+ end
116
+ new_pdf = Pdf.new(@save_path)
117
+ new_pdf.fields.should == @filled_fields
118
+ end
119
+
120
+ describe 'given the flatten option' do
121
+ it 'should be saved with the flatten option' do
122
+ Pdf.open(@pdf_path, @save_path, :flatten => true) do |pdf|
123
+ pdf.set_fields(@filled_fields)
124
+ end
125
+ new_pdf = Pdf.new(@save_path)
126
+ new_pdf.flattened_fields.should == @filled_fields
127
+ end
128
+ end
129
+ end
130
+
131
+ describe '#page_size' do
132
+ it 'should return the page size as an array of floats' do
133
+ @pdf.page_size.should == [612.0, 792.0]
134
+ end
135
+ end
136
+
137
+ describe '#page_type' do
138
+ it 'should return the page type if it exists' do
139
+ @pdf.page_type.should == :letter
140
+ end
141
+ end
142
+
143
+ describe '#certification_level' do
144
+ it 'should return nil for an unsigned pdf' do
145
+ @pdf.certification_level.should == :not_certified
146
+ end
147
+
148
+ describe 'called on a signed pdf' do
149
+ before(:each) do
150
+ @pdf = Pdf.new(File.join(@data_path, 'simple_form_flattened_signed.pdf'))
151
+ end
152
+
153
+ it 'it should return the certification level' do
154
+ @pdf.certification_level.should == :no_changes_allowed
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '#fields' do
160
+ it 'should return a hash of field name value pairs' do
161
+ @pdf.fields.should == @unfilled_fields
162
+ end
163
+ end
164
+
165
+ describe '#get_field' do
166
+ it 'should return the field value' do
167
+ @pdf.get_field(:important_field).should == ""
168
+ end
169
+
170
+ describe "with nonexistent field name" do
171
+ it "should raise Pdf::NonexistentFieldError" do
172
+ lambda { @pdf.get_field(:monkey) }.should raise_error(Pdf::NonexistentFieldError, /'monkey' field does not exist in form/)
173
+ end
174
+ end
175
+ end
176
+
177
+ describe '#set_field' do
178
+ it 'should fill the field with given name with given value' do
179
+ @pdf.set_field(:important_field, "I am important")
180
+ @pdf.get_field(:important_field).should == "I am important"
181
+ end
182
+
183
+ it 'should return the value written to the field' do
184
+ @pdf.set_field(:important_field, "I am important").should == 'I am important'
185
+ end
186
+
187
+ it 'should update fields' do
188
+ @pdf.set_field(:important_field, "I am important")
189
+ @pdf.fields.should == {
190
+ :important_field => 'I am important',
191
+ :unimportant_field => '',
192
+ :semiimportant_field => '',
193
+ :tuesday_field => '',
194
+ :must_not_be_left_blank_field => ''
195
+ }
196
+ end
197
+
198
+ describe "with existing field name" do
199
+ it "should not raise an error" do
200
+ lambda { @pdf.set_field(:important_field, 'I am important') }.should_not raise_error(Pdf::NonexistentFieldError)
201
+ end
202
+ end
203
+
204
+ describe "with nonexistent field name" do
205
+ it "should raise Pdf::NonexistentFieldError" do
206
+ lambda { @pdf.set_field(:monkey, 'Spider') }.should raise_error(Pdf::NonexistentFieldError, /'monkey' field does not exist in form/)
207
+ end
208
+ end
209
+ end
210
+
211
+ describe '#set_fields' do
212
+ it 'should fill the fields with given names with given values' do
213
+ @pdf.set_fields(@filled_fields)
214
+ @pdf.get_field(:important_field).should == "I am important"
215
+ @pdf.get_field(:unimportant_field).should == 'I am unimportant'
216
+ @pdf.get_field(:semiimportant_field).should == 'I am confused'
217
+ @pdf.get_field(:tuesday_field).should == 'Is it Tuesday already?'
218
+ @pdf.get_field(:must_not_be_left_blank_field).should == 'NOT BLANK'
219
+ end
220
+
221
+ it 'should update fields' do
222
+ @pdf.set_fields(@filled_fields)
223
+ @pdf.fields.should == @filled_fields
224
+ end
225
+
226
+ it 'should return the set fields' do
227
+ @pdf.set_fields(@filled_fields).should == @filled_fields
228
+ end
229
+
230
+ describe "with nonexistent field name" do
231
+ it "should raise Pdf::NonexistentFieldError" do
232
+ @filled_fields[:monkey] = "spider"
233
+ lambda { @pdf.set_fields(@filled_fields) }.should raise_error(Pdf::NonexistentFieldError, /'monkey' field does not exist in form/)
234
+ end
235
+ end
236
+ end
237
+
238
+ describe '#save_as' do
239
+ before(:each) do
240
+ @new_path = File.join(@data_path, 'simple_form_new.pdf')
241
+ FileUtils.rm_f(@new_path)
242
+ end
243
+
244
+ after(:each) do
245
+ FileUtils.rm_f(@new_path)
246
+ end
247
+
248
+ it 'should write the pdf to a new path' do
249
+ @pdf.save_as(@new_path)
250
+
251
+ new_pdf = Pdf.new(@new_path)
252
+ new_pdf.fields.should == {
253
+ :important_field => '',
254
+ :unimportant_field => '',
255
+ :semiimportant_field => '',
256
+ :tuesday_field => '',
257
+ :must_not_be_left_blank_field => ''
258
+ }
259
+ end
260
+
261
+ it 'should save updated fields to the new file' do
262
+ @pdf.set_field(:important_field, "I am important")
263
+ @pdf.save_as(@new_path)
264
+
265
+ new_pdf = Pdf.new(@new_path)
266
+ new_pdf.get_field(:important_field).should == "I am important"
267
+ end
268
+
269
+ describe 'given flatten=true' do
270
+ it 'should save the pdf without a form' do
271
+ @pdf.save_as(@new_path, true)
272
+
273
+ new_pdf = Pdf.new(@new_path)
274
+ new_pdf.should_not have_form
275
+ end
276
+ end
277
+
278
+ describe 'with a saved PDF' do
279
+ before(:each) do
280
+ @pdf.save_as(@new_path)
281
+ end
282
+
283
+ describe 'saving again' do
284
+ it "should raise Pdf::ReadOnlyError" do
285
+ lambda { @pdf.save_as(@new_path) }.should raise_error(Pdf::ReadOnlyError, /Cannot save a previously saved pdf/)
286
+ end
287
+ end
288
+
289
+ describe "#set_field" do
290
+ it "should raise Pdf:ReadOnlyError" do
291
+ lambda { @pdf.set_field(:important_field, 'I am important') }.should raise_error(Pdf::ReadOnlyError, /Previously saved pdfs are read-only/)
292
+ end
293
+ end
294
+
295
+ describe "#set_fields" do
296
+ it "should raise Pdf::ReadOnlyError" do
297
+ lambda { @pdf.set_fields(@filled_fields) }.should raise_error(Pdf::ReadOnlyError, /Previously saved pdfs are read-only/)
298
+ end
299
+ end
300
+ end
301
+ end
302
+
303
+ describe '#has_field?' do
304
+ describe "with field name as symbol" do
305
+ it 'should return true if the field exists' do
306
+ @pdf.has_field?(:important_field).should be(true)
307
+ end
308
+
309
+ it 'should return false if the field does not' do
310
+ @pdf.has_field?(:monkey).should be(false)
311
+ end
312
+ end
313
+
314
+ describe "with field name as string" do
315
+ it 'should return true if the field exists' do
316
+ @pdf.has_field?("important_field").should be(true)
317
+ end
318
+
319
+ it 'should return false if the field does not' do
320
+ @pdf.has_field?("monkey").should be(false)
321
+ end
322
+ end
323
+ end
324
+
325
+ describe '#has_form?' do
326
+ describe 'given a pdf with a form' do
327
+ it 'should return true' do
328
+ @pdf.should have_form
329
+ end
330
+ end
331
+
332
+ describe 'given a pdf without a form' do
333
+ it 'should return false' do
334
+ pdf = Pdf.new(File.join(@data_path, 'simple_form_flattened.pdf'))
335
+ pdf.should_not have_form
336
+ end
337
+ end
338
+ end
339
+
340
+ describe 'given a pdf that we have flattened' do
341
+ before(:each) do
342
+ @new_pdf_path = File.join(@data_path, 'we_flattened.pdf')
343
+ @pdf.set_fields(@filled_fields)
344
+ @pdf.save_as(@new_pdf_path, true)
345
+ @pdf = Pdf.new(@new_pdf_path)
346
+ end
347
+
348
+ after(:each) do
349
+ FileUtils.rm_f(@new_pdf_path)
350
+ end
351
+
352
+ describe '#flattened_fields' do
353
+ it 'returns a hash of field name value pairs of previous form fields' do
354
+ @pdf.flattened_fields.should == @filled_fields
355
+ end
356
+ end
357
+
358
+ describe '#has_flattened_fields?' do
359
+ it 'should return true' do
360
+ @pdf.should have_flattened_fields
361
+ end
362
+ end
363
+ end
364
+
365
+ describe 'given a pdf that is not flattened, or we did not flatten' do
366
+ before(:each) do
367
+ @pdf = Pdf.new(File.join(@data_path, 'simple_form_flattened.pdf'))
368
+ end
369
+
370
+ describe '#flattened_fields' do
371
+ it 'should return an empty hash' do
372
+ @pdf.flattened_fields.should == {}
373
+ end
374
+ end
375
+
376
+ describe '#has_flattened_fields' do
377
+ it 'should be false' do
378
+ @pdf.should_not have_flattened_fields
379
+ end
380
+ end
381
+ end
382
+
383
+ # set_field returns some error if the form field is incorrect (e.g. setting a checkbox with something silly like 'monkey' or 'true' instead of 'Yes'
384
+ # save_as returns *UNTESTED* if the PDF form is not valid
385
+ end
386
+
387
+ end
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ module Jpdfer
5
+ # IMPORTANT: These are model/unit specs! Test in isolation as much as possible!
6
+ describe Pdf do
7
+ before(:each) do
8
+ @data_path = File.join(ROOT, 'spec', 'data')
9
+ @pdf_path = File.join(@data_path, 'simple_form.pdf')
10
+ @pdf = Pdf.new(@pdf_path)
11
+ end
12
+
13
+ describe '.new' do
14
+ it 'should create new pdf' do
15
+ @pdf.should_not be_nil
16
+ end
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,3 @@
1
+ # spec_helper.rb
2
+ require 'jpdfer'
3
+ require 'fileutils'
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jpdfer
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.3.1
6
+ platform: java
7
+ authors:
8
+ - Scott Nielsen
9
+ - David Brady
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2012-09-21 00:00:00 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: nokogiri
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: json-jruby
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: rspec
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id003
49
+ description: Ruby-style wrapper in JRuby for reading and writing PDF forms
50
+ email: scottnielsen5@gmail.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - README.rdoc
59
+ - jars/bcmail-jdk16-146.jar
60
+ - jars/bcprov-jdk16-146.jar
61
+ - jars/bctsp-jdk16-146.jar
62
+ - jars/itextpdf-5.1.1.jar
63
+ - lib/jpdfer.rb
64
+ - lib/jpdfer/key_store.rb
65
+ - lib/jpdfer/page_sizes.rb
66
+ - lib/jpdfer/pdf.rb
67
+ - lib/jpdfer/version.rb
68
+ - spec/spec_helper.rb
69
+ - spec/acceptance/jpdfer/pdf_acceptance_spec.rb
70
+ - spec/lib/jpdfer/pdf_spec.rb
71
+ - spec/data/keystore.ks
72
+ - spec/data/flattened.pdf
73
+ - spec/data/simple_form.pdf
74
+ - spec/data/simple_form_flattened.pdf
75
+ - spec/data/simple_form_flattened_signed.pdf
76
+ - spec/data/simple_form_signed_by_someone_else.pdf
77
+ homepage: http://github.com/scizo/jpdfer
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options:
82
+ - --main
83
+ - README.rdoc
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0"
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: "0"
98
+ requirements: []
99
+
100
+ rubyforge_project:
101
+ rubygems_version: 1.8.15
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Read and write PDF forms in JRuby
105
+ test_files:
106
+ - spec/spec_helper.rb
107
+ - spec/acceptance/jpdfer/pdf_acceptance_spec.rb
108
+ - spec/lib/jpdfer/pdf_spec.rb
109
+ - spec/data/keystore.ks
110
+ - spec/data/flattened.pdf
111
+ - spec/data/simple_form.pdf
112
+ - spec/data/simple_form_flattened.pdf
113
+ - spec/data/simple_form_flattened_signed.pdf
114
+ - spec/data/simple_form_signed_by_someone_else.pdf