Module: RustPdf

Defined in:
lib/rustpdf.rb,
lib/rustpdf/native.rb,
lib/rustpdf/document.rb,
lib/rustpdf/editable_doc.rb

Overview

Idiomatic Ruby binding for the rust-pdf core over its C ABI (libpdf_ffi), using the built-in Fiddle stdlib. Covers the whole product surface: vector graphics, fonts/text, paragraphs, images, PDF/A (1b-3a), tagged/accessible output, attachments, AcroForm fields, manipulation, text extraction, encryption and digital signatures, plus feature licensing.

Defined Under Namespace

Modules: Align, Cipher, FacturxProfile, Native, Pdfa, Relationship Classes: Bookmark, Document, EditableDoc, Error

Class Method Summary collapse

Class Method Details

.activate_license(token) ⇒ Object

Activate a license token (unlocks PDF/A, signing, encryption, accessibility). Tokens may also be supplied via the RUSTPDF_LICENSE / RUSTPDF_LICENSE_FILE environment variables (auto-activated).



104
105
106
# File 'lib/rustpdf.rb', line 104

def activate_license(token)
  check(Native.call("pdf_activate_license", token))
end

.add_dss(pdf, certs: [], crls: []) ⇒ Object

Append a Document Security Store (/DSS, PAdES-B-LT).



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/rustpdf.rb', line 163

def add_dss(pdf, certs: [], crls: [])
  cptrs = certs.map { |c| Fiddle::Pointer[c] }
  rptrs = crls.map { |c| Fiddle::Pointer[c] }
  cptr_buf = cptrs.map(&:to_i).pack("J*")
  clen_buf = certs.map(&:bytesize).pack("J*")
  rptr_buf = rptrs.map(&:to_i).pack("J*")
  rlen_buf = crls.map(&:bytesize).pack("J*")
  result = take_bytes do |pp, pn|
    Native.call("pdf_add_dss", pdf, pdf.bytesize, cptr_buf, clen_buf, certs.size,
                rptr_buf, rlen_buf, crls.size, pp, pn)
  end
  # keep the per-item pointers alive until the call has returned
  cptrs.clear
  rptrs.clear
  result
end

.check(status) ⇒ Object

Raises:



187
188
189
# File 'lib/rustpdf.rb', line 187

def check(status)
  raise Error.new(last_error, status) unless status.zero?
end

.extract_images_to_dir(pdf, dir) ⇒ Object

Extract every raster image into dir (JPEG verbatim as .jpg, others as .png; files named pageN_name.ext). Returns the number written.



116
117
118
119
120
# File 'lib/rustpdf.rb', line 116

def extract_images_to_dir(pdf, dir)
  count = Fiddle::Pointer.malloc(Native::SIZEOF_SZ, Fiddle::RUBY_FREE)
  check(Native.call("pdf_extract_images_to_dir", pdf, pdf.bytesize, dir, count))
  count[0, Native::SIZEOF_SZ].unpack1("J")
end

.extract_text(pdf) ⇒ Object

Extract a document's text (Unicode via ToUnicode).



109
110
111
112
# File 'lib/rustpdf.rb', line 109

def extract_text(pdf)
  take_bytes { |pp, pn| Native.call("pdf_extract_text", pdf, pdf.bytesize, pp, pn) }
    .force_encoding(Encoding::UTF_8)
end

.last_error ⇒ Object

---- internal helpers (used by Document/EditableDoc) ----------------------



182
183
184
185
# File 'lib/rustpdf.rb', line 182

def last_error
  p = Native.call("pdf_last_error_message")
  p.null? ? "unknown error" : p.to_s
end

.out_int ⇒ Object

Run a producer { |out_int| status } and return the written int.



207
208
209
210
211
# File 'lib/rustpdf.rb', line 207

