Render and modify PDF images

This guide shows developers how to perform common PDF operations involving images. Use the PDFluent SDK to render pages and to add new images.

Add PDFluent to Cargo.toml

Image extraction is part of the base crate. No extra features are required.

toml
# Cargo.toml
[dependencies]
pdfluent = "1.0.0"

Render a PDF page to PNG in Rust

Rasterise any PDF page to a PNG image at a chosen DPI. Works headless with no display server required.

rust
use pdfluent::{PdfDocument, ImageFormat};

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("file.pdf")?;
    let png = doc.render_page(1, 150, ImageFormat::Png)?;
    std::fs::write("page-1.png", png)?;
    Ok(())
}

Complete program, compiled against the published crate: Code on GitHub →

The same code, typed and run on screen. Watch on YouTube

  1. Open the PDF

    Open the document. Rendering is a document-level operation in PDFluent: you choose the output pattern and a page range.

    rust
    use pdfluent::prelude::*;
    
    let doc = PdfDocument::open("slides.pdf")?;
  2. Build ToImagesOptions

    ToImagesOptions::new() defaults to 150 DPI and PNG output. Override DPI and format to taste. For sharp screen previews 150 DPI is fine; print-quality output is 300 DPI.

    rust
    let opts = ToImagesOptions::new()
        .with_dpi(150)
        .with_format(ImageFormat::Png);
  3. Render a specific page range

    Use .with_pages(from, to) with 1-based inclusive bounds. Omit to render every page.

    rust
    let opts = ToImagesOptions::new()
        .with_dpi(150)
        .with_pages(1, 3); // pages 1, 2, 3
  4. Write the output files

    Pass a filename pattern. The {page} placeholder is substituted with the 1-based page number. If the pattern has no {page}, PDFluent inserts _N before the extension.

    rust
    let report = doc.to_images("page_{page}.png", opts)?;
    for path in &report.paths {
        println!("wrote {}", path.display());
    }
  5. Render to JPEG

    Change the format via .with_format(ImageFormat::Jpeg). JPEG does not carry transparency, so RGBA pixels are flattened to RGB before encoding.

    rust
    use pdfluent::prelude::*;
    
    let doc = PdfDocument::open("document.pdf")?;
    
    doc.to_images(
        "thumb_{page}.jpg",
        ToImagesOptions::new()
            .with_dpi(72)
            .with_format(ImageFormat::Jpeg),
    )?;
  • to_images is native-only. On wasm32 targets it returns Error::UnsupportedOnWasm. See WASM_SUPPORT.md §2.5.
  • Rendering is CPU-bound. Page-level parallelism isn't exposed via to_images in 1.0; for large documents, split via extract_pages and run to_images on each slice in parallel.
  • Very high DPI values (above 600) produce large image files. 150 DPI is a good default for web previews.
  • The 1.0 renderer outputs RGBA8 pixels. CMYK-preserving export lands with the renderer changes in a later release.

Render PDF pages to JPEG images in Rust

Rasterize individual pages or an entire PDF document to JPEG files at a configurable DPI and quality level.

rust
use pdfluent::{PdfDocument, ImageFormat};

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("file.pdf")?;
    let jpg = doc.render_page(1, 150, ImageFormat::Jpeg)?;
    std::fs::write("page-1.jpg", jpg)?;
    Ok(())
}

Complete program, compiled against the published crate: Code on GitHub →

The same code, typed and run on screen. Watch on YouTube

  1. Open the PDF

    Load the document.

    rust
    use pdfluent::prelude::*;
    
    let doc = PdfDocument::open("document.pdf")?;
  2. Configure JPEG output

    ToImagesOptions defaults to PNG; switch to Jpeg with with_format. JPEG quality is fixed at 90 in 1.0; user-configurable quality is tracked for a later release.

    rust
    let opts = ToImagesOptions::new()
        .with_dpi(150)
        .with_format(ImageFormat::Jpeg);
  3. Render to files

    The {page} placeholder in the pattern is substituted with the 1-based page number.

    rust
    let report = doc.to_images("page_{page}.jpg", opts)?;
  • 150 DPI is sufficient for screen display and thumbnails. Use 300 DPI for print-quality rendering. 72 DPI is native PDF resolution (1 pt = 1 px at 72 DPI).
  • JPEG is lossy. For lossless archiving use ImageFormat::Png instead.
  • Pages with transparency need a background color. Without a white background, transparent areas render as black in JPEG.
  • Rendering speed scales approximately linearly with DPI squared. A 300 DPI render takes roughly 4x longer than 150 DPI for the same page.

Generate page thumbnails from a PDF in Rust

Render small preview images of every page in a PDF. Set a fixed width or height and PDFluent calculates the other dimension automatically.

rust
use pdfluent::{PdfDocument, ImageFormat};

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("file.pdf")?;
    for i in 1..=doc.page_count() {
        let png = doc.render_page(i, 72, ImageFormat::Png)?;
        std::fs::write(format!("thumb-{}.png", i), png)?;
    }
    Ok(())
}

Complete program, compiled against the published crate: Code on GitHub →

The same code, typed and run on screen. Watch on YouTube

  1. Open the source PDF

    Load the document.

    rust
    use pdfluent::prelude::*;
    
    let doc = PdfDocument::open("slides.pdf")?;
  2. Build ToImagesOptions

    72 DPI is typical for thumbnails; for crisper previews, go up to 150 DPI.

    rust
    let opts = ToImagesOptions::new()
        .with_dpi(72)
        .with_format(ImageFormat::Png);
  3. Render all pages

    The {page} marker in the filename pattern is substituted with the 1-based page number.

    rust
    let report = doc.to_images("thumb_{page}.png", opts)?;
    for path in &report.paths {
        println!("wrote {}", path.display());
    }
  4. Render only the first page

    Limit the range with with_pages(from, to) using 1-based inclusive bounds.

    rust
    let opts = ToImagesOptions::new()
        .with_dpi(72)
        .with_pages(1, 1);
    let _ = doc.to_images("cover.png", opts)?;
  • render_thumbnail() uses a faster code path than render() for small output sizes. Do not use render() at low DPI as a substitute.
  • The thumbnail background defaults to white. Set background color (not available in 1.0) in ThumbnailOptions to change it.
  • Pages with very large embedded images may take longer to thumbnail because the image must be decoded before downsampling.

Add an image to a PDF page in Rust

Embed JPEG, PNG, or WebP images at a specific position and size on any PDF page.

rust
use pdfluent::PdfDocument;
use pdfluent::parity::{ImageInsert, InsertImageFormat};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("report.pdf")?;
    let bytes = std::fs::read("logo.png")?;
    doc.insert_image(ImageInsert::new(bytes, InsertImageFormat::Png, 1, 50.0, 700.0, 120.0, 40.0))?;
    doc.save("with-logo.pdf")?;
    Ok(())
}
  1. Load an image from a file or from bytes

    Image::from_file() reads JPEG, PNG, or WebP. Image::from_bytes() accepts the raw image bytes if you are reading from memory or a network source.

    rust
    // Load the image bytes (PNG or JPEG)
    let bytes = std::fs::read("logo.png")?;
    println!("{} bytes loaded", bytes.len());
  2. Get a mutable reference to the target page

    insert_image takes the destination page in the ImageInsert itself. Pages are 1-based, so page 1 is the first page.

    rust
    use pdfluent::parity::{ImageInsert, InsertImageFormat};
    
    // Place on page 1. Pages are 1-based; coordinates are points from bottom-left.
    let insert = ImageInsert::new(bytes.clone(), InsertImageFormat::Png, 1, 50.0, 700.0, 150.0, 60.0);
  3. Position and embed the image

    PDF uses a coordinate system where (0, 0) is the bottom-left corner. Measurements are in points (1 pt = 1/72 inch). A4 is 595 x 842 pt, US Letter is 612 x 792 pt.

    rust
    doc.insert_image(insert)?;
  4. Preserve aspect ratio when sizing the image

    Use ImagePosition::fit_width() to scale the image to a given width while preserving the aspect ratio.

    rust
    use pdfluent::parity::{ImageInsert, InsertImageFormat};
    
    // Insert with reduced opacity (e.g. a watermark-style logo)
    doc.insert_image(
        ImageInsert::new(bytes, InsertImageFormat::Png, 1, 50.0, 700.0, 150.0, 60.0)
            .with_opacity(0.85),
    )?;
    doc.save("document-with-logo.pdf")?;
  • PDF coordinates have the origin at the bottom-left. If your image appears at the wrong position, check whether you are counting from the top or the bottom.
  • JPEG images are embedded as-is in the PDF stream (DCTDecode), with no quality loss. PNG images are embedded as FlateDecode streams.
  • Large images increase file size proportionally. Resize before embedding if the display size is much smaller than the source resolution.
  • Transparency in PNG images (alpha channel) is supported via a soft mask XObject. All compliant PDF readers render the transparency correctly.