Skip to content
Genom
API reference

@genomdev/pdf

PDF — parser, model, content adapter and viewer

120 exported symbols across 2 entry points

@genomdev/pdf

Classes

CMap
class CMap
codespaces
CodespaceRange[]
Ranges by byte length, in the order they must be tried.
single
Map<number, number>
Single-code mappings: code → CID, or code → text for a ToUnicode map.
text
Map<number, string>
ranges
{ low: number; high: number; cid: number; }[]
Range mappings, kept as ranges: a CJK font maps 20 000 codes in six lines.
vertical
boolean
True for a vertical writing mode CMap (`…-V`).
useCMap
string | undefined
The name of the CMap this one is `usecmap`-ed from, if any.
unicodeIsCode
boolean
True when a code is already the character it stands for. The `Uni…-UCS2` family of predefined CMaps is exactly this, and saying so is what lets a CJK document with no `/ToUnicode` be read.
legacyEncoding
string | undefined
The legacy encoding a predefined CMap's codes are written in.
builtInText
(code: number) => string | undefined
What a code says, for the CMaps that know without a table.
isEmpty
boolean
Whether anything at all was read: an empty CMap must not shadow a default.
cidOf
(code: number) => number
textOf
(code: number) => string | undefined
codes
(bytes: Uint8Array) => Generator<{ code: number; length: number; }>
Splits a string into codes. The codespace ranges decide how many bytes each code takes, and they are tried shortest first — that is what the specification says, and it is also the only reading under which a mixed one- and two-byte CMap (every `…-H` for a Latin-capable collection is one) does not eat the byte after a one-byte code. A byte that matches no range is taken as one byte. Guessing wrong there costs one character; refusing costs the rest of the string.
DecryptionFailed
class DecryptionFailed extends Error
Dict
class Dict

A dictionary. Keys are stored without the slash. Values are stored *raw* — a `Ref` stays a `Ref` — and resolving one needs the cross-reference table, which is why lookup that follows references lives on `XRef` and not here. That separation is what lets a dictionary be parsed out of a content stream, where there is no document to resolve against at all.

map
Map<string, PdfObject>
size
number
getRaw
(key: string) => PdfObject | undefined
The value under a key, without following an indirect reference.
getRawAny
(...keys: string[]) => PdfObject | undefined
The first of several keys that is present. PDF abbreviates: an inline image writes `/W` where an image XObject writes `/Width`, and `/BPC` for `/BitsPerComponent`. Asking for both at once is how the two spellings stay one code path.
set
(key: string, value: PdfObject) => void
has
(key: string) => boolean
keys
() => IterableIterator<string>
entries
() => IterableIterator<[string, PdfObject]>
Lexer
class Lexer
pos
number
skipWhitespace
() => void
Skips whitespace and comments; leaves `pos` on the next real byte.
next
() => Token
Name
class Name

A PDF name object: `/Type`, `/Font`, `/Contents`.

of
(name: string) => Name
toString
() => string
ParseError
class ParseError extends Error

Raised when the byte offset given for an object holds something else.

Parser
class Parser
position
number
peek
() => Token
The token about to be read, without consuming it.
skipToken
() => Token
Consumes the next token without interpreting it.
restartAt
(position: number) => void
Moves to a byte offset and re-primes the lookahead. The lookahead is what makes this necessary: after an inline image's `ID` the parser has already read two tokens out of the *image data*, and there is no way back to the syntax except by saying where it resumes.
parseObject
(ref?: Ref) => PdfObject
One object. `ref` is the indirect object being read, if any: a string or stream inside an encrypted document is decrypted with a key derived from the object number that contains it, so the identity has to travel down the parse.
parseIndirectObject
(expected?: Ref) => { ref: Ref; value: PdfObject; }
Reads `N G obj … endobj` at the current position. The object number is checked against what was expected: a cross-reference table that points at the wrong object is the single most common form of damage, and a reader that does not check silently returns the neighbouring object — a page's contents become another page's font, with no error anywhere.
PdfDocument
class PdfDocument implements GenomDocument
format
"pdf"
kind
"paged-document"
metadata
DocumentMetadata
xref
XRef
pageCount
number
Number of pages/sheets/slides, when the format exposes it cheaply. For docx this is `undefined` until the document has been laid out: splitting a text flow into pages depends on fonts, hyphenation and the printable area.
pages
readonly PageInfo[]
page
(index: number) => PageInfo | undefined
version
string | undefined
The PDF version the header claims, and the catalogue's if it overrides it.
encryption
import("/repo/packages/pdf/src/index").EncryptionInfo | undefined
Whether the file was encrypted, and how. Undefined when it was not.
wasRepaired
boolean
True when the cross-reference table was unusable and had to be rebuilt.
pageSize
(index: number) => { width: number; height: number; }
The size of a page as displayed, in points.
extractText
() => Promise<string>
Extracts the whole document text for search, indexing and previews. A dedicated method because this is the one operation every format needs in the same way and which requires no rendering.
dispose
() => void
Releases retained resources. Calling it twice is safe.
disposed
boolean
PdfFont
class PdfFont
dict
Dict
subtype
string
baseFont
string
standardName
string | undefined
The standard-14 name this font substitutes for, if any.
composite
boolean
cidSubtype
string | undefined
A composite font's descendant subtype: `CIDFontType0` or `CIDFontType2`.
vertical
boolean
flags
number
encoding
(string | undefined)[] | undefined
Code → glyph name, for simple fonts.
cmap
CMap | undefined
toUnicode
CMap | undefined
firstChar
number
widths
number[] | undefined
cidWidths
Map<number, number> | undefined
defaultWidth
number
missingWidth
number
programStream
PdfObject | undefined
The embedded font program, if there is one, and which kind.
programKind
"type1" | "truetype" | "cff" | "opentype" | undefined
programBytes
(() => Uint8Array | undefined) | undefined
The program's bytes, decoded on first use and not before.
charProcs
Dict | undefined
Type 3 fonts draw their glyphs with content streams of their own.
fontMatrix
number[] | undefined
type3Resources
Dict | undefined
isType3
boolean
symbolic
boolean
embedded
boolean
decode
(bytes: Uint8Array) => Glyph[]
Splits a PDF string into glyphs, with widths and text.
program
FontProgram | undefined
The embedded program, parsed on first use. Deliberately lazy and cached on the font: a page that uses ten fonts and shows a hundred words should not parse ten font programs, and one that shows ten thousand should parse each once.
widthOf
(code: number, cid: number) => number
The advance, in text space units. The order matters and is the specification's: the font's own `/Widths` first — a subset font may state a width that disagrees with the metrics of the face it came from, and the file is right — then the standard metrics for an unembedded standard face, then `/MissingWidth`, then nothing. "Nothing" is the case worth naming: a width of zero stacks every glyph of the run at one point, so a font with no widths at all falls back to a plausible average rather than to zero. A wrong width moves a word; a zero width destroys the line.
unicodeOf
(code: number, name?: string | undefined, cid?: number) => string
What a code says. The answer is passed through `unligature` before it is stored, so that a word set with an f-ligature is the word and not a lookalike. `/ToUnicode` outranks everything — it is the producer telling us directly. Then the glyph name through Adobe's list, which covers every Type 1 font ever made. Then, for a font with neither, the code as Latin-1, which is right often enough to be worth doing and wrong in a way that is visible.
glyphOfCid
(cid: number) => number | undefined
The glyph a CID selects. `/CIDToGIDMap` is either the name `Identity` — which is what an Identity encoding pairs with and is the overwhelming majority — or a stream of two-byte glyph indices. A font that has the stream and is read as though it were the identity produces the wrong glyph for every character past the first few, which looks like a font substitution rather than a bug.
cidToGid
Uint8Array<ArrayBufferLike> | undefined
The `/CIDToGIDMap` stream's bytes, when it is a stream.
PdfStream
class PdfStream

