Graphics-state stack for a content-stream interpreter — ISO 32000-2 §8.4.
Module pdfGraphics | Source packages/front/office/pdf/src/content/graphics.js | Deps pdfErrors | Worker-safe yes
Implements the device-independent subset (§8.4.1, Table 53) of the graphics
state: CTM, line width/cap/join, miter limit, dash pattern, rendering intent,
flatness — plus the nested text sub-tree (driven by pdfText)
and strokeColor/fillColor (driven by pdfColor). This is
not a renderer: it is a faithful model of the gstate chain that the upper
layers (text extraction, MCID tracking, redaction) consume.
Resolve
const g = runtime.resolve('pdfGraphics');
// Returns: { GStateStack, createStack, applyOp, initialGState, mulCtm }
API
| Member | Signature | Returns |
|---|---|---|
GStateStack |
new GStateStack() |
Instance, starting from initialGState. |
createStack |
() => GStateStack |
Equivalent helper. |
applyOp |
(stack, { op, args }, handlers?) => void |
Applies one gstate op. |
initialGState |
() => object |
Identity-state snapshot. |
mulCtm |
(a: number[6], b: number[6]) => number[6] |
2×3 matrix product. |
GStateStack methods
| Method | Description |
|---|---|
current() |
Top of stack (mutable). |
save() |
q — pushes a deep copy of the sensitive fields. |
restore() |
Q — pops; throws pdf/gstate/unbalanced-Q on an empty stack. |
depth() |
Current depth (≥ 1). |
concatCtm(matrix) |
cm — ctm = matrix × ctm. |
gstate shape
{
ctm: [1,0,0,1,0,0],
lineWidth: 1, lineCap: 0, lineJoin: 0,
miterLimit: 10,
dash: { array: [], phase: 0 },
renderingIntent: 'RelativeColorimetric',
flatness: 1,
text: { charSpace: 0, wordSpace: 0, scale: 100, leading: 0,
font: null, fontSize: 0, renderMode: 0, rise: 0 },
strokeColor: { space: 'DeviceGray', components: [0] },
fillColor: { space: 'DeviceGray', components: [0] }
}
applyOp routes only q, Q, cm, w, J, j, M, d, ri, i. Any
other operator falls through to handlers.unhandled(op, stack) when supplied.
Examples
Walking a content stream
const g = runtime.resolve('pdfGraphics');
const stack = g.createStack();
for (const op of ops) {
g.applyOp(stack, op, {
unhandled: (o) => {
if (o.op.startsWith('T')) applyTextOp(stack.current(), o);
}
});
}
Composing CTMs
const scale = [2, 0, 0, 2, 0, 0];
const translate = [1, 0, 0, 1, 100, 50];
const ctm = g.mulCtm(scale, translate);
Detecting an unbalanced stack
try { g.applyOp(stack, { op: 'Q', args: [] }); }
catch (e) { if (e.code === 'pdf/gstate/unbalanced-Q') { /* … */ } }
Errors
| Code | Class | When |
|---|---|---|
pdf/gstate/unbalanced-Q |
ContractError |
Q without a matching q. |
pdf/gstate/bad-cm |
ContractError |
cm matrix is not 6 elements. |
pdf/gstate/bad-arg-type |
ContractError |
Non-numeric argument for w, J, j, M, i. |
pdf/gstate/bad-name-arg |
ContractError |
Non-name argument for ri. |
See also
pdfContentStream— produces the op list.pdfText— the nested text state.pdfColor—strokeColor/fillColor.