HexaPDF
| Language | Ruby 2.7+ |
|---|---|
| License | AGPL-3.0 or commercial |
| Repository | github.com/gettalong/hexapdf |
| Documentation | hexapdf.gettalong.org |
| Depends on | geom2d, cmdparse, openssl, strscan (all gems) |
| Read / write | yes / 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#
gem install hexapdf
Basic usage: create a PDF with text#
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:
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#
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#
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#
doc = HexaPDF::Document.open("in.pdf")
doc.pages.each { |page| page.rotate(90) }
doc.write("rotated.pdf")
How to fill a PDF form#
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#
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#
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#
doc.write(path, incremental: true)appends changes as an incremental update, which keeps existing signatures valid.hexapdf optimize --compress-pages in.pdf out.pdfoften makes files much smaller.hexapdf inspect in.pdfopens an interactive shell to explore objects.