Files
suaveplan/packages/devtools/docs/pdf/src/image-registry/image-registry.md
2026-05-27 23:29:53 +01:00

3.2 KiB

image-registry

(no submodules)

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

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

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 ImageElements 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.