A stream: a dictionary and a run of bytes whose meaning the dictionary gives. The bytes are kept encoded. Decoding needs the filter chain, the document's decryption key and, for an image, a decision about whether to decode at all — a JPEG is better handed to the platform than unpacked here — so it is a method on the document rather than a property of the object.

PdfString
class PdfString

A PDF string: bytes, with the two text decodings available on request. `(Hello)` and `<48656C6C6F>` are the same object; the difference between literal and hexadecimal syntax is spelling. What the bytes *mean* depends entirely on where the string was found.

text
string
The string as text, for the positions where a string is text. Two encodings, and the file says which by writing a byte-order mark or not. `FEFF` means UTF-16BE — which is how every non-Latin title, author and bookmark in existence is written — and anything else is PDFDocEncoding, which agrees with Latin-1 over the range that matters here.
toString
() => string
Ref
class Ref

An indirect reference: `12 0 R`.

key
string
The key this reference has in the object cache and the xref table.
toString
() => string
StandardSecurityHandler
class StandardSecurityHandler
info
EncryptionInfo
open
(xref: XRef, encrypt: Dict, documentId: Uint8Array, password?: string) => StandardSecurityHandler
Reads `/Encrypt` and derives the file key, or reports that the password is wrong. The empty password is tried first and is almost always the answer; a supplied one is tried as both user and owner password, because a caller who has one rarely knows which it is.
decrypt
(bytes: Uint8Array, ref: Ref, isString: boolean) => Uint8Array
Deciphers one string or stream found inside object `ref`.
XRef
class XRef
bytes
Uint8Array<ArrayBufferLike>
trailer
Dict
wasReconstructed
boolean
True when the table had to be rebuilt by scanning. Reported by the tools.
objectCount
number
setDecryptor
(decryptor: Decryptor | undefined) => void
resolve
(value: PdfObject | undefined) => PdfObject
Follows indirect references until the value is a real object.
fetch
(ref: Ref) => PdfObject
The object a reference names, parsed and cached.
decode
(stream: PdfStream) => Uint8Array
The decoded bytes of a stream, with the filter chain applied.
dictGet
(dict: Dict | undefined, ...keys: string[]) => PdfObject
numberOf
(dict: Dict | undefined, ...keys: string[]) => number | undefined
nameOf
(dict: Dict | undefined, ...keys: string[]) => string | undefined
dictOf
(dict: Dict | undefined, ...keys: string[]) => Dict | undefined
arrayOf
(dict: Dict | undefined, ...keys: string[]) => PdfObject[] | undefined
numbersOf
(dict: Dict | undefined, ...keys: string[]) => number[] | undefined
An array of numbers, resolved element by element: `/MediaBox [0 0 612 792]`.
everyDictOfType
(type: string) => Generator<Dict>
Every object in the file whose `/Type` is the one asked for. The expensive way to find something, and the only way left when the structure that should have led to it is broken: a catalogue pointing at a free object, a page tree with an empty `/Kids`. Object numbers are walked in order, which is the order they were written, which for pages is the order they appear in.
parse
() => void
Reads the table, starting from `startxref` and following `/Prev`. Earlier tables are read *after* later ones and never overwrite an entry, so the newest definition of an object wins — which is the whole point of the chain. A loop in `/Prev` (files have them) is stopped by the set of offsets already visited rather than by a depth limit, because the legitimate chains are arbitrarily long.
reconstruct
() => void
Rebuilds the table by reading the file from the front. Every `N G obj` in the bytes is an object, and the *last* definition of a number wins — appended revisions come later in the file, and a document edited three times has three copies of the page that changed. That rule is what makes a scan a usable substitute for the table rather than a lottery. The trailer is looked for the same way, and if there is none (files exist whose trailer was the casualty) any object with `/Type /Catalog` will do.

Functions

apply
function apply(m: Matrix, x: number, y: number): [number, number]
boxOf
function boxOf(xref: XRef, dict: Dict | undefined, key: string): Box | undefined
ccittDecode
function ccittDecode(data: Uint8Array, params: CcittParams): Uint8Array

Decodes to one bit per pixel, rows padded to a byte. The output is what a PDF image of `/BitsPerComponent 1` expects, in the sense the image's own `/Decode` array will be applied to: a 0 bit is black unless `/BlackIs1` said otherwise.

collectPages
function collectPages(xref: XRef, limit?: number): PageInfo[]
decodeImage
function decodeImage(xref: XRef, stream: PdfStream, maskColor?: Rgb | undefined, resources?: Dict | undefined): DecodedImage | undefined
decodeJpeg
function decodeJpeg(data: Uint8Array): DecodedJpeg | undefined
decodeJpx
function decodeJpx(bytes: Uint8Array, options?: JpxOptions): DecodedJpx | undefined
decodeStream
function decodeStream(stream: PdfStream, resolver: Resolver): DecodeResult

