@trupdf/threed
3D PDF support. Detects /Subtype /3D and /Subtype /RichMedia
annotations, classifies the embedded geometry stream (U3D vs PRC via
the /3DD stream header), and renders the static poster appearance as
a positioned layer in the page composition stack.
Scope is poster preview by design. Interactive U3D / PRC rendering
is intentionally not provided — customers needing interactive CAD
viewing use a desktop tool outside the viewer. The package is
installed separately (it is not part of the rolled-up @trupdf/viewer
bundle) and stays independent of the annotation engine, so hosts
without 3D content ship zero extra bytes.
Detection
import { detectThreeDAnnotations } from '@trupdf/threed';
const threeDs = detectThreeDAnnotations(await page.getAnnotations());
// ThreeDAnnotation[] — empty for pages without 3D content| Export | Purpose |
|---|---|
detectThreeDAnnotations(annotations) | Filter + classify a page's raw annotations |
classifyAnnotation(raw) | One raw annotation → classified record (or null) |
classifyKind(raw) | '3d' | 'richmedia' subtype detection |
classifyStreamFormat(bytes) | 'u3d' | 'prc' | 'unknown' from the stream magic |
normalizeRect(rect) | Normalise a /Rect array into PdfRect |
pdfRectToScreen(rect, viewport) | PDF user space → screen-space ScreenRect |
interface ThreeDAnnotation {
id: string;
kind: ThreeDKind; // '3d' | 'richmedia'
format: ThreeDFormat; // 'u3d' | 'prc' | 'unknown'
rect: PdfRect; // /Rect in PDF user space
posterUrl: string | null;
streamBytes: Uint8Array | null; // the embedded geometry stream
// …crop/position metadata
}Poster rendering
import { renderPostersForPage, posterCanvasToDataUrl } from '@trupdf/threed';
const posters = await renderPostersForPage(page, threeDs, {
annotationModeEnableValue: 1, // the render engine's ENABLE annotation mode
scale: 1,
signal,
});
const dataUrl = posterCanvasToDataUrl(posters.get(threeDs[0].id)!);renderPostersForPage(page, annotations, options) renders each 3D
annotation's appearance-stream poster to its own canvas and returns a
PosterCanvasMap. An empty annotation array is a no-op and is not
license-gated.
ThreeDLayer (React)
The positioned page layer the viewer mounts automatically. Use it directly when composing your own page stack:
<ThreeDLayer
pageNumber={pageNumber}
annotations={threeDs}
viewport={viewport} // same object the page layers use — keeps pixel alignment
secureMode={false}
eventBus={engine} // optional; emits threed.* events
/>In secureMode every annotation renders as the fallback with reason
'secure-mode' — no embedded stream is touched.
PosterFallback ({ annotation }) is the labelled placeholder shown
when no poster can be produced.
Events
Emitted on the supplied eventBus:
| Event | Payload |
|---|---|
threed.detected | { pageIndex, kind, format, rendered } |
threed.rendered | { pageIndex, format: 'u3d' } |
threed.fallback | { pageIndex, reason, format } |
ThreeDFallbackReason: 'prc-unsupported' | 'no-renderer-available' | 'parse-failure' | 'no-3dd-stream' | 'secure-mode'.
License gate
Poster rendering and the layer require the threed feature
(gate.require('threed')). Trial tokens cannot render 3D posters.