fillable-pdf 0.9.1 → 0.9.4

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: 998124f748072a08e1a2f98593b85ba4297952c414bb800c7f05df607d76d1a7
4
- data.tar.gz: d9f16c8942b1c167b476bcc5c630724f631c40285fe05cae20894d542b2daeb7
3
+ metadata.gz: 3295f6a5e77e53fb9ae16f59cb1456483daecf203857cd903fd84622336f9d35
4
+ data.tar.gz: 4bb925c8f5908f663d36f93afb701c2dc5dcd744f476a50b2bf17fc73ff97f6d
5
5
  SHA512:
6
- metadata.gz: 8e864663a4319fc61ee01448839615ab6125960c5e7a4a39585baa0ce14a339701b748332ffe7dd35bcf7a69fbcc271435e883ee8be86438b818d00d8a034595
7
- data.tar.gz: 485287c89ed9073a01b97afc72d1ab96eec27b2024891c90b69fb2243d25409b14b22b76046ec0fcd2b4b2ed27262b8752f9d0037baff3bcf7a7004617ac83a3
6
+ metadata.gz: ae5c10683e12cda0c209126d256b6f223d2b4194c85b06be9af9e76c12a710c8fe5d8fe6b3ff646d0b285ff93d0f97a8a0bd0c655474b52746a7c43460fd7925
7
+ data.tar.gz: a793a4b88d8034f9d3194fd19c07244c4d1083477f9004307208c1b9b23764fc0dde863f6f2a54c0abb264c90f5b2d05360b850fbc074afe9f259d4f51a449cf
data/.rubocop.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  require:
2
+ - rubocop-md
2
3
  - rubocop-minitest
3
4
  - rubocop-performance
4
5
  - rubocop-rake
@@ -7,18 +8,35 @@ AllCops:
7
8
  NewCops: enable
8
9
  Exclude:
9
10
  - .git/**/*
11
+ - vendor/bundle/**/*
12
+
13
+ Gemspec/RequiredRubyVersion:
14
+ Enabled: false
10
15
 
11
16
  Layout/EmptyLineAfterGuardClause:
12
17
  Enabled: false
13
18
 
19
+ Layout/IndentationConsistency:
20
+ Exclude:
21
+ - README.md
22
+
23
+ Layout/InitialIndentation:
24
+ Exclude:
25
+ - README.md
26
+
14
27
  Layout/LineLength:
15
28
  Exclude:
29
+ - README.md
16
30
  - fillable-pdf.gemspec
17
31
  Max: 120
18
32
 
19
33
  Layout/SpaceInsideHashLiteralBraces:
20
34
  Enabled: false
21
35
 
36
+ Lint/ConstantDefinitionInBlock:
37
+ Exclude:
38
+ - lib/fillable-pdf/itext.rb
39
+
22
40
  Metrics/MethodLength:
23
41
  Max: 12
24
42
 
data/.travis.yml CHANGED
@@ -3,7 +3,7 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 3.0.0
6
+ - 3.0.3
7
7
  jdk:
8
8
  - openjdk8
9
9
  before_install:
data/README.md CHANGED
@@ -2,40 +2,51 @@
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.
6
8
 
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
 
9
10
  ## Known Issues
10
11
 
11
- If the gem hangs in `development`, removing the following gems may fix the issue:
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.
12
13
 
13
- ```ruby
14
- gem 'spring'
15
- gem 'spring-watcher-listen'
16
- ```
14
+ 2. If the gem hangs in `development`, removing the following gems may fix the issue:
17
15
 
18
- If the gem hangs in `production`, you could try to use `puma` with a reverse proxy to host the application.
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.
19
22
 
20
23
 
21
24
  ## Installation
22
25
 
23
- **Ensure that your `JAVA_HOME` variable is set before installing this gem (see examples below).**
24
-
25
- * OSX: `/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home`
26
- * Ubuntu/CentOS: `/usr/lib/jvm/java-1.8.0-openjdk`
26
+ **Prerequisites:** Java SE Development Kit v8, v11
27
+
28
+ - Ensure that your `JAVA_HOME` variable is set before installing this gem (see examples below).**
29
+
30
+ * OSX: `/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home`
31
+ * Ubuntu/CentOS: `/usr/lib/jvm/java-1.8.0-openjdk`
27
32
 