def out_int
  buf = Fiddle::Pointer.malloc(Native::SIZEOF_INT, Fiddle::RUBY_FREE)
  check(yield(buf))
  buf[0, Native::SIZEOF_INT].unpack1("i!")
end

.page_count(pdf) ⇒ Object

Number of pages in pdf (free — no license required).



130
131
132
133
134
# File 'lib/rustpdf.rb', line 130

def page_count(pdf)
  count = Fiddle::Pointer.malloc(Native::SIZEOF_SZ, Fiddle::RUBY_FREE)
  check(Native.call("pdf_page_count", pdf, pdf.bytesize, count))
  count[0, Native::SIZEOF_SZ].unpack1("J")
end

.render_page_to_png(pdf, page = 0, dpi = 150.0) ⇒ Object

Render page page (0-based) of pdf to a PNG image at dpi dots-per-inch. Page rendering is a licensed Pro feature: raises unless a license granting it is active.



125
126
127
# File 'lib/rustpdf.rb', line 125

def render_page_to_png(pdf, page = 0, dpi = 150.0)
  take_bytes { |pp, pn| Native.call("pdf_render_page_to_png", pdf, pdf.bytesize, page, dpi.to_f, pp, pn) }
end

.sign(pdf, key_der, cert_der, reason: nil, location: nil, name: nil, pades: false) ⇒ Object

Sign a PDF (PKCS#7 detached, incremental update). Requires a license.



147
148
149
150
151
152
# File 'lib/rustpdf.rb', line 147

def sign(pdf, key_der, cert_der, reason: nil, location: nil, name: nil, pades: false)
  take_bytes do |pp, pn|
    Native.call("pdf_sign", pdf, pdf.bytesize, key_der, key_der.bytesize,
                cert_der, cert_der.bytesize, reason, location, name, pades ? 1 : 0, pp, pn)
  end
end

.take_bytes ⇒ Object

Run an out-buffer producer { |out_ptr, out_len| status } and return the produced bytes, always freeing the native buffer.



193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/rustpdf.rb', line 193

def take_bytes
  pp = Fiddle::Pointer.malloc(Native::SIZEOF_SZ, Fiddle::RUBY_FREE)
  pn = Fiddle::Pointer.malloc(Native::SIZEOF_SZ, Fiddle::RUBY_FREE)
  check(yield(pp, pn))
  len = pn[0, Native::SIZEOF_SZ].unpack1("J")
  return "".b if len.zero?

  dptr = pp.ptr
  bytes = dptr[0, len]
  Native.call("pdf_buffer_free", dptr, len)
  bytes
end

.timestamp(pdf, tsa_key_der, tsa_cert_der, date: nil) ⇒ Object

Append a document timestamp (/DocTimeStamp, PAdES-B-LTA).



155
156
157
158
159
160
# File 'lib/rustpdf.rb', line 155

def timestamp(pdf, tsa_key_der, tsa_cert_der, date: nil)
  take_bytes do |pp, pn|
    Native.call("pdf_timestamp", pdf, pdf.bytesize, tsa_key_der, tsa_key_der.bytesize,
                tsa_cert_der, tsa_cert_der.bytesize, date, pp, pn)
  end
end

.verify_signatures(pdf) ⇒ Object

Validate every signature in pdf. Returns one Hash per signature with keys "field_name", "sub_filter", "signer", "covers_whole_document", "digest_valid", "signature_valid", "is_valid" and "byte_range". An empty array means the document is unsigned.



140
141
142
143
144
# File 'lib/rustpdf.rb', line 140

def verify_signatures(pdf)
  js = take_bytes { |pp, pn| Native.call("pdf_verify_signatures_json", pdf, pdf.bytesize, pp, pn) }
       .force_encoding(Encoding::UTF_8)
  js.empty? ? [] : JSON.parse(js)
end

.version ⇒ Object

Native library version string.



97
98
99
# File 'lib/rustpdf.rb', line 97

def version
  Native.call("pdf_version").to_s
end