rustpdf 0.1.0-x86_64-linux → 0.4.0-x86_64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 338ced9bc04f0b7ec32aaf50f6f3a6dae01b06330a635624af41c28ccaa255b2
4
- data.tar.gz: a3d214e870c8e3c7a064235ad273c8637cd801a423ed32500589eab7e13407a0
3
+ metadata.gz: 927dc1a17caf2b405b6f76aae4fcbb8e2fcb60eed178eba44aa314ff491c4d25
4
+ data.tar.gz: 57a660201a0e93cd3e0f2be2367819464a62f22c4cc5056f4e470b5edbbcaec4
5
5
  SHA512:
6
- metadata.gz: 6e9c32134dff46cf7c803414833a6a17e79538a4de354c71c6185179a0310f951292546180b652b0f3d95fdf949e6bd43ff30974e2f21a93836b0e42709d8c5b
7
- data.tar.gz: 8b0fc3c9289e2285535a6a58812e4dd99b2317b235bd4f309dc23399bb96ec9d466d8482efb02205d8eba5caf5bbbc5230eeddfbc60cac97f76672097480c9f1
6
+ metadata.gz: ec46a8638b228b00a6c2728b636824b924896554b8e8b3f0734b1a4d3108db2ac87680949a89852aab9bc596728e59e81e4ed665ca57d15140188e746fe000ba
7
+ data.tar.gz: 3c911b62b6e32ffcfba2e1d9a072a005d545d3f5d367902e42c8bfd2060969bfd3b96c291aa10fb28946f810b18de6dd3104b835e73538f1774ef8f2c1283e7e
data/README.md CHANGED
@@ -6,7 +6,7 @@ covers the whole product surface: vector graphics, embedded/subsetted fonts and
6
6
  text, wrapping paragraphs, images, **PDF/A** (levels 1b–3a),
7
7
  **tagged/accessible** output, embedded-file attachments, **AcroForm** fields,
8
8
  manipulation (merge/split/rotate/optimize/incremental update), **text
