Initial Commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# image-registry
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The `ImageRegistry` allocates and reuses PDF Image XObjects across the
|
||||
lifetime of a single `renderDocument` call. Each unique `ImageElement`
|
||||
instance maps to exactly one PDF indirect object, regardless of how
|
||||
many pages reference it. Per-page resource maps share the underlying
|
||||
object by id so a multi-page document with a repeating logo never
|
||||
allocates a copy per page. The module also owns the lower-level
|
||||
`registerImageXObject` helper that builds the canonical `/XObject
|
||||
/Image` dictionary, choosing `/DCTDecode` for JPEGs and `/FlateDecode`
|
||||
for decoded PNG RGB planes.
|
||||
|
||||
## Features
|
||||
|
||||
- One PDF object per `ImageElement` (identity-keyed, not src-hash).
|
||||
- Per-page `pageXObjects` map populated as a side-effect of `ensure`.
|
||||
- JPEG passthrough via `/DCTDecode` (no recompression).
|
||||
- PNG RGB plane re-emission via `/FlateDecode`.
|
||||
- `DeviceRGB` / `DeviceGray` colour-space selection follows the
|
||||
decoder output.
|
||||
- `BitsPerComponent` carried straight from the decoded image.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { ImageRegistry } from "@suaveplan/pdf/image-registry";
|
||||
import { ImageElement, PdfWriter } from "@suaveplan/pdf";
|
||||
|
||||
const writer = new PdfWriter();
|
||||
const registry = new ImageRegistry(writer);
|
||||
const pageMap = new Map<string, number>();
|
||||
const image = new ImageElement({ src: pngBytes, width: 100, height: 50 });
|
||||
const resourceName = registry.ensure(image, pageMap);
|
||||
// resourceName === "Im<id>"; pageMap now contains the entry.
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { ImageRegistry, registerImageXObject } from "@suaveplan/pdf/image-registry";
|
||||
import { ImageElement, PdfWriter } from "@suaveplan/pdf";
|
||||
|
||||
const writer = new PdfWriter();
|
||||
const registry = new ImageRegistry(writer);
|
||||
const logo = new ImageElement({ src: pngBytes, width: 80, height: 24 });
|
||||
// Share `logo` across three pages — only one XObject is allocated.
|
||||
for (const pageMap of pageMaps) {
|
||||
registry.ensure(logo, pageMap);
|
||||
}
|
||||
// Build a JPEG XObject directly (e.g. from a pre-decoded blob).
|
||||
const jpegId = registerImageXObject(writer, decodedJpeg);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `class ImageRegistry` — constructed with a `PdfWriter`. Exposes
|
||||
`ensure(element, pageXObjects)` which returns the `Im<id>` resource
|
||||
name and side-effects the supplied per-page map.
|
||||
- `registerImageXObject(writer, decoded)` → `number` — low-level
|
||||
helper used by `ImageRegistry.ensure`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The registry keys by `ImageElement` instance identity rather than
|
||||
content hash. This is intentional: callers that share the same
|
||||
element across pages benefit from the cache automatically, while
|
||||
callers that construct two `ImageElement`s with identical bytes pay
|
||||
for two PDF objects — the safe assumption when the bytes themselves
|
||||
might differ after `decodeImage` runs (e.g. PNG alpha extraction).
|
||||
`registerImageXObject` packs the dictionary entries in a fixed order
|
||||
(`/Type`, `/Subtype`, `/Width`, `/Height`, `/ColorSpace`,
|
||||
`/BitsPerComponent`, optional `/Filter`) so equal inputs produce
|
||||
byte-identical objects, preserving the writer's xref-byte-offset
|
||||
invariant. JPEG bytes are passed through unchanged via `"None"`
|
||||
because the JPEG bitstream itself is what `/DCTDecode` consumes.
|
||||
Reference in New Issue
Block a user