pdf_oxide 0.3.68 → 0.3.70

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: 2e8e54b9721132f15787ec211abc2c2079011d9fefa27a2b08d0dc3c6ba00e39
4
- data.tar.gz: 6d61bbbf08983b926a862af95a8c8c6af8b041bbc2ffc093921d9fd54ba764ab
3
+ metadata.gz: 69ee3975fd3514216f0145f91a2b53321b2837656900242f9292fdb4d31a3887
4
+ data.tar.gz: 2298ad2c2f64afd4eadedd408d1140e19436a3a5c5ee363295cc5053e789a61e
5
5
  SHA512:
6
- metadata.gz: 791ef9e50cb212a3c463bd3c66ffa5c064b794cfdad0413728397026138c2b2c42e6684c683e296393bcd8ec4e35e07c0f45db71f50e6153cf09014143749832
7
- data.tar.gz: e09e5bf746a771802ba142c7864859f6b3de2ee815c3a4718ee0f39cf15d28e6a55a6453ae9a12eb78f746c9afb13feefa96e42ddc9cc587ab7b5395989db89c
6
+ metadata.gz: c845247d4fba11a9af23f7b17595de5f410d9750dfeec28020617f2a00633e0664969c3aab8d6505dcc1c01f009f0fbedbfe443b135975fcd88ac8506b02f740
7
+ data.tar.gz: 491b40c860d487d04af14a2a1aa5196d3d74114838239d2b7676a988dd7766f11c19a0932e0d2bb6c70e979cfa989aabc60ea16e15fc560835a94c2c37e68e1a
@@ -1465,6 +1465,9 @@ module PdfOxide
1465
1465
  attach_function :pdf_oxide_word_get_bbox, %i[pointer pointer pointer pointer pointer pointer pointer pointer], :pointer, blocking: false
1466
1466
  attach_function :pdf_oxide_word_get_font_name, %i[pointer pointer pointer pointer pointer pointer pointer pointer], :pointer, blocking: false
1467
1467
  attach_function :pdf_oxide_word_get_font_size, %i[pointer pointer pointer pointer pointer pointer pointer pointer], :pointer, blocking: false
1468
+ # int64_t pdf_oxide_word_get_sequence(const void *words, int32_t index, int *error_code)
1469
+ # Content-stream emission order of the word's originating span; -1 on null/out-of-range.
1470
+ attach_function :pdf_oxide_word_get_sequence, %i[pointer int32 pointer], :int64, blocking: false
1468
1471
  attach_function :pdf_oxide_word_get_text, %i[pointer pointer pointer pointer pointer pointer pointer pointer], :pointer, blocking: false
1469
1472
  attach_function :pdf_oxide_word_is_bold, %i[pointer pointer pointer pointer pointer pointer pointer pointer], :pointer, blocking: false
1470
1473
  attach_function :pdf_oxide_word_list_free, %i[pointer pointer pointer pointer pointer pointer pointer pointer], :pointer, blocking: false
@@ -1693,6 +1696,39 @@ module PdfOxide
1693
1696
  %i[pointer size_t pointer], :int32
1694
1697
  attach_function :document_editor_apply_all_redactions,
1695
1698
  %i[pointer pointer], :int32
