Typed PDF objects, ISO 32000-2 §7.3 — token stream →
{ type, value/items/entries }tree.
Module pdfParser | Source packages/front/office/pdf/src/syntax/parser.js | Deps pdfErrors, pdfParserObj, pdfTokenizer | Worker-safe yes
Produces the typed objects null, bool, int, real, name, string,
array, dict, ref, stream. The <int> <int> R form is recognised
speculatively as a ref. Indirect definitions <num> <gen> obj … endobj are
read by parseIndirect, which promotes dict + stream into
{ type: 'stream', dict, raw }. The parser validates neither xref integrity nor
/Length — that is the document layer's job.
The module re-exposes the injected pdfTokenizer's tokenize and the whole
pdfParserObj surface (obj, getEntry, isType) so a consumer needs a
single resolve to go from bytes to a typed tree.
Resolve
const parser = runtime.resolve('pdfParser');
// Returns: { tokenize, parseObject, parseIndirect, parseFromBytes,
// parseIndirectFromBytes, parserLimits, setParserLimits,
// obj, getEntry, isType }
API
| Member | Signature | Returns |
|---|---|---|
tokenize |
(bytes: Uint8Array, opts?) => Tokenizer |
Re-export of pdfTokenizer.tokenize. |
parseObject |
(tok: Tokenizer) => PdfObject |
One typed object. |
parseIndirect |
(tok: Tokenizer, resolveRef?) => { num, gen, value } |
Full indirect definition. |
parseFromBytes |
(bytes: Uint8Array) => PdfObject |
Helper — tokenize then parseObject. |
parseIndirectFromBytes |
(bytes: Uint8Array, at: number, resolveRef?) => { num, gen, value } |
Offset helper. |
parserLimits |
{ maxDepth, maxArrayLen, maxStreamBytes } |
Live, mutable per-instance guard-rails. |
setParserLimits |
(partial) => parserLimits |
Merges positive integer overrides and returns the updated object. |
obj |
builders | Primitive constructors (see below). |
getEntry |
(dict, key: string) => PdfObject | undefined |
Lookup on {type:'dict'}. |
isType |
(v, kind: string) => boolean |
Test v.type === kind. |
parserLimits defaults
| Key | Default | Guards |
|---|---|---|
maxDepth |
200 |
Nesting depth — pdf/parser/depth-exceeded. |
maxArrayLen |
1_000_000 |
Array element count — pdf/parser/array-too-long. |
maxStreamBytes |
268_435_456 (256 MiB) |
Stream payload quota during parseIndirect. |
Only positive integers are accepted; anything else is ignored, so
setParserLimits({}) is a no-op that simply returns the current limits.
obj builders
| Helper | Signature |
|---|---|
obj.nul() |
→ { type: 'null' } |
obj.bool(v) |
→ { type: 'bool', value } |
obj.int(v) |
→ { type: 'int', value } |
obj.real(v) |
→ { type: 'real', value } |
obj.name(s) |
→ { type: 'name', value } |
obj.string(bytes, syntax?) |
syntax ∈ 'lit'|'hex' |
obj.array(items?) |
|
obj.dict(entries?) |
|
obj.ref(num, gen?) |
|
obj.stream(dict, raw) |
Token → type mapping
| Token | Output type |
|---|---|
kw 'null'/'true'/'false' |
null / bool |
name |
name |
string |
string (syntax:'lit') |
hex |
string (syntax:'hex') |
int + int + kw 'R' |
ref |
int |
int |
real |
real |
open_arr … close_arr |
array |
open_dict … close_dict |
dict |
dict + kw 'stream' (via parseIndirect) |
stream |
Examples
Parse a standalone object
const parser = runtime.resolve('pdfParser');
const o = parser.parseFromBytes(new TextEncoder().encode('<< /Size 6 /Root 1 0 R >>'));
o.type; // 'dict'
parser.getEntry(o, 'Size').value; // 6
parser.getEntry(o, 'Root'); // { type: 'ref', num: 1, gen: 0 }
Read an indirect definition carrying a stream
const tok = parser.tokenize(bytes, { start: offset });
const def = parser.parseIndirect(tok, ref => doc._raw.resolve(ref));
def.value.type; // 'stream'
def.value.raw; // Uint8Array
Tighten the limits for untrusted input
parser.setParserLimits({ maxDepth: 32, maxStreamBytes: 8 * 1024 * 1024 });
parser.parserLimits.maxDepth; // 32
Build with obj
const o = parser.obj.dict({
Type: parser.obj.name('Catalog'),
Pages: parser.obj.ref(2, 0)
});
Errors
| Code | Class | When |
|---|---|---|
pdf/parser/eof |
ParseError |
End of stream during parseObject. |
pdf/parser/unexpected-keyword |
ParseError |
Unrecognised keyword in object position. |
pdf/parser/unexpected-token |
ParseError |
Foreign token (e.g. a lone n). |
pdf/parser/unbalanced |
ParseError |
] or >> without an opener. |
pdf/parser/unterminated-array |
ParseError |
[ never closed. |
pdf/parser/unterminated-dict |
ParseError |
<< never closed. |
pdf/parser/dict-key-not-name |
ParseError |
Dictionary key is not a name. |
pdf/parser/depth-exceeded |
ParseError |
Nesting deeper than parserLimits.maxDepth. |
pdf/parser/array-too-long |
ParseError |
Array longer than parserLimits.maxArrayLen. |
pdf/parser/indirect-bad-num / -bad-gen / -missing-obj / -missing-endobj |
ParseError |
Malformed <n> <g> obj … endobj. |
pdf/parser/stream/no-endstream |
ParseError |
endstream not found. |
pdf/parser/stream/expected-endstream |
ParseError |
Something else found instead. |
See also
pdfTokenizerpdfParserObjpdfXref— usesparseObjectfor the trailer.pdfErrors