A stream's bytes with every non-image filter undone. Failure of one filter is not failure of the document: a stream that will not decompress yields what came out of it, and a page missing one image is a better answer than a document that will not open. The exception is the empty result, which is returned as such and lets the caller tell "nothing here" from "something unreadable".

deviceMatrix
function deviceMatrix(page: PageInfo, scale?: number): Matrix

The matrix from PDF user space to the page as displayed. Flips the y axis, moves the origin to the crop box's corner, and applies the page's `/Rotate`. Derived rather than tabulated so that the four rotations cannot drift apart: the centre of the box is a fixed point of the rotation, which is what the offsets below say.

dictOf
function dictOf(value: PdfObject | undefined): Dict | undefined

The dictionary of a stream, or the dictionary itself.

displaySize
function displaySize(page: PageInfo): { width: number; height: number; }

The size of a page as it is displayed: cropped, rotated, in points.

documentIdOf
function documentIdOf(xref: XRef): Uint8Array

The first element of `/ID`, which the key derivation mixes in.

embeddableFont
function embeddableFont(font: PdfFont, codes: Iterable<number>): EmbeddedFont | undefined

Rebuilds a font for the browser, or reports that it cannot be. Returns `undefined` for a font with no usable program — a standard-14 face the file did not embed, a Type 3 font (whose glyphs are content streams and are drawn rather than typeset), a program in a format nothing here reads. The caller then falls back to a substitute, which is what the viewer does anyway when this is not called at all.

evaluatePage
function evaluatePage(xref: XRef, page: PageInfo, options?: EvaluateOptions): PageDisplay

Runs a page's content streams and returns everything they draw.

extractPdf
function extractPdf(input: ByteSourceInput | PdfDocument, options?: ExtractOptions): Promise<ContentDocument>

Содержимое PDF — структура, восстановленная из геометрии страницы. Для того, кто знает, какой у него файл. Принимает и уже открытый документ: вьюверу незачем разбирать файл второй раз, чтобы поискать в нём.

filterChain
function filterChain(dict: Dict, resolver: Resolver): { name: string; parms?: Dict; }[]

Every filter named on a stream, in application order, with its parameters.

findTables
function findTables(lines: readonly TextLine[], marks?: readonly Mark[]): TextTable[]
glyphNameToUnicode
function glyphNameToUnicode(name: string): string | undefined

What a glyph name says, according to Adobe's list.

identityCMap
function identityCMap(vertical?: boolean): CMap

The identity CMap, which is what `/Identity-H` and `/Identity-V` name. Two bytes per code, code equals CID. Also the honest fallback for a predefined CJK CMap we do not carry: the *splitting* is right, so the glyphs and their advances land in the right places even where the characters cannot be named.

imageToPng
function imageToPng(image: DecodedImage): Uint8Array | undefined

The same image as a PNG, for consumers that want one file rather than pixels.

invert
function invert(m: Matrix): Matrix | undefined
isArray
function isArray(value: unknown): value is PdfObject[]
isDict
function isDict(value: unknown): value is Dict
isName
function isName(value: unknown, name?: string): value is Name
isNumber
function isNumber(value: unknown): value is number
isRef
function isRef(value: unknown): value is Ref
isStream
function isStream(value: unknown): value is PdfStream
isString
function isString(value: unknown): value is PdfString
jbig2Decode
function jbig2Decode(data: Uint8Array, globals: Uint8Array | undefined, width: number, height: number, options?: Jbig2Options): Uint8Array | undefined
jpegToRgba
function jpegToRgba(image: DecodedJpeg, options?: { invert?: boolean; }): Uint8Array

A decoded JPEG as RGBA. The colour transform is decided by the component count and the Adobe marker: three components are YCbCr unless the marker says otherwise, four are YCCK if it says 2 and CMYK if it says anything else. What the marker does **not** decide is whether the samples are inverted, and that is the trap. Photoshop writes CMYK JPEGs with every value flipped, and the PDF that embeds one says so with `/Decode [1 0 1 0 1 0 1 0]` — so the file's own statement is the one to follow. A reader that inverts whenever it sees an Adobe marker gets those files right and turns every *other* CMYK JPEG into a photographic negative, which is a striking way to be wrong.

linesToText
function linesToText(lines: readonly TextLine[], options?: PageTextOptions): string
loadColorSpace
function loadColorSpace(xref: XRef, value: PdfObject, resources?: Dict | undefined, depth?: number): ColorSpace

Resolves a colour space object: a name, or an array with its parameters.

loadFont
function loadFont(xref: XRef, dict: Dict): PdfFont

Builds a font from its dictionary.

loadFunction
function loadFunction(xref: XRef, value: PdfObject): PdfFunction | undefined

Builds a callable from a function object, or an array of them.

multiply
function multiply(a: Matrix, b: Matrix): Matrix

`a` then `b`: the matrix that applies `a` first.

nameToUnicode
function nameToUnicode(name: string, baseFont?: string): string | undefined

A glyph name to text, including the four conventions that are not the list. `uni0041`, `u1F600`, `g23`/`cid42` (no text at all — an index into the font), and the `name.alt` suffix a subsetter adds. Between them these cover most of what the glyph list misses in real files.

openPdf
function openPdf(input: ByteSourceInput, options?: OpenPdfOptions): Promise<PdfDocument>
pageContent
function pageContent(xref: XRef, page: PageInfo): Uint8Array | undefined

`/Contents` is one stream or an array of them, joined by a newline.

pageText
function pageText(document: PdfDocument, page: PageInfo, options?: PageTextOptions): string
parseCff
function parseCff(bytes: Uint8Array): CffFont | undefined
parseCMap
function parseCMap(bytes: Uint8Array, asEncoding?: boolean): CMap

Parses a CMap program — a ToUnicode stream, or an embedded encoding CMap. `asEncoding` is for the second of those. An encoding CMap is supposed to map its codes with `cidchar`/`cidrange`, but a working minority of producers write `bfchar`/`bfrange` instead — the syntax is the same and the destination, a two-byte string, is the CID written as hex. Read only as text, such a font maps every code to CID 0 and the page comes out blank.

parseTrueTypefrom @genomdev/core
function parseTrueType(bytes: Uint8Array, index?: number): TrueTypeTables | undefined

Reads a font program's table directory, and the tables worth reading. Takes the bytes of a `.ttf`, `.otf` or `.ttc`, and the first font of a collection where it is one. Nothing is returned for a file whose sfnt version is not one of the four in use — which is the check that keeps a `.pfb`, a bitmap font or a truncated download from being read as if the numbers at its head meant offsets. Every bounds check here is load-bearing rather than defensive: a font embedded in a document is arbitrary bytes from a stranger, and half of them have been through a subsetter.

parseType1
function parseType1(bytes: Uint8Array): Type1Font | undefined
readingFlow
function readingFlow(lines: TextLine[], pageWidth: number, marks?: readonly Mark[]): (TextLine | TextTable)[]

The same order, with the tables left standing. A table is the one thing on a page that the recursive cut below gets exactly wrong: its columns are, to the cut, columns, so a table is read downwards — every number of the first column, then every number of the second — which loses the only thing a table means. So the tables are found first (`tables.ts`) and each is carried through the ordering as a single unit, neither split nor reordered, and comes out with its rows intact.

readingOrder
function readingOrder(lines: TextLine[], pageWidth: number, marks?: readonly Mark[]): TextLine[]

Puts the lines of a page into reading order. A recursive cut: find the widest vertical gap that no line crosses, split there, and order the halves left to right; if there is no such gap, split on the widest horizontal one and order top to bottom. This is the classical XY-cut, and it is here because the alternative — sorting by y — reads a two-column paper as alternating sentences from both columns, which is the single most visible failure of naive PDF extraction.

rebuildSfnt
function rebuildSfnt(tables: Map<string, Uint8Array>, entries: CmapEntry[], options: { numGlyphs: number; unitsPerEm: number; postScriptName: string; }): BuiltFont | undefined

Rebuilds a TrueType or OpenType program with a new character map. The original tables are kept byte for byte — the outlines, the hinting, the metrics — so nothing about how the glyphs look can change here.

rotationOf
function rotationOf(m: Matrix): number

The rotation the matrix applies, in degrees, for text that is not upright.

runCharstring
function runCharstring(charstring: Uint8Array, context: CharstringContext): GlyphPath | undefined

Runs one charstring and returns the outline it draws.

runType2
function runType2(charstring: Uint8Array, context: Type2Context): GlyphPath
scaleOf
function scaleOf(m: Matrix): { x: number; y: number; }

How much the matrix scales lengths, along each axis. Used for the font size a glyph is really drawn at and for deciding whether two glyphs are on one line. Taken as the length of the transformed unit vectors, which is right for rotation and shear as well as for plain scaling — `m[0]` alone is the answer only for an upright matrix, and text set on its side has `m[0]` of zero.

stripSubsetTag
function stripSubsetTag(name: string): string

`ABCDEF+Helvetica` is Helvetica with six letters saying it is a subset.

textLines
function textLines(marks: readonly Mark[], options?: LineOptions): TextLine[]

Splits a page's text marks into lines. Two glyphs are on the same line when their baselines agree to within a third of the type size *and* they overlap or nearly touch along the writing direction. The second half matters: a two-column page has two lines at every baseline, and a rule that looks only at y joins them into one sentence that reads across the gutter.

walkPdf
function walkPdf(document: PdfDocument, hash: string, options: ResolvedOptions): Promise<{ blocks: Block[]; annotations: Annotation[]; }>

Reading a PDF as content. Every other walker in this package reads a structure the producer wrote down: a paragraph is a `<w:p>`, a cell is a `<c>`, a slide is a slide. A PDF has no such thing. It has glyphs at coordinates, and a paragraph is something this file *decides* — from where the lines are, how far apart, how they line up and what size they are set in. Two rules keep that honest. **The address is the file's order, not the reading order.** A block's locator names the index of the text run it starts at, counted in the order the page's content stream drew them, because that is a fact about the file that no change to these heuristics can move. The reading order decides what comes out first; the addresses stay where they are. It is the same separation the presentation walker makes, and for the same reason — a tuned heuristic must not invalidate every address anybody has stored. **Nothing is inferred that the geometry does not support.** A heading is a line set larger than the page's body size, with space above it, on its own — not a line that "looks like a title". A list item begins with a bullet or a number *and* is indented past the lines around it. Where the evidence is thin the block stays a paragraph, because a paragraph is never wrong, and a wrong heading reorganises somebody's whole retrieval index.

wrapCff
function wrapCff(cff: Uint8Array, entries: CmapEntry[], options: { numGlyphs: number; unitsPerEm: number; postScriptName: string; advances: (glyph: number) => number; ascent: number; descent: number; }): BuiltFont | undefined

Builds a whole OpenType file around a bare CFF program. `/FontFile3` is a CFF with no sfnt wrapper at all — the format PDF prefers for a Type 1 font and what Distiller writes for almost everything. A browser cannot load one; wrapped in the eight tables below, the same bytes are an ordinary OTF. The metrics come from the PDF rather than from the font, which is not a compromise but the correct source: the widths the document lays out with are the widths in its font dictionary, and a disagreement between those and the font's own is resolved in the document's favour by every reader.

writeCff
function writeCff(glyphs: readonly CffGlyph[], options: { fontName: string; fontMatrix?: readonly number[] | undefined; }): { bytes: Uint8Array; glyphNames: string[]; } | undefined

Builds a CFF from glyphs already interpreted into paths. Glyph 0 must be `.notdef` and is added if the caller did not supply one; the order of the rest is the order given, and that order is what a caller's character map will index by.

Interfaces

Box
interface Box

A rectangle as PDF states it, normalised so that x0 < x1 and y0 < y1.

x0
number
y0
number
x1
number
y1
number
BuiltFont
interface BuiltFont
bytes
Uint8Array<ArrayBufferLike>
format
"truetype" | "opentype"
CcittParams
interface CcittParams

