Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ debug/

# Benchmark fixtures (downloaded at runtime)
fixtures/benchmarks/
fixtures/private/

# Temporary files
tmp/
144 changes: 70 additions & 74 deletions content/docs/advanced/low-level-drawing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ page.drawOperators([
```

<Callout type="warn">
The low-level API requires understanding of PDF content stream structure.
Invalid operator sequences may produce corrupted PDFs. Use the high-level
methods when they're sufficient.
The low-level API requires understanding of PDF content stream structure. Invalid operator
sequences may produce corrupted PDFs. Use the high-level methods when they're sufficient.
</Callout>

---
Expand All @@ -36,15 +35,15 @@ page.drawOperators([

The high-level methods (`drawRectangle`, `drawText`, etc.) cover most needs. Reach for the low-level API when you need:

| Feature | Low-Level Approach |
| --- | --- |
| Matrix transforms | `ops.concatMatrix()` for arbitrary rotation/scale/skew |
| Gradients | `createAxialShading()` or `createRadialShading()` |
| Repeating patterns | `createTilingPattern()` or `createImagePattern()` |
| Blend modes | `createExtGState({ blendMode: "Multiply" })` |
| Clipping regions | `ops.clip()` with `ops.endPath()` |
| Reusable graphics | `createFormXObject()` for stamps/watermarks |
| Fine-grained control | Direct operator sequences |
| Feature | Low-Level Approach |
| -------------------- | ------------------------------------------------------ |
| Matrix transforms | `ops.concatMatrix()` for arbitrary rotation/scale/skew |
| Gradients | `createAxialShading()` or `createRadialShading()` |
| Repeating patterns | `createTilingPattern()` or `createImagePattern()` |
| Blend modes | `createExtGState({ blendMode: "Multiply" })` |
| Clipping regions | `ops.clip()` with `ops.endPath()` |
| Reusable graphics | `createFormXObject()` for stamps/watermarks |
| Fine-grained control | Direct operator sequences |

---

Expand All @@ -59,80 +58,80 @@ import { ops } from "@libpdf/core";
### Graphics State

```typescript
ops.pushGraphicsState() // Save current state (q)
ops.popGraphicsState() // Restore saved state (Q)
ops.setGraphicsState(name) // Apply ExtGState resource (gs)
ops.concatMatrix(a, b, c, d, e, f) // Transform CTM (cm)
ops.pushGraphicsState(); // Save current state (q)
ops.popGraphicsState(); // Restore saved state (Q)
ops.setGraphicsState(name); // Apply ExtGState resource (gs)
ops.concatMatrix(a, b, c, d, e, f); // Transform CTM (cm)
```

### Path Construction

```typescript
ops.moveTo(x, y) // Begin subpath (m)
ops.lineTo(x, y) // Line to point (l)
ops.curveTo(x1, y1, x2, y2, x3, y3) // Cubic bezier (c)
ops.rectangle(x, y, w, h) // Rectangle shorthand (re)
ops.closePath() // Close subpath (h)
ops.moveTo(x, y); // Begin subpath (m)
ops.lineTo(x, y); // Line to point (l)
ops.curveTo(x1, y1, x2, y2, x3, y3); // Cubic bezier (c)
ops.rectangle(x, y, w, h); // Rectangle shorthand (re)
ops.closePath(); // Close subpath (h)
```

### Path Painting

```typescript
ops.stroke() // Stroke path (S)
ops.fill() // Fill path, non-zero winding (f)
ops.fillEvenOdd() // Fill path, even-odd rule (f*)
ops.fillAndStroke() // Fill then stroke (B)
ops.endPath() // Discard path without painting (n)
ops.stroke(); // Stroke path (S)
ops.fill(); // Fill path, non-zero winding (f)
ops.fillEvenOdd(); // Fill path, even-odd rule (f*)
ops.fillAndStroke(); // Fill then stroke (B)
ops.endPath(); // Discard path without painting (n)
```

### Clipping

```typescript
ops.clip() // Set clip region, non-zero (W)
ops.clipEvenOdd() // Set clip region, even-odd (W*)
ops.clip(); // Set clip region, non-zero (W)
ops.clipEvenOdd(); // Set clip region, even-odd (W*)
```

### Color

```typescript
ops.setStrokingGray(g) // Stroke grayscale (G)
ops.setNonStrokingGray(g) // Fill grayscale (g)
ops.setStrokingRGB(r, g, b) // Stroke RGB (RG)
ops.setNonStrokingRGB(r, g, b) // Fill RGB (rg)
ops.setStrokingCMYK(c, m, y, k) // Stroke CMYK (K)
ops.setNonStrokingCMYK(c, m, y, k) // Fill CMYK (k)
ops.setStrokingColorSpace(cs) // Set stroke color space (CS)
ops.setNonStrokingColorSpace(cs) // Set fill color space (cs)
ops.setStrokingColorN(name) // Set stroke pattern (SCN)
ops.setNonStrokingColorN(name) // Set fill pattern (scn)
ops.setStrokingGray(g); // Stroke grayscale (G)
ops.setNonStrokingGray(g); // Fill grayscale (g)
ops.setStrokingRGB(r, g, b); // Stroke RGB (RG)
ops.setNonStrokingRGB(r, g, b); // Fill RGB (rg)
ops.setStrokingCMYK(c, m, y, k); // Stroke CMYK (K)
ops.setNonStrokingCMYK(c, m, y, k); // Fill CMYK (k)
ops.setStrokingColorSpace(cs); // Set stroke color space (CS)
ops.setNonStrokingColorSpace(cs); // Set fill color space (cs)
ops.setStrokingColorN(name); // Set stroke pattern (SCN)
ops.setNonStrokingColorN(name); // Set fill pattern (scn)
```

### Line Style

```typescript
ops.setLineWidth(w) // Line width (w)
ops.setLineCap(cap) // 0=butt, 1=round, 2=square (J)
ops.setLineJoin(join) // 0=miter, 1=round, 2=bevel (j)
ops.setMiterLimit(limit) // Miter limit ratio (M)
ops.setDashPattern(array, phase) // Dash pattern (d)
ops.setLineWidth(w); // Line width (w)
ops.setLineCap(cap); // 0=butt, 1=round, 2=square (J)
ops.setLineJoin(join); // 0=miter, 1=round, 2=bevel (j)
ops.setMiterLimit(limit); // Miter limit ratio (M)
ops.setDashPattern(array, phase); // Dash pattern (d)
```

### Text

```typescript
ops.beginText() // Begin text object (BT)
ops.endText() // End text object (ET)
ops.setFont(name, size) // Set font (Tf)
ops.moveText(tx, ty) // Position text (Td)
ops.setTextMatrix(a, b, c, d, e, f) // Text matrix (Tm)
ops.showText(string) // Show text (Tj)
ops.beginText(); // Begin text object (BT)
ops.endText(); // End text object (ET)
ops.setFont(name, size); // Set font (Tf)
ops.moveText(tx, ty); // Position text (Td)
ops.setTextMatrix(a, b, c, d, e, f); // Text matrix (Tm)
ops.showText(string); // Show text (Tj)
```

### XObjects and Shading

```typescript
ops.paintXObject(name) // Draw XObject (Do)
ops.paintShading(name) // Paint shading (sh)
ops.paintXObject(name); // Draw XObject (Do)
ops.paintShading(name); // Paint shading (sh)
```

---
Expand All @@ -146,7 +145,7 @@ import { Matrix, ops } from "@libpdf/core";

const matrix = Matrix.identity()
.translate(200, 300)
.rotate(45) // degrees
.rotate(45) // degrees
.scale(2, 1.5);

page.drawOperators([
Expand All @@ -162,14 +161,14 @@ Or use raw matrix components:

```typescript
// Translation: move 100 points right, 200 points up
ops.concatMatrix(1, 0, 0, 1, 100, 200)
ops.concatMatrix(1, 0, 0, 1, 100, 200);

// Scale: 2x horizontal, 0.5x vertical
ops.concatMatrix(2, 0, 0, 0.5, 0, 0)
ops.concatMatrix(2, 0, 0, 0.5, 0, 0);

// Rotation: 45 degrees around origin
const angle = 45 * Math.PI / 180;
ops.concatMatrix(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), 0, 0)
const angle = (45 * Math.PI) / 180;
ops.concatMatrix(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), 0, 0);
```

---
Expand All @@ -183,7 +182,7 @@ Create linear or radial gradients with color stops:
```typescript
// CSS-style: angle + length
const gradient = pdf.createLinearGradient({
angle: 90, // 0=up, 90=right, 180=down, 270=left
angle: 90, // 0=up, 90=right, 180=down, 270=left
length: 200,
stops: [
{ offset: 0, color: rgb(1, 0, 0) },
Expand All @@ -194,7 +193,7 @@ const gradient = pdf.createLinearGradient({

// Or explicit coordinates
const axial = pdf.createAxialShading({
coords: [0, 0, 200, 0], // x0, y0, x1, y1
coords: [0, 0, 200, 0], // x0, y0, x1, y1
stops: [
{ offset: 0, color: rgb(0, 0, 1) },
{ offset: 1, color: rgb(1, 0, 1) },
Expand All @@ -206,7 +205,7 @@ const axial = pdf.createAxialShading({

```typescript
const radial = pdf.createRadialShading({
coords: [100, 100, 0, 100, 100, 80], // x0, y0, r0, x1, y1, r1
coords: [100, 100, 0, 100, 100, 80], // x0, y0, r0, x1, y1, r1
stops: [
{ offset: 0, color: rgb(1, 1, 1) },
{ offset: 1, color: rgb(0, 0, 0) },
Expand Down Expand Up @@ -234,9 +233,7 @@ page.drawOperators([
// Or wrap in a pattern for PathBuilder
const pattern = pdf.createShadingPattern({ shading: gradient });

page.drawPath()
.rectangle(50, 200, 200, 100)
.fill({ pattern });
page.drawPath().rectangle(50, 200, 200, 100).fill({ pattern });
```

---
Expand Down Expand Up @@ -282,9 +279,7 @@ const pattern = pdf.createImagePattern({
height: 50,
});

page.drawPath()
.circle(200, 400, 80)
.fill({ pattern });
page.drawPath().circle(200, 400, 80).fill({ pattern });
```

### Gradient Pattern
Expand Down Expand Up @@ -387,23 +382,23 @@ Restrict drawing to a region:
```typescript
page.drawOperators([
ops.pushGraphicsState(),

// Define clip region (circle)
ops.moveTo(200, 300),
// ... circle path using bezier curves
ops.clip(),
ops.endPath(), // Required after clip
ops.endPath(), // Required after clip

// Everything here is clipped to the circle
ops.paintShading(gradientName),
ops.popGraphicsState(), // Clipping is restored

ops.popGraphicsState(), // Clipping is restored
]);
```

<Callout type="info">
Always follow `ops.clip()` with a path-painting operator. Use `ops.endPath()`
to discard the path, or `ops.fill()` to both clip and fill.
Always follow `ops.clip()` with a path-painting operator. Use `ops.endPath()` to discard the path,
or `ops.fill()` to both clip and fill.
</Callout>

---
Expand Down Expand Up @@ -444,7 +439,7 @@ const page = pdf.addPage();

// Create button gradient
const gradient = pdf.createLinearGradient({
angle: 180, // top to bottom
angle: 180, // top to bottom
length: 40,
stops: [
{ offset: 0, color: rgb(0.4, 0.6, 1) },
Expand Down Expand Up @@ -475,7 +470,8 @@ page.drawOperators([
]);

// Rounded rectangle path
page.drawPath()
page
.drawPath()
.moveTo(110, 700)
.lineTo(250, 700)
.curveTo(255, 700, 260, 705, 260, 710)
Expand Down
32 changes: 16 additions & 16 deletions content/docs/api/pdf-page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -570,9 +570,9 @@ For advanced graphics operations, PDFPage provides methods to emit raw operators

Emit raw PDF operators to the page content stream.

| Param | Type | Description |
| ----------- | ------------ | --------------------------- |
| `operators` | `Operator[]` | Array of operators to emit |
| Param | Type | Description |
| ----------- | ------------ | -------------------------- |
| `operators` | `Operator[]` | Array of operators to emit |

```typescript
import { ops } from "@libpdf/core";
Expand All @@ -588,8 +588,8 @@ page.drawOperators([
```

<Callout type="warn">
The caller is responsible for valid operator sequences. Invalid sequences
may produce corrupted PDFs.
The caller is responsible for valid operator sequences. Invalid sequences may produce corrupted
PDFs.
</Callout>

---
Expand All @@ -598,8 +598,8 @@ page.drawOperators([

Register a font resource and return its operator name.

| Param | Type | Description |
| ------ | ------------------------------ | ------------------ |
| Param | Type | Description |
| ------ | ------------------------------------ | ---------------- |
| `font` | `EmbeddedFont \| Standard14FontName` | Font to register |

**Returns**: `string` - Resource name (e.g., `"F0"`)
Expand All @@ -623,9 +623,9 @@ page.drawOperators([

Register an image resource and return its operator name.

| Param | Type | Description |
| ------- | ---------- | ------------------ |
| `image` | `PDFImage` | Embedded image |
| Param | Type | Description |
| ------- | ---------- | -------------- |
| `image` | `PDFImage` | Embedded image |

**Returns**: `string` - Resource name (e.g., `"Im0"`)

Expand All @@ -647,8 +647,8 @@ page.drawOperators([

Register a shading (gradient) resource and return its operator name.

| Param | Type | Description |
| --------- | ------------ | ------------ |
| Param | Type | Description |
| --------- | ------------ | ---------------- |
| `shading` | `PDFShading` | Shading resource |

**Returns**: `string` - Resource name (e.g., `"Sh0"`)
Expand Down Expand Up @@ -699,8 +699,8 @@ page.drawOperators([

Register an extended graphics state and return its operator name.

| Param | Type | Description |
| ------- | ------------- | -------------- |
| Param | Type | Description |
| ------- | -------------- | -------------- |
| `state` | `PDFExtGState` | Graphics state |

**Returns**: `string` - Resource name (e.g., `"GS0"`)
Expand Down Expand Up @@ -728,8 +728,8 @@ page.drawOperators([

Register a Form XObject or embedded page and return its operator name.

| Param | Type | Description |
| --------- | --------------------------------- | ---------------- |
| Param | Type | Description |
| --------- | ----------------------------------- | ------------------- |
| `xobject` | `PDFFormXObject \| PDFEmbeddedPage` | XObject to register |

**Returns**: `string` - Resource name (e.g., `"Fm0"`)
Expand Down
Loading