1699
+
1700
+ # ============================================================
1701
+ # PHASE 4 EXTEND: global tuning toggles + layer-filtered render.
1702
+ # Real ABI signatures from include/pdf_oxide_c/pdf_oxide.h.
1703
+ # ============================================================
1704
+
1705
+ # int64_t pdf_oxide_set_max_ops_per_stream(int64_t limit)
1706
+ # Process-global content-stream operator cap. `limit < 0` restores
1707
+ # the default (1,000,000); returns the previous cap (or -1 if the
1708
+ # default was active). No error channel.
1709
+ attach_function :pdf_oxide_set_max_ops_per_stream, [:int64], :int64
1710
+
1711
+ # int32_t pdf_oxide_set_preserve_unmapped_glyphs(int32_t preserve)
1712
+ # Toggle the global U+FFFD preservation flag. `1` = preserve,
1713
+ # `0` = filter. Returns the previous value (`0` or `1`). No error
1714
+ # channel.
1715
+ attach_function :pdf_oxide_set_preserve_unmapped_glyphs, [:int32], :int32
1716
+
1717
+ # FfiRenderedImage *pdf_render_page_with_options_ex(
1718
+ # PdfDocument *doc, int32_t page_index, int32_t dpi, int32_t format,
1719
+ # float bg_r, float bg_g, float bg_b, float bg_a,
1720
+ # int32_t transparent_background, int32_t render_annotations,
1721
+ # int32_t jpeg_quality,
1722
+ # const char *const *excluded_layers, uintptr_t excluded_layers_count,
1723
+ # int32_t *error_code)
1724
+ # Like pdf_render_page_with_options but with OCG layer filtering.
1725
+ attach_function :pdf_render_page_with_options_ex,
1726
+ %i[pointer int32 int32 int32
1727
+ float float float float
1728
+ int32 int32 int32
1729
+ pointer size_t
1730
+ pointer],
1731
+ :pointer
1696
1732
  end
1697
1733
  end
1698
1734
  end
@@ -258,7 +258,50 @@ module PdfOxide
258
258
  # path is the byte-buffer accessor introduced for v0.3.5x.
259
259
  bytes = read_rendered_image_bytes(img_ptr)
260
260
  Bindings.pdf_rendered_image_free(img_ptr) if Bindings.respond_to?(:pdf_rendered_image_free)
261
- bytes.force_encoding(Encoding::BINARY)
261
+ bytes.b # binary-encoded copy (never mutates; read_* may return a frozen empty string)
262
+ end
263
+
264
+ # Render a single page with the full RenderOptions surface plus
265
+ # Optional-Content-Group (OCG) layer filtering.
266
+ #
267
+ # @param page_index [Integer]
268
+ # @param dpi [Integer] resolution (default 150).
269
+ # @param format [Integer] 0 = PNG, 1 = JPEG.
270
+ # @param background [Array(Float,Float,Float,Float)] RGBA, each 0.0..1.0.
271
+ # @param transparent [Boolean] drop the background fill entirely.
272
+ # @param render_annotations [Boolean] paint annotation appearances.
273
+ # @param jpeg_quality [Integer] 1..100 (only used when format == 1).
274
+ # @param excluded_layers [Array<String>] OCG `/Name`s to suppress.
275
+ # @return [String] encoded image bytes (BINARY).
276
+ def render_with_layers(page_index, dpi: 150, format: 0,
277
+ background: [1.0, 1.0, 1.0, 1.0], transparent: false,
278
+ render_annotations: true, jpeg_quality: 90,
279
+ excluded_layers: [])
280
+ validate_page_index(page_index)
281
+ bg_r, bg_g, bg_b, bg_a = background
282
+ names = Array(excluded_layers).map(&:to_s)
283
+
284
+ # Build a NULL-terminated-string array (char *const *).
285
+ names_ptr = ::FFI::Pointer::NULL
286
+ unless names.empty?
287
+ str_ptrs = names.map { |n| ::FFI::MemoryPointer.from_string(n) }
288
+ names_ptr = ::FFI::MemoryPointer.new(:pointer, str_ptrs.length)
289
+ names_ptr.write_array_of_pointer(str_ptrs)
290
+ end
291
+
292
+ err = ::FFI::MemoryPointer.new(:int32)
293
+ img_ptr = Bindings.pdf_render_page_with_options_ex(
294
+ handle, page_index, dpi, format,
295
+ bg_r.to_f, bg_g.to_f, bg_b.to_f, bg_a.to_f,
296
+ transparent ? 1 : 0, render_annotations ? 1 : 0, jpeg_quality,
297
+ names_ptr, names.length, err
298
+ )
299
+ raise_for_code(err.read_int32, 'render_with_layers')
300
+ raise InternalError, 'render_with_layers returned null' if img_ptr.nil? || img_ptr.null?
301
+
302
+ bytes = read_rendered_image_bytes(img_ptr)
303
+ Bindings.pdf_rendered_image_free(img_ptr) if Bindings.respond_to?(:pdf_rendered_image_free)
304
+ bytes.b # binary-encoded copy (never mutates; read_* may return a frozen empty string)
262
305
  end
