README.md in fillable-pdf-0.9.1 vs README.md in fillable-pdf-0.9.2

- old
+ new

@@ -2,42 +2,109 @@ # FillablePDF [![Gem Version](https://badge.fury.io/rb/fillable-pdf.svg)](https://rubygems.org/gems/fillable-pdf) [![Build Status](https://api.travis-ci.org/vkononov/fillable-pdf.svg?branch=master)](http://travis-ci.org/vkononov/fillable-pdf) -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. +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. + ## Known Issues -If the gem hangs in `development`, removing the following gems may fix the issue: +1. This gem currently does not work with Phusion Passenger's [smart spawning](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/#the-smart-spawning-method). Please see [Deployment with Phusion Passenger + Nginx](#deployment-with-phusion-passenger--nginx) for more information. -```ruby -gem 'spring' -gem 'spring-watcher-listen' +2. If the gem hangs in `development`, removing the following gems may fix the issue: + + ```ruby + gem 'spring' + gem 'spring-watcher-listen' + ``` + + +## Deployment with Heroku + +When deploying to Heroku, be sure to install the following build packs (in this order): + +```bash +heroku buildpacks:add heroku/jvm +heroku buildpacks:add heroku/ruby ``` -If the gem hangs in `production`, you could try to use `puma` with a reverse proxy to host the application. +## Deployment with Phusion Passenger + Nginx +The way the gem is currently built makes it [fundamentally incompatible](https://github.com/phusion/passenger/issues/223#issuecomment-44504029) with Phusion Passenger's [smart spawning](https://www.phusionpassenger.com/library/indepth/ruby/spawn_methods/#the-smart-spawning-method). You must turn off smart spawning, or else your application will freeze as soon Ruby tries to access the Java bridge. +Below is an example of a simple Nginx virtual host configuration (note the use of `passenger_spawn_method`): + +```nginx +server { + server_name my-rails-app.com; + listen 443 ssl http2; + listen [::]:443 ssl http2; + passenger_enabled on; + passenger_spawn_method direct; + root /home/system/my-rails-app/public; +} +``` + +If you absolutely must have smart spawning, I recommend using `fillable-pdf` as a service that runs independently of your Rails application. + + +## Deployment with Puma + Nginx + +In order to use Puma in production, you need to configure a reverse proxy in your Nginx virtual host. Here is simple naive example: + +```nginx +server { + server_name my-rails-app.com; + listen 443 ssl http2; + listen [::]:443 ssl http2; + location / { + proxy_pass http://127.0.0.1:8888; + proxy_redirect off; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Real-IP $remote_addr; + } +} +``` + +Then you'll have to start Puma in production daemon mode as follows: + +```bash +RAILS_ENV=production bin/rails server -p 8888 --daemon +``` + +Naturally, there are many downsides (in terms of efficiency, scalability, security, etc) to running your application in production in this manner, so please use the above as an example only. + + ## Installation **Ensure that your `JAVA_HOME` variable is set before installing this gem (see examples below).** - -* OSX: `/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home` + +* OSX: `/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home` * Ubuntu/CentOS: `/usr/lib/jvm/java-1.8.0-openjdk` Add this line to your application's Gemfile: - gem 'fillable-pdf' +```ruby +gem 'fillable-pdf' +``` And then execute: - bundle +```bash +bundle +``` Or install it yourself as: - gem install fillable-pdf +```bash +gem install fillable-pdf +``` If you are using this gem in a script, you need to require it manually: ```ruby require 'fillable-pdf' @@ -55,131 +122,175 @@ ```ruby pdf.close ``` -## Instance Methods +### Checking / Unchecking Checkboxes +Use the values `'Yes'` and `'Off'` to check and uncheck checkboxes, respectively. For example: + +```ruby +pdf.set_field(:newsletter, 'Yes') +pdf.set_field(:newsletter, 'Off') +``` + +### Checking / Unchecking Radio Buttons + +Suppose you have the following a radio button field name `language` with the following options: + + - Ruby (`ruby`) + - Python (`python`) + - Dart (`dart`) + - Other (`other`) + +To select one of these options (or change the current option) use: + +```ruby +pdf.set_field(:language, 'dart') +``` + +To unset the radio button use the `'Off'` string: + +```ruby +pdf.set_field(:language, 'Off') +``` + +### Instance Methods + An instance of `FillablePDF` has the following methods at its disposal: * `any_fields?` - *Determines whether the form has any fields.* - ```ruby - pdf.any_fields? - # output example: true - ``` + *Determines whether the form has any fields.* + ```ruby + pdf.any_fields? + # output example: true + ``` + * `num_fields` - *Returns the total number of fillable form fields.* - ```ruby - # output example: 10 - pdf.num_fields - ``` + *Returns the total number of fillable form fields.* + ```ruby + # output example: 10 + pdf.num_fields + ``` + * `field` - *Retrieves the value of a field given its unique field name.* - ```ruby - pdf.field(:full_name) - # output example: 'Richard' - ``` + *Retrieves the value of a field given its unique field name.* + ```ruby + pdf.field(:full_name) + # output example: 'Richard' + ``` + * `field_type` - *Retrieves the numeric type of a field given its unique field name.* - ```ruby - pdf.field_type(:football) - # output example: 4 + *Retrieves the numeric type of a field given its unique field name.* - # list of all field types - Field::BUTTON - Field::CHOICE - Field::SIGNATURE - Field::TEXT - ``` + ```ruby + pdf.field_type(:football) + # output example: '/Btn' + # list of all field types + Field::BUTTON ('/Btn') + Field::CHOICE ('/Ch') + Field::SIGNATURE ('/Sig') + Field::TEXT ('/Tx') + ``` + * `fields` - *Retrieves a hash of all fields and their values.* - ```ruby - pdf.fields - # output example: {first_name: "Richard", last_name: "Rahl"} - ``` + *Retrieves a hash of all fields and their values.* + ```ruby + pdf.fields + # output example: {first_name: "Richard", last_name: "Rahl"} + ``` + * `set_field` - *Sets the value of a field given its unique field name and value.* - ```ruby - pdf.set_field(:first_name, 'Richard') - # result: changes the value of 'first_name' to 'Richard' - ``` + *Sets the value of a field given its unique field name and value.* + ```ruby + pdf.set_field(:first_name, 'Richard') + # result: changes the value of 'first_name' to 'Richard' + ``` + * `set_fields` - *Sets the values of multiple fields given a set of unique field names and values.* - ```ruby - pdf.set_fields(first_name: 'Richard', last_name: 'Rahl') - # result: changes the values of 'first_name' and 'last_name' - ``` + *Sets the values of multiple fields given a set of unique field names and values.* + ```ruby + pdf.set_fields(first_name: 'Richard', last_name: 'Rahl') + # result: changes the values of 'first_name' and 'last_name' + ``` + * `rename_field` - *Renames a field given its unique field name and the new field name.* - ```ruby - pdf.rename_field(:last_name, :surname) - # result: renames field name 'last_name' to 'surname' - # NOTE: this action does not take effect until the document is saved - ``` + *Renames a field given its unique field name and the new field name.* + ```ruby + pdf.rename_field(:last_name, :surname) + # result: renames field name 'last_name' to 'surname' + # NOTE: this action does not take effect until the document is saved + ``` + * `remove_field` - *Removes a field from the document given its unique field name.* - ```ruby - pdf.remove_field(:last_name) - # result: physically removes field 'last_name' from document - ``` + *Removes a field from the document given its unique field name.* + ```ruby + pdf.remove_field(:last_name) + # result: physically removes field 'last_name' from document + ``` + * `names` - *Returns a list of all field keys used in the document.* - ```ruby - pdf.names - # output example: [:first_name, :last_name] - ``` + *Returns a list of all field keys used in the document.* + ```ruby + pdf.names + # output example: [:first_name, :last_name] + ``` + * `values` - *Returns a list of all field values used in the document.* - ```ruby - pdf.values - # output example: ["Rahl", "Richard"] - ``` + *Returns a list of all field values used in the document.* + ```ruby + pdf.values + # output example: ["Rahl", "Richard"] + ``` + * `save` - *Overwrites the previously opened PDF document and flattens it if requested.* - ```ruby - pdf.save - # result: document is saved without flatenning - pdf.save_as(flatten: true) - # result: document is saved with flatenning - ``` + *Overwrites the previously opened PDF document and flattens it if requested.* + ```ruby + pdf.save + # result: document is saved without flattening + pdf.save_as(flatten: true) + # result: document is saved with flattening + ``` + * `save_as` - *Saves the filled out PDF document in a given path and flattens it if requested.* - ```ruby - pdf.save_as('output.pdf') - # result: document is saved in a given path without flatenning - pdf.save_as('output.pdf', flatten: true) - # result: document is saved in a given path with flatenning - ``` + *Saves the filled out PDF document in a given path and flattens it if requested.* - **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. + ```ruby + pdf.save_as('output.pdf') + # result: document is saved in a given path without flattening + pdf.save_as('output.pdf', flatten: true) + # result: document is saved in a given path with flattening + ``` + **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. + * `close` - *Closes the PDF document discarding all unsaved changes.* - ```ruby - pdf.close - # result: document is closed - ``` + *Closes the PDF document discarding all unsaved changes.* + ```ruby + pdf.close + # result: document is closed + ``` + ## Example 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). ```ruby -require 'fillable-pdf' +require_relative '../lib/fillable-pdf' # opening a fillable PDF pdf = FillablePDF.new('input.pdf') # total number of fields @@ -194,10 +305,12 @@ # setting form fields pdf.set_fields(first_name: 'Richard', last_name: 'Rahl') pdf.set_fields(football: 'Yes', baseball: 'Yes', basketball: 'Yes', nascar: 'Yes', hockey: 'Yes') pdf.set_field(:date, Time.now.strftime('%B %e, %Y')) +pdf.set_field(:newsletter, 'Off') # uncheck the checkbox +pdf.set_field(:language, 'dart') # select a radio button option # list of fields puts "Fields hash: #{pdf.fields}" puts @@ -246,17 +359,17 @@ pdf.close ``` The example above produces the following output and also generates the output file [output.pdf](example/output.pdf). -``` -The form has a total of 8 fields. +```text +The form has a total of 14 fields. -Fields hash: {:last_name=>"Rahl", :first_name=>"Richard", :football=>"Yes", :baseball=>"Yes", :basketball=>"Yes", :nascar=>"Yes", :hockey=>"Yes", :date=>"August 30, 2019"} +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"} -Keys: [:last_name, :first_name, :football, :baseball, :basketball, :nascar, :hockey, :date] +Keys: [:last_name, :first_name, :football, :baseball, :basketball, :hockey, :date, :newsletter, :nascar, :language, :"language.1", :"language.2", :"language.3", :"language.4"] -Values: ["Rahl", "Richard", "Yes", "Yes", "Yes", "Yes", "Yes", "August 30, 2019"] +Values: ["Rahl", "Richard", "Yes", "Yes", "Yes", "Yes", "November 15, 2021", "Off", "Yes", "dart", "dart", "dart", "dart", "dart"] Field 'football' is of type BUTTON Renamed field 'last_name' to 'surname' \ No newline at end of file