28
33
  Add this line to your application's Gemfile:
29
34
 
30
- gem 'fillable-pdf'
35
+ ```ruby
36
+ gem 'fillable-pdf'
37
+ ```
31
38
 
32
39
  And then execute:
33
40
 
34
- bundle
41
+ ```bash
42
+ bundle
43
+ ```
35
44
 
36
45
  Or install it yourself as:
37
46
 
38
- gem install fillable-pdf
47
+ ```bash
48
+ gem install fillable-pdf
49
+ ```
39
50
 
40
51
  If you are using this gem in a script, you need to require it manually:
41
52
 
@@ -57,127 +68,257 @@ pdf = FillablePDF.new 'input.pdf'
57
68
  pdf.close
58
69
  ```
59
70
 
60
- ## Instance Methods
71
+ ### Checking / Unchecking Checkboxes
72
+
73
+ Use the values `'Yes'` and `'Off'` to check and uncheck checkboxes, respectively. For example:
74
+
75
+ ```ruby
76
+ pdf.set_field(:newsletter, 'Yes')
77
+ pdf.set_field(:newsletter, 'Off')
78
+ ```
79
+
80
+ ### Checking / Unchecking Radio Buttons
81
+
82
+ Suppose you have the following a radio button field name `language` with the following options:
83
+
84
+ - Ruby (`ruby`)
85
+ - Python (`python`)
86
+ - Dart (`dart`)
87
+ - Other (`other`)
88
+
89
+ To select one of these options (or change the current option) use:
90
+
91
+ ```ruby
92
+ pdf.set_field(:language, 'dart')
93
+ ```
94
+
95
+ To unset the radio button use the `'Off'` string:
96
+
97
+ ```ruby
98
+ pdf.set_field(:language, 'Off')
99
+ ```
100
+
101
+ ### Adding Signatures or Images
102
+
103
+ Digital signatures are not supported, but you can place an image or a base64 encoded image within the bounds of any form field.
104
+
105
+ SVG images are not supported. You will have to convert them to a JPG or PNG first.
106
+
107
+ See methods `set_image` and `set_image_base64` below.
108
+
109
+ ### Instance Methods
61
110
 
62
111
  An instance of `FillablePDF` has the following methods at its disposal:
63
112
 
64
113
  * `any_fields?`
65
- *Determines whether the form has any fields.*
66
- ```ruby
67
- pdf.any_fields?
68
- # output example: true
69
- ```
114
+ *Determines whether the form has any fields.*
115
+
116
+ ```ruby
117
+ pdf.any_fields?
118
+ # output example: true
119
+ ```
70
120
 
71
121
  * `num_fields`
72
- *Returns the total number of fillable form fields.*
73
- ```ruby
74
- # output example: 10
75
- pdf.num_fields
76
- ```
122
+ *Returns the total number of fillable form fields.*
123
+
124
+ ```ruby
125
+ # output example: 10
126
+ pdf.num_fields
127
+ ```
77
128
 
78
129
  * `field`
79
- *Retrieves the value of a field given its unique field name.*
80
- ```ruby
81
- pdf.field(:full_name)
82
- # output example: 'Richard'
83
- ```
130
+ *Retrieves the value of a field given its unique field name.*
131
+
132
+ ```ruby
133
+ pdf.field(:full_name)
134
+ # output example: 'Richard'
135
+ ```
84
136
 
85
137
  * `field_type`
86
- *Retrieves the numeric type of a field given its unique field name.*
87
- ```ruby
88
- pdf.field_type(:football)
89
- # output example: 4
90
-
91
- # list of all field types
92
- Field::BUTTON
93
- Field::CHOICE
94
- Field::SIGNATURE
95
- Field::TEXT
96
- ```
138
+ *Retrieves the numeric type of a field given its unique field name.*
139
+
140
+ ```ruby
141
+ pdf.field_type(:football)
142
+ # output example: '/Btn'
143
+
144
+ # list of all field types
145
+ Field::BUTTON ('/Btn')
146
+ Field::CHOICE ('/Ch')
147
+ Field::SIGNATURE ('/Sig')
148
+ Field::TEXT ('/Tx')
149
+ ```
97
150
 
98
151
  * `fields`
99
- *Retrieves a hash of all fields and their values.*
100
- ```ruby
101
- pdf.fields
102
- # output example: {first_name: "Richard", last_name: "Rahl"}
103
- ```
152
+ *Retrieves a hash of all fields and their values.*
153
+
154
+ ```ruby
155
+ pdf.fields
156
+ # output example: {first_name: "Richard", last_name: "Rahl"}
157
+ ```
104
158
 
105
159
  * `set_field`
106
- *Sets the value of a field given its unique field name and value.*
107
- ```ruby
108
- pdf.set_field(:first_name, 'Richard')
109
- # result: changes the value of 'first_name' to 'Richard'
110
- ```
160
+ *Sets the value of a field given its unique field name and value.*
161
+
162
+ ```ruby
163
+ pdf.set_field(:first_name, 'Richard')
164
+ # result: changes the value of 'first_name' to 'Richard'
165
+ ```
111
166
 
112
167
  * `set_fields`
113
- *Sets the values of multiple fields given a set of unique field names and values.*
114
- ```ruby
115
- pdf.set_fields(first_name: 'Richard', last_name: 'Rahl')
116
- # result: changes the values of 'first_name' and 'last_name'
117
- ```
168
+ *Sets the values of multiple fields given a set of unique field names and values.*
169
+
170
+ ```ruby
171
+ pdf.set_fields(first_name: 'Richard', last_name: 'Rahl')
172
+ # result: changes the values of 'first_name' and 'last_name'
173
+ ```
174
+
175
+ * `set_image`
176
+ *Places an image file within the rectangular bounding box of the given form field.*
177
+
178
+ ```ruby
179
+ pdf.set_image(:signature, 'signature.png')
180
+ # result: the image 'signature.png' is shown in the foreground of the form field
181
+ ```
182
+
183
+ * `set_image_base64`
184
+ *Places a base64 encoded image within the rectangular bounding box of the given form field.*
185
+
186
+ ```ruby
187
+ pdf.set_image_base64(:signature, 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==')
188
+ # result: the base64 encoded image is shown in the foreground of the form field
189
+ ```
118
190
 
119
191
  * `rename_field`
120
- *Renames a field given its unique field name and the new field name.*
121
- ```ruby
122
- pdf.rename_field(:last_name, :surname)
123
- # result: renames field name 'last_name' to 'surname'
124
- # NOTE: this action does not take effect until the document is saved
125
- ```
192
+ *Renames a field given its unique field name and the new field name.*
193
+
194
+ ```ruby
195
+ pdf.rename_field(:last_name, :surname)
196
+ # result: renames field name 'last_name' to 'surname'
197
+ # NOTE: this action does not take effect until the document is saved
198
+ ```
126
199
 
127
200
  * `remove_field`
128
- *Removes a field from the document given its unique field name.*
129
- ```ruby
130
- pdf.remove_field(:last_name)
131
- # result: physically removes field 'last_name' from document
132
- ```
201
+ *Removes a field from the document given its unique field name.*
202
+
203
+ ```ruby
204
+ pdf.remove_field(:last_name)
205
+ # result: physically removes field 'last_name' from document
206
+ ```
133
207
 
134
208
  * `names`
135
- *Returns a list of all field keys used in the document.*
136
- ```ruby
137
- pdf.names
138
- # output example: [:first_name, :last_name]
139
- ```
209
+ *Returns a list of all field keys used in the document.*
210
+
211
+ ```ruby
212
+ pdf.names
213
+ # output example: [:first_name, :last_name]
214
+ ```
140
215
 
141
216
  * `values`
142
- *Returns a list of all field values used in the document.*
143
- ```ruby
144
- pdf.values
145
- # output example: ["Rahl", "Richard"]
146
- ```
217
+ *Returns a list of all field values used in the document.*
218
+
219
+ ```ruby
220
+ pdf.values
221
+ # output example: ["Rahl", "Richard"]
222
+ ```
147
223
 
148
224
  * `save`
149
- *Overwrites the previously opened PDF document and flattens it if requested.*
150
- ```ruby
151
- pdf.save
152
- # result: document is saved without flatenning
153
- pdf.save_as(flatten: true)
154
- # result: document is saved with flatenning
155
- ```
225
+ *Overwrites the previously opened PDF document and flattens it if requested.*
226
+
227
+ ```ruby
228
+ pdf.save
229
+ # result: document is saved without flattening
230
+ pdf.save_as(flatten: true)
231
+ # result: document is saved with flattening
232
+ ```
156
233
 
157
234
  * `save_as`
158
- *Saves the filled out PDF document in a given path and flattens it if requested.*
159
- ```ruby
160
- pdf.save_as('output.pdf')
161
- # result: document is saved in a given path without flatenning
162
- pdf.save_as('output.pdf', flatten: true)
163
- # result: document is saved in a given path with flatenning
164
- ```
235
+ *Saves the filled out PDF document in a given path and flattens it if requested.*
236
+
237
+ ```ruby
238
+ pdf.save_as('output.pdf')
239
+ # result: document is saved in a given path without flattening
240
+ pdf.save_as('output.pdf', flatten: true)
241
+ # result: document is saved in a given path with flattening
242
+ ```
165
243
 
166
- **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.
244
+ **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.
167
245
 
168
246
  * `close`
169
- *Closes the PDF document discarding all unsaved changes.*
170
- ```ruby
171
- pdf.close
172
- # result: document is closed
173
- ```
247
+ *Closes the PDF document discarding all unsaved changes.*
248
+
249
+ ```ruby
250
+ pdf.close
251
+ # result: document is closed
252
+ ```
253
+
254
+
255
+ ## Deployment with Heroku
256
+
257
+ When deploying to Heroku, be sure to install the following build packs (in this order):
258
+
259
+ ```bash
260
+ heroku buildpacks:add heroku/jvm
261
+ heroku buildpacks:add heroku/ruby
262
+ ```
263
+
264
+ ## Deployment with Phusion Passenger + Nginx
265
+
266
+ 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.
267
+
268
+ Below is an example of a simple Nginx virtual host configuration (note the use of `passenger_spawn_method`):
269
+
270
+ ```nginx
271
+ server {
272
+ server_name my-rails-app.com;
273
+ listen 443 ssl http2;
274
+ listen [::]:443 ssl http2;
275
+ passenger_enabled on;
276
+ passenger_spawn_method direct;
277
+ root /home/system/my-rails-app/public;
278
+ }
279
+ ```
280
+
281
+ If you absolutely must have smart spawning, I recommend using `fillable-pdf` as a service that runs independently of your Rails application.
282
+
283
+ ## Deployment with Puma + Nginx
284
+
285
+ In order to use Puma in production, you need to configure a reverse proxy in your Nginx virtual host. Here is simple naive example:
286
+
287
+ ```nginx
288
+ server {
289
+ server_name my-rails-app.com;
290
+ listen 443 ssl http2;
291
+ listen [::]:443 ssl http2;
292
+ location / {
293
+ proxy_pass http://127.0.0.1:8888;
294
+ proxy_redirect off;
295
+ proxy_set_header Connection "upgrade";
296
+ proxy_set_header Host $http_host;
297
+ proxy_set_header Upgrade $http_upgrade;
298
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
299
+ proxy_set_header X-Forwarded-Proto https;
300
+ proxy_set_header X-Real-IP $remote_addr;
301
+ }
302
+ }
303
+ ```
304
+
305
+ Then you'll have to start Puma in production daemon mode as follows:
306
+
307
+ ```bash
308
+ RAILS_ENV=production bin/rails server -p 8888 --daemon
309
+ ```
310
+
311
+ 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.
312
+
174
313
 
175
314
  ## Example
176
315
 
177
- 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).
316
+ 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).
178
317
 
179
318
  ```ruby
180
- require 'fillable-pdf'
319
+ require_relative '../lib/fillable-pdf'
320
+
321
+ BASE64_PHOTO = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==' # rubocop:disable Layout/LineLength
181
322
 
182
323
  # opening a fillable PDF
183
324
  pdf = FillablePDF.new('input.pdf')
@@ -193,9 +334,12 @@ puts
193
334
 
194
335
  # setting form fields
195
336
  pdf.set_fields(first_name: 'Richard', last_name: 'Rahl')
196
- pdf.set_fields(football: 'Yes', baseball: 'Yes',
197
- basketball: 'Yes', nascar: 'Yes', hockey: 'Yes')
337
+ pdf.set_fields(football: 'Yes', baseball: 'Yes', basketball: 'Yes', nascar: 'Yes', hockey: 'Yes')
198
338
  pdf.set_field(:date, Time.now.strftime('%B %e, %Y'))
339
+ pdf.set_field(:newsletter, 'Off') # uncheck the checkbox
340
+ pdf.set_field(:language, 'dart') # select a radio button option
341
+ pdf.set_image_base64(:photo, BASE64_PHOTO)
342
+ pdf.set_image(:signature, 'signature.png')
199
343
 
200
344
  # list of fields
201
345
  puts "Fields hash: #{pdf.fields}"
@@ -230,10 +374,6 @@ puts
230
374
  # Removing field
231
375
  pdf.remove_field :nascar
232
376
  puts "Removed field 'nascar'"
233
- puts
234
-
235
- # printing the name of the person used inside the PDF
236
- puts "Signatory: #{pdf.field(:first_name)} #{pdf.field(:last_name)}"
237
377
 
238
378
  # saving the filled out PDF in another file
239
379
  pdf.save_as('output.pdf')
@@ -248,22 +388,20 @@ pdf.close
248
388
 
249
389
  The example above produces the following output and also generates the output file [output.pdf](example/output.pdf).
250
390
 
251
- ```
252
- The form has a total of 8 fields.
391
+ ```text
392
+ The form has a total of 16 fields.
253
393
 
254
- Fields hash: {:last_name=>"Rahl", :first_name=>"Richard", :football=>"Yes", :baseball=>"Yes", :basketball=>"Yes", :nascar=>"Yes", :hockey=>"Yes", :date=>"August 30, 2019"}
394
+ 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=>""}
255
395
 
