imprimatur 0.4.0-aarch64-linux-gnu → 0.6.0-aarch64-linux-gnu
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 +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +59 -7
- data/lib/imprimatur/4.0/imprimatur-render-worker +0 -0
- data/lib/imprimatur/4.0/imprimatur_native.so +0 -0
- data/lib/imprimatur/document.rb +4 -2
- data/lib/imprimatur/native.rb +7 -0
- data/lib/imprimatur/pdf_reader.rb +27 -0
- data/lib/imprimatur/reader.rb +48 -11
- data/lib/imprimatur/spreadsheet.rb +1 -1
- data/lib/imprimatur/version.rb +1 -1
- data/lib/imprimatur.rb +28 -3
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60b0c7b03b95799ce827fc16a4c288dc929647e0fc94c6672bac555d75b85140
|
|
4
|
+
data.tar.gz: 11b2699bbe2481da51deeeeada85731c85d7577d3cfd06551093866edff29ab1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e710314613e0b8fde5895a7b751d6458fd52749cd44167828f7a3cee6e9abdebf98befe86c78a832913e3b362e0cbc0b57072ed35742b5bab1dd11e64918623e
|
|
7
|
+
data.tar.gz: 1a5eeda8859cafa2ae3ac1b3bac3b50954d4bfbcb7b4562d6c0c04280f5195192eb20fa07aaf167f364e7022e9d5b338ceab269852be9bebd0d40bf9ceaf6d11
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Add native PDF metadata, bounded page reads with explicit failure states,
|
|
6
|
+
and optional pure-Rust full-page PNG rendering.
|
|
7
|
+
- Keep scanned and blank page structure available without a text layer.
|
|
8
|
+
- Render PDFs through a bundled Rust worker with time, memory and output limits;
|
|
9
|
+
cancellation kills and reaps it. macOS uses an RSS watchdog, not a hard cap.
|
|
10
|
+
|
|
11
|
+
## [0.6.0](https://github.com/seuros/imprimatur/compare/v0.5.0...v0.6.0) (2026-10-25)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* bound PDF reads and rendering; deduplicate Rust ([420e096](https://github.com/seuros/imprimatur/commit/420e096591ef08190427c7b090840dcd761010b4))
|
|
17
|
+
* **doc:** set the body font size ([44f7617](https://github.com/seuros/imprimatur/commit/44f76170fd1ad29f143c1ef22c355fd1c1cbf8e1))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* enforce render limits across Unix platforms ([5c7f0ba](https://github.com/seuros/imprimatur/commit/5c7f0bab2076681782c6bfa2de6062ad53c0e7f9))
|
|
23
|
+
|
|
24
|
+
## [0.5.0](https://github.com/seuros/imprimatur/compare/v0.4.0...v0.5.0) (2026-10-18)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
* a binary String is always a document's bytes, a path must name a regular file, and reads stop at max_bytes ([3484a64](https://github.com/seuros/imprimatur/commit/3484a64b791beb2e9f90cd485441fae255e223f0))
|
|
30
|
+
|
|
3
31
|
## [0.4.0](https://github.com/seuros/imprimatur/compare/v0.3.0...v0.4.0) (2026-10-18)
|
|
4
32
|
|
|
5
33
|
|
data/README.md
CHANGED
|
@@ -7,10 +7,9 @@ a 40,000-character cell is refused at the door, not discovered by Excel's
|
|
|
7
7
|
|
|
8
8
|
- **A Ruby DSL over a native core.** The core is also published on its own,
|
|
9
9
|
as `imprimatur` on crates.io; the gem wraps it.
|
|
10
|
-
- **
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Data Matrix encoding (`qrcode`, `datamatrix`) for barcodes.
|
|
10
|
+
- **Own readers and writers.** XML, zip and PDF reading/writing live here.
|
|
11
|
+
Optional full-page PDF rendering uses Hayro, a pure-Rust CPU renderer;
|
|
12
|
+
no Poppler, PDFium, GPU or system fonts are required.
|
|
14
13
|
- **Deterministic.** The same document always gives the same bytes.
|
|
15
14
|
- **Native only.** CRuby 4.0+. No pure Ruby fallback, no JRuby, no TruffleRuby.
|
|
16
15
|
|
|
@@ -178,7 +177,7 @@ Not yet: merged cells, frozen panes, streaming for very large sheets.
|
|
|
178
177
|
One document, two formats:
|
|
179
178
|
|
|
180
179
|
```ruby
|
|
181
|
-
doc = Imprimatur::Document.new(page_size: :a4, page_numbers: true)
|
|
180
|
+
doc = Imprimatur::Document.new(page_size: :a4, font_size: 10, page_numbers: true)
|
|
182
181
|
doc.heading "Quarterly report"
|
|
183
182
|
doc.heading "Sales", level: 2
|
|
184
183
|
doc.paragraph do |p|
|
|
@@ -195,12 +194,14 @@ doc.paragraph "Appendix."
|
|
|
195
194
|
doc.save_pdf("report.pdf")
|
|
196
195
|
doc.save_docx("report.docx")
|
|
197
196
|
|
|
198
|
-
Imprimatur.pdf("memo.pdf") { it.paragraph "Or in one go." }
|
|
197
|
+
Imprimatur.pdf("memo.pdf", font_size: 10) { it.paragraph "Or in one go." } # Imprimatur.docx alike
|
|
199
198
|
Imprimatur.pdf(page_size: [200, 200], margin: 10) { it.image qr_png, width: 180 }
|
|
200
199
|
```
|
|
201
200
|
|
|
202
201
|
- Headings 1 to 3 (real Word heading styles in DOCX), paragraphs of runs
|
|
203
202
|
(bold, italic, underline, color), alignment, `\n` line breaks and tabs.
|
|
203
|
+
- Body text at `font_size:` points (1 to 1638, in steps of 0.5; 11 unless
|
|
204
|
+
set), in both formats; headings keep their own sizes.
|
|
204
205
|
- Tables: equal columns, borders, a shaded header row that repeats on every
|
|
205
206
|
page; cells are converted with `to_s`.
|
|
206
207
|
- Pictures: PNG or JPEG (a path, the bytes or an IO), at `width:` or
|
|
@@ -318,6 +319,13 @@ fibers keep running, and `Timeout` or `Thread#raise` cancels it.
|
|
|
318
319
|
`Imprimatur::PasswordRequired` and `Imprimatur::UnsupportedDocument` (legacy
|
|
319
320
|
`.doc`, `.xlsb`, presentations) are the other errors to expect.
|
|
320
321
|
|
|
322
|
+
A binary String is always the document's bytes, never a path: an upload
|
|
323
|
+
holding `/etc/passwd` or `/dev/zero` is refused as `UnsupportedDocument`,
|
|
324
|
+
not read from the server. A path (a `Pathname`, or a String that is not
|
|
325
|
+
binary and has no document signature) must name a regular file; devices,
|
|
326
|
+
pipes and directories are refused. An IO or a path is read no further than
|
|
327
|
+
`max_bytes:`, so an endless stream or a huge file stops there.
|
|
328
|
+
|
|
321
329
|
### Structure and images
|
|
322
330
|
|
|
323
331
|
Markdown is one rendering of what a read finds. `Imprimatur.content` gives
|
|
@@ -326,6 +334,50 @@ pages of headings and paragraphs with their position, a workbook's typed
|
|
|
326
334
|
cells and comments, a Word document's blocks with their bold, italic and
|
|
327
335
|
linked stretches.
|
|
328
336
|
|
|
337
|
+
For a PDF without text, `content` still returns every page, its empty
|
|
338
|
+
`blocks` and its `images` count. `markdown` and the text-oriented CLI commands
|
|
339
|
+
still report `NoTextLayer`;
|
|
340
|
+
OCR callers need the structure of scanned and blank pages too.
|
|
341
|
+
|
|
342
|
+
### Bounded PDF pages and OCR rendering
|
|
343
|
+
|
|
344
|
+
```ruby
|
|
345
|
+
Imprimatur.pdf_info(upload.download) # => {page_count: 20, encrypted: false}
|
|
346
|
+
result = Imprimatur.pdf_pages(upload.download, first_page: 2, last_page: 10, max_pages: 5)
|
|
347
|
+
result[:pages].each { |page| puts [page[:page], page[:status], page[:text]] }
|
|
348
|
+
png = Imprimatur.render_pdf_page(upload.download, page: 2, max_side: 1568)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
`pdf_info` counts actual pages without interpreting their content. `pdf_pages`
|
|
352
|
+
only interprets the selected streams, at most 500 pages by default. Each page
|
|
353
|
+
has `status` (`text`, `empty`, `failed`, `skipped`), `text`, `images` (nil if
|
|
354
|
+
unknown), `truncated` and `reason`. It keeps healthy pages when another is
|
|
355
|
+
damaged. Unlike the tolerant Markdown reader, corrupt Flate streams fail
|
|
356
|
+
explicitly. Limits are 256 KiB of text per page and 8 MiB total
|
|
357
|
+
(`max_page_bytes:`, `max_output_bytes:`); truncation respects UTF-8.
|
|
358
|
+
|
|
359
|
+
`render_pdf_page` returns an opaque grayscale PNG of the **whole page**, not
|
|
360
|
+
just its scan image; `grayscale: false` keeps color. It bounds the output surface
|
|
361
|
+
before allocation (`max_pixels:`, 4 million) and encoded output
|
|
362
|
+
(`max_output_bytes:`, 8 MiB). Preflight rejects pages drawing more than 40 million
|
|
363
|
+
source-image pixels. Renderer warnings fail instead of silently dropping content.
|
|
364
|
+
The crate exposes `read::pdf::{info, pages, render_page}` (`render` feature) and
|
|
365
|
+
`read::pdf::isolation::render_page_isolated` (`isolation` feature). The latter
|
|
366
|
+
takes an absolute path to the trusted `imprimatur-render-worker` executable.
|
|
367
|
+
The gem bundles that worker and always uses isolated rendering; it never falls
|
|
368
|
+
back to an in-process render or searches PATH.
|
|
369
|
+
|
|
370
|
+
All three accept the normal read limits. Rendering also accepts `memory_bytes:`
|
|
371
|
+
(1 GiB by default): Linux/FreeBSD enforce an address-space cap; macOS uses a
|
|
372
|
+
sampled RSS watchdog, which can briefly overshoot. A worker is killed and reaped
|
|
373
|
+
on timeout, cancellation or supervisor failure. Rendering has a mandatory
|
|
374
|
+
60-second process ceiling even with `timeout: nil`; a shorter `timeout:` still
|
|
375
|
+
applies. CPU time, output files and core dumps are also limited.
|
|
376
|
+
|
|
377
|
+
This is resource containment, **not a filesystem/network security sandbox**.
|
|
378
|
+
The raw crate `render_page` remains in-process and cannot interrupt Hayro.
|
|
379
|
+
`max_inflated` bounds our preflight, not Hayro's internal decoders.
|
|
380
|
+
|
|
329
381
|
```ruby
|
|
330
382
|
sheet = Imprimatur.content("sales.xlsx")[:sheets].first
|
|
331
383
|
sheet[:rows][1][:cells]
|
|
@@ -434,7 +486,7 @@ The core works without Ruby too; see
|
|
|
434
486
|
|
|
435
487
|
```sh
|
|
436
488
|
bundle install
|
|
437
|
-
rake test:all #
|
|
489
|
+
rake test:all # core, CLI and Ruby suites on the compiled extension
|
|
438
490
|
rake lint # dictator, cargo fmt --check, clippy
|
|
439
491
|
rake gems:native # the precompiled gem for this machine, in pkg/
|
|
440
492
|
```
|
|
Binary file
|
|
Binary file
|
data/lib/imprimatur/document.rb
CHANGED
|
@@ -21,10 +21,11 @@ module Imprimatur
|
|
|
21
21
|
# @param page_size [:a4, :letter, Array(Numeric, Numeric)] or its width
|
|
22
22
|
# and height in points, 36 to 1584 a side
|
|
23
23
|
# @param margin [Numeric, nil] points on every side, one inch (72) unless set
|
|
24
|
+
# @param font_size [Numeric, nil] body text in points, 1 to 1638 in steps of 0.5; 11 unless set
|
|
24
25
|
# @param page_numbers [Boolean] number pages "1 / N" in the footer
|
|
25
26
|
# @raise [ArgumentError] an unknown page size
|
|
26
|
-
# @raise [Imprimatur::Error] a page size or
|
|
27
|
-
def initialize(page_size: :a4, margin: nil, page_numbers: false)
|
|
27
|
+
# @raise [Imprimatur::Error] a page size, margin or font size out of range
|
|
28
|
+
def initialize(page_size: :a4, margin: nil, font_size: nil, page_numbers: false)
|
|
28
29
|
@native = Native::Document.new
|
|
29
30
|
numbers = page_numbers ? true : false
|
|
30
31
|
if page_size.is_a?(Array)
|
|
@@ -34,6 +35,7 @@ module Imprimatur
|
|
|
34
35
|
@native.set_page(page_size.to_s, numbers)
|
|
35
36
|
end
|
|
36
37
|
@native.set_margin(Float(margin)) if margin
|
|
38
|
+
@native.set_font_size(Float(font_size)) if font_size
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# A PNG or JPEG in the flow, at +width+ or +height+ points (the other
|
data/lib/imprimatur/native.rb
CHANGED
|
@@ -15,3 +15,10 @@ rescue LoadError => e
|
|
|
15
15
|
raise LoadError, "imprimatur: the native extension did not load (#{e.message}). " \
|
|
16
16
|
'Install a platform gem, or build it with `rake compile` (toolchain: https://rustup.rs).'
|
|
17
17
|
end
|
|
18
|
+
|
|
19
|
+
module Imprimatur
|
|
20
|
+
# Always use the companion of the loaded extension, never PATH.
|
|
21
|
+
RENDER_WORKER = File.join(File.dirname($LOADED_FEATURES.find do |path|
|
|
22
|
+
path.end_with?("/imprimatur_native.#{RbConfig::CONFIG['DLEXT']}")
|
|
23
|
+
end), 'imprimatur-render-worker').freeze
|
|
24
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Imprimatur
|
|
4
|
+
# Keyword handling only; parsing, limits and rendering live in the native core.
|
|
5
|
+
module PdfReader
|
|
6
|
+
def self.info(document, max_bytes: nil, **limits)
|
|
7
|
+
JSON.parse(Native.pdf_info(Reader.read(document, max_bytes), Reader.limits(max_bytes:, **limits)),
|
|
8
|
+
symbolize_names: true)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.pages(document, first_page: 1, last_page: 0xFFFFFFFF, max_pages: 500,
|
|
12
|
+
max_page_bytes: 256 * 1024, max_output_bytes: 8 * 1024 * 1024,
|
|
13
|
+
max_bytes: nil, **limits)
|
|
14
|
+
options = [first_page, last_page, max_pages, max_page_bytes, max_output_bytes]
|
|
15
|
+
JSON.parse(Native.pdf_pages(Reader.read(document, max_bytes), Reader.limits(max_bytes:, **limits), options),
|
|
16
|
+
symbolize_names: true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.render(document, page:, max_side: 1568, max_pixels: 4_000_000,
|
|
20
|
+
max_output_bytes: 8 * 1024 * 1024, grayscale: true,
|
|
21
|
+
memory_bytes: 1024 * 1024 * 1024, max_bytes: nil, **limits)
|
|
22
|
+
Native.render_pdf_page(Reader.read(document, max_bytes), Reader.limits(max_bytes:, **limits),
|
|
23
|
+
[page, max_side, max_pixels, max_output_bytes, grayscale],
|
|
24
|
+
RENDER_WORKER, memory_bytes)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/imprimatur/reader.rb
CHANGED
|
@@ -12,22 +12,29 @@ module Imprimatur
|
|
|
12
12
|
# Documents in: Markdown, their structure, their images. See
|
|
13
13
|
# {Imprimatur.markdown}, {Imprimatur.content} and {Imprimatur.images}.
|
|
14
14
|
module Reader
|
|
15
|
-
# File signatures a document's bytes start with; a String that
|
|
16
|
-
# with neither is taken for a path.
|
|
15
|
+
# File signatures a document's bytes start with; a String that is not
|
|
16
|
+
# binary and starts with neither is taken for a path.
|
|
17
17
|
SIGNATURES = ["%PDF-".b, "PK\x03\x04".b, "PK\x05\x06".b, "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1".b].freeze
|
|
18
18
|
private_constant :SIGNATURES
|
|
19
19
|
|
|
20
|
+
# The largest document read in unless told, as the native core's own limit.
|
|
21
|
+
MAX_BYTES = 512 * 1024 * 1024
|
|
22
|
+
private_constant :MAX_BYTES
|
|
23
|
+
|
|
20
24
|
# @see Imprimatur.markdown
|
|
21
|
-
def self.markdown(document,
|
|
25
|
+
def self.markdown(document, max_bytes: nil, **)
|
|
26
|
+
Native.markdown(read(document, max_bytes), *limits(max_bytes:, **))
|
|
27
|
+
end
|
|
22
28
|
|
|
23
29
|
# @see Imprimatur.content
|
|
24
|
-
def self.content(document, styles: false, **)
|
|
25
|
-
|
|
30
|
+
def self.content(document, styles: false, max_bytes: nil, **)
|
|
31
|
+
bytes = read(document, max_bytes)
|
|
32
|
+
JSON.parse(Native.content(bytes, *limits(max_bytes:, **), styles ? true : false), symbolize_names: true)
|
|
26
33
|
end
|
|
27
34
|
|
|
28
35
|
# @see Imprimatur.images
|
|
29
|
-
def self.images(document, **)
|
|
30
|
-
Native.images(read(document), *limits(**)).map do |data, description|
|
|
36
|
+
def self.images(document, max_bytes: nil, **)
|
|
37
|
+
Native.images(read(document, max_bytes), *limits(max_bytes:, **)).map do |data, description|
|
|
31
38
|
found = JSON.parse(description, symbolize_names: true)
|
|
32
39
|
Image.new(data:, **Image.members.drop(1).to_h { [it, found[it]] })
|
|
33
40
|
end
|
|
@@ -38,22 +45,52 @@ module Imprimatur
|
|
|
38
45
|
[max_bytes, max_inflated, timeout && Float(timeout)]
|
|
39
46
|
end
|
|
40
47
|
|
|
48
|
+
# A path or an IO is read no further than +max_bytes+ (one byte past it
|
|
49
|
+
# tells it is passed): an endless stream or a huge file stops there.
|
|
50
|
+
#
|
|
41
51
|
# @api private
|
|
42
|
-
def self.read(document)
|
|
52
|
+
def self.read(document, max_bytes = nil)
|
|
53
|
+
limit = max_bytes.is_a?(Integer) && max_bytes >= 0 ? max_bytes : MAX_BYTES
|
|
43
54
|
case document
|
|
44
|
-
when Pathname then
|
|
45
|
-
when String then bytes?(document) ? document :
|
|
55
|
+
when Pathname then file(document, limit)
|
|
56
|
+
when String then bytes?(document) ? document : file(document, limit)
|
|
46
57
|
else
|
|
47
58
|
raise TypeError, "expected a path, bytes or an IO, not #{document.class}" unless document.respond_to?(:read)
|
|
48
59
|
|
|
49
|
-
document.read.to_s.b
|
|
60
|
+
within(document.read(limit + 1).to_s.b, limit)
|
|
50
61
|
end
|
|
51
62
|
end
|
|
52
63
|
|
|
64
|
+
# A binary String (what +File.binread+, a binary IO or a download give)
|
|
65
|
+
# is bytes, whatever it holds: uploaded bytes never name a file to read.
|
|
66
|
+
#
|
|
53
67
|
# @api private
|
|
54
68
|
def self.bytes?(string)
|
|
69
|
+
return true if string.encoding == Encoding::BINARY
|
|
70
|
+
|
|
55
71
|
head = string.byteslice(0, 1024).b
|
|
56
72
|
SIGNATURES.any? { head.start_with?(it) } || head.include?("%PDF-") || head.include?("\0")
|
|
57
73
|
end
|
|
74
|
+
|
|
75
|
+
# A regular file's bytes. Opened without blocking and checked through
|
|
76
|
+
# the open file, so a pipe cannot hang the read, a device such as
|
|
77
|
+
# +/dev/zero+ is refused, and the file cannot be swapped in between.
|
|
78
|
+
#
|
|
79
|
+
# @api private
|
|
80
|
+
def self.file(path, limit)
|
|
81
|
+
File.open(path, File::RDONLY | File::NONBLOCK | File::BINARY) do |io|
|
|
82
|
+
stat = io.stat
|
|
83
|
+
raise UnsupportedDocument, "cannot read this document: #{path} is not a regular file" unless stat.file?
|
|
84
|
+
|
|
85
|
+
within(stat.size > limit ? '' : io.read(limit + 1).to_s, limit, stat.size)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @api private
|
|
90
|
+
def self.within(bytes, limit, size = bytes.bytesize)
|
|
91
|
+
raise LimitExceeded, 'the document exceeds the input size limit' if [size, bytes.bytesize].max > limit
|
|
92
|
+
|
|
93
|
+
bytes
|
|
94
|
+
end
|
|
58
95
|
end
|
|
59
96
|
end
|
|
@@ -123,7 +123,7 @@ module Imprimatur
|
|
|
123
123
|
# @raise [UnsupportedDocument] a PDF, a Word document or another format
|
|
124
124
|
# @raise [Imprimatur::Error] a damaged workbook, or past a limit
|
|
125
125
|
def self.open(document, styles: false, **limits)
|
|
126
|
-
new(Native.sheets(Reader.read(document), *Reader.limits(**limits), styles ? true : false))
|
|
126
|
+
new(Native.sheets(Reader.read(document, limits[:max_bytes]), *Reader.limits(**limits), styles ? true : false))
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
# @return [Array<Sheet>] in the workbook's order, hidden ones included
|
data/lib/imprimatur/version.rb
CHANGED
data/lib/imprimatur.rb
CHANGED
|
@@ -10,6 +10,7 @@ require_relative 'imprimatur/barcode'
|
|
|
10
10
|
require_relative 'imprimatur/xlsx'
|
|
11
11
|
require_relative 'imprimatur/document'
|
|
12
12
|
require_relative 'imprimatur/reader'
|
|
13
|
+
require_relative 'imprimatur/pdf_reader'
|
|
13
14
|
require_relative 'imprimatur/spreadsheet'
|
|
14
15
|
require_relative 'imprimatur/zip'
|
|
15
16
|
|
|
@@ -45,7 +46,7 @@ module Imprimatur
|
|
|
45
46
|
# Builds a {Document} and renders it as PDF. With a path, saves it there
|
|
46
47
|
# and returns the path; without, returns the bytes.
|
|
47
48
|
#
|
|
48
|
-
# @param options [Hash] +page_size:+ and +page_numbers:+, see {Document#initialize}
|
|
49
|
+
# @param options [Hash] +page_size:+, +margin:+, +font_size:+ and +page_numbers:+, see {Document#initialize}
|
|
49
50
|
# @yieldparam document [Document]
|
|
50
51
|
def pdf(path = nil, **options, &) = Document.build(:pdf, path, **options, &)
|
|
51
52
|
|
|
@@ -66,7 +67,8 @@ module Imprimatur
|
|
|
66
67
|
# Imprimatur.markdown(File.open('sales.xlsx'))
|
|
67
68
|
#
|
|
68
69
|
# @param document [String, Pathname, #read] a path, the document's bytes
|
|
69
|
-
# (
|
|
70
|
+
# (a binary String always is; another String only with a document's
|
|
71
|
+
# signature), or an IO. A path must name a regular file.
|
|
70
72
|
# @param timeout [Numeric, nil] seconds the read may take (60), +nil+ for none
|
|
71
73
|
# @param max_bytes [Integer, nil] the largest document accepted (512 MiB)
|
|
72
74
|
# @param max_inflated [Integer, nil] decompressed bytes allowed in all (1 GiB)
|
|
@@ -91,7 +93,11 @@ module Imprimatur
|
|
|
91
93
|
# sheet = Imprimatur.content('sales.xlsx')[:sheets].first
|
|
92
94
|
# sheet[:rows][1][:cells] # => [{cell: "A2", type: "text", value: "North"}, ...]
|
|
93
95
|
#
|
|
94
|
-
#
|
|
96
|
+
# Unlike {.markdown}, a PDF without text still returns every page, with
|
|
97
|
+
# empty +:blocks+ and its image count, rather than raising {NoTextLayer}.
|
|
98
|
+
# This keeps scanned and blank pages visible to OCR callers.
|
|
99
|
+
#
|
|
100
|
+
# Takes the arguments of {.markdown}, and raises as it does otherwise. With
|
|
95
101
|
# +styles: true+ each sheet also lists its formatted cells' +:styles+:
|
|
96
102
|
# font, size, bold, italic, underline, ARGB color and fill, alignment,
|
|
97
103
|
# wrap, border and number format.
|
|
@@ -99,6 +105,25 @@ module Imprimatur
|
|
|
99
105
|
# @return [Hash] with symbol keys; +:format+ is +"pdf"+, +"xlsx"+ or +"docx"+
|
|
100
106
|
def content(document, ...) = Reader.content(document, ...)
|
|
101
107
|
|
|
108
|
+
# PDF metadata without reading page content.
|
|
109
|
+
# @return [Hash] +page_count:+ and +encrypted:+
|
|
110
|
+
def pdf_info(document, ...) = PdfReader.info(document, ...)
|
|
111
|
+
|
|
112
|
+
# A bounded, one-based page selection. Each page has its status, text,
|
|
113
|
+
# image count (nil when unknown), truncation flag and failure reason.
|
|
114
|
+
# Takes +first_page:+, +last_page:+, +max_pages:+ (500),
|
|
115
|
+
# +max_page_bytes:+ (256 KiB), +max_output_bytes:+ (8 MiB), and read limits.
|
|
116
|
+
# @return [Hash] +page_count:+, +encrypted:+ and +pages:+
|
|
117
|
+
def pdf_pages(document, ...) = PdfReader.pages(document, ...)
|
|
118
|
+
|
|
119
|
+
# A full page as an opaque PNG, not just its embedded images.
|
|
120
|
+
# Takes +page:+ (one-based), +max_side:+ (1568), +max_pixels:+ (4 million),
|
|
121
|
+
# +max_output_bytes:+ (8 MiB), +grayscale:+ (true), and read limits.
|
|
122
|
+
# Rendering checks time/cancellation before and after Hayro, not during it.
|
|
123
|
+
# Untrusted documents still need an externally limited worker process.
|
|
124
|
+
# @return [String] PNG bytes
|
|
125
|
+
def render_pdf_page(document, ...) = PdfReader.render(document, ...)
|
|
126
|
+
|
|
102
127
|
# A workbook's sheets and their values, read from the same documents
|
|
103
128
|
# {.markdown} takes (+.xlsx+, +.xls+, +.ods+); see {Spreadsheet}.
|
|
104
129
|
#
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: imprimatur
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: aarch64-linux-gnu
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -10,9 +10,10 @@ cert_chain: []
|
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: 'A Ruby DSL over a native core that writes Excel workbooks, and PDF and
|
|
13
|
-
Word documents from one model, with its own XML, zip and PDF
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
Word documents from one model, with its own XML, zip and PDF readers and writers.
|
|
14
|
+
It reads PDF, XLSX, XLS, ODS and DOCX back: as Markdown, as typed structure, and
|
|
15
|
+
their images. Full-page PDF rendering uses the pure-Rust Hayro renderer. Precompiled
|
|
16
|
+
for Linux, macOS and FreeBSD; CRuby only.'
|
|
16
17
|
email:
|
|
17
18
|
- terminale@gmail.com
|
|
18
19
|
executables: []
|
|
@@ -23,12 +24,14 @@ files:
|
|
|
23
24
|
- LICENSE.txt
|
|
24
25
|
- README.md
|
|
25
26
|
- lib/imprimatur.rb
|
|
27
|
+
- lib/imprimatur/4.0/imprimatur-render-worker
|
|
26
28
|
- lib/imprimatur/4.0/imprimatur_native.so
|
|
27
29
|
- lib/imprimatur/barcode.rb
|
|
28
30
|
- lib/imprimatur/document.rb
|
|
29
31
|
- lib/imprimatur/error.rb
|
|
30
32
|
- lib/imprimatur/formula.rb
|
|
31
33
|
- lib/imprimatur/native.rb
|
|
34
|
+
- lib/imprimatur/pdf_reader.rb
|
|
32
35
|
- lib/imprimatur/reader.rb
|
|
33
36
|
- lib/imprimatur/spreadsheet.rb
|
|
34
37
|
- lib/imprimatur/style.rb
|