/** * PDF content stream builder. * * Provides a high-level API for constructing PDF content streams using * PDF graphics operators. Content streams control what is drawn on a page: * text, lines, rectangles, colors, etc. * * @see PDF Reference 1.7, Chapter 4 - Graphics * @see PDF Reference 1.7, Chapter 5 - Text */ import type { PdfColor } from "../types.js"; /** * Builds a PDF content stream using graphics and text operators. * * PDF uses a postfix notation where operands precede the operator. * For example: `100 200 m` means "move to point (100, 200)". * * Color model: PDF uses separate color state for stroking (lines/borders) * and non-stroking (fills/text). We provide methods for both. */ export declare class PdfContentStream { /** * Content stream fragments in draw order. Most entries are plain operator * strings produced eagerly. A function entry is a *deferred* fragment: * its body is only evaluated at serialization time. This is required for * text whose final byte encoding depends on font decisions (embedded * CIDFont vs. Type1/WinAnsi vs. Type3 fallback) that are not finalised * until `PdfDocumentBuilder.build()`. Deferring keeps the fragment at its * exact draw-order position (preserving z-order) while letting the actual * encoding run after the font manager's state is settled. */ private parts; /** * Append a raw PDF operator string to the content stream. * Use this for operators not covered by the typed API (e.g. `d1` for Type3 glyphs). */ raw(operator: string): this; /** * Append a deferred fragment whose body is evaluated only at serialization * time. Used by text drawing so the final byte encoding can be chosen after * the document's fonts are resolved at build time. The fragment occupies its * draw-order slot immediately, so z-order relative to other operators is * preserved. */ deferred(produce: () => string): this; /** * Save the current graphics state (push onto state stack). * Must be balanced with a corresponding restore(). */ save(): this; /** * Restore the previously saved graphics state (pop from state stack). */ restore(): this; /** * Set the current graphics state from an ExtGState resource. * Used for transparency (alpha), blend modes, etc. */ setGraphicsState(name: string): this; /** * Set the stroking color (used for lines, borders). */ setStrokeColor(color: PdfColor): this; /** * Set the non-stroking color (used for fills, text). */ setFillColor(color: PdfColor): this; /** * Set the line width for stroking operations. */ setLineWidth(width: number): this; /** * Set the line dash pattern. * @param dashArray - Array of dash/gap lengths. Empty = solid line. * @param phase - Starting phase offset. */ setDashPattern(dashArray: number[], phase?: number): this; /** * Set the line cap style. * 0 = butt cap, 1 = round cap, 2 = projecting square cap */ setLineCap(style: 0 | 1 | 2): this; /** * Set the line join style. * 0 = miter join, 1 = round join, 2 = bevel join */ setLineJoin(style: 0 | 1 | 2): this; /** * Begin a new subpath by moving to the given point. */ moveTo(x: number, y: number): this; /** * Append a straight line segment from the current point to (x, y). */ lineTo(x: number, y: number): this; /** * Append a rectangle to the current path. * PDF convention: (x, y) is the lower-left corner. */ rect(x: number, y: number, width: number, height: number): this; /** * Append a cubic Bezier curve to the current path. * From current point to (x3, y3), with control points (x1, y1) and (x2, y2). */ curveTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): this; /** * Append a cubic Bezier curve where the first control point is the current point. * From current point to (x3, y3), with control points (current, y1) and (x2, y2). */ curveToV(x2: number, y2: number, x3: number, y3: number): this; /** * Append a cubic Bezier curve where the second control point equals (x3, y3). * From current point to (x3, y3), with control point (x1, y1). */ curveToY(x1: number, y1: number, x3: number, y3: number): this; /** * Stroke the current path. */ stroke(): this; /** * Fill the current path using the nonzero winding number rule. */ fill(): this; /** * Fill and then stroke the current path. */ fillAndStroke(): this; /** * Close the current subpath by appending a line from current point to start. */ closePath(): this; /** * End the path without filling or stroking (used for clipping). */ endPath(): this; /** * Set the current path as the clipping boundary (nonzero winding rule). * Must be followed by endPath() or a painting operator. */ clip(): this; /** * Begin a text object. */ beginText(): this; /** * End the current text object. */ endText(): this; /** * Set the font and size for subsequent text operations. * @param fontName - The font resource name (e.g., "F1") * @param size - Font size in points */ setFont(fontName: string, size: number): this; /** * Set the text matrix (position and transform for text). * For simple positioning, use Td instead. * @param a - Horizontal scaling * @param b - Vertical skew * @param c - Horizontal skew * @param d - Vertical scaling * @param e - Horizontal translation * @param f - Vertical translation */ setTextMatrix(a: number, b: number, c: number, d: number, e: number, f: number): this; /** * Move to the start of the next line, offset from the start of the current line. */ moveText(tx: number, ty: number): this; /** * Set the text leading (line spacing) for T* operator. */ setTextLeading(leading: number): this; /** * Show a text string. The string is escaped for PDF. */ showText(text: string): this; /** * Show a text string encoded as WinAnsi hex string. * Used for Type1 fonts where non-ASCII characters need single-byte encoding. */ showTextWinAnsi(text: string): this; /** * Show a text string using a pre-encoded hex string (for CIDFonts). * The hexString should be in the format `<0012003A...>`. */ showTextHex(hexString: string): this; /** * Move to the next line and show a text string. */ nextLineShowText(text: string): this; /** * Set the text rise (baseline offset), used for superscript/subscript. */ setTextRise(rise: number): this; /** * Set character spacing (extra space between characters). */ setCharacterSpacing(spacing: number): this; /** * Set word spacing (extra space for space character). */ setWordSpacing(spacing: number): this; /** * Draw an XObject (image) at the given position and size. * The image must be registered as a resource with the given name. */ drawImage(name: string, x: number, y: number, width: number, height: number): this; /** * Apply a transformation matrix (cm operator). */ concat(a: number, b: number, c: number, d: number, e: number, f: number): this; /** * Invoke a named XObject (Do operator). */ doXObject(name: string): this; /** * Draw a filled rectangle. */ fillRect(x: number, y: number, width: number, height: number, color: PdfColor): this; /** * Draw a stroked line. */ drawLine(x1: number, y1: number, x2: number, y2: number, color: PdfColor, lineWidth: number, dashPattern?: number[]): this; /** * Append an ellipse to the current path using 4 cubic Bezier curves. * (cx, cy) is the center; rx, ry are the radii. * * Uses the standard kappa = 4 * (sqrt(2) - 1) / 3 ≈ 0.5522847 approximation. */ ellipse(cx: number, cy: number, rx: number, ry: number): this; /** * Append a circle to the current path. * (cx, cy) is the center; r is the radius. */ circle(cx: number, cy: number, r: number): this; /** * Append a rounded rectangle to the current path. * (x, y) is the lower-left corner; r is the corner radius. */ roundedRect(x: number, y: number, width: number, height: number, r: number): this; /** * Whether any fragment has been appended. Unlike `toString().length > 0`, * this does NOT evaluate deferred fragments, so it is safe to call before * fonts are resolved (e.g. when probing for overlay content during an * editor save, prior to `writeFontResources`). A deferred text fragment * counts as content even though its bytes are not produced yet. */ hasContent(): boolean; /** * Get the content stream as a string. Deferred fragments (see `deferred`) * are evaluated here, after font resolution has completed at build time. */ toString(): string; /** * Get the content stream as a Uint8Array (UTF-8 encoded). */ toUint8Array(): Uint8Array; } /** * Check whether a single code point is representable in WinAnsi encoding. */ export declare function isWinAnsiCodePoint(cp: number): boolean; /** * Check whether a string contains characters outside the WinAnsi repertoire. * When true, standard Type1 fonts cannot render those characters and an * embedded TrueType font is required for correct output. */ export declare function hasNonWinAnsiChars(text: string): boolean;