263
306
 
264
307
  # @return [PdfPage] a lightweight view of the page at `index`.
@@ -324,14 +367,17 @@ module PdfOxide
324
367
  def open_native(source)
325
368
  err = ::FFI::MemoryPointer.new(:int32)
326
369
  handle, path =
327
- if source.is_a?(String) && File.exist?(source)
328
- [Bindings.pdf_document_open(File.absolute_path(source), err), File.absolute_path(source)]
329
- elsif source.is_a?(String) && source.start_with?('%PDF')
370
+ # NB: detect in-memory bytes (%PDF…) BEFORE the path branch — binary PDF
371
+ # bytes contain null bytes, and File.exist? raises ArgumentError
372
+ # ("path name contains null byte") on those, so the bytes check must run first.
373
+ if source.is_a?(String) && source.start_with?('%PDF')
330
374
  # in-memory PDF bytes
331
375
  buf = source.dup.force_encoding(Encoding::BINARY)
332
376
  mem = ::FFI::MemoryPointer.new(:uint8, buf.bytesize)
333
377
  mem.write_bytes(buf, 0, buf.bytesize)
334
378
  [Bindings.pdf_document_open_from_bytes(mem, buf.bytesize, err), '<in-memory>']
379
+ elsif source.is_a?(String) && File.exist?(source)
380
+ [Bindings.pdf_document_open(File.absolute_path(source), err), File.absolute_path(source)]
335
381
  else
336
382
  raise FileNotFoundError, "file not found: #{source}"
337
383
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PdfOxide
4
- VERSION = '0.3.68'
4
+ VERSION = '0.3.70'
5
5
  end
data/lib/pdf_oxide.rb CHANGED
@@ -56,5 +56,29 @@ module PdfOxide
56
56
  def version
57
57
  VERSION
58
58
  end
59
+
60
+ # Set the process-global content-stream operator cap.
61
+ #
62
+ # A negative `limit` restores the default (1,000,000); any
63
+ # non-negative value (including 0) becomes the explicit cap.
64
+ #
65
+ # @param limit [Integer]
66
+ # @return [Integer] the previous cap (or -1 if the default was active).
67
+ def set_max_ops_per_stream(limit)
68
+ Bindings.pdf_oxide_set_max_ops_per_stream(Integer(limit))
69
+ end
70
+
71
+ # Toggle the process-global U+FFFD (unmapped-glyph) preservation flag
72
+ # used by the high-level text extraction accessors.
73
+ #
74
+ # @param preserve [Boolean, Integer] truthy / non-zero = preserve,
75
+ # falsey / 0 = filter (the v0.3.54 default).
76
+ # @return [Integer] the previous value (`0` or `1`).
77
+ def set_preserve_unmapped_glyphs(preserve)
78
+ # preserve may be Boolean or Integer; avoid numeric predicates that
79
+ # would raise on a Boolean (e.g. true.zero?). falsey / 0 = filter.
80
+ flag = [false, nil, 0].include?(preserve) ? 0 : 1
81
+ Bindings.pdf_oxide_set_preserve_unmapped_glyphs(flag)
82
+ end
59
83
  end
60
84
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdf_oxide
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.68
4
+ version: 0.3.70
5
5
  platform: ruby
6
6
  authors:
7
7
  - PDF Oxide Contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-24 00:00:00.000000000 Z
11
+ date: 2026-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi