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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 389bff629090f3998b0981c03d347801d3dde03b7a1e829634980a38524cc7c4
4
- data.tar.gz: c23c74c352cd65c434cca51056276757c6e7d0f217119d9a2de42bc76203db92
3
+ metadata.gz: 7043c6e7b7149563d54e87952727cbd2b769d7ab6cbdf6eebfbea91c89e0e6f8
4
+ data.tar.gz: cad339bef276e6baf84473ff4e496b9e44e2a0ea487262850e27954cc29a4df4
5
5
  SHA512:
6
- metadata.gz: dfb50d0139200dce237e1074496acbc3b24cb0b0b89884a233763c988892b8700cd597433989666ba68e3edb1dd20afb4d451b56644002f19115e18359f9314f
7
- data.tar.gz: 5a9e3d9341305c82a8df63215e02d4e9c590a4e0ef405a0c5a5748ec49f7a9c46284b8dace44907c3adbc2792f516e61e67f5a6c07b91375979ecb3f0b6d362f
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
@@ -3,7 +3,7 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  rvm:
6
- - 3.0.2
6
+ - 3.0.3
7
7
  jdk:
8
8
  - openjdk8
9
9
  before_install:
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://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
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 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).
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 14 fields.
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 15, 2021", :newsletter=>"Off", :nascar=>"Yes", :language=>"dart", :"language.1"=>"dart", :"language.2"=>"dart", :"language.3"=>"dart", :"language.4"=>"dart"}
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 15, 2021", "Off", "Yes", "dart", "dart", "dart", "dart", "dart"]
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 = 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.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
@@ -1,3 +1,3 @@
1
1
  class FillablePDF
2
- VERSION = '0.9.2'
2
+ VERSION = '0.9.3'
3
3
  end
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 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)
22
15
  @file_path = file_path
23
16
  begin
24
- @byte_stream = BYTE_STREAM.new
25
- @pdf_reader = PDF_READER.new @file_path.to_s
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
30
23
  rescue StandardError => e
31
- raise "#{e.message} (input file may be corrupt, incompatible, or may not have any forms)"
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.2
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-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
@@ -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