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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0217f9c6c445e4d79c0a8af4514547beb6297ef401659993097a5bb4be57b14
4
- data.tar.gz: 6478016e8553f280217c0bc7f0a633f2cf8ac8aba6030639ef1a691d8b6d8aa7
3
+ metadata.gz: 21838c4d6acfe2fee7d07688316c0b4fc32e7e5898dfcd0f77559424e3770802
4
+ data.tar.gz: 153fc518eee4b2fa76a91ce6228739d5d3cef4d5d4f1be05e3ee6a5f6fcf7ce7
5
5
  SHA512:
6
- metadata.gz: ff0ce41331cabc1b0cffc8aca6d5560e0a86855ad9cf1a705a1e5b15345c57e1e165c2a77b50f7fdc6c38e49f89308c1ffff851a98d14a9dbb0bdb735e994abd
7
- data.tar.gz: 3a2e98246b9d7ddbd617bfd0552e2e4891dac0d6ba241f85d23603cc139b88eb2c6a2d0570e73d1a5178dffd1b3e3923f0e9ca0b47b5d7d34c5bfa622e23f996
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
@@ -1,32 +1,35 @@
1
- # This workflow uses actions that are not certified by GitHub.
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
- # GitHub recommends pinning actions to a commit SHA.
7
- # To get a newer version, you will need to update the SHA.
8
- # You can also reference a tag or branch, but the action may change without warning.
9
-
10
- name: test
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-version: ['3.1', '3.0', '2.7', '2.6', '2.5', '2.4']
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@v3
25
- - name: Set up Ruby ${{ matrix.ruby-version }}
26
- uses: ruby/setup-ruby@359bebbc29cbe6c87da6bc9ea3bc930432750108
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-version }}
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
@@ -8,6 +8,7 @@
8
8
  /coverage/
9
9
  /doc/
10
10
  /Gemfile.lock
11
+ /gemfiles/
11
12
  /pkg/
12
13
  /spec/reports/
13
14
  /tmp/
data/.rubocop.yml CHANGED
@@ -1,10 +1,11 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-md
3
3
  - rubocop-minitest
4
4
  - rubocop-performance
5
5
  - rubocop-rake
6
6
 
7
7
  AllCops:
8
+ TargetRubyVersion: 2.4.0
8
9
  NewCops: enable
9
10
  Exclude:
10
11
  - .git/**/*
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
  [![Gem Version](https://badge.fury.io/rb/fillable-pdf.svg)](https://rubygems.org/gems/fillable-pdf)
5
- [![Test Status](https://github.com/vkononov/fillable-pdf/actions/workflows/test.yml/badge.svg)](https://github.com/vkononov/fillable-pdf/actions)
5
+ [![Lint Status](https://github.com/vkononov/fillable-pdf/actions/workflows/lint.yml/badge.svg)](https://github.com/vkononov/fillable-pdf/actions/workflows/lint.yml)
6
+ [![Test Status](https://github.com/vkononov/fillable-pdf/actions/workflows/test.yml/badge.svg)](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
  [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png)](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
  ![Blank](images/checked.png)
40
43
 
41
- If only of the fields are blank, try setting the `generate_appearance` flag to `true` when calling `set_field` or `set_fields`.
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 v8, v11
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
- * OSX: `/Library/Java/JavaVirtualMachines/jdk-11.0.2.jdk/Contents/Home`
62
- * Ubuntu/CentOS: `/usr/lib/jvm/java-1.8.0-openjdk`
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
- * `num_fields`
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.num_fields} fields."
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/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.add_development_dependency 'bundler'
25
- spec.add_development_dependency 'minitest'
26
- spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'rubocop'
28
- spec.add_development_dependency 'rubocop-md'
29
- spec.add_development_dependency 'rubocop-minitest'
30
- spec.add_development_dependency 'rubocop-performance'
31
- spec.add_development_dependency 'rubocop-rake'
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
@@ -1,24 +1,28 @@
1
- require_relative 'kernel'
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
- 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'
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
@@ -0,0 +1,11 @@
1
+ class FillablePDF
2
+ module SuppressWarnings
3
+ def suppress_warnings
4
+ original_verbosity = $VERBOSE
5
+ $VERBOSE = nil
6
+ result = yield
7
+ $VERBOSE = original_verbosity
8
+ result
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  class FillablePDF
2
- VERSION = '0.9.5.2'
2
+ VERSION = '1.0.0'
3
3
  end
data/lib/fillable-pdf.rb CHANGED
@@ -1,76 +1,89 @@
1
1
  require_relative 'fillable-pdf/itext'
2
- require_relative 'field'
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 IOError, "File <#{file_path}> is not found" unless File.exist?(file_path)
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.getFormFields
28
+ @form_fields = @pdf_form.getAllFormFields
23
29
  rescue StandardError => e
24
- raise "#{e.message} (Input file may be corrupt, incompatible, read-only, write-protected, encrypted, or may not have any form fields)" # rubocop:disable Layout/LineLength
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
- num_fields.positive?
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 num_fields
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
- # @return the value of the field
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 "unknown key name `#{key}'"
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
- # @return the type of the field
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.toString
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 the hash of field keys and values
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 [NilClass|TrueClass|FalseClass] 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
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
- pdf_field(key).setValue(value.to_s)
114
+ field.setValue(value.to_s)
95
115
  else
96
- pdf_field(key).setValue(value.to_s, generate_appearance)
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
- raise IOError, "File <#{file_path}> is not found" unless File.exist?(file_path)
111
- field = pdf_field(key)
112
- widgets = field.getWidgets
113
- widget_dict = suppress_warnings { widgets.isEmpty ? field.getPdfObject : widgets.get(0).getPdfObject }
114
- orig_rect = widget_dict.getAsRectangle(ITEXT::PdfName.Rect)
115
- border_width = field.getBorderWidth
116
- bounding_rectangle = ITEXT::Rectangle.new(
117
- orig_rect.getWidth - (border_width * 2),
118
- orig_rect.getHeight - (border_width * 2)
119
- )
120
-
121
- pdf_form_x_object = ITEXT::PdfFormXObject.new(bounding_rectangle)
122
- canvas = ITEXT::Canvas.new(pdf_form_x_object, @pdf_doc)
123
- image = ITEXT::Image.new(ITEXT::ImageDataFactory.create(file_path.to_s))
124
- .setAutoScale(true)
125
- .setHorizontalAlignment(ITEXT::HorizontalAlignment.CENTER)
126
- container = ITEXT::Div.new
127
- .setMargin(border_width).add(image)
128
- .setVerticalAlignment(ITEXT::VerticalAlignment.MIDDLE)
129
- .setFillAvailableArea(true)
130
- canvas.add(container)
131
- canvas.close
132
-
133
- pdf_dict = ITEXT::PdfDictionary.new
134
- widget_dict.put(ITEXT::PdfName.AP, pdf_dict)
135
- pdf_dict.put(ITEXT::PdfName.N, pdf_form_x_object.getPdfObject)
136
- widget_dict.setModified
137
- rescue StandardError => e
138
- raise "#{e.message} (there may be something wrong with your image)"
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|Symbol] base64_image_data base64 encoded data image
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
- File.binwrite(tmp_file, Base64.decode64(base64_image_data))
153
- set_image(key, tmp_file)
154
- ensure
155
- FileUtils.rm tmp_file
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 [NilClass|TrueClass|FalseClass] 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
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
- fields.each { |key, value| set_field key, value, generate_appearance: generate_appearance }
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
- pdf_field(old_key).setFieldName(new_key.to_s)
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 [bool] flatten true if PDF should be flattened, false otherwise
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 [TrueClass|FalseClass] flatten true if PDF should be flattened, false otherwise
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
- else
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, false otherwise
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
- @pdf_doc.isClosed
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 [TrueClass|FalseClass] flatten: true if PDF should be flattened, false otherwise
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 "unknown key name `#{key}'" if field.nil?
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.9.5.2
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: 2022-12-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: bundler
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: :development
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-7.2.4.jar
158
- - ext/font-asian-7.2.4.jar
159
- - ext/forms-7.2.4.jar
160
- - ext/io-7.2.4.jar
161
- - ext/kernel-7.2.4.jar
162
- - ext/layout-7.2.4.jar
163
- - ext/slf4j-api-2.0.4.jar
164
- - ext/slf4j-simple-2.0.4.jar
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/kernel.rb
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.x - 11.x
196
- rubygems_version: 3.3.7
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: []
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
Binary file
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
@@ -1,9 +0,0 @@
1
- module Kernel
2
- def suppress_warnings
3
- original_verbosity = $VERBOSE
4
- $VERBOSE = nil
5
- result = yield
6
- $VERBOSE = original_verbosity
7
- result
8
- end
9
- end