fillable-pdf 0.8.0 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5ab7087bc8f229f961c0da0082be323be426d8ec7ab4b2393f40bc802f4d43a
4
- data.tar.gz: bb1470fd017ec78765c4fe4885ab946bd94167e01c28b7768d0fcca7e490729a
3
+ metadata.gz: 7043c6e7b7149563d54e87952727cbd2b769d7ab6cbdf6eebfbea91c89e0e6f8
4
+ data.tar.gz: cad339bef276e6baf84473ff4e496b9e44e2a0ea487262850e27954cc29a4df4
5
5
  SHA512:
6
- metadata.gz: '0058f8ca36432a67568682669156536accd1da68bf345617289ac0b1defab458f6f47ab69ebc08ea872d60ad5573e8fab743904e8bbb71620eff25c4511a727a'
7
- data.tar.gz: b77167e17b342c26e3e0003ab468016947e06567f042aef227175bfa368dc062728803347fc75565a81c871fde4a2a0807d1f3bf35e28a773477ea26022a893b
6
+ metadata.gz: f5df81f03db25731784a1c16d132d0d08366a60e02374c1c4d16a1611a45520270d5f91c4fb1fa351a1f05f1d2881400e5cf304c2965063b9fc71cbce617edf2
7
+ data.tar.gz: 7cedf3ff9e49d09c7d3fc4467baa5ec7f8bf2761bc247dd03b70d0e620cba5d121aedf33e725e8a79088f601ec77282c491447ae5f149d01c316f70bbc7df60f
data/.rubocop.yml CHANGED
@@ -1,21 +1,45 @@
1
1
  require:
2
+ - rubocop-md
3
+ - rubocop-minitest
2
4
  - rubocop-performance
5
+ - rubocop-rake
3
6
 
4
7
  AllCops:
8
+ NewCops: enable
5
9
  Exclude:
6
10
  - .git/**/*
11
+ - vendor/bundle/**/*
12
+
13
+ Gemspec/RequiredRubyVersion:
14
+ Enabled: false
7
15
 
8
16
  Layout/EmptyLineAfterGuardClause:
9
17
  Enabled: false
10
18
 
19
+ Layout/IndentationConsistency:
20
+ Exclude:
21
+ - README.md
22
+
23
+ Layout/InitialIndentation:
24
+ Exclude:
25
+ - README.md
26
+
11
27
  Layout/LineLength:
12
28
  Exclude:
29
+ - README.md
13
30
  - fillable-pdf.gemspec
14
31
  Max: 120
15
32
 
16
33
  Layout/SpaceInsideHashLiteralBraces:
17
34
  Enabled: false
18
35
 
36
+ Lint/ConstantDefinitionInBlock:
37
+ Exclude:
38
+ - lib/fillable-pdf/itext.rb
39
+
40
+ Metrics/MethodLength:
41
+ Max: 12
42
+
19
43
  Naming/AccessorMethodName:
20
44
  Enabled: false
21
45
 
data/.travis.yml CHANGED
@@ -3,8 +3,8 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 2.6.4
6
+ - 3.0.3
7
7
  jdk:
8
8
  - openjdk8
9
9
  before_install:
