| name | pdfjs-syntax-document-loading |
| description | Use when loading PDF documents with getDocument(), handling loading progress, or working with PDFDocumentProxy/PDFPageProxy. Prevents common mistakes like forgetting to configure the worker before calling getDocument() or not destroying the document. Covers all source types (URL, ArrayBuffer, TypedArray), password-protected PDFs, metadata extraction, and document lifecycle management. Keywords: getDocument, PDFDocumentLoadingTask, PDFDocumentProxy, PDFPageProxy, password PDF, load PDF file, open PDF, fetch PDF, PDF from URL, load PDF from ArrayBuffer.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires pdfjs-dist 5.x. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
pdfjs-syntax-document-loading
Quick Reference
Core Loading API
| Function / Class | Purpose | Import |
|---|
getDocument(source) | Create a loading task from any source type | import { getDocument } from 'pdfjs-dist' |
PDFDocumentLoadingTask | Returned by getDocument() — tracks loading progress, provides promise | Returned type |
PDFDocumentProxy | Resolved from loading task — represents a loaded PDF document | Resolved type |
PDFPageProxy | Resolved from doc.getPage(n) — represents a single page | Resolved type |
Source Types
| Source | Parameter | When to Use |
|---|
| Remote URL | { url: 'https://...' } | PDF hosted on a server |
| ArrayBuffer | { data: arrayBuffer } | File from <input type="file"> via FileReader |
| Uint8Array | { data: uint8Array } | Raw binary data, Node.js buffers |
| Base64 string | { data: atob(b64) } | PDF received as base64 from an API |
Critical Warnings
NEVER call getDocument() before setting GlobalWorkerOptions.workerSrc — the worker is required for parsing. Without it, PDF.js falls back to a fake worker on the main thread, causing UI freezes on large documents.
ALWAYS call doc.destroy() when finished with a document to prevent memory leaks. Each loaded document holds parsed page data, fonts, and image caches in memory.
NEVER pass a raw string of binary data to getDocument() — ALWAYS convert to Uint8Array first. Strings undergo encoding transformations that corrupt binary PDF data.
ALWAYS handle PasswordException for user-uploaded PDFs — a missing password causes an unrecoverable rejection without a meaningful error message if not caught.
ALWAYS use 1-based page numbers with getPage() — page 1 is getPage(1), NOT getPage(0). Using 0 throws a "Page number out of range" error.
NEVER load all pages synchronously in a tight loop — ALWAYS use async iteration or lazy loading to avoid blocking the main thread and consuming excessive memory.
Decision Tree: Choosing a Source Type
How do you have the PDF data?
│
├─ URL to a remote/local file
│ └─ Use { url: '...' }
│ ├─ Need auth? → Add httpHeaders / withCredentials
│ └─ Large file? → Set rangeChunkSize for streaming
│
├─ File from <input type="file"> or drag-and-drop
│ └─ Use FileReader.readAsArrayBuffer() → { data: arrayBuffer }
│
├─ Raw bytes (fetch response, Node.js Buffer)
│ └─ Use { data: new Uint8Array(buffer) }
│
└─ Base64-encoded string (from API response)
└─ Decode first:
const raw = atob(base64String);
const bytes = new Uint8Array(raw.length);
for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
→ { data: bytes }
Loading Lifecycle
getDocument(source)
│
├─ Returns: PDFDocumentLoadingTask
│ ├─ .promise → Promise<PDFDocumentProxy>
│ ├─ .onProgress → callback({loaded, total})
│ └─ .destroy() → cancel loading
│
▼
PDFDocumentProxy (loaded document)
│
├─ .numPages → total page count
├─ .fingerprints → [string, string | null]
├─ .isPureXfa → boolean (XFA-only form)
│
├─ .getPage(num) → Promise<PDFPageProxy>
├─ .getMetadata() → Promise<{info, metadata, ...}>
├─ .getOutline() → Promise<Array | null>
├─ .getAttachments() → Promise<Object | null>
├─ .getFieldObjects() → Promise<Object | null>
├─ .getPermissions() → Promise<Array | null>
│
├─ .cleanup() → release rendering caches (keep document)
└─ .destroy() → release ALL resources (document gone)
│
▼
PDFPageProxy (single page)
│
├─ .pageNumber → 1-based page number
├─ .rotate → rotation in degrees (0, 90, 180, 270)
├─ .userUnit → user space unit (default 1.0 = 1/72 inch)
├─ .view → [x1, y1, x2, y2] bounding box
│
├─ .getViewport(params) → PageViewport
├─ .render(params) → RenderTask
├─ .getTextContent(params) → Promise<TextContent>
├─ .getAnnotations(params) → Promise<Array>
└─ .cleanup() → release page rendering resources
Essential Patterns
Minimal Document Load
import { getDocument, GlobalWorkerOptions } from 'pdfjs-dist';
GlobalWorkerOptions.workerSrc = new URL(
'pdfjs-dist/build/pdf.worker.min.mjs',
import.meta.url
).toString();
const loadingTask = getDocument('https://example.com/document.pdf');
const doc = await loadingTask.promise;
console.log(`Pages: ${doc.numPages}`);
await doc.destroy();
Loading with Progress Tracking
const loadingTask = getDocument({ url: '/large-document.pdf' });
loadingTask.onProgress = ({ loaded, total }) => {
if (total > 0) {
const percent = Math.round((loaded / total) * 100);
console.log(`Loading: ${percent}%`);
}
};
const doc = await loadingTask.promise;
Loading Password-Protected PDFs
import { getDocument, PasswordResponses } from 'pdfjs-dist';
async function loadProtectedPdf(
source: string | Uint8Array,
passwordProvider: () => Promise<string>
): Promise<PDFDocumentProxy> {
try {
const loadingTask = getDocument({ url: source });
return await loadingTask.promise;
} catch (error) {
if (error.name === 'PasswordException') {
if (error.code === PasswordResponses.NEED_PASSWORD ||
error.code === PasswordResponses.INCORRECT_PASSWORD) {
const password = await passwordProvider();
const retryTask = getDocument({ url: source, password });
return await retryTask.promise;
}
}
throw error;
}
}
Cancelling a Load
const loadingTask = getDocument({ url: '/document.pdf' });
setTimeout(() => {
loadingTask.destroy();
}, 5000);
try {
const doc = await loadingTask.promise;
} catch (error) {
if (error.message === 'Loading aborted') {
console.log('Load was cancelled');
}
}
Memory Management
cleanup() vs destroy()
| Method | Scope | Effect | Use When |
|---|
page.cleanup() | Single page | Releases cached rendering data (images, fonts) for that page | Scrolling away from a page in a viewer |
doc.cleanup() | All pages | Calls cleanup() on every cached page | Reducing memory in long-running viewer |
doc.destroy() | Entire document | Releases ALL resources, terminates worker connection | Done with the document entirely |
Rules
- ALWAYS call
doc.destroy() before loading a new document in a single-document viewer
- ALWAYS call
doc.destroy() in component unmount/cleanup handlers (React useEffect return, Angular ngOnDestroy)
- NEVER use a
PDFDocumentProxy or its pages after calling destroy() — all methods will reject
useEffect(() => {
const loadingTask = getDocument({ url: pdfUrl });
let doc: PDFDocumentProxy | null = null;
loadingTask.promise.then((loadedDoc) => {
doc = loadedDoc;
});
return () => {
loadingTask.destroy();
doc?.destroy();
};
}, [pdfUrl]);
DocumentInitParameters Reference
ALWAYS pass an object to getDocument() for anything beyond a simple URL string. Key parameters:
| Parameter | Type | Default | Purpose |
|---|
url | string | -- | URL to PDF |
data | ArrayBuffer | TypedArray | -- | Raw PDF binary data |
httpHeaders | Record<string, string> | -- | Custom HTTP headers for URL loading |
withCredentials | boolean | false | Include cookies in cross-origin requests |
password | string | -- | Password for encrypted PDFs |
cMapUrl | string | -- | Path to CMap files (required for CJK text) |
cMapPacked | boolean | true | Whether CMap files are binary packed |
standardFontDataUrl | string | -- | Path to standard font files |
rangeChunkSize | number | 65536 | Bytes per range request chunk |
docBaseUrl | string | -- | Base URL for resolving relative links in the PDF |
stopAtErrors | boolean | false | Stop parsing on recoverable errors |
maxImageSize | number | -1 | Max pixels for images (-1 = unlimited) |
For complete parameter list and method signatures, see references/methods.md.
Reference Links
Official Sources