CCITT Group 3 and Group 4, the fax codecs. This is what a black-and-white scanned page is made of, and it is the reason a scanned document without it draws as nothing at all rather than as something imperfect: there is no partial reading of a fax stream. The idea is older than the file format and simpler than it looks. A row of a bilevel image is a sequence of runs of white and black; Group 3 writes those run lengths with a Huffman code (one table for white, one for black, because the lengths are distributed quite differently); Group 4 writes each row as the *difference* from the row above it, which for a page of text is almost nothing at all. Everything below is those two things plus the housekeeping. `K` in the parameters says which: negative is Group 4, zero is Group 3 one dimension, positive is Group 3 with a mode bit at the start of every row.

columns
number
Pixels per row. 1728 is the fax default and is wrong for most PDFs.
rows
number
k
number
< 0 two-dimensional, 0 one-dimensional, > 0 mixed.
blackIs1
boolean
When true, a 1 bit is black. The default is the other way round.
byteAlign
boolean
Rows start on a byte boundary.
CffFont
interface CffFont
glyphNames
string[]
Glyph index → name, from the charset. Empty for a CID-keyed font.
cids
number[] | undefined
Glyph index → CID, for a CID-keyed font (where the charset means CIDs).
encoding
Map<number, number> | undefined
The font's built-in encoding: code → glyph index.
isCid
boolean
fontMatrix
number[] | undefined
`/FontMatrix` from the top dictionary, when it is not the usual one.
glyphCount
number
charstrings
Uint8Array<ArrayBufferLike>[]
The charstrings themselves, for a caller that means to run them.
localSubrs
Uint8Array<ArrayBufferLike>[]
The subroutines they call: the font's own, and the ones shared globally.
subrsOfGlyph?
((glyph: number) => Uint8Array[]) | undefined
The local subroutines a given glyph may call, for a CID-keyed font.
globalSubrs
Uint8Array<ArrayBufferLike>[]
CffGlyph
interface CffGlyph
name
string
path
GlyphPath
CmapEntry
interface CmapEntry

A glyph selected by a character: what the new `cmap` will say.

unicode
number
glyph
number
ColorSpace
interface ColorSpace
name
string
components
number
toRgb
(values: number[]) => Rgb
Components (each usually 0..1) to sRGB, each 0..255.
defaultDecode
(bitsPerComponent: number) => number[]
For an image: the range each component is decoded into by default.
indexed?
{ base: ColorSpace; lookup: Uint8Array; highest: number; } | undefined
Indexed spaces carry their table; an image reads it directly.
isPattern?
boolean | undefined
True for a pattern space, whose "colour" is a pattern name.
DecodedImage
interface DecodedImage

An image ready to be handed to a browser or written out.

width
number
height
number
mimeType?
string | undefined
A MIME type when the bytes are already a picture a browser can read.
bytes?
Uint8Array<ArrayBufferLike> | undefined
The encoded bytes (JPEG, PNG, JPEG 2000), when `mimeType` is set.
rgba?
Uint8Array<ArrayBufferLike> | undefined
Otherwise: RGBA, four bytes a pixel, top row first.
DecodedJpeg
interface DecodedJpeg
width
number
height
number
components
number
data
Uint8Array<ArrayBufferLike>
One byte per component per pixel, in the file's own colour space.
transform
number | undefined
The `APP14` transform, when the file states one: 0 none, 1 YCbCr, 2 YCCK.
adobe
boolean
True when an Adobe marker was present, which is what says CMYK is inverted.
DecodedJpx
interface DecodedJpx
width
number
height
number
components
number
data
Uint8Array<ArrayBufferLike>
One byte per component per pixel, at the codestream's own precision. Not scaled to eight bits: a JPEG 2000 image inside a PDF may be the index side of an `/Indexed` colour space, and a four-bit index stretched to fill a byte is no longer an index. The caller scales when it knows the samples are intensities and reads them as they are when they are not.
precision
number
Bits per component, 1–8. Deeper components are shifted down to eight.
transformed
boolean
True when the codestream says its components are a colour transform.
EmbeddedFont
interface EmbeddedFont extends BuiltFont
charOfCode
Map<number, string>
The character to put in the document for a code, which is the character the font is now keyed by.
family
string
A name unique to this font, for the `@font-face` rule.
unitsPerEm
number
Font units per em, so a caller can scale anything it measures.
EncryptionInfo
interface EncryptionInfo
encryptMetadata?
boolean | undefined
False when the document's metadata stream is left in the clear.
revision
number
version
number
streamCipher
Cipher
How streams are enciphered; strings may differ, and in a few files do.
stringCipher
Cipher
ownerAccess
boolean
Whether the document was opened with the owner password.
permissions
number
The permission bits, for reporting rather than enforcement.
EvaluateOptions
interface EvaluateOptions
honourOptionalContent?
boolean | undefined
Skip content in optional-content groups the document has turned off.
maxOperations?
number | undefined
Stop after this many operators; guards a hostile or looping file.
annotations?
boolean | undefined
Draw the page's annotations. Default: yes — see `drawAnnotations`.
Glyph
interface Glyph

One character code, decoded.

code
number
The code as it appeared in the string.
cid
number
The glyph selector: a CID for composite fonts, the code otherwise.
width
number
Advance width in text space units, already divided by 1000.
unicode
string
What the character says. Empty when the file does not say.
name?
string | undefined
The glyph's name, where the font has names.
isSpace
boolean
True for the code a simple font uses for a space.
GlyphPath
interface GlyphPath

Type 1 charstrings, interpreted into outlines. The conversion to CFF could be done operator by operator — Type 1 and Type 2 charstrings are cousins — and it is not done that way here. Type 1's oddities are not in its drawing commands but in the *escape hatch* it grew: flex and hint replacement are implemented by calling PostScript subroutines through `callothersubr`, with arguments passed on a separate stack and results fetched back with `pop`. A translation that tried to preserve those would have to preserve the protocol. So the charstring is *run* instead, and what comes out is a path: absolute points in glyph space, plus the advance width. Emitting a Type 2 charstring from a path is then arithmetic (`cff-writer.ts`). What is lost by going through a path is the hinting, which a screen at any reasonable size and a renderer with its own greyscale antialiasing do not miss.

