jpdfer 0.3.1-java

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.
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