This guide shows developers how to perform common PDF operations involving images. Use the PDFluent SDK to render pages, extract embedded images, and add new ones.
Image extraction is part of the base crate. No extra features are required.
# Cargo.toml
[dependencies]
pdfluent = "1.0.0-beta.18"Rasterise any PDF page to a PNG image at a chosen DPI. Works headless with no display server required.
use pdfluent::{PdfDocument, ImageFormat};
fn main() -> pdfluent::Result<()> {
let doc = PdfDocument::open("file.pdf")?;
let png = doc.render_page(0, 150, ImageFormat::Png)?;
std::fs::write("page-1.png", png)?;
Ok(())
}Open the document. Rendering is a document-level operation in PDFluent: you choose the output pattern and a page range.
use pdfluent::prelude::*;
let doc = PdfDocument::open("slides.pdf")?;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.
let opts = ToImagesOptions::new()
.with_dpi(150)
.with_format(ImageFormat::Png);Use .with_pages(from, to) with 1-based inclusive bounds. Omit to render every page.
let opts = ToImagesOptions::new()
.with_dpi(150)
.with_pages(1, 3); // pages 1, 2, 3Pass 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.
let report = doc.to_images("page_{page}.png", opts)?;
for path in &report.paths {
println!("wrote {}", path.display());
}Change the format via .with_format(ImageFormat::Jpeg). JPEG does not carry transparency, so RGBA pixels are flattened to RGB before encoding.
use pdfluent::prelude::*;
let doc = PdfDocument::open("document.pdf")?;
doc.to_images(
"thumb_{page}.jpg",
ToImagesOptions::new()
.with_dpi(72)
.with_format(ImageFormat::Jpeg),
)?;Rasterize individual pages or an entire PDF document to JPEG files at a configurable DPI and quality level.
use pdfluent::{PdfDocument, ImageFormat};
fn main() -> pdfluent::Result<()> {
let doc = PdfDocument::open("file.pdf")?;
let jpg = doc.render_page(0, 150, ImageFormat::Jpeg)?;
std::fs::write("page-1.jpg", jpg)?;
Ok(())
}Load the document.
use pdfluent::prelude::*;
let doc = PdfDocument::open("document.pdf")?;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.
let opts = ToImagesOptions::new()
.with_dpi(150)
.with_format(ImageFormat::Jpeg);The {page} placeholder in the pattern is substituted with the 1-based page number.
let report = doc.to_images("page_{page}.jpg", opts)?;Render small preview images of every page in a PDF. Set a fixed width or height and PDFluent calculates the other dimension automatically.
use pdfluent::{PdfDocument, ImageFormat};
fn main() -> pdfluent::Result<()> {
let doc = PdfDocument::open("file.pdf")?;
for i in 0..doc.page_count() {
let png = doc.render_page(i, 72, ImageFormat::Png)?;
std::fs::write(format!("thumb-{}.png", i + 1), png)?;
}
Ok(())
}Load the document.
use pdfluent::prelude::*;
let doc = PdfDocument::open("slides.pdf")?;72 DPI is typical for thumbnails; for crisper previews, go up to 150 DPI.
let opts = ToImagesOptions::new()
.with_dpi(72)
.with_format(ImageFormat::Png);The {page} marker in the filename pattern is substituted with the 1-based page number.
let report = doc.to_images("thumb_{page}.png", opts)?;
for path in &report.paths {
println!("wrote {}", path.display());
}Limit the range with with_pages(from, to) using 1-based inclusive bounds.
let opts = ToImagesOptions::new()
.with_dpi(72)
.with_pages(1, 1);
let _ = doc.to_images("cover.png", opts)?;Pull JPEG, PNG, and JBIG2 images out of a PDF without re-encoding. Preserves original compression and quality.
use pdfluent::PdfDocument;
fn main() -> pdfluent::Result<()> {
let doc = PdfDocument::open("document.pdf")?;
for (page_idx, page) in doc.pages().enumerate() {
for (img_idx, image) in page.images().enumerate() {
let filename = format!(
"page{}_img{}.{}",
page_idx + 1,
img_idx + 1,
image.format().extension()
);
image.save(&filename)?;
println!("Saved {} ({}x{})", filename, image.width(), image.height());
}
}
Ok(())
}page.images() returns an iterator over all XObject images on the page. Inline images are included.
use pdfluent::PdfDocument;
let doc = PdfDocument::open("document.pdf")?;
for (i, page) in doc.pages().enumerate() {
let count = page.images().count();
println!("Page {}: {} image(s)", i + 1, count);
}image.save() writes the image bytes to a file without re-encoding. JPEG images stay JPEG, preserving the original quality.
for (i, page) in doc.pages().enumerate() {
for (j, image) in page.images().enumerate() {
let ext = image.format().extension(); // "jpg", "png", "jbig2"
let path = format!("output/p{}_i{}.{}", i + 1, j + 1, ext);
image.save(&path)?;
}
}Call image.to_png_bytes() to decode the image and re-encode as PNG, regardless of its original format.
use std::fs;
for (i, page) in doc.pages().enumerate() {
for (j, image) in page.images().enumerate() {
let png_bytes = image.to_png_bytes()?;
let path = format!("output/p{}_i{}.png", i + 1, j + 1);
fs::write(&path, &png_bytes)?;
println!("Wrote PNG: {}", path);
}
}Skip thumbnails and decorative images by checking pixel dimensions before saving.
for (i, page) in doc.pages().enumerate() {
for (j, image) in page.images()
.filter(|img| img.width() >= 100 && img.height() >= 100)
.enumerate()
{
let path = format!("output/p{}_i{}.jpg", i + 1, j + 1);
image.save(&path)?;
println!(
"Saved {}x{} image: {}",
image.width(), image.height(), path
);
}
}Embed JPEG, PNG, or WebP images at a specific position and size on any PDF page.
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, 0, 50.0, 700.0, 120.0, 40.0))?;
doc.save("with-logo.pdf")?;
Ok(())
}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.
// Load the image bytes (PNG or JPEG)
let bytes = std::fs::read("logo.png")?;
println!("{} bytes loaded", bytes.len());page_mut(n) returns a mutable handle to page n. PDF page indices are zero-based.
use pdfluent::parity::{ImageInsert, InsertImageFormat};
// Place on the first page (index 0). Coordinates are points from bottom-left.
let insert = ImageInsert::new(bytes.clone(), InsertImageFormat::Png, 0, 50.0, 700.0, 150.0, 60.0);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.
doc.insert_image(insert)?;Use ImagePosition::fit_width() to scale the image to a given width while preserving the aspect ratio.
use pdfluent::parity::{ImageInsert, InsertImageFormat};
// Insert with reduced opacity (e.g. a watermark-style logo)
doc.insert_image(
ImageInsert::new(bytes, InsertImageFormat::Png, 0, 50.0, 700.0, 150.0, 60.0)
.with_opacity(0.85),
)?;
doc.save("document-with-logo.pdf")?;Remap all color space operations in a PDF to DeviceGray to produce a greyscale-only file.
use pdfluent::{PdfDocument, color::GreyscaleOptions};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("input.pdf")?;
doc.convert_to_greyscale(GreyscaleOptions::default())?;
doc.save("greyscale.pdf")?;
Ok(())
}Load the color PDF into a mutable Document.
let mut doc = PdfDocument::open("color_input.pdf")?;GreyscaleOptions controls the luminance formula and whether images are converted in place.
use pdfluent::color::GreyscaleOptions;
let opts = GreyscaleOptions::default()
.convert_images(true) // downsample color images to greyscale
.luminance_formula(pdfluent::color::LuminanceFormula::Bt709); // standard TV luminanceconvert_to_greyscale rewrites all color operators (rg, RG, k, K, cs, CS) and converts embedded images to /DeviceGray.
doc.convert_to_greyscale(opts)?;Inspect the color spaces in the output to confirm conversion.
for page in doc.pages() {
for cs in page.color_spaces() {
println!("page color space: {:?}", cs);
}
}Write the converted document. File size typically decreases because image data is smaller without color channels.
doc.save("greyscale.pdf")?;