256
- Keys: [:last_name, :first_name, :football, :baseball, :basketball, :nascar, :hockey, :date]
396
+ Keys: [:last_name, :first_name, :football, :baseball, :basketball, :hockey, :date, :newsletter, :nascar, :language, :"language.1", :"language.2", :"language.3", :"language.4", :signature, :photo]
257
397
 
258
- Values: ["Rahl", "Richard", "Yes", "Yes", "Yes", "Yes", "Yes", "August 30, 2019"]
398
+ Values: ["Rahl", "Richard", "Yes", "Yes", "Yes", "Yes", "November 16, 2021", "Off", "Yes", "dart", "dart", "dart", "dart", "dart", "", ""]
259
399
 
260
400
  Field 'football' is of type BUTTON
261
401
 
262
402
  Renamed field 'last_name' to 'surname'
263
403
 
264
404
  Removed field 'nascar'
265
-
266
- Signatory: Richard Rahl
267
405
  ```
268
406
 
269
407
  ## Contributing
Binary file
Binary file
data/ext/io-7.2.3.jar ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
data/fillable-pdf.gemspec CHANGED
@@ -31,4 +31,9 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency 'rubocop-rake'
32
32
 
33
33
  spec.add_runtime_dependency 'rjb', '~> 1.6'
34
+ spec.requirements << 'JDK 8.x - 11.x'
35
+
36
+ spec.metadata = {
37
+ 'rubygems_mfa_required' => 'true'
38
+ }
34
39
  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') } # rubocop:disable Lint/ConstantDefinitionInBlock
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
9
+ BUTTON = ITEXT::PdfName.Btn.toString
10
+ CHOICE = ITEXT::PdfName.Ch.toString
11
+ SIGNATURE = ITEXT::PdfName.Sig.toString
12
+ TEXT = ITEXT::PdfName.Tx.toString
15
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.properties.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.properties.VerticalAlignment'
23
+ end
24
+ end
File without changes
@@ -1,3 +1,3 @@
1
1
  class FillablePDF
2
- VERSION = '0.9.1'
2
+ VERSION = '0.9.4'
3
3
  end
data/lib/fillable-pdf.rb CHANGED
@@ -1,34 +1,28 @@
1
1
  require_relative 'fillable-pdf/itext'
2
2
  require_relative 'field'
3
+ require 'base64'
3
4
  require 'fileutils'
4
5
  require 'securerandom'
5
6
 
6
- class FillablePDF
7
- # required Java imports
8
- BYTE_STREAM = Rjb.import 'com.itextpdf.io.source.ByteArrayOutputStream'
9
- PDF_READER = Rjb.import 'com.itextpdf.kernel.pdf.PdfReader'
10
- PDF_WRITER = Rjb.import 'com.itextpdf.kernel.pdf.PdfWriter'
11
- PDF_DOCUMENT = Rjb.import 'com.itextpdf.kernel.pdf.PdfDocument'
12
- PDF_ACRO_FORM = Rjb.import 'com.itextpdf.forms.PdfAcroForm'
13
- PDF_FORM_FIELD = Rjb.import 'com.itextpdf.forms.fields.PdfFormField'
14
-
7
+ class FillablePDF # rubocop:disable Metrics/ClassLength
15
8
  ##
16
9
  # Opens a given fillable-pdf PDF file and prepares it for modification.
17
10
  #
18
11
  # @param [String|Symbol] file_path the name of the PDF file or file path
19
12
  #
20
- def initialize(file_path)
21
- raise IOError, "File at `#{file_path}' is not found" unless File.exist?(file_path)
13
+ def initialize(file_path) # rubocop:disable Metrics/MethodLength
14
+ raise IOError, "File <#{file_path}> is not found" unless File.exist?(file_path)
22
15
  @file_path = file_path