9
- extraction**, **encryption** (RC4 / AES-128 / AES-256) and **digital signatures**
9
+ extraction**, **page rendering** (page to PNG image), **encryption** (RC4 / AES-128 / AES-256) and **digital signatures**
10
10
  (PKCS#7 / PAdES) — plus **feature licensing**.
11
11
 
12
12
  Files (module `RustPdf`):
@@ -46,7 +46,7 @@ ed.encrypt(method: RustPdf::Cipher::AES256, owner: "owner").save("secured.pdf")
46
46
  signed = RustPdf.sign(data, key_der, cert_der, pades: true)
47
47
  ```
48
48
 
49
- Corporate features (PDF/A, signing, encryption, accessibility) require a license;
49
+ Corporate features (PDF/A, signing, encryption, accessibility, page rendering — a **Pro** feature) require a license;
50
50
  without one they raise `RustPdf::Error`. See [`docs/LICENSING.md`](../../docs/LICENSING.md).
51
51
 
52
52
  ## Test
@@ -160,6 +160,44 @@ module RustPdf
160
160
  self
161
161
  end
162
162
 
163
+ # ---- hyperlinks + bookmarks (Tier 1) ------------------------------------
164
+
165
+ # rect = [x0, y0, x1, y1]; link to an external URI.
166
+ def link_uri(rect, uri)
167
+ RustPdf.check(Native.call("pdf_page_link_uri", ptr, rect[0], rect[1], rect[2], rect[3], uri))
168
+ self
169
+ end
170
+
171
+ # rect = [x0, y0, x1, y1]; link to another page (optional +top+ y-offset).
172
+ def link_to_page(rect, page_index, top: nil)
173
+ RustPdf.check(Native.call("pdf_page_link_to_page", ptr, rect[0], rect[1], rect[2], rect[3],
174
+ page_index, top || 0.0, top.nil? ? 0 : 1))
175
+ self
176
+ end
177
+
178
+ # Append one outline tree (a RustPdf::Bookmark). Pre-order flattened into
179
+ # parallel arrays and emitted in a single native call.
180
+ def add_bookmark(bookmark)
181
+ entries = bookmark.flatten_into(0, [])
182
+ n = entries.size
183
+ levels = entries.map { |e| e[0] }.pack("i!*")
184
+ pages = entries.map { |e| e[2] }.pack("J*")
185
+ tops = entries.map { |e| e[3] || 0.0 }.pack("d*")
186
+ has = entries.map { |e| e[3].nil? ? 0 : 1 }.pack("i!*")
187
+ cstrs = entries.map { |e| Fiddle::Pointer[e[1].to_s] }
188
+ titles = cstrs.map(&:to_i).pack("J*")
189
+ RustPdf.check(Native.call("pdf_document_add_bookmarks", ptr, n, levels, titles, pages, tops, has))
190
+ cstrs.clear # released after the call returned
191
+ self
192
+ end
193
+
194
+ # ---- ZUGFeRD / Factur-X (Tier 2) ----------------------------------------
195
+
196
+ def facturx(xml, profile: FacturxProfile::EN16931)
197
+ RustPdf.check(Native.call("pdf_document_facturx", ptr, xml, xml.bytesize, profile))
198
+ self
199
+ end
200
+
163
201
  # ---- output -------------------------------------------------------------
164
202
 
165
203
  def page_count
@@ -93,6 +93,69 @@ module RustPdf
93
93
  found != 0
94
94
  end
95
95
 
96
+ # ---- form fill + flatten + watermark (Tier 1) ---------------------------
97
+
98
+ # Set a checkbox by field name. Returns whether the field existed.
99
+ def set_checkbox(name, checked = true)
100
+ found = RustPdf.out_int { |buf| Native.call("pdf_editable_set_checkbox", ptr, name, checked ? 1 : 0, buf) }
101
+ found != 0
102
+ end
103
+
104
+ # Select a radio button by field name + export value. Returns whether found.
105
+ def set_radio(name, export_value)
106
+ found = RustPdf.out_int { |buf| Native.call("pdf_editable_set_radio", ptr, name, export_value, buf) }
107
+ found != 0
108
+ end
109
+
110
+ # Set a choice (dropdown/list) value by field name. Returns whether found.
111
+ def set_choice(name, value)
112
+ found = RustPdf.out_int { |buf| Native.call("pdf_editable_set_choice", ptr, name, value, buf) }
113
+ found != 0
114
+ end
115
+
116
+ # Flatten all AcroForm fields into page content (removes interactivity).
117
+ def flatten_forms
118
+ RustPdf.check(Native.call("pdf_editable_flatten_forms", ptr))
119
+ self
120
+ end
121
+
122
+ # Returns the list of AcroForm field names.
123
+ def field_names
124
+ p = ptr
125
+ text = RustPdf.take_bytes { |pp, pn| Native.call("pdf_editable_field_names", p, pp, pn) }
126
+ .force_encoding(Encoding::UTF_8)
127
+ text.split("\n").reject(&:empty?)
128
+ end
129
+
130
+ # Stamp a diagonal text watermark across every page.
131
+ def watermark_text(text, size: 64.0, color: [0.5, 0.5, 0.5], opacity: 0.30, rotation_deg: 45.0)
132
+ r, g, b = color
133
+ RustPdf.check(Native.call("pdf_editable_watermark_text", ptr, text, size, r, g, b, opacity, rotation_deg))
134
+ self
135
+ end
136
+
137
+ # Stamp an image watermark (from a file) across every page.
138
+ def watermark_image_file(path, width, height, opacity: 0.30)
139
+ RustPdf.check(Native.call("pdf_editable_watermark_image_file", ptr, path, width, height, opacity))
140
+ self
141
+ end
142
+
143
+ # ---- redaction + PDF/A conversion (Tier 2) ------------------------------
144
+
145
+ # Black out rectangles on a page. rects = [[x0,y0,x1,y1], ...].
146
+ # Returns whether the page existed.
147
+ def redact(page_index, rects)
148
+ flat = rects.flatten.pack("d*")
149
+ found = RustPdf.out_int { |buf| Native.call("pdf_editable_redact", ptr, page_index, flat, rects.size, buf) }
150
+ found != 0
151
+ end
152
+
153
+ # Convert the document to PDF/A (B-levels only: A1B=0, A2B=1, A3B=3).
154
+ def convert_to_pdfa(level = Pdfa::A2B)
155
+ RustPdf.check(Native.call("pdf_editable_convert_to_pdfa", ptr, level))
156
+ self
157
+ end
158
+
96
159
  def optimize
97
160
  RustPdf.check(Native.call("pdf_editable_optimize", ptr))
98
161
  self
@@ -78,9 +78,32 @@ module RustPdf
78
78
  "pdf_editable_save" => [[VP, VP], I],
79
79
 
80
80
  "pdf_extract_text" => [[VP, SZ, VP, VP], I],
81
+ "pdf_extract_images_to_dir" => [[VP, SZ, VP, VP], I],
82
+ "pdf_render_page_to_png" => [[VP, SZ, SZ, D, VP, VP], I],
83
+ "pdf_page_count" => [[VP, SZ, VP], I],
81
84
  "pdf_sign" => [[VP, SZ, VP, SZ, VP, SZ, VP, VP, VP, I, VP, VP], I],
82
85
  "pdf_timestamp" => [[VP, SZ, VP, SZ, VP, SZ, VP, VP, VP], I],
83
86
  "pdf_add_dss" => [[VP, SZ, VP, VP, SZ, VP, VP, SZ, VP, VP], I],
87
+
88
+ # Tier 1: hyperlinks + bookmarks (Document)
89
+ "pdf_page_link_uri" => [[VP, D, D, D, D, VP], I],
90
+ "pdf_page_link_to_page" => [[VP, D, D, D, D, SZ, D, I], I],
91
+ "pdf_document_add_bookmarks" => [[VP, SZ, VP, VP, VP, VP, VP], I],
92
+ # Tier 2: ZUGFeRD / Factur-X (Document)
93
+ "pdf_document_facturx" => [[VP, VP, SZ, I], I],
94
+ # Tier 1: form fill + flatten + watermark (EditableDoc)
95
+ "pdf_editable_set_checkbox" => [[VP, VP, I, VP], I],
96
+ "pdf_editable_set_radio" => [[VP, VP, VP, VP], I],
97
+ "pdf_editable_set_choice" => [[VP, VP, VP, VP], I],
98
+ "pdf_editable_flatten_forms" => [[VP], I],
99
+ "pdf_editable_field_names" => [[VP, VP, VP], I],
100
+ "pdf_editable_watermark_text" => [[VP, VP, D, D, D, D, D, D], I],
101
+ "pdf_editable_watermark_image_file" => [[VP, VP, D, D, D], I],
102
+ # Tier 2: redaction + PDF/A conversion (EditableDoc)
103
+ "pdf_editable_redact" => [[VP, SZ, VP, SZ, VP], I],
104
+ "pdf_editable_convert_to_pdfa" => [[VP, I], I],
105
+ # Tier 2: signature validation (module-level)
106
+ "pdf_verify_signatures_json" => [[VP, SZ, VP, VP], I],
84
107
  }.freeze
85
108
 
86
109
  def lib
data/lib/rustpdf.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "fiddle"
2
+ require "json"
2
3
  require_relative "rustpdf/native"
3
4
 
4
5
  # Idiomatic Ruby binding for the rust-pdf core over its C ABI (libpdf_ffi),
@@ -24,6 +25,10 @@ module RustPdf
24
25
  A2A = 2
25
26
  A3B = 3
26
27
  A3A = 4
28
+ # PDF/A-4 (ISO 19005-4), based on PDF 2.0.
29
+ A4 = 5
30
+ A4E = 6
31
+ A4F = 7
27
32
  end
28
33
 
29
34
  # Paragraph alignment.
@@ -50,6 +55,42 @@ module RustPdf
50
55
  AES256 = 2
51
56
  end
52
57
 
58
+ # ZUGFeRD / Factur-X conformance profiles.
59
+ module FacturxProfile
60
+ MINIMUM = 0
61
+ BASIC_WL = 1
62
+ BASIC = 2
63
+ EN16931 = 3
64
+ EXTENDED = 4
65
+ end
66
+
67
+ # A document outline (bookmark) entry. Nest with #child to build a tree.
68
+ # Each #add_bookmark call on a Document appends one root tree (pre-order
69
+ # flattened into parallel arrays).
70
+ class Bookmark
71
+ attr_accessor :title, :page, :top, :children
72
+
73
+ def initialize(title, page, top: nil, children: nil)
74
+ @title = title
75
+ @page = page
76
+ @top = top
77
+ @children = children || []
78
+ end
79
+
80
+ # Append a child bookmark; returns self for chaining.
81
+ def child(bookmark)
82
+ @children << bookmark
83
+ self
84
+ end
85
+
86
+ # Pre-order flatten into +out+ as [level, title, page, top] tuples.
87
+ def flatten_into(level, out)
88
+ out << [level, title, page, top]
89
+ children.each { |c| c.flatten_into(level + 1, out) }
90
+ out
91
+ end
92
+ end
93
+
53
94
  module_function
54
95
 
55
96
  # Native library version string.
@@ -70,6 +111,38 @@ module RustPdf
70
111
  .force_encoding(Encoding::UTF_8)
71
112
  end
72
113
 
114
+ # Extract every raster image into +dir+ (JPEG verbatim as .jpg, others as
115
+ # .png; files named page{N}_{name}.{ext}). Returns the number written.
116
+ def extract_images_to_dir(pdf, dir)
117
+ count = Fiddle::Pointer.malloc(Native::SIZEOF_SZ, Fiddle::RUBY_FREE)
118
+ check(Native.call("pdf_extract_images_to_dir", pdf, pdf.bytesize, dir, count))
119
+ count[0, Native::SIZEOF_SZ].unpack1("J")
120
+ end
121
+
122
+ # Render page +page+ (0-based) of +pdf+ to a PNG image at +dpi+
123
+ # dots-per-inch. Page rendering is a licensed Pro feature: raises unless a
124
+ # license granting it is active.
125
+ def render_page_to_png(pdf, page = 0, dpi = 150.0)
126
+ take_bytes { |pp, pn| Native.call("pdf_render_page_to_png", pdf, pdf.bytesize, page, dpi.to_f, pp, pn) }
127
+ end
128
+
129
+ # Number of pages in +pdf+ (free — no license required).
130
+ def page_count(pdf)
131
+ count = Fiddle::Pointer.malloc(Native::SIZEOF_SZ, Fiddle::RUBY_FREE)
132
+ check(Native.call("pdf_page_count", pdf, pdf.bytesize, count))
133
+ count[0, Native::SIZEOF_SZ].unpack1("J")
134
+ end
135
+
136
+ # Validate every signature in +pdf+. Returns one Hash per signature with keys
137
+ # "field_name", "sub_filter", "signer", "covers_whole_document",
138
+ # "digest_valid", "signature_valid", "is_valid" and "byte_range". An empty
139
+ # array means the document is unsigned.
140
+ def verify_signatures(pdf)
141
+ js = take_bytes { |pp, pn| Native.call("pdf_verify_signatures_json", pdf, pdf.bytesize, pp, pn) }
142
+ .force_encoding(Encoding::UTF_8)
143
+ js.empty? ? [] : JSON.parse(js)
144
+ end
145
+
73
146
  # Sign a PDF (PKCS#7 detached, incremental update). Requires a license.
74
147
  def sign(pdf, key_der, cert_der, reason: nil, location: nil, name: nil, pades: false)
75
148
  take_bytes do |pp, pn|
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rustpdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.0
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - rust-pdf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-27 00:00:00.000000000 Z
11
+ date: 2026-06-29 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Idiomatic Ruby binding over the rust-pdf C ABI (libpdf_ffi) using the
14
14
  built-in Fiddle stdlib.