djvu-rs 0.30.0 → 0.30.2

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.
package/README.md CHANGED
@@ -20,6 +20,7 @@ specification.
20
20
  | Convert DjVu → PDF, EPUB, TIFF, PNG, CBZ | [`djvu render`](#cli) or [`djvu_to_pdf`](#pdf-export) / [`djvu_to_epub`](#epub-export) / [`djvu_to_tiff`](#tiff-export) |
21
21
  | Extract text (plain, hOCR, ALTO XML) | [`djvu text`](#cli) or [`page.text()`](#text-extraction), [`to_hocr` / `to_alto`](#hocr-and-alto-xml-export) |
22
22
  | Render pages to RGBA pixels | [`render_pixmap`](#quick-start) — sync, [async](#async-render), or [parallel](#feature-flags) |
23
+ | Build a zoomable viewer (tiles) | [`djvu_tile`](#tile-rendering) — cached, prefetchable, cancellable tile rendering |
23
24
  | Show DjVu in the browser | [WebAssembly bindings](#webassembly), incl. lazy HTTP-Range loading |
24
25
  | Read DjVu from Python | `pip install djvu-rs` — [PyO3 bindings](#python) |
25
26
  | Create DjVu from images (PNG/JPEG/TIFF) | [`djvu encode`](#cli) or [`PageEncoder`](#encoding--low-level-api) |
@@ -138,6 +139,8 @@ djvu split book.djvu --pages 10-25 --output chapter.djvu
138
139
  djvu optimize book.djvu --output optimized.djvu --preset lossless-cleanup --dry-run
139
140
  djvu optimize book.djvu --output optimized.djvu --preset lossless-cleanup
140
141
  djvu optimize book.djvu --output optimized.djvu --preset archival --target-size 26214400
142
+ # (--max-ssim-loss is reserved for the planned archival re-encode; the current
143
+ # lossless cleanup is pixel-exact by construction and reports this)
141
144
  djvu optimize book.djvu --output optimized.djvu --max-ssim-loss 0.001
142
145
 
143
146
  # Encode an image (PNG, JPEG, or TIFF) into a single-page DjVu (bilevel JB2, lossless)
@@ -432,6 +435,45 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
432
435
  }
433
436
  ```
434
437
 
438
+ ### Tile rendering
439
+
440
+ For viewer engines: [`djvu_tile`](https://docs.rs/djvu-rs/latest/djvu_rs/djvu_tile/)
441
+ renders a page as a display-space tile grid over the region renderer. Tile
442
+ pixels are byte-identical to the same rectangle of a full-page render, in any
443
+ request order.
444
+
445
+ ```rust,no_run
446
+ use djvu_rs::{DjVuDocument, djvu_render::RenderOptions};
447
+ use djvu_rs::djvu_tile::{TileLayout, render_tile_cached};
448
+
449
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
450
+ let data = std::fs::read("book.djvu")?;
451
+ let doc = DjVuDocument::parse(&data)?;
452
+ let page = doc.page(0)?;
453
+
454
+ let opts = RenderOptions { width: 2400, height: 3200, ..Default::default() };
455
+ let layout = TileLayout::new(page, &opts, 256)?;
456
+
457
+ for row in 0..layout.rows() {
458
+ for col in 0..layout.cols() {
459
+ let tile = render_tile_cached(page, &opts, 256, col, row)?;
460
+ // tile.data — RGBA bytes of exactly this tile rectangle
461
+ let _ = tile;
462
+ }
463
+ }
464
+ Ok(())
465
+ }
466
+ ```
467
+
468
+ `render_tile_cached` memoizes composited tiles per page; the cache is
469
+ tile-granular and controllable (`tile_cache_usage`, `set_tile_cache_budget`,
470
+ `clear_tile_cache`, `invalidate_tile_region`). `render_tile_with` +
471
+ `TileRenderControls` / `TileCancelToken` add progressive quality steps and
472
+ cooperative cancellation, and with the `parallel` feature `prefetch_tiles` /
473
+ `prefetch_tiles_cancellable` warm the cache in the background with a bounded
474
+ worker pool. The full contract lives in
475
+ [`docs/tile-rendering.md`](docs/tile-rendering.md).
476
+
435
477
  ## Encoding & low-level API
436
478
 
437
479
  ### JB2 bilevel image encoder
@@ -551,6 +593,16 @@ whole is not transactional). Opening an indirect index directly with
551
593
  `DjVuDocumentMut::from_bytes` and calling `page_mut` remains unsupported; see
552
594
  [`docs/indirect-djvm-mutation.md`](docs/indirect-djvm-mutation.md).
553
595
 
596
+ The reverse direction is covered too: `djvm::to_indirect` splits a bundled
597
+ `FORM:DJVM` into an indirect index plus standalone component files, keeping
598
+ component ids, names, titles, and the document `NAVM` stable. Related
599
+ bundled-document operations in the same module: `djvm::remove_pages` deletes
600
+ pages with an explicit `UnreachablePolicy` (preserve or garbage-collect shared
601
+ components that lose their last including page),
602
+ `djvm::dedup_shared_components` merges byte-identical shared components, and
603
+ `djvm::DjvmStreamWriter` writes a bundle to any `io::Write` sink with memory
604
+ bounded to the spooled component being appended.
605
+
554
606
  ### Typed document editing
555
607
 
556
608
  `DocumentEditor` provides a versioned, typed operation list with a semantic
@@ -687,7 +739,9 @@ Honest boundaries, so you can decide fast:
687
739
  and single-page `FORM:DJVU` only; indirect returns a clean `Unsupported`
688
740
  error.
689
741
  - **`create_indirect` does not emit shared `DJVI` dictionary components** —
690
- build a bundled document with `djvu merge` when pages share a dictionary.
742
+ build a bundled document with `djvu merge` when pages share a dictionary, or
743
+ convert an existing bundled document with `djvm::to_indirect`, which
744
+ preserves shared components.
691
745
  - **Encoder size parity is corpus- and profile-dependent.** Run the
692
746
  reproducible [`encoder parity scorecard`](docs/encoder-parity.md) to compare
693
747
  the same raster through DjVuLibre 3.5.29's `c44`/`cjb2` and the archival-safe
@@ -767,7 +821,10 @@ combinations and targets), and is enforced in CI. In short:
767
821
  [`tests/panic_free_corpus.rs`](tests/panic_free_corpus.rs), proptests, and
768
822
  libFuzzer/OSS-Fuzz targets.
769
823
  - **Resource limits** — decode/render inherit documented, bounded memory/work
770
- ceilings; exceeding one returns a typed error naming the codec and axis. See
824
+ ceilings; exceeding one returns a typed error naming the codec and axis. The
825
+ ceilings are caller-configurable: pass `ResourceLimits` via `ParseOptions` to
826
+ `DjVuDocument::parse_with_options` (pages inherit them at render time), or
827
+ use `render_pixmap_with_limits` / `render_into_with_limits` directly. See
771
828
  [`SECURITY.md`](SECURITY.md#decode-time-resource-ceilings).
772
829
 
773
830
  ## Performance
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "djvu-rs",
3
3
  "type": "module",
4
4
  "description": "Read, render, convert, and create DjVu files. Pure-Rust DjVu decoder/encoder with CLI, WebAssembly, and Python bindings. DjVu to PDF, EPUB, TIFF, PNG, and text. MIT licensed, no GPL dependencies.",
5
- "version": "0.30.0",
5
+ "version": "0.30.2",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
package/scalar/README.md CHANGED
@@ -20,6 +20,7 @@ specification.
20
20
  | Convert DjVu → PDF, EPUB, TIFF, PNG, CBZ | [`djvu render`](#cli) or [`djvu_to_pdf`](#pdf-export) / [`djvu_to_epub`](#epub-export) / [`djvu_to_tiff`](#tiff-export) |
21
21
  | Extract text (plain, hOCR, ALTO XML) | [`djvu text`](#cli) or [`page.text()`](#text-extraction), [`to_hocr` / `to_alto`](#hocr-and-alto-xml-export) |
22
22
  | Render pages to RGBA pixels | [`render_pixmap`](#quick-start) — sync, [async](#async-render), or [parallel](#feature-flags) |
23
+ | Build a zoomable viewer (tiles) | [`djvu_tile`](#tile-rendering) — cached, prefetchable, cancellable tile rendering |
23
24
  | Show DjVu in the browser | [WebAssembly bindings](#webassembly), incl. lazy HTTP-Range loading |
24
25
  | Read DjVu from Python | `pip install djvu-rs` — [PyO3 bindings](#python) |
25
26
  | Create DjVu from images (PNG/JPEG/TIFF) | [`djvu encode`](#cli) or [`PageEncoder`](#encoding--low-level-api) |
@@ -138,6 +139,8 @@ djvu split book.djvu --pages 10-25 --output chapter.djvu
138
139
  djvu optimize book.djvu --output optimized.djvu --preset lossless-cleanup --dry-run
139
140
  djvu optimize book.djvu --output optimized.djvu --preset lossless-cleanup
140
141
  djvu optimize book.djvu --output optimized.djvu --preset archival --target-size 26214400
142
+ # (--max-ssim-loss is reserved for the planned archival re-encode; the current
143
+ # lossless cleanup is pixel-exact by construction and reports this)
141
144
  djvu optimize book.djvu --output optimized.djvu --max-ssim-loss 0.001
142
145
 
143
146
  # Encode an image (PNG, JPEG, or TIFF) into a single-page DjVu (bilevel JB2, lossless)
@@ -432,6 +435,45 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
432
435
  }
433
436
  ```
434
437
 
438
+ ### Tile rendering
439
+
440
+ For viewer engines: [`djvu_tile`](https://docs.rs/djvu-rs/latest/djvu_rs/djvu_tile/)
441
+ renders a page as a display-space tile grid over the region renderer. Tile
442
+ pixels are byte-identical to the same rectangle of a full-page render, in any
443
+ request order.
444
+
445
+ ```rust,no_run
446
+ use djvu_rs::{DjVuDocument, djvu_render::RenderOptions};
447
+ use djvu_rs::djvu_tile::{TileLayout, render_tile_cached};
448
+
449
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
450
+ let data = std::fs::read("book.djvu")?;
451
+ let doc = DjVuDocument::parse(&data)?;
452
+ let page = doc.page(0)?;
453
+
454
+ let opts = RenderOptions { width: 2400, height: 3200, ..Default::default() };
455
+ let layout = TileLayout::new(page, &opts, 256)?;
456
+
457
+ for row in 0..layout.rows() {
458
+ for col in 0..layout.cols() {
459
+ let tile = render_tile_cached(page, &opts, 256, col, row)?;
460
+ // tile.data — RGBA bytes of exactly this tile rectangle
461
+ let _ = tile;
462
+ }
463
+ }
464
+ Ok(())
465
+ }
466
+ ```
467
+
468
+ `render_tile_cached` memoizes composited tiles per page; the cache is
469
+ tile-granular and controllable (`tile_cache_usage`, `set_tile_cache_budget`,
470
+ `clear_tile_cache`, `invalidate_tile_region`). `render_tile_with` +
471
+ `TileRenderControls` / `TileCancelToken` add progressive quality steps and
472
+ cooperative cancellation, and with the `parallel` feature `prefetch_tiles` /
473
+ `prefetch_tiles_cancellable` warm the cache in the background with a bounded
474
+ worker pool. The full contract lives in
475
+ [`docs/tile-rendering.md`](docs/tile-rendering.md).
476
+
435
477
  ## Encoding & low-level API
436
478
 
437
479
  ### JB2 bilevel image encoder
@@ -551,6 +593,16 @@ whole is not transactional). Opening an indirect index directly with
551
593
  `DjVuDocumentMut::from_bytes` and calling `page_mut` remains unsupported; see
552
594
  [`docs/indirect-djvm-mutation.md`](docs/indirect-djvm-mutation.md).
553
595
 
596
+ The reverse direction is covered too: `djvm::to_indirect` splits a bundled
597
+ `FORM:DJVM` into an indirect index plus standalone component files, keeping
598
+ component ids, names, titles, and the document `NAVM` stable. Related
599
+ bundled-document operations in the same module: `djvm::remove_pages` deletes
600
+ pages with an explicit `UnreachablePolicy` (preserve or garbage-collect shared
601
+ components that lose their last including page),
602
+ `djvm::dedup_shared_components` merges byte-identical shared components, and
603
+ `djvm::DjvmStreamWriter` writes a bundle to any `io::Write` sink with memory
604
+ bounded to the spooled component being appended.
605
+
554
606
  ### Typed document editing
555
607
 
556
608
  `DocumentEditor` provides a versioned, typed operation list with a semantic
@@ -687,7 +739,9 @@ Honest boundaries, so you can decide fast:
687
739
  and single-page `FORM:DJVU` only; indirect returns a clean `Unsupported`
688
740
  error.
689
741
  - **`create_indirect` does not emit shared `DJVI` dictionary components** —
690
- build a bundled document with `djvu merge` when pages share a dictionary.
742
+ build a bundled document with `djvu merge` when pages share a dictionary, or
743
+ convert an existing bundled document with `djvm::to_indirect`, which
744
+ preserves shared components.
691
745
  - **Encoder size parity is corpus- and profile-dependent.** Run the
692
746
  reproducible [`encoder parity scorecard`](docs/encoder-parity.md) to compare
693
747
  the same raster through DjVuLibre 3.5.29's `c44`/`cjb2` and the archival-safe
@@ -767,7 +821,10 @@ combinations and targets), and is enforced in CI. In short:
767
821
  [`tests/panic_free_corpus.rs`](tests/panic_free_corpus.rs), proptests, and
768
822
  libFuzzer/OSS-Fuzz targets.
769
823
  - **Resource limits** — decode/render inherit documented, bounded memory/work
770
- ceilings; exceeding one returns a typed error naming the codec and axis. See
824
+ ceilings; exceeding one returns a typed error naming the codec and axis. The
825
+ ceilings are caller-configurable: pass `ResourceLimits` via `ParseOptions` to
826
+ `DjVuDocument::parse_with_options` (pages inherit them at render time), or
827
+ use `render_pixmap_with_limits` / `render_into_with_limits` directly. See
771
828
  [`SECURITY.md`](SECURITY.md#decode-time-resource-ceilings).
772
829
 
773
830
  ## Performance
Binary file
package/simd128/README.md CHANGED
@@ -20,6 +20,7 @@ specification.
20
20
  | Convert DjVu → PDF, EPUB, TIFF, PNG, CBZ | [`djvu render`](#cli) or [`djvu_to_pdf`](#pdf-export) / [`djvu_to_epub`](#epub-export) / [`djvu_to_tiff`](#tiff-export) |
21
21
  | Extract text (plain, hOCR, ALTO XML) | [`djvu text`](#cli) or [`page.text()`](#text-extraction), [`to_hocr` / `to_alto`](#hocr-and-alto-xml-export) |
22
22
  | Render pages to RGBA pixels | [`render_pixmap`](#quick-start) — sync, [async](#async-render), or [parallel](#feature-flags) |
23
+ | Build a zoomable viewer (tiles) | [`djvu_tile`](#tile-rendering) — cached, prefetchable, cancellable tile rendering |
23
24
  | Show DjVu in the browser | [WebAssembly bindings](#webassembly), incl. lazy HTTP-Range loading |
24
25
  | Read DjVu from Python | `pip install djvu-rs` — [PyO3 bindings](#python) |
25
26
  | Create DjVu from images (PNG/JPEG/TIFF) | [`djvu encode`](#cli) or [`PageEncoder`](#encoding--low-level-api) |
@@ -138,6 +139,8 @@ djvu split book.djvu --pages 10-25 --output chapter.djvu
138
139
  djvu optimize book.djvu --output optimized.djvu --preset lossless-cleanup --dry-run
139
140
  djvu optimize book.djvu --output optimized.djvu --preset lossless-cleanup
140
141
  djvu optimize book.djvu --output optimized.djvu --preset archival --target-size 26214400
142
+ # (--max-ssim-loss is reserved for the planned archival re-encode; the current
143
+ # lossless cleanup is pixel-exact by construction and reports this)
141
144
  djvu optimize book.djvu --output optimized.djvu --max-ssim-loss 0.001
142
145
 
143
146
  # Encode an image (PNG, JPEG, or TIFF) into a single-page DjVu (bilevel JB2, lossless)
@@ -432,6 +435,45 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
432
435
  }
433
436
  ```
434
437
 
438
+ ### Tile rendering
439
+
440
+ For viewer engines: [`djvu_tile`](https://docs.rs/djvu-rs/latest/djvu_rs/djvu_tile/)
441
+ renders a page as a display-space tile grid over the region renderer. Tile
442
+ pixels are byte-identical to the same rectangle of a full-page render, in any
443
+ request order.
444
+
445
+ ```rust,no_run
446
+ use djvu_rs::{DjVuDocument, djvu_render::RenderOptions};
447
+ use djvu_rs::djvu_tile::{TileLayout, render_tile_cached};
448
+
449
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
450
+ let data = std::fs::read("book.djvu")?;
451
+ let doc = DjVuDocument::parse(&data)?;
452
+ let page = doc.page(0)?;
453
+
454
+ let opts = RenderOptions { width: 2400, height: 3200, ..Default::default() };
455
+ let layout = TileLayout::new(page, &opts, 256)?;
456
+
457
+ for row in 0..layout.rows() {
458
+ for col in 0..layout.cols() {
459
+ let tile = render_tile_cached(page, &opts, 256, col, row)?;
460
+ // tile.data — RGBA bytes of exactly this tile rectangle
461
+ let _ = tile;
462
+ }
463
+ }
464
+ Ok(())
465
+ }
466
+ ```
467
+
468
+ `render_tile_cached` memoizes composited tiles per page; the cache is
469
+ tile-granular and controllable (`tile_cache_usage`, `set_tile_cache_budget`,
470
+ `clear_tile_cache`, `invalidate_tile_region`). `render_tile_with` +
471
+ `TileRenderControls` / `TileCancelToken` add progressive quality steps and
472
+ cooperative cancellation, and with the `parallel` feature `prefetch_tiles` /
473
+ `prefetch_tiles_cancellable` warm the cache in the background with a bounded
474
+ worker pool. The full contract lives in
475
+ [`docs/tile-rendering.md`](docs/tile-rendering.md).
476
+
435
477
  ## Encoding & low-level API
436
478
 
437
479
  ### JB2 bilevel image encoder
@@ -551,6 +593,16 @@ whole is not transactional). Opening an indirect index directly with
551
593
  `DjVuDocumentMut::from_bytes` and calling `page_mut` remains unsupported; see
552
594
  [`docs/indirect-djvm-mutation.md`](docs/indirect-djvm-mutation.md).
553
595
 
596
+ The reverse direction is covered too: `djvm::to_indirect` splits a bundled
597
+ `FORM:DJVM` into an indirect index plus standalone component files, keeping
598
+ component ids, names, titles, and the document `NAVM` stable. Related
599
+ bundled-document operations in the same module: `djvm::remove_pages` deletes
600
+ pages with an explicit `UnreachablePolicy` (preserve or garbage-collect shared
601
+ components that lose their last including page),
602
+ `djvm::dedup_shared_components` merges byte-identical shared components, and
603
+ `djvm::DjvmStreamWriter` writes a bundle to any `io::Write` sink with memory
604
+ bounded to the spooled component being appended.
605
+
554
606
  ### Typed document editing
555
607
 
556
608
  `DocumentEditor` provides a versioned, typed operation list with a semantic
@@ -687,7 +739,9 @@ Honest boundaries, so you can decide fast:
687
739
  and single-page `FORM:DJVU` only; indirect returns a clean `Unsupported`
688
740
  error.
689
741
  - **`create_indirect` does not emit shared `DJVI` dictionary components** —
690
- build a bundled document with `djvu merge` when pages share a dictionary.
742
+ build a bundled document with `djvu merge` when pages share a dictionary, or
743
+ convert an existing bundled document with `djvm::to_indirect`, which
744
+ preserves shared components.
691
745
  - **Encoder size parity is corpus- and profile-dependent.** Run the
692
746
  reproducible [`encoder parity scorecard`](docs/encoder-parity.md) to compare
693
747
  the same raster through DjVuLibre 3.5.29's `c44`/`cjb2` and the archival-safe
@@ -767,7 +821,10 @@ combinations and targets), and is enforced in CI. In short:
767
821
  [`tests/panic_free_corpus.rs`](tests/panic_free_corpus.rs), proptests, and
768
822
  libFuzzer/OSS-Fuzz targets.
769
823
  - **Resource limits** — decode/render inherit documented, bounded memory/work
770
- ceilings; exceeding one returns a typed error naming the codec and axis. See
824
+ ceilings; exceeding one returns a typed error naming the codec and axis. The
825
+ ceilings are caller-configurable: pass `ResourceLimits` via `ParseOptions` to
826
+ `DjVuDocument::parse_with_options` (pages inherit them at render time), or
827
+ use `render_pixmap_with_limits` / `render_into_with_limits` directly. See
771
828
  [`SECURITY.md`](SECURITY.md#decode-time-resource-ceilings).
772
829
 
773
830
  ## Performance
Binary file