23
16
  begin
24
- @byte_stream = BYTE_STREAM.new
25
- @pdf_reader = PDF_READER.new @file_path
26
- @pdf_writer = PDF_WRITER.new @byte_stream
27
- @pdf_doc = PDF_DOCUMENT.new @pdf_reader, @pdf_writer
28
- @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)
29
22
  @form_fields = @pdf_form.getFormFields
23
+ @pdf_form.setGenerateAppearance false
30
24
  rescue StandardError => e
31
- raise "#{e.message} (input file may be corrupt, incompatible, or may not have any forms)"
25
+ 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
32
26
  end
33
27
  end
34
28
 
@@ -99,6 +93,64 @@ class FillablePDF
99
93
  pdf_field(key).setValue(value.to_s)
100
94
  end
101
95
 
96
+ ##
97
+ # Sets an image within the bounds of the given form field. It doesn't matter
98
+ # what type of form field it is (signature, image, etc). The image will be scaled
99
+ # to fill the available space while preserving its aspect ratio. All previous
100
+ # content will be removed, which means you cannot have both text and image.
101
+ #
102
+ # @param [String|Symbol] key the field name
103
+ # @param [String|Symbol] file_path the name of the image file or image path
104
+ #
105
+ def set_image(key, file_path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
106
+ raise IOError, "File <#{file_path}> is not found" unless File.exist?(file_path)
107
+ field = pdf_field(key)
108
+ widgets = field.getWidgets
109
+ widget_dict = suppress_warnings { widgets.isEmpty ? field.getPdfObject : widgets.get(0).getPdfObject }
110
+ orig_rect = widget_dict.getAsRectangle(ITEXT::PdfName.Rect)
111
+ border_width = field.getBorderWidth
112
+ bounding_rectangle = ITEXT::Rectangle.new(
113
+ orig_rect.getWidth - (border_width * 2),
114
+ orig_rect.getHeight - (border_width * 2)
115
+ )
116
+
117
+ pdf_form_x_object = ITEXT::PdfFormXObject.new(bounding_rectangle)
118
+ canvas = ITEXT::Canvas.new(pdf_form_x_object, @pdf_doc)
119
+ image = ITEXT::Image.new(ITEXT::ImageDataFactory.create(file_path.to_s))
120
+ .setAutoScale(true)
121
+ .setHorizontalAlignment(ITEXT::HorizontalAlignment.CENTER)
122
+ container = ITEXT::Div.new
123
+ .setMargin(border_width).add(image)
124
+ .setVerticalAlignment(ITEXT::VerticalAlignment.MIDDLE)
125
+ .setFillAvailableArea(true)
126
+ canvas.add(container)
127
+ canvas.close
128
+
129
+ pdf_dict = ITEXT::PdfDictionary.new
130
+ widget_dict.put(ITEXT::PdfName.AP, pdf_dict)
131
+ pdf_dict.put(ITEXT::PdfName.N, pdf_form_x_object.getPdfObject)
132
+ widget_dict.setModified
133
+ rescue StandardError => e
134
+ raise "#{e.message} (there may be something wrong with your image)"
135
+ end
136
+
137
+ ##
138
+ # Sets an image within the bounds of the given form field. It doesn't matter
139
+ # what type of form field it is (signature, image, etc). The image will be scaled
140
+ # to fill the available space while preserving its aspect ratio. All previous
141
+ # content will be removed, which means you cannot have both text and image.
142
+ #
143
+ # @param [String|Symbol] key the field name
144
+ # @param [String|Symbol] base64_image_data base64 encoded data image
145
+ #
146
+ def set_image_base64(key, base64_image_data)
147
+ tmp_file = SecureRandom.uuid
148
+ File.binwrite(tmp_file, Base64.decode64(base64_image_data))
149
+ set_image(key, tmp_file)
150
+ ensure
151
+ FileUtils.rm tmp_file
152
+ end
153
+
102
154
  ##
103
155
  # Sets the values of multiple fields given a set of unique field names and values.
104
156
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fillable-pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vadim Kononov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-30 00:00:00.000000000 Z
11
+ date: 2022-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -154,23 +154,25 @@ files:
154
154
  - Rakefile
155
155
  - bin/console
156
156
  - bin/setup
157
- - ext/font-asian-7.1.12.jar
158
- - ext/forms-7.1.12.jar
159
- - ext/io-7.1.12.jar
160
- - ext/kernel-7.1.12.jar
161
- - ext/layout-7.1.12.jar
162
- - ext/slf4j-api-1.7.29.jar
163
- - ext/slf4j-simple-1.7.29.jar
157
+ - ext/commons-7.2.3.jar
158
+ - ext/font-asian-7.2.3.jar
159
+ - ext/forms-7.2.3.jar
160
+ - ext/io-7.2.3.jar
161
+ - ext/kernel-7.2.3.jar
162
+ - ext/layout-7.2.3.jar
163
+ - ext/slf4j-api-1.7.32.jar
164
+ - ext/slf4j-simple-1.7.32.jar
164
165
  - fillable-pdf.gemspec
165
166
  - lib/field.rb
166
167
  - lib/fillable-pdf.rb
167
168
  - lib/fillable-pdf/itext.rb
169
+ - lib/fillable-pdf/kernel.rb
168
170
  - lib/fillable-pdf/version.rb
169
- - lib/kernel.rb
170
171
  homepage: https://github.com/vkononov/fillable-pdf
171
172
  licenses:
172
173
  - MIT
173
- metadata: {}
174
+ metadata:
175
+ rubygems_mfa_required: 'true'
174
176
  post_install_message:
175
177
  rdoc_options: []
176
178
  require_paths:
@@ -186,8 +188,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
188
  - - ">="
187
189
  - !ruby/object:Gem::Version
188
190
  version: '0'
189
- requirements: []
190
- rubygems_version: 3.2.3
191
+ requirements:
192
+ - JDK 8.x - 11.x
193
+ rubygems_version: 3.3.7
191
194
  signing_key:
192
195
  specification_version: 4
193
196
  summary: Fill out or extract field values from simple fillable PDF forms using iText.
data/ext/forms-7.1.12.jar DELETED
Binary file
data/ext/io-7.1.12.jar DELETED
Binary file
Binary file
Binary file
Binary file
Binary file