Skip to content

Graphics, Patterns & Shadings

PDF Oxide provides low-level graphics primitives through ContentStreamBuilder for drawing paths and shapes, TilingPatternBuilder for repeating patterns, gradient builders for linear and radial gradients, and ExtGStateBuilder for transparency and blend modes.

Binding coverage. The FluentPageBuilder primitives rect(x, y, w, h), filled_rect(x, y, w, h, color), and line(x1, y1, x2, y2) ship in Rust, Python, Node, C#, Go, and WASM as of v0.3.38 — see DocumentBuilder → Graphics Primitives. The full ContentStreamBuilder surface on this page (arbitrary paths, bezier curves, colour spaces, TilingPatternBuilder, LinearGradientBuilder / RadialGradientBuilder, ExtGStateBuilder) remains Rust-only. Other bindings should chain the simple primitives through DocumentBuilder, generate PDFs through Pdf::from_html_css / Pdf::from_markdown / Pdf::from_images, or call the Rust CLI for custom drawing.

Quick Example

Rust

use pdf_oxide::writer::{ContentStreamBuilder, LineCap, LineJoin};

let mut builder = ContentStreamBuilder::new();
builder
    .save_state()
    .set_stroke_color(0.0, 0.0, 1.0)    // Blue stroke
    .set_fill_color(0.8, 0.8, 1.0)      // Light blue fill
    .set_line_width(2.0)
    .rect(72.0, 600.0, 200.0, 100.0)
    .fill_and_stroke()
    .restore_state();

ContentStreamBuilder – Drawing Primitives

ContentStreamBuilder generates PDF content stream operators for rendering graphics on a page.

Path Operations

use pdf_oxide::writer::ContentStreamBuilder;

let mut cs = ContentStreamBuilder::new();

// Move/Line/Curve
cs.move_to(72.0, 700.0)
  .line_to(200.0, 700.0)
  .line_to(200.0, 600.0)
  .close_path()
  .stroke();

// Rectangle
cs.rect(72.0, 500.0, 150.0, 80.0)
  .fill();

// Bezier curve
cs.move_to(72.0, 400.0)
  .curve_to(100.0, 450.0, 200.0, 350.0, 250.0, 400.0)
  .stroke();

Color Operations

// RGB colors (0.0 to 1.0)
cs.set_fill_color(1.0, 0.0, 0.0);      // Red fill
cs.set_stroke_color(0.0, 0.5, 0.0);    // Green stroke

// Grayscale
cs.set_fill_color_gray(0.5);           // 50% gray fill
cs.set_stroke_color_gray(0.0);         // Black stroke

// CMYK
cs.set_fill_color_cmyk(0.0, 1.0, 1.0, 0.0);    // Red in CMYK
cs.set_stroke_color_cmyk(1.0, 0.0, 0.0, 0.0);  // Cyan stroke

Line Style

use pdf_oxide::writer::{ContentStreamBuilder, LineCap, LineJoin};

let mut cs = ContentStreamBuilder::new();

cs.set_line_width(2.0)
  .set_line_cap(LineCap::Round)
  .set_line_join(LineJoin::Round)
  .set_miter_limit(10.0)
  .set_dash_pattern(vec![5.0, 3.0], 0.0);  // 5pt dash, 3pt gap

LineCap variants: Butt (default), Round, Square

LineJoin variants: Miter (default), Round, Bevel

Graphics State

// Save/restore state for isolated operations
cs.save_state()
  .set_fill_color(1.0, 0.0, 0.0)
  .rect(100.0, 100.0, 50.0, 50.0)
  .fill()
  .restore_state();
// State is restored to what it was before save_state()

Path Filling and Stroking

Method Description
.stroke() Stroke the path outline
.fill() Fill the path interior (non-zero winding)
.fill_even_odd() Fill using even-odd rule
.fill_and_stroke() Fill and stroke
.fill_and_stroke_even_odd() Fill (even-odd) and stroke
.close_and_stroke() Close path then stroke
.close_fill_and_stroke() Close, fill, and stroke
.end_path() End path without painting

Clipping

// Clip to rectangle, then draw inside
cs.save_state()
  .rect(100.0, 100.0, 200.0, 200.0)
  .clip()
  .end_path()
  // Everything drawn here is clipped to the rectangle
  .set_fill_color(1.0, 0.0, 0.0)
  .rect(50.0, 50.0, 300.0, 300.0)  // Only visible within clip
  .fill()
  .restore_state();

Transformations

// Apply transformation matrix [a b c d e f]
cs.save_state()
  .transform(1.0, 0.0, 0.0, 1.0, 100.0, 200.0)  // Translate
  .rect(0.0, 0.0, 50.0, 50.0)
  .fill()
  .restore_state();

ContentStreamOp Enum

For maximum control, build operations directly:

use pdf_oxide::writer::ContentStreamOp;

let ops = vec![
    ContentStreamOp::SaveState,
    ContentStreamOp::SetLineWidth(2.0),
    ContentStreamOp::SetStrokeColorRGB(0.0, 0.0, 1.0),
    ContentStreamOp::MoveTo(72.0, 500.0),
    ContentStreamOp::LineTo(300.0, 500.0),
    ContentStreamOp::Stroke,
    ContentStreamOp::RestoreState,
];

TilingPatternBuilder – Repeating Patterns

Tiling patterns repeat a small cell across an area.

use pdf_oxide::writer::{TilingPatternBuilder, PatternPaintType, PatternTilingType};

// Striped pattern
let (pattern_dict, content_bytes) = TilingPatternBuilder::new()
    .bbox(0.0, 0.0, 10.0, 10.0)
    .x_step(10.0)
    .y_step(10.0)
    .colored()
    .tiling_type(PatternTilingType::ConstantSpacing)
    .content_bytes(b"0.8 0 0 rg 0 0 5 10 re f".to_vec())
    .build();

Configuration Methods

Method Description
.bbox(x, y, w, h) Set bounding box of pattern cell
.x_step(step) Horizontal spacing between cells
.y_step(step) Vertical spacing between cells
.step(x, y) Set both steps at once
.colored() Colors defined in pattern content
.uncolored() Color specified when pattern is used
.tiling_type(type) Set tiling algorithm
.matrix(a, b, c, d, e, f) Apply transformation to pattern
.content_bytes(bytes) Set raw content stream bytes
.build() Returns (Object, Vec<u8>)

PatternPresets

PDF Oxide includes preset patterns for common use cases:

use pdf_oxide::writer::PatternPresets;

// Access presets for common patterns like hatching, dots, etc.

LinearGradientBuilder – Linear Gradients

Create axial (linear) gradient shadings.

use pdf_oxide::writer::{LinearGradientBuilder, GradientStop};
use pdf_oxide::layout::Color;

let (shading_dict, function_dict) = LinearGradientBuilder::new()
    .from(0.0, 0.0)
    .to(468.0, 0.0)
    .add_stop(0.0, Color { r: 1.0, g: 0.0, b: 0.0 })   // Red
    .add_stop(0.5, Color { r: 1.0, g: 1.0, b: 0.0 })   // Yellow
    .add_stop(1.0, Color { r: 0.0, g: 0.0, b: 1.0 })   // Blue
    .extend(true)
    .build();

Two-Color Shortcut

use pdf_oxide::writer::LinearGradientBuilder;
use pdf_oxide::layout::Color;

let gradient = LinearGradientBuilder::two_color(
    Color { r: 0.0, g: 0.0, b: 0.5 },  // Dark blue
    Color { r: 0.5, g: 0.8, b: 1.0 },   // Light blue
);

Configuration Methods

Method Description
.from(x, y) Start point of gradient
.to(x, y) End point of gradient
.add_stop(pos, color) Add color stop (position 0.0-1.0)
.extend_start(bool) Extend gradient before start point
.extend_end(bool) Extend gradient after end point
.extend(bool) Set both extend flags

RadialGradientBuilder – Radial Gradients

Create circular gradient shadings.

use pdf_oxide::writer::RadialGradientBuilder;
use pdf_oxide::layout::Color;

let (shading_dict, function_dict) = RadialGradientBuilder::new()
    .center(200.0, 400.0)
    .radius(0.0, 150.0)  // Inner radius 0, outer radius 150
    .add_stop(0.0, Color { r: 1.0, g: 1.0, b: 1.0 })  // White center
    .add_stop(1.0, Color { r: 0.0, g: 0.0, b: 0.5 })  // Dark blue edge
    .build();

ExtGStateBuilder – Transparency & Blend Modes

Control transparency, blend modes, and other graphics state parameters.

use pdf_oxide::writer::{ExtGStateBuilder, BlendMode};

let gs_dict = ExtGStateBuilder::new()
    .fill_alpha(0.5)             // 50% transparent fill
    .stroke_alpha(0.8)           // 80% opaque stroke
    .blend_mode(BlendMode::Multiply)
    .build();

Configuration Methods

Method Description
.fill_alpha(a) Fill opacity (0.0 transparent, 1.0 opaque)
.stroke_alpha(a) Stroke opacity
.blend_mode(mode) Set blend mode
.line_width(w) Override line width
.line_cap(cap) Override line cap style
.line_join(join) Override line join style
.miter_limit(limit) Override miter limit
.flatness(f) Flatness tolerance
.overprint_stroke(b) Overprint mode for stroke
.overprint_fill(b) Overprint mode for fill

BlendMode Variants

Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight

GradientPresets

Convenience methods for common gradient patterns:

use pdf_oxide::writer::GradientPresets;

// Access preset gradient configurations

Advanced Example

Drawing a Chart Background with Gradient

use pdf_oxide::writer::{
    ContentStreamBuilder, LinearGradientBuilder, ExtGStateBuilder, BlendMode,
};
use pdf_oxide::layout::Color;

let mut cs = ContentStreamBuilder::new();

// Draw chart area with rounded-corner appearance
cs.save_state()
  .set_fill_color(0.95, 0.95, 0.97)
  .rect(72.0, 200.0, 468.0, 400.0)
  .fill()
  .restore_state();

// Draw grid lines
cs.save_state()
  .set_stroke_color(0.85, 0.85, 0.85)
  .set_line_width(0.5);

for i in 0..5 {
    let y = 200.0 + (i as f32 * 100.0);
    cs.move_to(72.0, y).line_to(540.0, y).stroke();
}

cs.restore_state();

// Draw data bars
let values = [280.0, 350.0, 180.0, 420.0, 310.0];
let colors = [
    (0.2, 0.5, 0.8),
    (0.3, 0.7, 0.4),
    (0.8, 0.3, 0.3),
    (0.6, 0.4, 0.8),
    (0.9, 0.6, 0.2),
];

for (i, (&val, &(r, g, b))) in values.iter().zip(colors.iter()).enumerate() {
    let x = 100.0 + (i as f32 * 85.0);
    cs.save_state()
      .set_fill_color(r, g, b)
      .rect(x, 200.0, 50.0, val)
      .fill()
      .restore_state();
}