10
- - gem install bundler -v 2.0.2
10
+ - gem install bundler
@@ -1,3 +1,7 @@
1
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
2
+
3
+ However, you must also adhere to the [iText License](https://github.com/itext/itext7) when using this gem in your project.
4
+
1
5
  The MIT License (MIT)
2
6
 
3
7
  Copyright (c) 2016 Vadim Kononov
data/README.md CHANGED
@@ -2,28 +2,108 @@
2
2
  # FillablePDF
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/fillable-pdf.svg)](https://rubygems.org/gems/fillable-pdf)
5
- [![Build Status](https://api.travis-ci.org/vkononov/fillable-pdf.svg?branch=master)](http://travis-ci.org/vkononov/fillable-pdf)
5
+ [![Build Status](https://app.travis-ci.com/vkononov/fillable-pdf.svg?branch=master)](http://travis-ci.org/vkononov/fillable-pdf)
6
+
7
+ FillablePDF is an extremely simple and lightweight utility that bridges iText and Ruby in order to fill out fillable PDF forms or extract field values from previously filled out PDF forms.
8
+
9
+
10
+ ## Known Issues
11
+
12
+ 1. This gem currently does not work with Phusion Passenger's [smart spawning](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/#the-smart-spawning-method). Please see [Deployment with Phusion Passenger + Nginx](#deployment-with-phusion-passenger--nginx) for more information.
13
+
14
+ 2. If the gem hangs in `development`, removing the following gems may fix the issue:
15
+
16
+ ```ruby
17
+ gem 'spring'
18
+ gem 'spring-watcher-listen'
19
+ ```
20
+
21
+ 3. Read-only, write-protected or encrypted PDF files are currently not supported.
22
+
23
+ ## Deployment with Heroku
24
+
25
+ When deploying to Heroku, be sure to install the following build packs (in this order):
26
+
27
+ ```bash
28
+ heroku buildpacks:add heroku/jvm
29
+ heroku buildpacks:add heroku/ruby
30
+ ```
31
+
32
+ ## Deployment with Phusion Passenger + Nginx
33
+
34
+ The way the gem is currently built makes it [fundamentally incompatible](https://github.com/phusion/passenger/issues/223#issuecomment-44504029) with Phusion Passenger's [smart spawning](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/#the-smart-spawning-method). You must turn off smart spawning, or else your application will freeze as soon Ruby tries to access the Java bridge.
35
+
36
+ Below is an example of a simple Nginx virtual host configuration (note the use of `passenger_spawn_method`):
37
+
38
+ ```nginx
39
+ server {
40
+ server_name my-rails-app.com;
41
+ listen 443 ssl http2;
42
+ listen [::]:443 ssl http2;
43
+ passenger_enabled on;
44
+ passenger_spawn_method direct;
45
+ root /home/system/my-rails-app/public;
46
+ }
47
+ ```
48
+
49
+ If you absolutely must have smart spawning, I recommend using `fillable-pdf` as a service that runs independently of your Rails application.
50
+
51
+
52
+ ## Deployment with Puma + Nginx
53
+
54
+ In order to use Puma in production, you need to configure a reverse proxy in your Nginx virtual host. Here is simple naive example:
55
+
56
+ ```nginx
57
+ server {
58
+ server_name my-rails-app.com;
59
+ listen 443 ssl http2;
60
+ listen [::]:443 ssl http2;
61
+ location / {
62
+ proxy_pass http://127.0.0.1:8888;
63
+ proxy_redirect off;
64
+ proxy_set_header Connection "upgrade";
65
+ proxy_set_header Host $http_host;
66
+ proxy_set_header Upgrade $http_upgrade;
67
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
68
+ proxy_set_header X-Forwarded-Proto https;
69
+ proxy_set_header X-Real-IP $remote_addr;
70
+ }
71
+ }
72
+ ```
73
+
74
+ Then you'll have to start Puma in production daemon mode as follows:
75
+
76
+ ```bash
77
+ RAILS_ENV=production bin/rails server -p 8888 --daemon
78
+ ```
79
+
80
+ Naturally, there are many downsides (in terms of efficiency, scalability, security, etc) to running your application in production in this manner, so please use the above as an example only.
6
81
 
7
- FillablePDF is an extremely simple and lightweight utility that bridges iText and Ruby in order to fill out fillable PDF forms or extract field values from previously filled out PDF forms.
8
82
 
9
83
  ## Installation
10
84
 
11
85
  **Ensure that your `JAVA_HOME` variable is set before installing this gem (see examples below).**
12
-
13
- * OSX: `/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home`
86
+
87
+ * OSX: `/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home`
14
88
  * Ubuntu/CentOS: `/usr/lib/jvm/java-1.8.0-openjdk`
15
89
 
16
90
  Add this line to your application's Gemfile:
17
91
 
18
- gem 'fillable-pdf'
92
+ ```ruby
93
+ gem 'fillable-pdf'
94
+ ```
19
95
 
20
96
  And then execute:
21
97
 
22
- bundle
98
+ ```bash
99
+ bundle
100
+ ```
23
101
 
24
102
  Or install it yourself as:
25
103
 
26
- gem install fillable-pdf
104
+ ```bash
105
+ gem install fillable-pdf
106
+ ```
27
107
 
28
108
  If you are using this gem in a script, you need to require it manually:
29
109
 
@@ -45,127 +125,197 @@ pdf = FillablePDF.new 'input.pdf'
45
125
  pdf.close
46
126
  ```
47
127
 
48
- ## Instance Methods
128
+ ### Checking / Unchecking Checkboxes
129
+
130
+ Use the values `'Yes'` and `'Off'` to check and uncheck checkboxes, respectively. For example:
131
+
132
+ ```ruby
133
+ pdf.set_field(:newsletter, 'Yes')
134
+ pdf.set_field(:newsletter, 'Off')
135
+ ```
136
+
137
+ ### Checking / Unchecking Radio Buttons
138
+
139
+ Suppose you have the following a radio button field name `language` with the following options:
140
+
141
+ - Ruby (`ruby`)
142
+ - Python (`python`)
143
+ - Dart (`dart`)
144
+ - Other (`other`)
145
+
146
+ To select one of these options (or change the current option) use:
147
+
148
+ ```ruby
149
+ pdf.set_field(:language, 'dart')
150
+ ```
151
+
152
+ To unset the radio button use the `'Off'` string:
153
+
154
+ ```ruby
155
+ pdf.set_field(:language, 'Off')
156
+ ```
157
+
158
+ ### Adding Signatures or Images
159
+
160
+ Digital signatures are not supported, but you can place an image or a base64 encoded image within the bounds of any form field.
161
+
162
+ SVG images are not supported. You will have to convert them to a JPG or PNG first.
163
+
164
+ See methods `set_image` and `set_image_base64` below.
165
+
166
+ ### Instance Methods
49
167
 
50
168
  An instance of `FillablePDF` has the following methods at its disposal:
51
169
 
52
170
  * `any_fields?`
53
- *Determines whether the form has any fields.*
54
- ```ruby
55
- pdf.any_fields?
56
- # output example: true
57
- ```
171
+ *Determines whether the form has any fields.*
172
+
173
+ ```ruby
174
+ pdf.any_fields?
175
+ # output example: true
176
+ ```
58
177
 
59
178
  * `num_fields`
60
- *Returns the total number of fillable form fields.*
61
- ```ruby
62
- # output example: 10
63
- pdf.num_fields
64
- ```
179
+ *Returns the total number of fillable form fields.*
180
+
181
+ ```ruby
182
+ # output example: 10
183
+ pdf.num_fields
184
+ ```
65
185
 
66
186
  * `field`
67
- *Retrieves the value of a field given its unique field name.*
68
- ```ruby
69
- pdf.field(:full_name)
70
- # output example: 'Richard'
71
- ```
187
+ *Retrieves the value of a field given its unique field name.*
188
+
189
+ ```ruby
190
+ pdf.field(:full_name)
191
+ # output example: 'Richard'
192
+ ```
72
193
 
73
194
  * `field_type`
74
- *Retrieves the numeric type of a field given its unique field name.*
75
- ```ruby
76
- pdf.field_type(:football)
77
- # output example: 4
78
-
79
- # list of all field types
80
- Field::BUTTON
81
- Field::CHOICE
82
- Field::SIGNATURE
83
- Field::TEXT
84
- ```
195
+ *Retrieves the numeric type of a field given its unique field name.*
196
+
197
+ ```ruby
198
+ pdf.field_type(:football)
199
+ # output example: '/Btn'
200
+
201
+ # list of all field types
202
+ Field::BUTTON ('/Btn')
203
+ Field::CHOICE ('/Ch')
204
+ Field::SIGNATURE ('/Sig')
205
+ Field::TEXT ('/Tx')
206
+ ```
85
207
 
86
208
  * `fields`
87
- *Retrieves a hash of all fields and their values.*
88
- ```ruby
89
- pdf.fields
90
- # output example: {first_name: "Richard", last_name: "Rahl"}
91
- ```
209
+ *Retrieves a hash of all fields and their values.*
210
+
211
+ ```ruby
212
+ pdf.fields
213
+ # output example: {first_name: "Richard", last_name: "Rahl"}
214
+ ```
92
215
 
93
216
  * `set_field`
94
- *Sets the value of a field given its unique field name and value.*
95
- ```ruby
96
- pdf.set_field(:first_name, 'Richard')
97
- # result: changes the value of 'first_name' to 'Richard'
98
- ```
217
+ *Sets the value of a field given its unique field name and value.*
218
+
219
+ ```ruby
220
+ pdf.set_field(:first_name, 'Richard')
221
+ # result: changes the value of 'first_name' to 'Richard'
222
+ ```
99
223
 
100
224
  * `set_fields`
101
- *Sets the values of multiple fields given a set of unique field names and values.*
102
- ```ruby
103
- pdf.set_fields(first_name: 'Richard', last_name: 'Rahl')
104
- # result: changes the values of 'first_name' and 'last_name'
105
- ```
225
+ *Sets the values of multiple fields given a set of unique field names and values.*
226
+
227
+ ```ruby
228
+ pdf.set_fields(first_name: 'Richard', last_name: 'Rahl')
229
+ # result: changes the values of 'first_name' and 'last_name'
230
+ ```
231
+
232
+ * `set_image`
233
+ *Places an image file within the rectangular bounding box of the given form field.*
234
+
235
+ ```ruby
236
+ pdf.set_image(:signature, 'signature.png')
237
+ # result: the image 'signature.png' is shown in the foreground of the form field
238
+ ```
239
+
240
+ * `set_image_base64`
241
+ *Places a base64 encoded image within the rectangular bounding box of the given form field.*
242
+
243
+ ```ruby
244
+ pdf.set_image_base64(:signature, 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==')
245
+ # result: the base64 encoded image is shown in the foreground of the form field
246
+ ```
106
247
 
107
248
  * `rename_field`
108
- *Renames a field given its unique field name and the new field name.*
109
- ```ruby
110
- pdf.rename_field(:last_name, :surname)
111
- # result: renames field name 'last_name' to 'surname'
112
- # NOTE: this action does not take effect until the document is saved
113
- ```
249
+ *Renames a field given its unique field name and the new field name.*
250
+
251
+ ```ruby
252
+ pdf.rename_field(:last_name, :surname)
253
+ # result: renames field name 'last_name' to 'surname'
254
+ # NOTE: this action does not take effect until the document is saved
255
+ ```
114
256
 
115
257
  * `remove_field`
116
- *Removes a field from the document given its unique field name.*
117
- ```ruby
118
- pdf.remove_field(:last_name)
119
- # result: physically removes field 'last_name' from document
120
- ```
258
+ *Removes a field from the document given its unique field name.*
259
+
260
+ ```ruby
261
+ pdf.remove_field(:last_name)
262
+ # result: physically removes field 'last_name' from document
263
+ ```
121
264
 
122
265
  * `names`
123
- *Returns a list of all field keys used in the document.*
124
- ```ruby
125
- pdf.names
126
- # output example: [:first_name, :last_name]
127
- ```
266
+ *Returns a list of all field keys used in the document.*
267
+
268
+ ```ruby
269
+ pdf.names
270
+ # output example: [:first_name, :last_name]
271
+ ```
128
272
 
129
273
  * `values`
130
- *Returns a list of all field values used in the document.*
131
- ```ruby
132
- pdf.values
133
- # output example: ["Rahl", "Richard"]
134
- ```
274
+ *Returns a list of all field values used in the document.*
275
+
276
+ ```ruby
277
+ pdf.values
278
+ # output example: ["Rahl", "Richard"]
279
+ ```
135
280
 
136
281
  * `save`
137
- *Overwrites the previously opened PDF document and flattens it if requested.*
138
- ```ruby
139
- pdf.save
140
- # result: document is saved without flatenning
141
- pdf.save_as(flatten: true)
142
- # result: document is saved with flatenning
143
- ```
282
+ *Overwrites the previously opened PDF document and flattens it if requested.*
283
+
284
+ ```ruby
285
+ pdf.save
286
+ # result: document is saved without flattening
287
+ pdf.save_as(flatten: true)
288
+ # result: document is saved with flattening
289
+ ```
144
290
 
145
291
  * `save_as`
146
- *Saves the filled out PDF document in a given path and flattens it if requested.*
147
- ```ruby
148
- pdf.save_as('output.pdf')
149
- # result: document is saved in a given path without flatenning
150
- pdf.save_as('output.pdf', flatten: true)
151
- # result: document is saved in a given path with flatenning
152
- ```
292
+ *Saves the filled out PDF document in a given path and flattens it if requested.*
153
293
 
154
- **NOTE:** Saving the file automatically closes the input file, so you would need to reinitialize the `FillabePDF` class before making any more changes or saving another copy.
294
+ ```ruby
295
+ pdf.save_as('output.pdf')
296
+ # result: document is saved in a given path without flattening
297
+ pdf.save_as('output.pdf', flatten: true)
298
+ # result: document is saved in a given path with flattening
299
+ ```
300
+
301
+ **NOTE:** Saving the file automatically closes the input file, so you would need to reinitialize the `FillabePDF` class before making any more changes or saving another copy.
155
302
 
156
303
  * `close`
157
- *Closes the PDF document discarding all unsaved changes.*
158
- ```ruby
159
- pdf.close
160
- # result: document is closed
161
- ```
304
+ *Closes the PDF document discarding all unsaved changes.*
305
+
306
+ ```ruby
307
+ pdf.close
308
+ # result: document is closed
309
+ ```
162
310
 
163
311
  ## Example
164
312
 
165
- The following example [example.rb](example/run.rb) and the input file [input.pdf](example/input.pdf) are located in the `test` directory. It uses all of the methods that are described above and generates the output files [output.pdf](example/output.pdf) and [output.flat.pdf](example/output.flat.pdf).
313
+ The following [example.rb](example/run.rb) with [input.pdf](example/input.pdf) is located in the [example](example) directory. It uses all of the methods that are described above and generates the output files [output.pdf](example/output.pdf) and [output.flat.pdf](example/output.flat.pdf).
166
314
 
167
315
  ```ruby
168
- require 'fillable-pdf'
316
+ require_relative '../lib/fillable-pdf'
317
+
318
+ BASE64_PHOTO = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==' # rubocop:disable Layout/LineLength
169
319
 
170
320
  # opening a fillable PDF
171
321
  pdf = FillablePDF.new('input.pdf')
@@ -181,9 +331,12 @@ puts
181
331
 
182
332
  # setting form fields
183
333
  pdf.set_fields(first_name: 'Richard', last_name: 'Rahl')
184
- pdf.set_fields(football: 'Yes', baseball: 'Yes',
185
- basketball: 'Yes', nascar: 'Yes', hockey: 'Yes')
334
+ pdf.set_fields(football: 'Yes', baseball: 'Yes', basketball: 'Yes', nascar: 'Yes', hockey: 'Yes')
186
335
  pdf.set_field(:date, Time.now.strftime('%B %e, %Y'))
336
+ pdf.set_field(:newsletter, 'Off') # uncheck the checkbox
337
+ pdf.set_field(:language, 'dart') # select a radio button option
338
+ pdf.set_image_base64(:photo, BASE64_PHOTO)
339
+ pdf.set_image(:signature, 'signature.png')
187
340
 
188
341
  # list of fields
189
342
  puts "Fields hash: #{pdf.fields}"
@@ -218,10 +371,6 @@ puts
218
371
  # Removing field
219
372
  pdf.remove_field :nascar
220
373
  puts "Removed field 'nascar'"
221
- puts
222
-
223
- # printing the name of the person used inside the PDF
224
- puts "Signatory: #{pdf.field(:first_name)} #{pdf.field(:last_name)}"
225
374
 
226
375
  # saving the filled out PDF in another file
227
376
  pdf.save_as('output.pdf')
@@ -236,22 +385,20 @@ pdf.close
236
385
 
237
386
  The example above produces the following output and also generates the output file [output.pdf](example/output.pdf).
238
387
 
239
- ```
240
- The form has a total of 8 fields.
388
+ ```text
389
+ The form has a total of 16 fields.
241
390
 
242
- Fields hash: {:last_name=>"Rahl", :first_name=>"Richard", :football=>"Yes", :baseball=>"Yes", :basketball=>"Yes", :nascar=>"Yes", :hockey=>"Yes", :date=>"August 30, 2019"}
391
+ Fields hash: {:last_name=>"Rahl", :first_name=>"Richard", :football=>"Yes", :baseball=>"Yes", :basketball=>"Yes", :hockey=>"Yes", :date=>"November 16, 2021", :newsletter=>"Off", :nascar=>"Yes", :language=>"dart", :"language.1"=>"dart", :"language.2"=>"dart", :"language.3"=>"dart", :"language.4"=>"dart", :signature=>"", :photo=>""}
243
392
 
244
- Keys: [:last_name, :first_name, :football, :baseball, :basketball, :nascar, :hockey, :date]
393
+ Keys: [:last_name, :first_name, :football, :baseball, :basketball, :hockey, :date, :newsletter, :nascar, :language, :"language.1", :"language.2", :"language.3", :"language.4", :signature, :photo]
245
394
 
246
- Values: ["Rahl", "Richard", "Yes", "Yes", "Yes", "Yes", "Yes", "August 30, 2019"]
395
+ Values: ["Rahl", "Richard", "Yes", "Yes", "Yes", "Yes", "November 16, 2021", "Off", "Yes", "dart", "dart", "dart", "dart", "dart", "", ""]
247
396
 
248
397
  Field 'football' is of type BUTTON
249
398
 
250
399
  Renamed field 'last_name' to 'surname'
251
400
 
252
401
  Removed field 'nascar'
253
-
254
- Signatory: Richard Rahl
255
402
  ```
256
403
 
257
404
  ## Contributing
@@ -266,3 +413,5 @@ Signatory: Richard Rahl
266
413
  ## License
267
414
 
268
415
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
416
+
417
+ However, you must also adhere to the [iText License](https://github.com/itext/itext7) when using this gem in your project.
data/Rakefile CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
6
  t.libs << 'test'
@@ -7,4 +8,6 @@ Rake::TestTask.new(:test) do |t|
7
8
  t.test_files = FileList['test/**/*_test.rb']
8
9
  end
9
10
 
10
- task default: :test
11
+ RuboCop::RakeTask.new
12
+
13
+ task default: %i[test rubocop]
Binary file
Binary file
data/ext/io-7.1.17.jar ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
data/fillable-pdf.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = 'FillablePDF is an extremely simple and lightweight utility that bridges iText and Ruby in order to fill out fillable PDF forms or extract field values from previously filled out PDF forms.'
13
13
  spec.homepage = 'https://github.com/vkononov/fillable-pdf'
14
14
  spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
15
16
 
16
17
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
17
18
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(example|test|spec|features)/}) }
@@ -20,9 +21,18 @@ Gem::Specification.new do |spec|
20
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
22
  spec.require_paths = %w[ext lib]
22
23
 
23
- spec.add_development_dependency 'bundler', '~> 2.0'
24
- spec.add_development_dependency 'minitest', '~> 5.0'
25
- spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'bundler'
25
+ spec.add_development_dependency 'minitest'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rubocop'
28
+ spec.add_development_dependency 'rubocop-md'
29
+ spec.add_development_dependency 'rubocop-minitest'
30
+ spec.add_development_dependency 'rubocop-performance'
31
+ spec.add_development_dependency 'rubocop-rake'
26
32
 
27
- spec.add_runtime_dependency 'rjb', '~> 1.6'
33
+ spec.add_runtime_dependency 'rjb', '1.6.2'
34
+
35
+ spec.metadata = {
36
+ 'rubygems_mfa_required' => 'true'
37
+ }
28
38
  end
data/lib/field.rb CHANGED
@@ -1,15 +1,13 @@
1
1
  require_relative 'fillable-pdf/itext'
2
- require_relative 'kernel'
3
2
 
4
3
  class Field
5
4
  # PdfName has a constant "A" and a constant "a". Unfortunately, RJB does not differentiate
6
5
  # between these constants and tries to create the same constant ("A") for both, which causes
7
6
  # an annoying warning "already initialized constant Rjb::Com_itextpdf_kernel_pdf_PdfName::A".
8
7
  # As long as RJB has not fixed this issue, this warning will remain suppressed.
9
- suppress_warnings { PDF_NAME = Rjb.import('com.itextpdf.kernel.pdf.PdfName') }
10
8
 
11
- BUTTON = PDF_NAME.Btn.toString
12
- CHOICE = PDF_NAME.Ch.toString
13
- SIGNATURE = PDF_NAME.Sig.toString
14
- TEXT = PDF_NAME.Tx.toString
15
- end
9
+ BUTTON = ITEXT::PdfName.Btn.toString
10
+ CHOICE = ITEXT::PdfName.Ch.toString
11
+ SIGNATURE = ITEXT::PdfName.Sig.toString
12
+ TEXT = ITEXT::PdfName.Tx.toString
13
+ end
@@ -1,3 +1,24 @@
1
+ require_relative 'kernel'
1
2
  require 'rjb'
2
3
 
3
4
  Rjb.load(Dir.glob(File.expand_path('../../ext/*.jar', __dir__)).join(':'))
5
+
6
+ module ITEXT
7
+ suppress_warnings do
8
+ ByteArrayOutputStream = Rjb.import 'com.itextpdf.io.source.ByteArrayOutputStream'
9
+ Canvas = Rjb.import 'com.itextpdf.layout.Canvas'
10
+ Div = Rjb.import 'com.itextpdf.layout.element.Div'
11
+ HorizontalAlignment = Rjb.import 'com.itextpdf.layout.property.HorizontalAlignment'
12
+ Image = Rjb.import 'com.itextpdf.layout.element.Image'
13
+ ImageDataFactory = Rjb.import 'com.itextpdf.io.image.ImageDataFactory'
14
+ PdfAcroForm = Rjb.import 'com.itextpdf.forms.PdfAcroForm'
15
+ PdfDictionary = Rjb.import 'com.itextpdf.kernel.pdf.PdfDictionary'
16
+ PdfDocument = Rjb.import 'com.itextpdf.kernel.pdf.PdfDocument'
17
+ PdfFormXObject = Rjb.import 'com.itextpdf.kernel.pdf.xobject.PdfFormXObject'
18
+ PdfName = Rjb.import 'com.itextpdf.kernel.pdf.PdfName'
19
+ PdfReader = Rjb.import 'com.itextpdf.kernel.pdf.PdfReader'
20
+ PdfWriter = Rjb.import 'com.itextpdf.kernel.pdf.PdfWriter'
21
+ Rectangle = Rjb.import 'com.itextpdf.kernel.geom.Rectangle'
22
+ VerticalAlignment = Rjb.import 'com.itextpdf.layout.property.VerticalAlignment'
23
+ end
24
+ end
@@ -6,4 +6,4 @@ module Kernel
6
6
  $VERBOSE = original_verbosity
7
7
  result
8
8
  end
9
- end
9
+ end
@@ -1,3 +1,3 @@
1
1
  class FillablePDF
2
- VERSION = '0.8.0'
2
+ VERSION = '0.9.3'
3
3
  end
data/lib/fillable-pdf.rb CHANGED
@@ -1,33 +1,27 @@
1
1
  require_relative 'fillable-pdf/itext'
2
2
  require_relative 'field'
3
+ require 'base64'
4
+ require 'fileutils'
3
5
  require 'securerandom'
4
6
 
5
- class FillablePDF
6
- # required Java imports
7
- BYTE_STREAM = Rjb.import 'com.itextpdf.io.source.ByteArrayOutputStream'
8
- PDF_READER = Rjb.import 'com.itextpdf.kernel.pdf.PdfReader'
9
- PDF_WRITER = Rjb.import 'com.itextpdf.kernel.pdf.PdfWriter'
10
- PDF_DOCUMENT = Rjb.import 'com.itextpdf.kernel.pdf.PdfDocument'
11
- PDF_ACRO_FORM = Rjb.import 'com.itextpdf.forms.PdfAcroForm'
12
- PDF_FORM_FIELD = Rjb.import 'com.itextpdf.forms.fields.PdfFormField'
13
-
7
+ class FillablePDF # rubocop:disable Metrics/ClassLength
14
8
  ##
15
9
  # Opens a given fillable-pdf PDF file and prepares it for modification.
16
10
  #
17
11
  # @param [String|Symbol] file_path the name of the PDF file or file path
18
12
  #
19
13
  def initialize(file_path)
20
- raise IOError, "File at `#{file_path}' is not found" unless File.exist?(file_path)
14
+ raise IOError, "File <#{file_path}> is not found" unless File.exist?(file_path)
21
15
  @file_path = file_path
22
16
  begin
23
- @byte_stream = BYTE_STREAM.new
24
- @pdf_reader = PDF_READER.new @file_path
25
- @pdf_writer = PDF_WRITER.new @byte_stream
26
- @pdf_doc = PDF_DOCUMENT.new @pdf_reader, @pdf_writer
27
- @pdf_form = PDF_ACRO_FORM.getAcroForm(@pdf_doc, true)
17
+ @byte_stream = ITEXT::ByteArrayOutputStream.new
18
+ @pdf_reader = ITEXT::PdfReader.new @file_path.to_s
19
+ @pdf_writer = ITEXT::PdfWriter.new @byte_stream
20
+ @pdf_doc = ITEXT::PdfDocument.new @pdf_reader, @pdf_writer
21
+ @pdf_form = ITEXT::PdfAcroForm.getAcroForm(@pdf_doc, true)
28
22
  @form_fields = @pdf_form.getFormFields
29
- rescue StandardError => ex
30
- raise "#{ex.message} (input file may be corrupt, incompatible, or may not have any forms)"
23
+ rescue StandardError => e
24
+ raise "#{e.message} (Input file may be corrupt, incompatible, read-only, write-protected, encrypted, or may not have any form fields)" # rubocop:disable Layout/LineLength
31
25
  end
32
26
  end
33
27
 
@@ -98,6 +92,64 @@ class FillablePDF
98
92
  pdf_field(key).setValue(value.to_s)
99
93
  end
100
94
 
95
+ ##
96
+ # Sets an image within the bounds of the given form field. It doesn't matter
97
+ # what type of form field it is (signature, image, etc). The image will be scaled
98
+ # to fill the available space while preserving its aspect ratio. All previous
99
+ # content will be removed, which means you cannot have both text and image.
100
+ #
101
+ # @param [String|Symbol] key the field name
102
+ # @param [String|Symbol] file_path the name of the image file or image path
103
+ #
104
+ def set_image(key, file_path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
105
+ raise IOError, "File <#{file_path}> is not found" unless File.exist?(file_path)
106
+ field = pdf_field(key)
107
+ widgets = field.getWidgets
108
+ widget_dict = suppress_warnings { widgets.isEmpty ? field.getPdfObject : widgets.get(0).getPdfObject }
109
+ orig_rect = widget_dict.getAsRectangle(ITEXT::PdfName.Rect)
110
+ border_width = field.getBorderWidth
111
+ bounding_rectangle = ITEXT::Rectangle.new(
112
+ orig_rect.getWidth - (border_width * 2),
113
+ orig_rect.getHeight - (border_width * 2)
114
+ )
115
+
116
+ pdf_form_x_object = ITEXT::PdfFormXObject.new(bounding_rectangle)
117
+ canvas = ITEXT::Canvas.new(pdf_form_x_object, @pdf_doc)
118
+ image = ITEXT::Image.new(ITEXT::ImageDataFactory.create(file_path.to_s))
119
+ .setAutoScale(true)
120
+ .setHorizontalAlignment(ITEXT::HorizontalAlignment.CENTER)
121
+ container = ITEXT::Div.new
122
+ .setMargin(border_width).add(image)
123
+ .setVerticalAlignment(ITEXT::VerticalAlignment.MIDDLE)
124
+ .setFillAvailableArea(true)
125
+ canvas.add(container)
126
+ canvas.close
127
+
128
+ pdf_dict = ITEXT::PdfDictionary.new
129
+ widget_dict.put(ITEXT::PdfName.AP, pdf_dict)
130
+ pdf_dict.put(ITEXT::PdfName.N, pdf_form_x_object.getPdfObject)
131
+ widget_dict.setModified
132
+ rescue StandardError => e
133
+ raise "#{e.message} (there may be something wrong with your image)"
134
+ end
135
+
136
+ ##
137
+ # Sets an image within the bounds of the given form field. It doesn't matter
138
+ # what type of form field it is (signature, image, etc). The image will be scaled
139
+ # to fill the available space while preserving its aspect ratio. All previous
140
+ # content will be removed, which means you cannot have both text and image.
141
+ #
142
+ # @param [String|Symbol] key the field name
143
+ # @param [String|Symbol] base64_image_data base64 encoded data image
144
+ #
145
+ def set_image_base64(key, base64_image_data)
146
+ tmp_file = SecureRandom.uuid
147
+ File.open(tmp_file, 'wb') { |f| f.write(Base64.decode64(base64_image_data)) }
148
+ set_image(key, tmp_file)
149
+ ensure
150
+ FileUtils.rm tmp_file
151
+ end
152
+
101
153
  ##
102
154
  # Sets the values of multiple fields given a set of unique field names and values.
103
155
  #
@@ -158,7 +210,7 @@ class FillablePDF
158
210
  def save(flatten: false)
159
211
  tmp_file = SecureRandom.uuid
160
212
  save_as(tmp_file, flatten: flatten)
161
- File.rename tmp_file, @file_path
213
+ FileUtils.mv tmp_file, @file_path
162
214
  end
163
215
 
164
216
  ##
metadata CHANGED
@@ -1,71 +1,141 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fillable-pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vadim Kononov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-16 00:00:00.000000000 Z
11
+ date: 2021-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '5.0'
33
+ version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '5.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-md
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-performance
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
55
125
  - !ruby/object:Gem::Dependency
56
126
  name: rjb
57
127
  requirement: !ruby/object:Gem::Requirement
58
128
  requirements:
59
- - - "~>"
129
+ - - '='
60
130
  - !ruby/object:Gem::Version
61
- version: '1.6'
131
+ version: 1.6.2
62
132
  type: :runtime
63
133
  prerelease: false
64
134
  version_requirements: !ruby/object:Gem::Requirement
65
135
  requirements:
66
- - - "~>"
136
+ - - '='
67
137
  - !ruby/object:Gem::Version
68
- version: '1.6'
138
+ version: 1.6.2
69
139
  description: FillablePDF is an extremely simple and lightweight utility that bridges
70
140
  iText and Ruby in order to fill out fillable PDF forms or extract field values from
71
141
  previously filled out PDF forms.
@@ -79,29 +149,30 @@ files:
79
149
  - ".rubocop.yml"
80
150
  - ".travis.yml"
81
151
  - Gemfile
82
- - LICENSE.txt
152
+ - LICENSE.md
83
153
  - README.md
84
154
  - Rakefile
85
155
  - bin/console
86
156
  - bin/setup
87
- - ext/font-asian-7.1.9.jar
88
- - ext/forms-7.1.9.jar
89
- - ext/io-7.1.9.jar
90
- - ext/kernel-7.1.9.jar
91
- - ext/layout-7.1.9.jar
92
- - ext/slf4j-api-1.7.29.jar
93
- - ext/slf4j-simple-1.7.29.jar
157
+ - ext/font-asian-7.1.17.jar
158
+ - ext/forms-7.1.17.jar
159
+ - ext/io-7.1.17.jar
160
+ - ext/kernel-7.1.17.jar
161
+ - ext/layout-7.1.17.jar
162
+ - ext/slf4j-api-1.7.32.jar
163
+ - ext/slf4j-simple-1.7.32.jar
94
164
  - fillable-pdf.gemspec
95
165
  - lib/field.rb
96
166
  - lib/fillable-pdf.rb
97
167
  - lib/fillable-pdf/itext.rb
168
+ - lib/fillable-pdf/kernel.rb
98
169
  - lib/fillable-pdf/version.rb
99
- - lib/kernel.rb
100
170
  homepage: https://github.com/vkononov/fillable-pdf
101
171
  licenses:
102
172
  - MIT
103
- metadata: {}
104
- post_install_message:
173
+ metadata:
174
+ rubygems_mfa_required: 'true'
175
+ post_install_message:
105
176
  rdoc_options: []
106
177
  require_paths:
107
178
  - ext
@@ -110,15 +181,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
110
181
  requirements:
111
182
  - - ">="
112
183
  - !ruby/object:Gem::Version
113
- version: '0'
184
+ version: 2.4.0
114
185
  required_rubygems_version: !ruby/object:Gem::Requirement
115
186
  requirements:
116
187
  - - ">="
117
188
  - !ruby/object:Gem::Version
118
189
  version: '0'
119
190
  requirements: []
120
- rubygems_version: 3.1.2
121
- signing_key:
191
+ rubygems_version: 3.2.22
192
+ signing_key:
122
193
  specification_version: 4
123
194
  summary: Fill out or extract field values from simple fillable PDF forms using iText.
124
195
  test_files: []
Binary file
data/ext/forms-7.1.9.jar DELETED
Binary file
data/ext/io-7.1.9.jar DELETED
Binary file
data/ext/kernel-7.1.9.jar DELETED
Binary file
data/ext/layout-7.1.9.jar DELETED
Binary file
Binary file
Binary file