All formats

HexaPDF

LanguageRuby 2.7+
LicenseAGPL-3.0 or commercial
Repositorygithub.com/gettalong/hexapdf
Documentationhexapdf.gettalong.org
Depends ongeom2d, cmdparse, openssl, strscan (all gems)
Read / writeyes / yes: create (canvas and layout), merge, split, rotate, forms, annotations, outlines, encryption, signatures, optimization, text extraction

HexaPDF is a complete PDF library written in Ruby. For creation it has a low-level Canvas (text, graphics, images) and HexaPDF::Composer, a layout engine with flowing text, lists, tables, images and multi-column frames. For existing files it reads and writes the object model, imports pages between documents, fills and flattens AcroForms, adds annotations and outlines, encrypts with AES-256, signs, and optimizes files (object streams, removing unused objects). The hexapdf command line merges, splits, optimizes, encrypts, decrypts and inspects files.

HexaPDF is AGPL-3.0: if you distribute your application or run it as a network service, you must release its source under the AGPL, or buy a commercial license.

Installation#

Shell
gem install hexapdf

Basic usage: create a PDF with text#

Ruby
require "hexapdf"

doc = HexaPDF::Document.new
canvas = doc.pages.add.canvas  # A4 by default, origin is bottom-left
canvas.font("Helvetica", size: 24)
canvas.text("Hello, PDF!", at: [72, 770])
canvas.line_width(1).line(72, 760, 300, 760).stroke
doc.metadata.title("Hello")
doc.metadata.author("Me")
doc.write("hello.pdf", optimize: true)

For documents with flowing text and tables, use the Composer:

Ruby
HexaPDF::Composer.create("invoice.pdf", page_size: :A4, margin: 50) do |composer|
  composer.text("Invoice #1001", font_size: 24, font: "Helvetica bold")
  composer.text("Thank you for your order. " * 10)
  composer.table([["Item", "Qty", "Price"], ["Widget", "2", "9.99"], ["Gadget", "1", "24.50"]],
                 column_widths: [200, 60, 80])
end

How to extract text from a PDF#

Ruby
doc = HexaPDF::Document.open("in.pdf")
doc.pages.each_with_index do |page, i|
  puts "--- page #{i + 1} ---"
  puts page.extract_text
end

How to merge and split PDFs#

Ruby
target = HexaPDF::Document.new
["a.pdf", "b.pdf"].each do |file|
  src = HexaPDF::Document.open(file)
  src.pages.each { |page| target.pages << target.import(page) }
end
target.write("merged.pdf", optimize: true)

src = HexaPDF::Document.open("merged.pdf")
src.pages.each_with_index do |page, i|
  single = HexaPDF::Document.new
  single.pages << single.import(page)
  single.write("page-#{i + 1}.pdf")
end

Command line: hexapdf merge a.pdf b.pdf merged.pdf and hexapdf split in.pdf.

How to rotate pages#

Ruby
doc = HexaPDF::Document.open("in.pdf")
doc.pages.each { |page| page.rotate(90) }
doc.write("rotated.pdf")

How to fill a PDF form#

Ruby
doc = HexaPDF::Document.open("form.pdf")
doc.acro_form.each_field do |field|
  puts "#{field.full_field_name} (#{field.concrete_field_type})"
end
doc.acro_form.fill("name" => "Jane Doe")
# or: doc.acro_form.field_by_name("name").field_value = "Jane Doe"
doc.write("filled.pdf")

How to read PDF metadata#

Ruby
doc = HexaPDF::Document.open("in.pdf")
puts doc.version                 # "1.7"
puts doc.metadata.title, doc.metadata.author, doc.metadata.producer
puts doc.trailer.info[:Title]    # raw Info dictionary

How to encrypt and decrypt a PDF#

Ruby
doc = HexaPDF::Document.open("in.pdf")
doc.encrypt(algorithm: :aes, key_length: 256, user_password: "user", owner_password: "owner",
            permissions: [:print, :extract_content])
doc.write("encrypted.pdf")

doc = HexaPDF::Document.open("encrypted.pdf", decryption_opts: { password: "owner" })
doc.encrypt(name: nil)  # remove encryption
doc.write("decrypted.pdf")

Tips#