fillable-pdf 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/.travis.yml +1 -1
- data/README.md +36 -14
- data/lib/field.rb +4 -6
- data/lib/fillable-pdf/itext.rb +21 -0
- data/lib/{kernel.rb → fillable-pdf/kernel.rb} +0 -0
- data/lib/fillable-pdf/version.rb +1 -1
- data/lib/fillable-pdf.rb +67 -16
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7043c6e7b7149563d54e87952727cbd2b769d7ab6cbdf6eebfbea91c89e0e6f8
|
4
|
+
data.tar.gz: cad339bef276e6baf84473ff4e496b9e44e2a0ea487262850e27954cc29a4df4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5df81f03db25731784a1c16d132d0d08366a60e02374c1c4d16a1611a45520270d5f91c4fb1fa351a1f05f1d2881400e5cf304c2965063b9fc71cbce617edf2
|
7
|
+
data.tar.gz: 7cedf3ff9e49d09c7d3fc4467baa5ec7f8bf2761bc247dd03b70d0e620cba5d121aedf33e725e8a79088f601ec77282c491447ae5f149d01c316f70bbc7df60f
|
data/.rubocop.yml
CHANGED
@@ -8,6 +8,7 @@ AllCops:
|
|
8
8
|
NewCops: enable
|
9
9
|
Exclude:
|
10
10
|
- .git/**/*
|
11
|
+
- vendor/bundle/**/*
|
11
12
|
|
12
13
|
Gemspec/RequiredRubyVersion:
|
13
14
|
Enabled: false
|
@@ -25,12 +26,17 @@ Layout/InitialIndentation:
|
|
25
26
|
|
26
27
|
Layout/LineLength:
|
27
28
|
Exclude:
|
29
|
+
- README.md
|
28
30
|
- fillable-pdf.gemspec
|
29
31
|
Max: 120
|
30
32
|
|
31
33
|
Layout/SpaceInsideHashLiteralBraces:
|
32
34
|
Enabled: false
|
33
35
|
|
36
|
+
Lint/ConstantDefinitionInBlock:
|
37
|
+
Exclude:
|
38
|
+
- lib/fillable-pdf/itext.rb
|
39
|
+
|
34
40
|
Metrics/MethodLength:
|
35
41
|
Max: 12
|
36
42
|
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
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://
|
5
|
+
[![Build Status](https://app.travis-ci.com/vkononov/fillable-pdf.svg?branch=master)](http://travis-ci.org/vkononov/fillable-pdf)
|
6
6
|
|
7
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
8
|
|
@@ -18,6 +18,7 @@ FillablePDF is an extremely simple and lightweight utility that bridges iText an
|
|
18
18
|
gem 'spring-watcher-listen'
|
19
19
|
```
|
20
20
|
|
21
|
+
3. Read-only, write-protected or encrypted PDF files are currently not supported.
|
21
22
|
|
22
23
|
## Deployment with Heroku
|
23
24
|
|
@@ -154,6 +155,14 @@ To unset the radio button use the `'Off'` string:
|
|
154
155
|
pdf.set_field(:language, 'Off')
|
155
156
|
```
|
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
|
+
|
157
166
|
### Instance Methods
|
158
167
|
|
159
168
|
An instance of `FillablePDF` has the following methods at its disposal:
|
@@ -220,6 +229,22 @@ An instance of `FillablePDF` has the following methods at its disposal:
|
|
220
229
|
# result: changes the values of 'first_name' and 'last_name'
|
221
230
|
```
|
222
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
|
+
```
|
247
|
+
|
223
248
|
* `rename_field`
|
224
249
|
*Renames a field given its unique field name and the new field name.*
|
225
250
|
|
@@ -285,11 +310,13 @@ An instance of `FillablePDF` has the following methods at its disposal:
|
|
285
310
|
|
286
311
|
## Example
|
287
312
|
|
288
|
-
The following
|
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).
|
289
314
|
|
290
315
|
```ruby
|
291
316
|
require_relative '../lib/fillable-pdf'
|
292
317
|
|
318
|
+
BASE64_PHOTO = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==' # rubocop:disable Layout/LineLength
|
319
|
+
|
293
320
|
# opening a fillable PDF
|
294
321
|
pdf = FillablePDF.new('input.pdf')
|
295
322
|
|
@@ -304,11 +331,12 @@ puts
|
|
304
331
|
|
305
332
|
# setting form fields
|
306
333
|
pdf.set_fields(first_name: 'Richard', last_name: 'Rahl')
|
307
|
-
pdf.set_fields(football: 'Yes', baseball: 'Yes',
|
308
|
-
basketball: 'Yes', nascar: 'Yes', hockey: 'Yes')
|
334
|
+
pdf.set_fields(football: 'Yes', baseball: 'Yes', basketball: 'Yes', nascar: 'Yes', hockey: 'Yes')
|
309
335
|
pdf.set_field(:date, Time.now.strftime('%B %e, %Y'))
|
310
336
|
pdf.set_field(:newsletter, 'Off') # uncheck the checkbox
|
311
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')
|
312
340
|
|
313
341
|
# list of fields
|
314
342
|
puts "Fields hash: #{pdf.fields}"
|
@@ -343,10 +371,6 @@ puts
|
|
343
371
|
# Removing field
|
344
372
|
pdf.remove_field :nascar
|
345
373
|
puts "Removed field 'nascar'"
|
346
|
-
puts
|
347
|
-
|
348
|
-
# printing the name of the person used inside the PDF
|
349
|
-
puts "Signatory: #{pdf.field(:first_name)} #{pdf.field(:last_name)}"
|
350
374
|
|
351
375
|
# saving the filled out PDF in another file
|
352
376
|
pdf.save_as('output.pdf')
|
@@ -362,21 +386,19 @@ pdf.close
|
|
362
386
|
The example above produces the following output and also generates the output file [output.pdf](example/output.pdf).
|
363
387
|
|
364
388
|
```text
|
365
|
-
The form has a total of
|
389
|
+
The form has a total of 16 fields.
|
366
390
|
|
367
|
-
Fields hash: {:last_name=>"Rahl", :first_name=>"Richard", :football=>"Yes", :baseball=>"Yes", :basketball=>"Yes", :hockey=>"Yes", :date=>"November
|
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=>""}
|
368
392
|
|
369
|
-
Keys: [:last_name, :first_name, :football, :baseball, :basketball, :hockey, :date, :newsletter, :nascar, :language, :"language.1", :"language.2", :"language.3", :"language.4"]
|
393
|
+
Keys: [:last_name, :first_name, :football, :baseball, :basketball, :hockey, :date, :newsletter, :nascar, :language, :"language.1", :"language.2", :"language.3", :"language.4", :signature, :photo]
|
370
394
|
|
371
|
-
Values: ["Rahl", "Richard", "Yes", "Yes", "Yes", "Yes", "November
|
395
|
+
Values: ["Rahl", "Richard", "Yes", "Yes", "Yes", "Yes", "November 16, 2021", "Off", "Yes", "dart", "dart", "dart", "dart", "dart", "", ""]
|
372
396
|
|
373
397
|
Field 'football' is of type BUTTON
|
374
398
|
|
375
399
|
Renamed field 'last_name' to 'surname'
|
376
400
|
|
377
401
|
Removed field 'nascar'
|
378
|
-
|
379
|
-
Signatory: Richard Rahl
|
380
402
|
```
|
381
403
|
|
382
404
|
## Contributing
|
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 =
|
12
|
-
CHOICE =
|
13
|
-
SIGNATURE =
|
14
|
-
TEXT =
|
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
|
data/lib/fillable-pdf/itext.rb
CHANGED
@@ -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
|
File without changes
|
data/lib/fillable-pdf/version.rb
CHANGED
data/lib/fillable-pdf.rb
CHANGED
@@ -1,34 +1,27 @@
|
|
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
13
|
def initialize(file_path)
|
21
|
-
raise IOError, "File
|
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 =
|
25
|
-
@pdf_reader =
|
26
|
-
@pdf_writer =
|
27
|
-
@pdf_doc =
|
28
|
-
@pdf_form =
|
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
|
30
23
|
rescue StandardError => e
|
31
|
-
raise "#{e.message} (
|
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
|
32
25
|
end
|
33
26
|
end
|
34
27
|
|
@@ -99,6 +92,64 @@ class FillablePDF
|
|
99
92
|
pdf_field(key).setValue(value.to_s)
|
100
93
|
end
|
101
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
|
+
|
102
153
|
##
|
103
154
|
# Sets the values of multiple fields given a set of unique field names and values.
|
104
155
|
#
|
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.
|
4
|
+
version: 0.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vadim Kononov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -165,8 +165,8 @@ files:
|
|
165
165
|
- lib/field.rb
|
166
166
|
- lib/fillable-pdf.rb
|
167
167
|
- lib/fillable-pdf/itext.rb
|
168
|
+
- lib/fillable-pdf/kernel.rb
|
168
169
|
- lib/fillable-pdf/version.rb
|
169
|
-
- lib/kernel.rb
|
170
170
|
homepage: https://github.com/vkononov/fillable-pdf
|
171
171
|
licenses:
|
172
172
|
- MIT
|