fillable-pdf 0.9.5.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/lint.yml +22 -0
- data/.github/workflows/test.yml +23 -20
- data/.gitignore +1 -0
- data/.rubocop.yml +2 -1
- data/Appraisals +29 -0
- data/Gemfile +19 -0
- data/README.md +42 -17
- data/bin/lint +30 -0
- data/ext/commons-9.4.0.jar +0 -0
- data/ext/{font-asian-7.2.4.jar → font-asian-9.4.0.jar} +0 -0
- data/ext/forms-9.4.0.jar +0 -0
- data/ext/io-9.4.0.jar +0 -0
- data/ext/kernel-9.4.0.jar +0 -0
- data/ext/layout-9.4.0.jar +0 -0
- data/ext/slf4j-api-2.0.17.jar +0 -0
- data/ext/slf4j-simple-2.0.17.jar +0 -0
- data/fillable-pdf.gemspec +3 -11
- data/lib/fillable-pdf/errors.rb +13 -0
- data/lib/fillable-pdf/field.rb +15 -0
- data/lib/fillable-pdf/itext.rb +22 -18
- data/lib/fillable-pdf/suppress_warnings.rb +11 -0
- data/lib/fillable-pdf/version.rb +1 -1
- data/lib/fillable-pdf.rb +219 -71
- metadata +24 -121
- data/ext/commons-7.2.4.jar +0 -0
- data/ext/forms-7.2.4.jar +0 -0
- data/ext/io-7.2.4.jar +0 -0
- data/ext/kernel-7.2.4.jar +0 -0
- data/ext/layout-7.2.4.jar +0 -0
- data/ext/slf4j-api-2.0.4.jar +0 -0
- data/ext/slf4j-simple-2.0.4.jar +0 -0
- data/lib/field.rb +0 -13
- data/lib/fillable-pdf/kernel.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 21838c4d6acfe2fee7d07688316c0b4fc32e7e5898dfcd0f77559424e3770802
|
|
4
|
+
data.tar.gz: 153fc518eee4b2fa76a91ce6228739d5d3cef4d5d4f1be05e3ee6a5f6fcf7ce7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41d10ee4f4751d2a8213ed969f16c1c1bb4e80e87e2da9e4dce0a8af73df26bf80c148edbfdbfd5bfdb4876ac090da6783a5304891110802b43a0d6251c7881a
|
|
7
|
+
data.tar.gz: 14b5a5d075ab12359ecda3d93ec1502c2b9b114376e81fbb2946fa9ff7fa1fbac93e091b90df8599280114735fedd3d2d201f5d607bcf3a2c1bd130e388a76c0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
name: Rubocop
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- name: Set up latest Ruby
|
|
17
|
+
uses: ruby/setup-ruby@v1
|
|
18
|
+
with:
|
|
19
|
+
ruby-version: 'ruby'
|
|
20
|
+
bundler-cache: true
|
|
21
|
+
- name: Run Rubocop
|
|
22
|
+
run: bin/lint --no-fix
|
data/.github/workflows/test.yml
CHANGED
|
@@ -1,32 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
# They are provided by a third-party and are governed by
|
|
3
|
-
# separate terms of service, privacy policy, and support
|
|
4
|
-
# documentation.
|
|
1
|
+
name: Test
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
on: [push, pull_request]
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
pull_request:
|
|
13
8
|
|
|
14
9
|
jobs:
|
|
15
10
|
test:
|
|
16
|
-
|
|
17
11
|
runs-on: ubuntu-latest
|
|
18
|
-
|
|
12
|
+
name: Ruby ${{ matrix.ruby }} / Java ${{ matrix.java }}
|
|
19
13
|
strategy:
|
|
14
|
+
fail-fast: false
|
|
20
15
|
matrix:
|
|
21
|
-
ruby
|
|
16
|
+
ruby: [ '3.4', '3.3', '3.2', '3.1', '3.0', '2.7', '2.6', '2.5', '2.4' ]
|
|
17
|
+
java: [ '8', '11', '17', '21', '23' ]
|
|
22
18
|
|
|
23
19
|
steps:
|
|
24
|
-
- uses: actions/checkout@
|
|
25
|
-
- name: Set up
|
|
26
|
-
uses:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- name: Set up Java ${{ matrix.java }}
|
|
22
|
+
uses: actions/setup-java@v4
|
|
23
|
+
with:
|
|
24
|
+
distribution: 'temurin'
|
|
25
|
+
java-version: ${{ matrix.java }}
|
|
26
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
|
27
|
+
uses: ruby/setup-ruby@v1
|
|
27
28
|
with:
|
|
28
|
-
ruby-version: ${{ matrix.ruby
|
|
29
|
+
ruby-version: ${{ matrix.ruby }}
|
|
30
|
+
bundler-cache: true
|
|
31
|
+
cache-version: java-${{ matrix.java }}
|
|
29
32
|
- name: Install dependencies
|
|
30
|
-
run: bundle install
|
|
33
|
+
run: bundle install && bundle exec appraisal install
|
|
31
34
|
- name: Run tests
|
|
32
|
-
run: bundle exec rake test
|
|
35
|
+
run: bundle exec appraisal rake test
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/Appraisals
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
appraise 'rjb-1.6-base64-0.1' do
|
|
2
|
+
gem 'rjb', '~> 1.6.0'
|
|
3
|
+
gem 'base64', '~> 0.1.0'
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
appraise 'rjb-1.6-base64-0.2' do
|
|
7
|
+
gem 'rjb', '~> 1.6.0'
|
|
8
|
+
gem 'base64', '~> 0.2.0'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
appraise 'rjb-1.6-base64-0.3' do
|
|
12
|
+
gem 'rjb', '~> 1.6.0'
|
|
13
|
+
gem 'base64', '~> 0.3.0'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
appraise 'rjb-1.7-base64-0.1' do
|
|
17
|
+
gem 'rjb', '~> 1.7.0'
|
|
18
|
+
gem 'base64', '~> 0.1.0'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
appraise 'rjb-1.7-base64-0.2' do
|
|
22
|
+
gem 'rjb', '~> 1.7.0'
|
|
23
|
+
gem 'base64', '~> 0.2.0'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
appraise 'rjb-1.7-base64-0.3' do
|
|
27
|
+
gem 'rjb', '~> 1.7.0'
|
|
28
|
+
gem 'base64', '~> 0.3.0'
|
|
29
|
+
end
|
data/Gemfile
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
2
|
|
|
3
|
+
# Specify your gem's dependencies in cloudflare-turnstile-rails.gemspec
|
|
3
4
|
gemspec
|
|
5
|
+
|
|
6
|
+
# A Ruby library for testing your library against different versions of dependencies
|
|
7
|
+
gem 'appraisal'
|
|
8
|
+
|
|
9
|
+
group :development do
|
|
10
|
+
gem 'rake'
|
|
11
|
+
|
|
12
|
+
# Code Linting
|
|
13
|
+
gem 'rubocop', require: false
|
|
14
|
+
gem 'rubocop-md', require: false
|
|
15
|
+
gem 'rubocop-minitest', require: false
|
|
16
|
+
gem 'rubocop-performance', require: false
|
|
17
|
+
gem 'rubocop-rake', require: false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
group :test do
|
|
21
|
+
gem 'minitest'
|
|
22
|
+
end
|
data/README.md
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
# FillablePDF
|
|
3
3
|
|
|
4
4
|
[](https://rubygems.org/gems/fillable-pdf)
|
|
5
|
-
[](https://github.com/vkononov/fillable-pdf/actions/workflows/lint.yml)
|
|
6
|
+
[](https://github.com/vkononov/fillable-pdf/actions/workflows/test.yml)
|
|
6
7
|
|
|
7
8
|
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
|
+
Supports `Ruby >= 2.4.0` with `JDK >= 8`
|
|
11
|
+
|
|
9
12
|
[](https://www.buymeacoffee.com/vkononov)
|
|
10
13
|
|
|
11
14
|
## Known Issues
|
|
@@ -38,7 +41,7 @@ FillablePDF is an extremely simple and lightweight utility that bridges iText an
|
|
|
38
41
|
|
|
39
42
|

|
|
40
43
|
|
|
41
|
-
If
|
|
44
|
+
If all of the fields are blank, try setting the `generate_appearance` flag to `true` when calling `set_field` or `set_fields`.
|
|
42
45
|
|
|
43
46
|
### Invalid Checkbox Appearances
|
|
44
47
|
|
|
@@ -54,12 +57,13 @@ If your checkboxes are showing incorrectly, it's likely because iText is overwri
|
|
|
54
57
|
|
|
55
58
|
## Installation
|
|
56
59
|
|
|
57
|
-
**Prerequisites:** Java SE Development Kit
|
|
60
|
+
**Prerequisites:** Java SE Development Kit (JDK)
|
|
58
61
|
|
|
59
62
|
- Ensure that your `JAVA_HOME` variable is set before installing this gem (see examples below).
|
|
60
63
|
|
|
61
|
-
*
|
|
62
|
-
*
|
|
64
|
+
* macOS: `/Library/Java/JavaVirtualMachines/jdk-<version>.jdk/Contents/Home`
|
|
65
|
+
* Linux: `/usr/lib/jvm/java-<version>-openjdk` or `/usr/lib/jvm/temurin-<version>`
|
|
66
|
+
* Windows: `C:\Program Files\Java\jdk-<version>`
|
|
63
67
|
|
|
64
68
|
Add this line to your application's Gemfile:
|
|
65
69
|
|
|
@@ -149,12 +153,12 @@ An instance of `FillablePDF` has the following methods at its disposal:
|
|
|
149
153
|
# output example: true
|
|
150
154
|
```
|
|
151
155
|
|
|
152
|
-
* `
|
|
156
|
+
* `field_count`
|
|
153
157
|
*Returns the total number of fillable form fields.*
|
|
154
158
|
|
|
155
159
|
```ruby
|
|
160
|
+
pdf.field_count
|
|
156
161
|
# output example: 10
|
|
157
|
-
pdf.num_fields
|
|
158
162
|
```
|
|
159
163
|
|
|
160
164
|
* `field(key)`
|
|
@@ -175,17 +179,17 @@ An instance of `FillablePDF` has the following methods at its disposal:
|
|
|
175
179
|
# output example: '/Btn'
|
|
176
180
|
|
|
177
181
|
# list of all field types
|
|
178
|
-
Field::BUTTON ('/Btn')
|
|
179
|
-
Field::CHOICE ('/Ch')
|
|
180
|
-
Field::SIGNATURE ('/Sig')
|
|
181
|
-
Field::TEXT ('/Tx')
|
|
182
|
+
FillablePDF::Field::BUTTON ('/Btn')
|
|
183
|
+
FillablePDF::Field::CHOICE ('/Ch')
|
|
184
|
+
FillablePDF::Field::SIGNATURE ('/Sig')
|
|
185
|
+
FillablePDF::Field::TEXT ('/Tx')
|
|
182
186
|
```
|
|
183
187
|
|
|
184
188
|
You can check the field type by using:
|
|
185
189
|
|
|
186
190
|
```ruby
|
|
187
|
-
pdf.field_type(:football) == Field::BUTTON
|
|
188
|
-
pdf.field_type('football') == Field::BUTTON
|
|
191
|
+
pdf.field_type(:football) == FillablePDF::Field::BUTTON
|
|
192
|
+
pdf.field_type('football') == FillablePDF::Field::BUTTON
|
|
189
193
|
```
|
|
190
194
|
|
|
191
195
|
* `fields`
|
|
@@ -291,7 +295,7 @@ An instance of `FillablePDF` has the following methods at its disposal:
|
|
|
291
295
|
```
|
|
292
296
|
|
|
293
297
|
* `save_as(file_path, flatten: false)`
|
|
294
|
-
*Saves the filled out PDF document in a given path and flattens it if requested.*
|
|
298
|
+
*Saves the filled out PDF document in a given path and flattens it if requested. If the path matches the current file, calls save() instead.*
|
|
295
299
|
|
|
296
300
|
```ruby
|
|
297
301
|
pdf.save_as('output.pdf')
|
|
@@ -302,6 +306,16 @@ An instance of `FillablePDF` has the following methods at its disposal:
|
|
|
302
306
|
|
|
303
307
|
**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.
|
|
304
308
|
|
|
309
|
+
* `save_as!(file_path, flatten: false)`
|
|
310
|
+
*Saves the filled out PDF document in a given path and flattens it if requested. Raises an error if the path matches the current file (use save() instead).*
|
|
311
|
+
|
|
312
|
+
```ruby
|
|
313
|
+
pdf.save_as!('output.pdf')
|
|
314
|
+
# result: document is saved in a new path
|
|
315
|
+
pdf.save_as!(pdf.path)
|
|
316
|
+
# raises InvalidArgumentError
|
|
317
|
+
```
|
|
318
|
+
|
|
305
319
|
* `close`
|
|
306
320
|
*Closes the PDF document discarding all unsaved changes.*
|
|
307
321
|
|
|
@@ -310,6 +324,17 @@ An instance of `FillablePDF` has the following methods at its disposal:
|
|
|
310
324
|
# result: document is closed
|
|
311
325
|
```
|
|
312
326
|
|
|
327
|
+
* `closed?`
|
|
328
|
+
*Checks if the PDF document is closed.*
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
pdf.closed?
|
|
332
|
+
# output example: false
|
|
333
|
+
pdf.close
|
|
334
|
+
pdf.closed?
|
|
335
|
+
# output example: true
|
|
336
|
+
```
|
|
337
|
+
|
|
313
338
|
|
|
314
339
|
## Deployment with Heroku
|
|
315
340
|
|
|
@@ -384,7 +409,7 @@ pdf = FillablePDF.new('input.pdf')
|
|
|
384
409
|
|
|
385
410
|
# total number of fields
|
|
386
411
|
if pdf.any_fields?
|
|
387
|
-
puts "The form has a total of #{pdf.
|
|
412
|
+
puts "The form has a total of #{pdf.field_count} fields."
|
|
388
413
|
else
|
|
389
414
|
puts 'The form is not fillable.'
|
|
390
415
|
end
|
|
@@ -416,7 +441,7 @@ puts "Values: #{pdf.values}"
|
|
|
416
441
|
puts
|
|
417
442
|
|
|
418
443
|
# Checking field type
|
|
419
|
-
if pdf.field_type(:rugby) == Field::BUTTON
|
|
444
|
+
if pdf.field_type(:rugby) == FillablePDF::Field::BUTTON
|
|
420
445
|
puts "Field 'football' is of type BUTTON"
|
|
421
446
|
else
|
|
422
447
|
puts "Field 'football' is not of type BUTTON"
|
|
@@ -476,4 +501,4 @@ Removed field 'nascar'
|
|
|
476
501
|
|
|
477
502
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
|
478
503
|
|
|
479
|
-
However, you must also adhere to the [iText License](https://github.com/itext/itext7) when using this gem in your project.
|
|
504
|
+
However, you must also adhere to the [iText License](https://github.com/itext/itext7) when using this gem in your project.
|
data/bin/lint
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
RESET_COLOR='\033[0m'
|
|
4
|
+
HIGHLIGHT_COLOR='\033[0;35m'
|
|
5
|
+
SUCCESS_COLOR='\033[0;32m'
|
|
6
|
+
ERROR_COLOR='\033[0;31m'
|
|
7
|
+
|
|
8
|
+
clear
|
|
9
|
+
|
|
10
|
+
# linter configuration paths
|
|
11
|
+
RUBOCOP_CONFIG=.rubocop.yml
|
|
12
|
+
|
|
13
|
+
# Checking for existence of configuration files...
|
|
14
|
+
test -e ${RUBOCOP_CONFIG} || { echo -e "${ERROR_COLOR}"ERROR: ${RUBOCOP_CONFIG} not found."${RESET_COLOR}"; exit 1; }
|
|
15
|
+
|
|
16
|
+
# Running linters
|
|
17
|
+
echo -e "${HIGHLIGHT_COLOR}"Linting Ruby on Rails using RuboCop..."${RESET_COLOR}"
|
|
18
|
+
if [[ "$1" == "--no-fix" ]]; then
|
|
19
|
+
bundle exec rubocop || { valid=false; }
|
|
20
|
+
else
|
|
21
|
+
bundle exec rubocop --autocorrect-all || { valid=false; }
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Printing summary
|
|
25
|
+
if [[ "$valid" == false ]]; then
|
|
26
|
+
echo -e "${ERROR_COLOR}"ERROR: Lint errors have been found in your code."${RESET_COLOR}"
|
|
27
|
+
exit 1
|
|
28
|
+
else
|
|
29
|
+
echo -e "${SUCCESS_COLOR}"SUCCESS: All lints have completed without errors."${RESET_COLOR}"
|
|
30
|
+
fi
|
|
Binary file
|
|
Binary file
|
data/ext/forms-9.4.0.jar
ADDED
|
Binary file
|
data/ext/io-9.4.0.jar
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/fillable-pdf.gemspec
CHANGED
|
@@ -21,17 +21,9 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
22
22
|
spec.require_paths = %w[ext lib]
|
|
23
23
|
|
|
24
|
-
spec.
|
|
25
|
-
spec.
|
|
26
|
-
spec.
|
|
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'
|
|
32
|
-
|
|
33
|
-
spec.add_runtime_dependency 'rjb', '~> 1.6'
|
|
34
|
-
spec.requirements << 'JDK 8.x - 11.x'
|
|
24
|
+
spec.add_dependency 'base64', '~> 0.1'
|
|
25
|
+
spec.add_dependency 'rjb', '~> 1.6'
|
|
26
|
+
spec.requirements << 'JDK >= 8'
|
|
35
27
|
|
|
36
28
|
spec.metadata = {
|
|
37
29
|
'rubygems_mfa_required' => 'true'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class FillablePDF
|
|
2
|
+
# Base error class for all FillablePDF errors
|
|
3
|
+
class Error < StandardError; end
|
|
4
|
+
|
|
5
|
+
# Raised when a field is not found in the PDF form
|
|
6
|
+
class FieldNotFoundError < Error; end
|
|
7
|
+
|
|
8
|
+
# Raised when invalid arguments are provided to a method
|
|
9
|
+
class InvalidArgumentError < Error; end
|
|
10
|
+
|
|
11
|
+
# Raised when a PDF file operation fails
|
|
12
|
+
class FileOperationError < Error; end
|
|
13
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require_relative 'itext'
|
|
2
|
+
|
|
3
|
+
class FillablePDF
|
|
4
|
+
class Field
|
|
5
|
+
# PdfName has a constant "A" and a constant "a". Unfortunately, RJB does not differentiate
|
|
6
|
+
# between these constants and tries to create the same constant ("A") for both, which causes
|
|
7
|
+
# an annoying warning "already initialized constant Rjb::Com_itextpdf_kernel_pdf_PdfName::A".
|
|
8
|
+
# As long as RJB has not fixed this issue, this warning will remain suppressed.
|
|
9
|
+
|
|
10
|
+
BUTTON = ITEXT::PdfName.Btn.toString
|
|
11
|
+
CHOICE = ITEXT::PdfName.Ch.toString
|
|
12
|
+
SIGNATURE = ITEXT::PdfName.Sig.toString
|
|
13
|
+
TEXT = ITEXT::PdfName.Tx.toString
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/fillable-pdf/itext.rb
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
-
require_relative '
|
|
1
|
+
require_relative 'suppress_warnings'
|
|
2
2
|
require 'rjb'
|
|
3
3
|
|
|
4
4
|
Rjb.load(Dir.glob(File.expand_path('../../ext/*.jar', __dir__)).join(':'))
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
class FillablePDF
|
|
7
|
+
module ITEXT
|
|
8
|
+
extend FillablePDF::SuppressWarnings
|
|
9
|
+
|
|
10
|
+
suppress_warnings do
|
|
11
|
+
ByteArrayOutputStream = Rjb.import 'com.itextpdf.io.source.ByteArrayOutputStream'
|
|
12
|
+
Canvas = Rjb.import 'com.itextpdf.layout.Canvas'
|
|
13
|
+
Div = Rjb.import 'com.itextpdf.layout.element.Div'
|
|
14
|
+
HorizontalAlignment = Rjb.import 'com.itextpdf.layout.properties.HorizontalAlignment'
|
|
15
|
+
Image = Rjb.import 'com.itextpdf.layout.element.Image'
|
|
16
|
+
ImageDataFactory = Rjb.import 'com.itextpdf.io.image.ImageDataFactory'
|
|
17
|
+
PdfAcroForm = Rjb.import 'com.itextpdf.forms.PdfAcroForm'
|
|
18
|
+
PdfDictionary = Rjb.import 'com.itextpdf.kernel.pdf.PdfDictionary'
|
|
19
|
+
PdfDocument = Rjb.import 'com.itextpdf.kernel.pdf.PdfDocument'
|
|
20
|
+
PdfFormXObject = Rjb.import 'com.itextpdf.kernel.pdf.xobject.PdfFormXObject'
|
|
21
|
+
PdfName = Rjb.import 'com.itextpdf.kernel.pdf.PdfName'
|
|
22
|
+
PdfReader = Rjb.import 'com.itextpdf.kernel.pdf.PdfReader'
|
|
23
|
+
PdfWriter = Rjb.import 'com.itextpdf.kernel.pdf.PdfWriter'
|
|
24
|
+
Rectangle = Rjb.import 'com.itextpdf.kernel.geom.Rectangle'
|
|
25
|
+
VerticalAlignment = Rjb.import 'com.itextpdf.layout.properties.VerticalAlignment'
|
|
26
|
+
end
|
|
23
27
|
end
|
|
24
28
|
end
|
data/lib/fillable-pdf/version.rb
CHANGED
data/lib/fillable-pdf.rb
CHANGED
|
@@ -1,76 +1,89 @@
|
|
|
1
1
|
require_relative 'fillable-pdf/itext'
|
|
2
|
-
require_relative '
|
|
2
|
+
require_relative 'fillable-pdf/suppress_warnings'
|
|
3
|
+
require_relative 'fillable-pdf/field'
|
|
4
|
+
require_relative 'fillable-pdf/errors'
|
|
3
5
|
require 'base64'
|
|
4
6
|
require 'securerandom'
|
|
5
7
|
require 'tmpdir'
|
|
6
8
|
|
|
7
9
|
class FillablePDF # rubocop:disable Metrics/ClassLength
|
|
10
|
+
include SuppressWarnings
|
|
11
|
+
|
|
8
12
|
##
|
|
9
13
|
# Opens a given fillable-pdf PDF file and prepares it for modification.
|
|
10
14
|
#
|
|
11
15
|
# @param [String|Symbol] file_path the name of the PDF file or file path
|
|
16
|
+
# @raise [FileOperationError] if the file is not found or cannot be opened
|
|
12
17
|
#
|
|
13
|
-
def initialize(file_path)
|
|
14
|
-
raise
|
|
18
|
+
def initialize(file_path) # rubocop:disable Metrics/MethodLength
|
|
19
|
+
raise FileOperationError, "File <#{file_path}> is not found" unless File.exist?(file_path)
|
|
15
20
|
@file_path = file_path
|
|
21
|
+
@closed = false
|
|
16
22
|
begin
|
|
17
23
|
@byte_stream = ITEXT::ByteArrayOutputStream.new
|
|
18
24
|
@pdf_reader = ITEXT::PdfReader.new @file_path.to_s
|
|
19
25
|
@pdf_writer = ITEXT::PdfWriter.new @byte_stream
|
|
20
26
|
@pdf_doc = ITEXT::PdfDocument.new @pdf_reader, @pdf_writer
|
|
21
27
|
@pdf_form = ITEXT::PdfAcroForm.getAcroForm(@pdf_doc, true)
|
|
22
|
-
@form_fields = @pdf_form.
|
|
28
|
+
@form_fields = @pdf_form.getAllFormFields
|
|
23
29
|
rescue StandardError => e
|
|
24
|
-
|
|
30
|
+
handle_pdf_open_error(e)
|
|
25
31
|
end
|
|
26
32
|
end
|
|
27
33
|
|
|
28
34
|
##
|
|
29
35
|
# Determines whether the form has any fields.
|
|
30
36
|
#
|
|
31
|
-
# @return true if form has fields, false otherwise
|
|
37
|
+
# @return [Boolean] true if form has fields, false otherwise
|
|
32
38
|
#
|
|
33
39
|
def any_fields?
|
|
34
|
-
|
|
40
|
+
field_count.positive?
|
|
35
41
|
end
|
|
36
42
|
|
|
37
43
|
##
|
|
38
44
|
# Returns the total number of fillable form fields.
|
|
39
45
|
#
|
|
40
|
-
# @return the number of fields
|
|
46
|
+
# @return [Integer] the number of fields
|
|
41
47
|
#
|
|
42
|
-
def
|
|
48
|
+
def field_count
|
|
43
49
|
@form_fields.size
|
|
44
50
|
end
|
|
45
51
|
|
|
52
|
+
##
|
|
53
|
+
# @deprecated Use {#field_count} instead
|
|
54
|
+
def num_fields
|
|
55
|
+
warn '[DEPRECATION] `num_fields` is deprecated. Use `field_count` instead.'
|
|
56
|
+
field_count
|
|
57
|
+
end
|
|
58
|
+
|
|
46
59
|
##
|
|
47
60
|
# Retrieves the value of a field given its unique field name.
|
|
48
61
|
#
|
|
49
62
|
# @param [String|Symbol] key the field name
|
|
50
|
-
#
|
|
51
|
-
# @
|
|
63
|
+
# @return [String] the value of the field
|
|
64
|
+
# @raise [FieldNotFoundError] if the field does not exist
|
|
52
65
|
#
|
|
53
66
|
def field(key)
|
|
54
67
|
pdf_field(key).getValueAsString
|
|
55
68
|
rescue NoMethodError
|
|
56
|
-
raise "
|
|
69
|
+
raise FieldNotFoundError, "Unknown key name `#{key}'"
|
|
57
70
|
end
|
|
58
71
|
|
|
59
72
|
##
|
|
60
73
|
# Retrieves the string type of a field given its unique field name.
|
|
61
74
|
#
|
|
62
75
|
# @param [String|Symbol] key the field name
|
|
63
|
-
#
|
|
64
|
-
# @
|
|
76
|
+
# @return [String, nil] the type of the field (e.g., '/Btn', '/Tx', '/Ch', '/Sig')
|
|
77
|
+
# @raise [FieldNotFoundError] if the field does not exist
|
|
65
78
|
#
|
|
66
79
|
def field_type(key)
|
|
67
|
-
pdf_field(key).getFormType
|
|
80
|
+
pdf_field(key).getFormType&.toString
|
|
68
81
|
end
|
|
69
82
|
|
|
70
83
|
##
|
|
71
84
|
# Retrieves a hash of all fields and their values.
|
|
72
85
|
#
|
|
73
|
-
# @return
|
|
86
|
+
# @return [Hash{Symbol => String}] hash of field keys (as symbols) and values
|
|
74
87
|
#
|
|
75
88
|
def fields
|
|
76
89
|
iterator = @form_fields.keySet.iterator
|
|
@@ -87,14 +100,23 @@ class FillablePDF # rubocop:disable Metrics/ClassLength
|
|
|
87
100
|
#
|
|
88
101
|
# @param [String|Symbol] key the field name
|
|
89
102
|
# @param [String|Symbol] value the field value
|
|
90
|
-
# @param [
|
|
103
|
+
# @param [Boolean, nil] generate_appearance true to generate appearance, false to let the PDF viewer application generate form field appearance, nil (default) to let iText decide what's appropriate
|
|
104
|
+
# @return [self] returns self for method chaining
|
|
105
|
+
# @raise [InvalidArgumentError] if key or value are invalid
|
|
106
|
+
# @raise [FieldNotFoundError] if the field does not exist
|
|
91
107
|
#
|
|
92
108
|
def set_field(key, value, generate_appearance: nil)
|
|
109
|
+
ensure_document_open
|
|
110
|
+
validate_input(key, value)
|
|
111
|
+
field = pdf_field(key)
|
|
112
|
+
|
|
93
113
|
if generate_appearance.nil?
|
|
94
|
-
|
|
114
|
+
field.setValue(value.to_s)
|
|
95
115
|
else
|
|
96
|
-
|
|
116
|
+
field.setValue(value.to_s, generate_appearance)
|
|
97
117
|
end
|
|
118
|
+
|
|
119
|
+
self
|
|
98
120
|
end
|
|
99
121
|
|
|
100
122
|
##
|
|
@@ -105,37 +127,49 @@ class FillablePDF # rubocop:disable Metrics/ClassLength
|
|
|
105
127
|
#
|
|
106
128
|
# @param [String|Symbol] key the field name
|
|
107
129
|
# @param [String|Symbol] file_path the name of the image file or image path
|
|
130
|
+
# @return [self] returns self for method chaining
|
|
131
|
+
# @raise [FileOperationError] if the image file is not found
|
|
132
|
+
# @raise [FieldNotFoundError] if the field does not exist
|
|
108
133
|
#
|
|
109
134
|
def set_image(key, file_path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
orig_rect
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
ensure_document_open
|
|
136
|
+
raise FileOperationError, "File <#{file_path}> is not found" unless File.exist?(file_path)
|
|
137
|
+
|
|
138
|
+
begin
|
|
139
|
+
field = pdf_field(key)
|
|
140
|
+
widgets = field.getWidgets
|
|
141
|
+
widget_dict = suppress_warnings { widgets.isEmpty ? field.getPdfObject : widgets.get(0).getPdfObject }
|
|
142
|
+
orig_rect = widget_dict.getAsRectangle(ITEXT::PdfName.Rect)
|
|
143
|
+
|
|
144
|
+
border_style = field.getWidgets.get(0).getBorderStyle
|
|
145
|
+
border_width = border_style.nil? ? 0 : border_style.getWidth
|
|
146
|
+
|
|
147
|
+
bounding_rectangle = ITEXT::Rectangle.new(
|
|
148
|
+
orig_rect.getWidth - (border_width * 2),
|
|
149
|
+
orig_rect.getHeight - (border_width * 2)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
pdf_form_x_object = ITEXT::PdfFormXObject.new(bounding_rectangle)
|
|
153
|
+
canvas = ITEXT::Canvas.new(pdf_form_x_object, @pdf_doc)
|
|
154
|
+
image = ITEXT::Image.new(ITEXT::ImageDataFactory.create(file_path.to_s))
|
|
155
|
+
.setAutoScale(true)
|
|
156
|
+
.setHorizontalAlignment(ITEXT::HorizontalAlignment.CENTER)
|
|
157
|
+
container = ITEXT::Div.new
|
|
158
|
+
.setMargin(border_width).add(image)
|
|
159
|
+
.setVerticalAlignment(ITEXT::VerticalAlignment.MIDDLE)
|
|
160
|
+
.setFillAvailableArea(true)
|
|
161
|
+
canvas.add(container)
|
|
162
|
+
canvas.close
|
|
163
|
+
|
|
164
|
+
pdf_dict = ITEXT::PdfDictionary.new
|
|
165
|
+
widget_dict.put(ITEXT::PdfName.AP, pdf_dict)
|
|
166
|
+
pdf_dict.put(ITEXT::PdfName.N, pdf_form_x_object.getPdfObject)
|
|
167
|
+
widget_dict.setModified
|
|
168
|
+
rescue StandardError => e
|
|
169
|
+
raise FileOperationError, "Failed to set image for field '#{key}': #{e.message}"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
self
|
|
139
173
|
end
|
|
140
174
|
|
|
141
175
|
##
|
|
@@ -145,49 +179,97 @@ class FillablePDF # rubocop:disable Metrics/ClassLength
|
|
|
145
179
|
# content will be removed, which means you cannot have both text and image.
|
|
146
180
|
#
|
|
147
181
|
# @param [String|Symbol] key the field name
|
|
148
|
-
# @param [String
|
|
182
|
+
# @param [String] base64_image_data base64 encoded image data
|
|
183
|
+
# @return [self] returns self for method chaining
|
|
184
|
+
# @raise [InvalidArgumentError] if the base64 data is invalid
|
|
185
|
+
# @raise [FieldNotFoundError] if the field does not exist
|
|
149
186
|
#
|
|
150
187
|
def set_image_base64(key, base64_image_data)
|
|
188
|
+
ensure_document_open
|
|
151
189
|
tmp_file = "#{Dir.tmpdir}/#{SecureRandom.uuid}"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
190
|
+
begin
|
|
191
|
+
decoded_data = Base64.strict_decode64(base64_image_data)
|
|
192
|
+
File.binwrite(tmp_file, decoded_data)
|
|
193
|
+
set_image(key, tmp_file)
|
|
194
|
+
rescue ArgumentError => e
|
|
195
|
+
raise InvalidArgumentError, "Invalid base64 data: #{e.message}"
|
|
196
|
+
ensure
|
|
197
|
+
FileUtils.rm_f(tmp_file)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
self
|
|
156
201
|
end
|
|
157
202
|
|
|
158
203
|
##
|
|
159
204
|
# Sets the values of multiple fields given a set of unique field names and values.
|
|
160
205
|
#
|
|
161
|
-
# @param [Hash] fields the set of field names and values
|
|
162
|
-
# @param [
|
|
206
|
+
# @param [Hash{String, Symbol => String}] fields the set of field names and values
|
|
207
|
+
# @param [Boolean, nil] generate_appearance true to generate appearance, false to let the PDF viewer application generate form field appearance, nil (default) to let iText decide what's appropriate
|
|
208
|
+
# @return [self] returns self for method chaining
|
|
209
|
+
# @raise [InvalidArgumentError] if any key or value is invalid
|
|
210
|
+
# @raise [FieldNotFoundError] if any field does not exist
|
|
163
211
|
#
|
|
164
212
|
def set_fields(fields, generate_appearance: nil)
|
|
165
|
-
|
|
213
|
+
ensure_document_open
|
|
214
|
+
fields.each { |key, value| set_field(key, value, generate_appearance: generate_appearance) }
|
|
215
|
+
self
|
|
166
216
|
end
|
|
167
217
|
|
|
168
218
|
##
|
|
169
219
|
# Renames a field given its unique field name and the new field name.
|
|
170
220
|
#
|
|
171
|
-
# @param [String|Symbol] old_key the field name
|
|
172
|
-
# @param [String|Symbol] new_key the field name
|
|
221
|
+
# @param [String|Symbol] old_key the current field name
|
|
222
|
+
# @param [String|Symbol] new_key the new field name
|
|
223
|
+
# @return [self] returns self for method chaining
|
|
224
|
+
# @raise [FieldNotFoundError] if the field does not exist
|
|
225
|
+
# @raise [InvalidArgumentError] if the new field name already exists
|
|
173
226
|
#
|
|
174
|
-
def rename_field(old_key, new_key)
|
|
175
|
-
|
|
227
|
+
def rename_field(old_key, new_key) # rubocop:disable Metrics/MethodLength
|
|
228
|
+
ensure_document_open
|
|
229
|
+
validate_field_name(old_key)
|
|
230
|
+
validate_field_name(new_key)
|
|
231
|
+
|
|
232
|
+
old_key = old_key.to_s
|
|
233
|
+
new_key = new_key.to_s
|
|
234
|
+
|
|
235
|
+
raise FieldNotFoundError, "Field `#{old_key}` not found" unless @form_fields.containsKey(old_key)
|
|
236
|
+
raise InvalidArgumentError, "Field name `#{new_key}` already exists" if @form_fields.containsKey(new_key)
|
|
237
|
+
|
|
238
|
+
field = pdf_field(old_key)
|
|
239
|
+
field.setFieldName(new_key)
|
|
240
|
+
|
|
241
|
+
@form_fields.remove(old_key)
|
|
242
|
+
@form_fields.put(new_key, field)
|
|
243
|
+
|
|
244
|
+
self
|
|
245
|
+
rescue FieldNotFoundError, InvalidArgumentError
|
|
246
|
+
raise
|
|
247
|
+
rescue StandardError => e
|
|
248
|
+
raise FileOperationError, "Unable to rename field `#{old_key}` to `#{new_key}`: #{e.message}"
|
|
176
249
|
end
|
|
177
250
|
|
|
178
251
|
##
|
|
179
252
|
# Removes a field from the document given its unique field name.
|
|
180
253
|
#
|
|
181
254
|
# @param [String|Symbol] key the field name
|
|
255
|
+
# @return [self] returns self for method chaining
|
|
256
|
+
# @raise [FieldNotFoundError] if the field does not exist
|
|
182
257
|
#
|
|
183
258
|
def remove_field(key)
|
|
259
|
+
ensure_document_open
|
|
260
|
+
validate_field_name(key)
|
|
261
|
+
raise FieldNotFoundError, "Unknown key name `#{key}'" unless @form_fields.containsKey(key.to_s)
|
|
262
|
+
|
|
184
263
|
@pdf_form.removeField(key.to_s)
|
|
264
|
+
@form_fields.remove(key.to_s)
|
|
265
|
+
|
|
266
|
+
self
|
|
185
267
|
end
|
|
186
268
|
|
|
187
269
|
##
|
|
188
270
|
# Returns a list of all field keys used in the document.
|
|
189
271
|
#
|
|
190
|
-
# @return array of field names
|
|
272
|
+
# @return [Array<Symbol>] array of field names as symbols
|
|
191
273
|
#
|
|
192
274
|
def names
|
|
193
275
|
iterator = @form_fields.keySet.iterator
|
|
@@ -199,7 +281,7 @@ class FillablePDF # rubocop:disable Metrics/ClassLength
|
|
|
199
281
|
##
|
|
200
282
|
# Returns a list of all field values used in the document.
|
|
201
283
|
#
|
|
202
|
-
# @return array of field values
|
|
284
|
+
# @return [Array<String>] array of field values
|
|
203
285
|
#
|
|
204
286
|
def values
|
|
205
287
|
iterator = @form_fields.keySet.iterator
|
|
@@ -211,36 +293,82 @@ class FillablePDF # rubocop:disable Metrics/ClassLength
|
|
|
211
293
|
##
|
|
212
294
|
# Overwrites the previously opened PDF document and flattens it if requested.
|
|
213
295
|
#
|
|
214
|
-
# @param [
|
|
296
|
+
# @param [Boolean] flatten true if PDF should be flattened, false otherwise
|
|
297
|
+
# @return [self] returns self for method chaining
|
|
298
|
+
# @raise [FileOperationError] if the save operation fails
|
|
215
299
|
#
|
|
216
300
|
def save(flatten: false)
|
|
301
|
+
ensure_document_open
|
|
217
302
|
tmp_file = "#{Dir.tmpdir}/#{SecureRandom.uuid}"
|
|
218
303
|
save_as(tmp_file, flatten: flatten)
|
|
219
304
|
FileUtils.mv tmp_file, @file_path
|
|
305
|
+
self
|
|
220
306
|
end
|
|
221
307
|
|
|
222
308
|
##
|
|
223
309
|
# Saves the filled out PDF document in a given path and flattens it if requested.
|
|
310
|
+
# If the path matches the current file path, it will call save() instead.
|
|
224
311
|
#
|
|
225
312
|
# @param [String] file_path the name of the PDF file or file path
|
|
226
|
-
# @param [
|
|
313
|
+
# @param [Boolean] flatten true if PDF should be flattened, false otherwise
|
|
314
|
+
# @return [self] returns self for method chaining
|
|
315
|
+
# @raise [FileOperationError] if the save operation fails
|
|
227
316
|
#
|
|
228
317
|
def save_as(file_path, flatten: false)
|
|
318
|
+
ensure_document_open
|
|
229
319
|
if @file_path == file_path
|
|
230
320
|
save(flatten: flatten)
|
|
231
|
-
|
|
232
|
-
File.open(file_path, 'wb') { |f| f.write(finalize(flatten: flatten)) && f.close }
|
|
321
|
+
return self
|
|
233
322
|
end
|
|
323
|
+
|
|
324
|
+
File.open(file_path, 'wb') { |f| f.write(finalize(flatten: flatten)) && f.close }
|
|
325
|
+
self
|
|
326
|
+
rescue StandardError => e
|
|
327
|
+
raise FileOperationError, "Failed to save file `#{file_path}`: #{e.message}"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
##
|
|
331
|
+
# Saves the filled out PDF document in a given path and flattens it if requested.
|
|
332
|
+
# Raises an error if the path matches the current file path (use save() instead).
|
|
333
|
+
#
|
|
334
|
+
# @param [String] file_path the name of the PDF file or file path
|
|
335
|
+
# @param [Boolean] flatten true if PDF should be flattened, false otherwise
|
|
336
|
+
# @return [self] returns self for method chaining
|
|
337
|
+
# @raise [InvalidArgumentError] if file_path matches the current file path
|
|
338
|
+
# @raise [FileOperationError] if the save operation fails
|
|
339
|
+
#
|
|
340
|
+
def save_as!(file_path, flatten: false)
|
|
341
|
+
ensure_document_open
|
|
342
|
+
raise InvalidArgumentError, 'Cannot save_as! to the same file path. Use save() instead.' if @file_path == file_path
|
|
343
|
+
|
|
344
|
+
File.open(file_path, 'wb') { |f| f.write(finalize(flatten: flatten)) && f.close }
|
|
345
|
+
self
|
|
346
|
+
rescue InvalidArgumentError
|
|
347
|
+
raise
|
|
348
|
+
rescue StandardError => e
|
|
349
|
+
raise FileOperationError, "Failed to save file `#{file_path}`: #{e.message}"
|
|
234
350
|
end
|
|
235
351
|
|
|
236
352
|
##
|
|
237
353
|
# Closes the PDF document discarding all unsaved changes.
|
|
238
354
|
#
|
|
239
|
-
# @return [Boolean] true if document is closed
|
|
355
|
+
# @return [Boolean] true if document is closed
|
|
240
356
|
#
|
|
241
|
-
def close
|
|
357
|
+
def close # rubocop:disable Naming/PredicateMethod
|
|
358
|
+
return true if closed?
|
|
359
|
+
|
|
242
360
|
@pdf_doc.close
|
|
243
|
-
@
|
|
361
|
+
@closed = true
|
|
362
|
+
true
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
##
|
|
366
|
+
# Checks if the PDF document is closed.
|
|
367
|
+
#
|
|
368
|
+
# @return [Boolean] true if document is closed, false otherwise
|
|
369
|
+
#
|
|
370
|
+
def closed?
|
|
371
|
+
@closed ||= false
|
|
244
372
|
end
|
|
245
373
|
|
|
246
374
|
private
|
|
@@ -248,17 +376,37 @@ class FillablePDF # rubocop:disable Metrics/ClassLength
|
|
|
248
376
|
##
|
|
249
377
|
# Writes the contents of the modified fields to the previously opened PDF file.
|
|
250
378
|
#
|
|
251
|
-
# @param [
|
|
379
|
+
# @param [Boolean] flatten true if PDF should be flattened, false otherwise
|
|
380
|
+
# @return [Java::byte[]] byte array of the PDF document
|
|
252
381
|
#
|
|
253
382
|
def finalize(flatten: false)
|
|
254
383
|
@pdf_form.flattenFields if flatten
|
|
255
384
|
close
|
|
256
385
|
@byte_stream.toByteArray
|
|
386
|
+
rescue StandardError => e
|
|
387
|
+
raise FileOperationError, "Failed to finalize document: #{e.message}"
|
|
257
388
|
end
|
|
258
389
|
|
|
259
390
|
def pdf_field(key)
|
|
260
391
|
field = @form_fields.get(key.to_s)
|
|
261
|
-
raise "
|
|
392
|
+
raise FieldNotFoundError, "Unknown key name `#{key}'" if field.nil?
|
|
262
393
|
field
|
|
263
394
|
end
|
|
395
|
+
|
|
396
|
+
def validate_input(key, value)
|
|
397
|
+
validate_field_name(key)
|
|
398
|
+
raise InvalidArgumentError, 'Field value cannot be nil' if value.nil?
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def validate_field_name(key)
|
|
402
|
+
raise InvalidArgumentError, 'Field name must be a string or symbol' unless key.is_a?(String) || key.is_a?(Symbol)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def ensure_document_open
|
|
406
|
+
raise FileOperationError, 'Cannot perform operation on a closed PDF document' if closed?
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def handle_pdf_open_error(err)
|
|
410
|
+
raise FileOperationError, "#{err.message} (Input file may be corrupt, incompatible, read-only, write-protected, encrypted, or may not have any form fields)" # rubocop:disable Layout/LineLength
|
|
411
|
+
end
|
|
264
412
|
end
|
metadata
CHANGED
|
@@ -1,127 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fillable-pdf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vadim Kononov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
13
|
+
name: base64
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - "
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
20
|
-
type: :development
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - ">="
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: minitest
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">="
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: rake
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
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
|
-
- - ">="
|
|
16
|
+
- - "~>"
|
|
116
17
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '0'
|
|
118
|
-
type: :
|
|
18
|
+
version: '0.1'
|
|
19
|
+
type: :runtime
|
|
119
20
|
prerelease: false
|
|
120
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
22
|
requirements:
|
|
122
|
-
- - "
|
|
23
|
+
- - "~>"
|
|
123
24
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '0'
|
|
25
|
+
version: '0.1'
|
|
125
26
|
- !ruby/object:Gem::Dependency
|
|
126
27
|
name: rjb
|
|
127
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -145,38 +46,41 @@ executables: []
|
|
|
145
46
|
extensions: []
|
|
146
47
|
extra_rdoc_files: []
|
|
147
48
|
files:
|
|
49
|
+
- ".github/workflows/lint.yml"
|
|
148
50
|
- ".github/workflows/test.yml"
|
|
149
51
|
- ".gitignore"
|
|
150
52
|
- ".rubocop.yml"
|
|
53
|
+
- Appraisals
|
|
151
54
|
- Gemfile
|
|
152
55
|
- LICENSE.md
|
|
153
56
|
- README.md
|
|
154
57
|
- Rakefile
|
|
155
58
|
- bin/console
|
|
59
|
+
- bin/lint
|
|
156
60
|
- bin/setup
|
|
157
|
-
- ext/commons-
|
|
158
|
-
- ext/font-asian-
|
|
159
|
-
- ext/forms-
|
|
160
|
-
- ext/io-
|
|
161
|
-
- ext/kernel-
|
|
162
|
-
- ext/layout-
|
|
163
|
-
- ext/slf4j-api-2.0.
|
|
164
|
-
- ext/slf4j-simple-2.0.
|
|
61
|
+
- ext/commons-9.4.0.jar
|
|
62
|
+
- ext/font-asian-9.4.0.jar
|
|
63
|
+
- ext/forms-9.4.0.jar
|
|
64
|
+
- ext/io-9.4.0.jar
|
|
65
|
+
- ext/kernel-9.4.0.jar
|
|
66
|
+
- ext/layout-9.4.0.jar
|
|
67
|
+
- ext/slf4j-api-2.0.17.jar
|
|
68
|
+
- ext/slf4j-simple-2.0.17.jar
|
|
165
69
|
- fillable-pdf.gemspec
|
|
166
70
|
- images/blank.png
|
|
167
71
|
- images/checked.png
|
|
168
72
|
- images/distinct.png
|
|
169
|
-
- lib/field.rb
|
|
170
73
|
- lib/fillable-pdf.rb
|
|
74
|
+
- lib/fillable-pdf/errors.rb
|
|
75
|
+
- lib/fillable-pdf/field.rb
|
|
171
76
|
- lib/fillable-pdf/itext.rb
|
|
172
|
-
- lib/fillable-pdf/
|
|
77
|
+
- lib/fillable-pdf/suppress_warnings.rb
|
|
173
78
|
- lib/fillable-pdf/version.rb
|
|
174
79
|
homepage: https://github.com/vkononov/fillable-pdf
|
|
175
80
|
licenses:
|
|
176
81
|
- MIT
|
|
177
82
|
metadata:
|
|
178
83
|
rubygems_mfa_required: 'true'
|
|
179
|
-
post_install_message:
|
|
180
84
|
rdoc_options: []
|
|
181
85
|
require_paths:
|
|
182
86
|
- ext
|
|
@@ -192,9 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
192
96
|
- !ruby/object:Gem::Version
|
|
193
97
|
version: '0'
|
|
194
98
|
requirements:
|
|
195
|
-
- JDK 8
|
|
196
|
-
rubygems_version: 3.
|
|
197
|
-
signing_key:
|
|
99
|
+
- JDK >= 8
|
|
100
|
+
rubygems_version: 3.6.9
|
|
198
101
|
specification_version: 4
|
|
199
102
|
summary: Fill out or extract field values from simple fillable PDF forms using iText.
|
|
200
103
|
test_files: []
|
data/ext/commons-7.2.4.jar
DELETED
|
Binary file
|
data/ext/forms-7.2.4.jar
DELETED
|
Binary file
|
data/ext/io-7.2.4.jar
DELETED
|
Binary file
|
data/ext/kernel-7.2.4.jar
DELETED
|
Binary file
|
data/ext/layout-7.2.4.jar
DELETED
|
Binary file
|
data/ext/slf4j-api-2.0.4.jar
DELETED
|
Binary file
|
data/ext/slf4j-simple-2.0.4.jar
DELETED
|
Binary file
|
data/lib/field.rb
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
require_relative 'fillable-pdf/itext'
|
|
2
|
-
|
|
3
|
-
class Field
|
|
4
|
-
# PdfName has a constant "A" and a constant "a". Unfortunately, RJB does not differentiate
|
|
5
|
-
# between these constants and tries to create the same constant ("A") for both, which causes
|
|
6
|
-
# an annoying warning "already initialized constant Rjb::Com_itextpdf_kernel_pdf_PdfName::A".
|
|
7
|
-
# As long as RJB has not fixed this issue, this warning will remain suppressed.
|
|
8
|
-
|
|
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
|