PDF Graphics State
The PDF graphics state is a set of parameters that control how content is rendered on a page — current transformation matrix, line width, stroke and fill colour, font, clipping path, rendering intent, and opacity. It is maintained as a stack; the q (save) and Q (restore) operators push and pop the stack, allowing temporary graphics state changes that can be undone.
The PDF graphics state is a set of parameters that control how subsequent drawing operations are rendered. Before a drawing operator is executed — painting a path, showing text, invoking an XObject — the graphics state determines the visual result: what colour, what line thickness, what transparency, what transformation, what rendering mode.
The graphics state is maintained as a stack. The <code>q</code> operator saves (pushes) the current graphics state; the <code>Q</code> operator restores (pops) the previously saved state. This stack discipline allows localised graphics state changes without affecting the surrounding context — exactly as a programming function's local variables don't affect the caller.
The graphics state is defined in ISO 32000 as a device-independent representation of rendering parameters. The actual rendering of those parameters is device-dependent — a screen viewer and a printer may render the same graphics state parameters with different physical outputs.
Core graphics state parameters
The graphics state includes the following key parameters:
<strong>Current Transformation Matrix (CTM)</strong> — A 3×3 matrix (represented as 6 values [a b c d e f]) that maps from user space coordinates (as written in the content stream) to device space coordinates. The CTM controls scaling, rotation, shearing, and translation. The <code>cm</code> operator concatenates (multiplies) a new matrix onto the current CTM. The initial CTM for a page is determined by the page's /MediaBox and optional /UserUnit.
<strong>Colour spaces and colours</strong> — The graphics state maintains separate colour specifications for stroke operations (painting paths) and fill operations (filling areas): current stroke colour space and colour value; current fill colour space and colour value. Set with <code>CS</code>/<code>cs</code> (colour space) and <code>SC</code>/<code>sc</code>/<code>SCN</code>/<code>scn</code> (colour values), or with the shorthand operators <code>RG</code>/<code>rg</code> (DeviceRGB), <code>K</code>/<code>k</code> (DeviceCMYK), <code>G</code>/<code>g</code> (DeviceGray).
<strong>Line parameters</strong> — Line width (<code>w</code> operator, default 1.0 user space units); line cap style (<code>J</code>: butt=0, round=1, projecting square=2); line join style (<code>j</code>: miter=0, round=1, bevel=2); miter limit (<code>M</code>, default 10.0); dash pattern (<code>d</code>: array of on/off lengths, phase).
<strong>Text state</strong> — Several graphics state parameters relate specifically to text: current font and size (<code>Tf</code>); character spacing (<code>Tc</code>); word spacing (<code>Tw</code>); horizontal scaling (<code>Tz</code>); text leading (<code>TL</code>); text rendering mode (<code>Tr</code>: fill, stroke, fill+stroke, invisible, clip); text rise (<code>Ts</code> for superscript/subscript).
<strong>Current clipping path</strong> — The clipping path defines the region within which painting is allowed. Painting outside the clipping path has no visible effect. Set with the <code>W</code> or <code>W*</code> operators after a path construction sequence. The initial clipping path covers the entire page.
<strong>Alpha constants and blend mode</strong> — For transparency: constant alpha for stroke (ca) and fill (CA); blend mode (BM); soft mask (SMask). Set via the <code>gs</code> operator using an ExtGState dictionary in the page resources.
The q/Q graphics state stack
The most important mechanism in the graphics state is the <code>q</code>/<code>Q</code> save/restore stack. Its usage is ubiquitous in PDF content streams:
<code>q % save current state</code> <code> 1 0 0 1 100 200 cm % translate to (100,200)</code> <code> 0.5 w % set line width to 0.5</code> <code> 0 0 1 RG % set stroke colour to blue</code> <code> 0 0 200 100 re S % draw blue 200×100 rectangle</code> <code>Q % restore: CTM, line width, colour all back to pre-q values</code>
This pattern means that any graphics state changes made between <code>q</code> and <code>Q</code> are automatically reverted when <code>Q</code> is executed. Well-formed PDF content streams balance every <code>q</code> with a corresponding <code>Q</code>. Unbalanced <code>q</code>/<code>Q</code> pairs can cause state pollution — subsequent drawing operations inherit unintended graphics state from within an unclosed <code>q</code> block.
The PDF specification permits a maximum nesting depth of 28 for the graphics state stack. Exceeding this limit is a conformance error. In practice, most PDFs stay well within this limit, but pathological cases (deeply nested form XObjects each using q/Q) can approach it.
<strong>Extended graphics state (ExtGState)</strong> — Some graphics state parameters are not directly settable with individual operators. They are grouped into an ExtGState dictionary referenced by name from the page's /ExtGState resource dictionary. The <code>gs</code> operator applies an entire ExtGState dictionary to the current graphics state at once. ExtGState parameters include: alpha constants (ca/CA), blend mode (BM), soft mask (SMask), overprint mode (OPM), smoothness tolerance, and others. This dictionary-based mechanism allows setting multiple related parameters atomically and allows the specification to add new parameters without requiring new operators.
The Current Transformation Matrix in detail
The CTM is a 2D affine transformation represented as a 3×3 matrix in homogeneous coordinates, but expressed as 6 values [a b c d e f]:
<code>[ a b 0 ]</code> <code>[ c d 0 ]</code> <code>[ e f 1 ]</code>
The CTM maps user space point (x, y) to device space point (x', y'): - x' = a*x + c*y + e - y' = b*x + d*y + f
Common transformations: - <strong>Identity</strong>: [1 0 0 1 0 0] (no transformation) - <strong>Translate by (tx, ty)</strong>: [1 0 0 1 tx ty] - <strong>Scale by (sx, sy)</strong>: [sx 0 0 sy 0 0] - <strong>Rotate by θ</strong>: [cos(θ) sin(θ) −sin(θ) cos(θ) 0 0]
The <code>cm</code> operator concatenates the specified matrix onto the current CTM by matrix multiplication. Since matrix multiplication is not commutative, order matters: translate-then-scale produces different results than scale-then-translate.
In a web-based PDF editor, converting between CSS pixel coordinates and PDF user space requires the inverse of the CTM composition. If the page is rendered at scale=1.5 and the CTM maps page coordinates to PDF coordinates, the full conversion from screen click to PDF annotation position requires accounting for: (1) CSS display scale, (2) device pixel ratio, (3) page origin, and (4) the bottom-left vs top-left origin convention.
Frequently asked questions
What is the difference between stroke colour and fill colour in PDF?
In PDF, every path operation can be either a stroke (paint the outline of the path) or a fill (paint the interior of the path) — or both. The graphics state maintains two separate colour specifications: stroke colour (used by S, s, and the stroke component of B/b) and fill colour (used by f, F, f*, and the fill component of B/b). These are set independently: RG sets the stroke colour in DeviceRGB; rg sets the fill colour in DeviceRGB. When painting a framed and filled rectangle, the frame outline uses the stroke colour and the interior uses the fill colour. For text, the text rendering mode (Tr) controls whether text is stroked, filled, or both — the dominant mode (3, invisible) is used for text that appears in the text layer of scanned PDFs without being visible.
What happens if q and Q are not balanced in a PDF content stream?
An unbalanced q/Q pair is a conformance error in PDF. If there are more qs than Qs, the graphics state stack grows without being restored — subsequent pages or content streams may inherit graphics state changes from the unbalanced block. If there are more Qs than qs, the viewer may attempt to pop below the initial state, which is undefined behaviour and may cause rendering errors. In practice, PDF viewers are generally tolerant of minor imbalances — they typically cap the stack pop at the initial state to avoid crashing. However, relying on this tolerance is a PDF generator bug. Tools like qpdf and pdfinfo report unbalanced q/Q pairs when analysing PDF content stream conformance.
What is the difference between DeviceRGB, DeviceCMYK, and DeviceGray colour spaces?
These are PDF's three device colour spaces, used for direct colour specification without ICC profiles: DeviceRGB — red/green/blue in [0,1] range; appropriate for screen display, web output. DeviceCMYK — cyan/magenta/yellow/black in [0,1] range (1 = full ink); appropriate for print production. DeviceGray — single component in [0,1] range (0 = black, 1 = white); for monochrome content. Device colour spaces are called "device" because their visual result depends on the output device's colour profile — the same DeviceRGB value will appear differently on different monitors without colour management. For colour-accurate documents, use calibrated or ICC-based colour spaces instead. For archival PDFs, PDF/A mandates that either device colour spaces be used consistently (with output intent defined) or that all colours use embedded ICC profiles.