commands
PathCommand[]
Absolute coordinates in glyph space, contours already closed.
width
number
The advance width `hsbw` or `sbw` gave.
sidebearing
number
The left sidebearing, which Type 1 states and Type 2 does not.
ImageMark
interface ImageMark
kind
"image"
name
string
The name in the resources, or a generated one for an inline image.
matrix
Matrix
Where it goes: the unit square mapped by this matrix.
box
Box
width
number
height
number
isMask
boolean
True for an image that is a stencil mask painted in the fill colour.
fill
Rgb | undefined
opacity
number
resolve
() => DecodedImage | undefined
How to get the bytes; resolved lazily because most callers never need them.
pattern?
PatternPaint | undefined
For a stencil mask, the pattern its ink is painted in rather than a colour.
blend?
string | undefined
The blend mode, as CSS spells it; absent for `Normal`.
clip?
Box | undefined
clipPaths?
readonly ClipPath[] | undefined
The clipping paths in force, outermost first, when a box will not do.
group?
MarkGroup | undefined
mask?
SoftMask | undefined
Jbig2Options
interface Jbig2Options

Decodes an embedded JBIG2 image to one bit per pixel, rows padded to a byte. A 1 bit is **black** here, which is JBIG2's convention and the opposite of what a PDF image of `/BitsPerComponent 1` means by it — the caller inverts.

onSegment?
((info: { number: number; type: number; note: string; }) => void) | undefined
Told what each segment turned into. The dump tool's way in. A JBIG2 stream that decodes to a blank page is the normal failure — every step is arithmetic and a wrong bit produces nothing rather than something wrong — so being able to ask "how many symbols did the dictionary make" is the difference between finding it and guessing.
LineOptions
interface LineOptions
keepInvisible?
boolean | undefined
Keep text drawn in render mode 3 (an OCR layer under a scan).
OpenPdfOptions
interface OpenPdfOptions extends OpenOptions
maxPages?
number | undefined
Stop after this many pages. Guards against a page tree that is a bomb.
PageDisplay
interface PageDisplay
width
number
Page size in points, as displayed.
height
number
marks
Mark[]
warnings
string[]
Anything that stopped the interpreter, for the harnesses to count.
PageInfo
interface PageInfo
index
number
Zero-based index in the document.
dict
Dict
ref
Ref | undefined
mediaBox
Box
The page's own sheet of paper.
cropBox
Box
The part of it that is meant to be seen; defaults to the media box.
rotate
number
Clockwise, a multiple of 90.
userUnit
number
`/UserUnit`: how many points one unit of this page's user space is. One, always, except that it is not: a page holding a plan or a poster states a larger unit so that its coordinates stay within the fifteen- thousand-unit limit older readers had. A page of 612 by 792 units with a user unit of three is twenty-five inches across, and a reader that ignores the key draws it at a third of its size — correct in every proportion and wrong in every measurement.
resources
Dict | undefined
PageTextOptions
interface PageTextOptions
dehyphenate?
boolean | undefined
Join a word broken across two lines by a hyphen. Default: true.
keepInvisible?
boolean | undefined
Keep an invisible OCR layer. Default: true — it is the only text there.
PathMark
interface PathMark
kind
"path"
d
string
SVG path data in device coordinates: the one shape both consumers agree on.
fill
Rgb | undefined
stroke
Rgb | undefined
strokeWidth
number
evenOdd
boolean
box
Box
dash?
{ pattern: number[]; phase: number; } | undefined
Dash pattern in device units, if the line is dashed.
lineCap?
number | undefined
0 butt, 1 round, 2 projecting square — omitted when it is the default.
lineJoin?
number | undefined
0 mitre, 1 round, 2 bevel — omitted when it is the default.
miterLimit?
number | undefined
opacity
number
strokeOpacity
number
pattern?
PatternPaint | undefined
The pattern the fill colour stands in for, when the fill is one.
blend?
string | undefined
The blend mode, as CSS spells it; absent for `Normal`.
clip?
Box | undefined
clipPaths?
readonly ClipPath[] | undefined
The clipping paths in force, outermost first, when a box will not do.
group?
MarkGroup | undefined
mask?
SoftMask | undefined
PlacedGlyph
interface PlacedGlyph

One glyph, placed.

text
string
What it says. Empty when the font does not say.
x
number
Left edge along the baseline, in device units.
y
number
The baseline, in device units.
width
number
Advance in device units, including letter and word spacing.
code
number
The character code, kept so an address can name a glyph and not a letter.
ShadingMark
interface ShadingMark
kind
"shading"
name
string
The shading's own dictionary name, for a viewer that can draw gradients.
matrix
Matrix
box
Box
average
Rgb | undefined
A representative colour, for consumers that cannot draw the real thing.
gradient?
Gradient | undefined
The gradient itself, for consumers that can. Sampled rather than described: a PDF shading's colour along its axis is a *function* — often a PostScript program — and no drawing system takes one. What every drawing system does take is a list of stops, so the function is evaluated at a few dozen points and the stops are the answer. Thirty-two samples is past the point where another one is visible on a screen.
mesh?
MeshFace[] | undefined
A mesh shading (types 4 to 7), flattened into flat-coloured polygons. There is no way to describe one to a drawing system, so it arrives already subdivided: see `mesh.ts` for why the subdivision is by colour rather than by size. When this is present it *is* the shading, and `average` is only what a consumer that cannot draw polygons would use.
blend?
string | undefined
The blend mode, as CSS spells it; absent for `Normal`.
clip?
Box | undefined
clipPaths?
readonly ClipPath[] | undefined
The clipping paths in force, outermost first, when a box will not do.
group?
MarkGroup | undefined
mask?
SoftMask | undefined
TextLine
interface TextLine
text
string
glyphs
PlacedGlyph[]
y
number
The baseline.
x0
number
x1
number
top
number
Top and bottom of the line's em box.
bottom
number
size
number
The dominant type size on the line.
rotation
number
Degrees; lines are grouped by rotation before anything else.
invisible
boolean
True when every run on the line was drawn in an invisible render mode.
runs
TextRun[]
The runs that contributed, in the order the file drew them.
pieces
{ run: TextRun; text: string; }[]
The line's text split by which run produced it. Concatenating the pieces gives `text` exactly — *including* the spaces this file inferred from the gaps, which belong to no run at all. That property is what lets a consumer address a span of the line: without it, a caller rebuilding the line from its runs gets the words with no spaces between them, which is a bug that looks like a font problem and is not.
TextRun
interface TextRun

