lib/fillable-pdf.rb in fillable-pdf-0.9.2 vs lib/fillable-pdf.rb in fillable-pdf-0.9.3
- old
+ new
@@ -1,36 +1,29 @@
require_relative 'fillable-pdf/itext'
require_relative 'field'
+require 'base64'
require 'fileutils'
require 'securerandom'
-class FillablePDF
- # required Java imports
- BYTE_STREAM = Rjb.import 'com.itextpdf.io.source.ByteArrayOutputStream'
- PDF_READER = Rjb.import 'com.itextpdf.kernel.pdf.PdfReader'
- PDF_WRITER = Rjb.import 'com.itextpdf.kernel.pdf.PdfWriter'
- PDF_DOCUMENT = Rjb.import 'com.itextpdf.kernel.pdf.PdfDocument'
- PDF_ACRO_FORM = Rjb.import 'com.itextpdf.forms.PdfAcroForm'
- PDF_FORM_FIELD = Rjb.import 'com.itextpdf.forms.fields.PdfFormField'
-
+class FillablePDF # rubocop:disable Metrics/ClassLength
##
# Opens a given fillable-pdf PDF file and prepares it for modification.
#
# @param [String|Symbol] file_path the name of the PDF file or file path
#
def initialize(file_path)
- raise IOError, "File at `#{file_path}' is not found" unless File.exist?(file_path)
+ raise IOError, "File <#{file_path}> is not found" unless File.exist?(file_path)
@file_path = file_path
begin
- @byte_stream = BYTE_STREAM.new
- @pdf_reader = PDF_READER.new @file_path.to_s
- @pdf_writer = PDF_WRITER.new @byte_stream
- @pdf_doc = PDF_DOCUMENT.new @pdf_reader, @pdf_writer
- @pdf_form = PDF_ACRO_FORM.getAcroForm(@pdf_doc, true)
+ @byte_stream = ITEXT::ByteArrayOutputStream.new
+ @pdf_reader = ITEXT::PdfReader.new @file_path.to_s
+ @pdf_writer = ITEXT::PdfWriter.new @byte_stream
+ @pdf_doc = ITEXT::PdfDocument.new @pdf_reader, @pdf_writer
+ @pdf_form = ITEXT::PdfAcroForm.getAcroForm(@pdf_doc, true)
@form_fields = @pdf_form.getFormFields
rescue StandardError => e
- raise "#{e.message} (input file may be corrupt, incompatible, or may not have any forms)"
+ 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
end
end
##
# Determines whether the form has any fields.
@@ -95,9 +88,67 @@
# @param [String|Symbol] key the field name
# @param [String|Symbol] value the field value
#
def set_field(key, value)
pdf_field(key).setValue(value.to_s)
+ end
+
+ ##
+ # Sets an image within the bounds of the given form field. It doesn't matter
+ # what type of form field it is (signature, image, etc). The image will be scaled
+ # to fill the available space while preserving its aspect ratio. All previous
+ # content will be removed, which means you cannot have both text and image.
+ #
+ # @param [String|Symbol] key the field name
+ # @param [String|Symbol] file_path the name of the image file or image path
+ #
+ def set_image(key, file_path) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
+ raise IOError, "File <#{file_path}> is not found" unless File.exist?(file_path)
+ field = pdf_field(key)
+ widgets = field.getWidgets
+ widget_dict = suppress_warnings { widgets.isEmpty ? field.getPdfObject : widgets.get(0).getPdfObject }
+ orig_rect = widget_dict.getAsRectangle(ITEXT::PdfName.Rect)
+ border_width = field.getBorderWidth
+ bounding_rectangle = ITEXT::Rectangle.new(
+ orig_rect.getWidth - (border_width * 2),
+ orig_rect.getHeight - (border_width * 2)
+ )
+
+ pdf_form_x_object = ITEXT::PdfFormXObject.new(bounding_rectangle)
+ canvas = ITEXT::Canvas.new(pdf_form_x_object, @pdf_doc)
+ image = ITEXT::Image.new(ITEXT::ImageDataFactory.create(file_path.to_s))
+ .setAutoScale(true)
+ .setHorizontalAlignment(ITEXT::HorizontalAlignment.CENTER)
+ container = ITEXT::Div.new
+ .setMargin(border_width).add(image)
+ .setVerticalAlignment(ITEXT::VerticalAlignment.MIDDLE)
+ .setFillAvailableArea(true)
+ canvas.add(container)
+ canvas.close
+
+ pdf_dict = ITEXT::PdfDictionary.new
+ widget_dict.put(ITEXT::PdfName.AP, pdf_dict)
+ pdf_dict.put(ITEXT::PdfName.N, pdf_form_x_object.getPdfObject)
+ widget_dict.setModified
+ rescue StandardError => e
+ raise "#{e.message} (there may be something wrong with your image)"
+ end
+
+ ##
+ # Sets an image within the bounds of the given form field. It doesn't matter
+ # what type of form field it is (signature, image, etc). The image will be scaled
+ # to fill the available space while preserving its aspect ratio. All previous
+ # content will be removed, which means you cannot have both text and image.
+ #
+ # @param [String|Symbol] key the field name
+ # @param [String|Symbol] base64_image_data base64 encoded data image
+ #
+ def set_image_base64(key, base64_image_data)
+ tmp_file = SecureRandom.uuid
+ File.open(tmp_file, 'wb') { |f| f.write(Base64.decode64(base64_image_data)) }
+ set_image(key, tmp_file)
+ ensure
+ FileUtils.rm tmp_file
end
##
# Sets the values of multiple fields given a set of unique field names and values.
#