One show operation: the glyphs of a single `Tj`, `TJ`, `'` or `"`. Kept as the file wrote it rather than split into words or lines. What a line is, is a question about geometry that the extractor answers with rules of its own; a run is what the *producer* considered one thing, and that information is worth keeping because it is often the only clue that two glyphs a millimetre apart belong to the same word.

kind
"text"
text
string
glyphs
PlacedGlyph[]
font
PdfFont
fontRef
string
The font's name as the page's resources call it (`/F1`).
size
number
Type size in device units — the size the glyphs are actually drawn at.
matrix
Matrix
The full text matrix, for a viewer that must reproduce shear and rotation.
rotation
number
Degrees clockwise; zero for ordinary text.
renderMode
number
0 fill, 1 stroke, 2 both, 3 invisible, 4–7 the same plus clipping.
fill
Rgb | undefined
stroke
Rgb | undefined
box
Box
Left, top, right, bottom of the run's ink box in device units.
type3?
boolean | undefined
True when the run was drawn by a Type 3 font's own content stream.
pattern?
PatternPaint | undefined
The pattern the fill colour stands in for, when the fill is one.
blend?
string | undefined
The blend mode, as CSS spells it; absent for `Normal`.
clip?
Box | undefined
clipPaths?
readonly ClipPath[] | undefined
The clipping paths in force, outermost first, when a box will not do.
group?
MarkGroup | undefined
mask?
SoftMask | undefined
TextTable
interface TextTable
rows
TableCell[][]
Cells, row-major. A cell the page left blank is present and empty.
sources
TextLine[]
The lines the table consumed. Not the same objects as the cells: a cell holding two lines is a third line made of both, so a caller that took the cells for the sources would emit the originals a second time as loose paragraphs after the table.
columnCount
number
How many columns the rules or the alignment found.
top
number
bottom
number
x0
number
x1
number
Token
interface Token
kind
TokenKind
value
number
Set for `number`.
text
string
Set for `name` and `keyword`.
bytes
Uint8Array<ArrayBufferLike>
Set for `string`.
start
number
Offset of the token's first byte.
TrueTypeTablesfrom @genomdev/core
interface TrueTypeTables

One font program, read. Everything a caller can learn about a font without rasterising it: how many glyphs it has, how big its em is, which character reaches which glyph, what the glyphs are called — and the raw bytes of every table, so that a question this does not answer can still be asked of the file rather than of a second copy of it.

numGlyphs
number
How many glyphs the font holds, from `maxp`; the bound on every index.
unitsPerEm
number
The em, in the font's own units, from `head`. Everything else the file states about size — an advance, an ascent, a bearing — is in these units, so nothing here means anything until it is divided by this. Two thousand and forty-eight for most TrueType faces and a thousand for most CFF ones; a reader that assumes either is wrong about half the world's fonts.
cmap
Map<number, number> | undefined
Character code (in the font's own cmap) → glyph index.
cmapKind
"symbol" | "unicode" | "mac" | undefined
Which cmap subtable was taken, because the answer changes the meaning.
unicodeOfGlyph
Map<number, number> | undefined
Glyph index → Unicode, from the Unicode subtable when there is one.
glyphNames
string[] | undefined
Glyph names from the `post` table, when it carries them.
hasCff
boolean
True when the file is really a CFF wrapped in an OpenType shell.
cff
Uint8Array<ArrayBufferLike> | undefined
The CFF table's bytes, for the wrapped case.
tables
Map<string, Uint8Array<ArrayBufferLike>>
Every table, by tag. Kept because rebuilding the font for a browser means keeping most of them and replacing one — the character map — and a rebuild that had to re-parse the directory would be a second reading of the same bytes.
Type1Font
interface Type1Font

What a Type 1 program says about itself.

encoding
(string | undefined)[] | undefined
Code → glyph name, from the font's own `/Encoding`.
standardEncoding
boolean
True when the font says `/Encoding StandardEncoding def`.
glyphNames
string[] | undefined
The names in `/CharStrings`, in the order the font defines them.
fontMatrix
number[] | undefined
charstrings
Map<string, Uint8Array<ArrayBufferLike>> | undefined
Glyph name → its charstring, decrypted and with `lenIV` bytes removed.
subrs
Uint8Array<ArrayBufferLike>[] | undefined
The local subroutines, by number, decrypted the same way.
Type2Context
interface Type2Context
localSubrs
readonly Uint8Array<ArrayBufferLike>[]
globalSubrs
readonly Uint8Array<ArrayBufferLike>[]
defaultWidthX
number
The width a glyph has when it states none.
nominalWidthX
number
What a stated width is a difference from.
charstringOfName?
((name: string) => Uint8Array | undefined) | undefined
For `endchar`'s four-argument form: a glyph's charstring by name.
standardEncoding?
readonly (string | undefined)[] | undefined

Type aliases

FontProgram
type FontProgram = | { kind: 'truetype'; truetype: TrueTypeTables } | { kind: 'cff'; cff: CffFont } | { kind: 'type1'; type1: Type1Font }

An embedded program, parsed as far as this package reads it.

Mark
type Mark = TextRun | PathMark | ImageMark | ShadingMark

Every mark may carry a `clip`: the rectangle the page had in force when it was drawn, in device units, and present only when it actually cuts the mark. It is a *box* and not a path, deliberately. The interpreter intersects the bounding boxes of the clipping paths it meets, so what a mark carries is a superset of the region the file describes — never smaller, so nothing that should be visible is ever hidden by it, and for the overwhelming majority of clips (`re W n`, which is what every producer writes to keep a picture inside its frame) it is exact. Without it a picture drawn twice its frame's size covers the text beside it, and a page that draws its own previous revision behind a clip shows both. Absent means "nothing was cut": either there was no clip, or the clip contained the mark entirely, in which case saying so would cost a `clipPath` per mark for no visible difference.

Matrix
type Matrix = readonly [number, number, number, number, number, number]

The 2×3 affine matrix everything in a PDF is positioned by. `[a b c d e f]` stands for the matrix that takes (x, y) to (a·x + c·y + e, b·x + d·y + f). Written as a tuple rather than an object because it is created constantly — one per glyph in a long run — and because that is the order the file states it in, so no reader of this code has to hold a translation in their head.

PathCommand
type PathCommand = | { op: 'm'; x: number; y: number } | { op: 'l'; x: number; y: number } | { op: 'c'; x1: number; y1: number; x2: number; y2: number; x: number; y: number }
PdfFunction
type PdfFunction = (inputs: number[]) => number[]
PdfObject
type PdfObject = null | boolean | number | PdfString | Name | Ref | PdfObject[] | Dict | PdfStream
Rgb
type Rgb = readonly [number, number, number]

Values

DeviceCMYK
DeviceCMYK: ColorSpace

CMYK to RGB by the naive formula. `255 · (1 − c) · (1 − k)` is not what a printer does and is what every screen reader does, including Acrobat's own preview. The alternative needs the output profile, which the file does not carry and the browser will not apply.

DeviceGray
DeviceGray: ColorSpace
DeviceRGB
DeviceRGB: ColorSpace
IDENTITY
IDENTITY: Matrix
IMAGE_FILTERS
IMAGE_FILTERS: Set<string>

The codecs whose output is an image, not a byte stream.

pdf
pdf: FormatModule<PdfDocument>

PDF, as one module. The only format here that recognises itself outright: `%PDF-` at the start of the file and nothing else can claim it, so the registry never has to load a second module to find out. It is also the one format that shares nothing with the others — no ZIP, no OPC, no DrawingML — which is why an application that shows PDFs and nothing else carries neither `@genomdev/office-core` nor a line of it.

@genomdev/pdf/view

Classes

PdfView
class PdfView extends BaseDocumentView<PdfDocument>
renderContent
() => void
Renders the content into {@link root}. Called on every update.
setZoom
(zoom: number) => void
Changes the zoom. Overridden rather than inherited because the base class re-renders, and re-rendering is exactly what this arrangement exists to avoid: the pages are already drawn at their natural size, so the zoom is a transform on each frame and nothing else.
goTo
(index: number) => void
Scrolls to a page, zero-based.
currentPage
number
The page currently in view, zero-based.
displayOf
(index: number) => PageDisplay | undefined
The display list of a page, rendering it if necessary. The seam again: search, highlighting and extraction all want the marks, and the viewer is where they already exist.
destroy
() => void
Tear the view down and release resources. The container is left empty.
onContainerResize
() => void
A resize does not re-render. The page is a fixed size in points; the window getting wider changes nothing about it. Only a `fit` mode would, and that is a zoom change.

Functions

renderPage
function renderPage(ownerDocument: Document, display: PageDisplay, options?: PageRenderOptions): SVGSVGElement

Interfaces

PageRenderOptions
interface PageRenderOptions
showInvisibleText?
boolean | undefined
Draw text that the file marks invisible (an OCR layer under a scan).
locators?
boolean | { hash: string; } | undefined
Put `data-locator` on each run, for highlighting. `true` stamps addresses with an empty document hash; `{ hash }` stamps the document's own, which is what a highlight arriving from a store carries. The same shape every other view in this project takes — it used to be a callback here, and an application that wired all the views the same way got `options.locators is not a function` from this one alone.
pageIndex?
number | undefined
Which page this is, for the addresses. A PDF's address names the page as its flow, so a run's address cannot be built from the run alone. Rendering a page without saying which one stamps every page as page one, and a highlight then lands on whichever page has a run at that index — usually the first.
imageUrl?
((mark: ImageMark) => string | undefined) | undefined
Resolves an image mark to a URL. Called at most once per mark.
fonts?
Map<PdfFont, EmbeddedFont> | undefined
The document's own fonts, rebuilt for the browser. When a run's font is in here the page is drawn with the file's glyphs and the text is written in the characters that font is keyed by; when it is not, the run falls back to a substitute face and the file's own text.
idPrefix?
string | undefined
Prefix for the ids this page mints (gradients, clips, masks). A soft mask is drawn by this same function into a `<mask>` of the outer page, so both pages' definitions end up in one document — and two `apf-grad-0`s in one document is one gradient, drawn wherever the first happens to be. Set internally; callers have no reason to.
PdfViewOptions
interface PdfViewOptions extends ViewOptions, PageRenderOptions
overscan?
number | undefined
How many pages beyond the visible one to keep drawn. Default: 1.
onPageChange?
((index: number) => void) | undefined
Called when the page in view changes, with its zero-based index.

Values

PDF_VIEW_CSS
PDF_VIEW_CSS: "\n.genom-pdf {\n display: block;\n width: 100%;\n background: var(--genom-pdf-backdrop, #52565b);\n padding: 16px 0;\n box-sizing: border-box;\n}\n\n.genom-pdf__pages {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n}\n\n.genom-pdf__page {\n background: #fff;\n box-shadow: 0 1px 4px rgb(0 0 0 / 0.35);\n}\n\n.genom-pdf__page-inner {\n /*\n * The glyphs are real text and the browser will hint and antialias them as\n * text, which is what makes this readable at small sizes where a rasterised\n * page is mush. `optimizeLegibility` is deliberately not asked for: it turns\n * on ligatures, and a ligature substituted into text whose glyphs were placed\n * individually moves letters.\n */\n text-rendering: geometricPrecision;\n}\n\n.genom-pdf__page svg {\n display: block;\n}\n\n/* Text drawn by the file in an invisible render mode — an OCR layer under a\n scan. It must be selectable and searchable and must not be seen. */\n.genom-pdf__page svg text[fill='transparent'] {\n fill: transparent;\n user-select: text;\n}\n\n@media print {\n .genom-pdf {\n background: none;\n padding: 0;\n }\n .genom-pdf__page {\n box-shadow: none;\n break-after: page;\n }\n}\n"

The viewer's stylesheet. Small on purpose: a PDF carries its own appearance completely, so anything this adds is about the *reader* — the paper's shadow, the gaps between sheets, the selection colour — and never about the page.

pdfView
pdfView: ViewModule<PdfDocument>

PDF with its renderer attached. What `@genomdev/pdf/view` exports and what an application passes to the viewer. The universal half is the same object — the renderer is a field on it, not a second registration — so a format either brings a way to draw itself or it does not, and nothing has to pair them up by hand.