Merge, split, edit, and inspect PDFs

This guide covers common PDF manipulation tasks using the PDFluent SDK. It is for Rust developers who need to process PDF files programmatically.

Add PDFluent to your project

Add the pdfluent crate to your Cargo.toml. No system libraries are required.

toml
[dependencies]
pdfluent = "1.0.0"

Merge PDFs in Rust

Combine multiple PDF files into one document. PDFluent preserves bookmarks across all input files.

rust
use pdfluent::prelude::*;

fn main() -> Result<()> {
    let merged = PdfMerger::new()
        .add(PdfDocument::open("part1.pdf")?)
        .add(PdfDocument::open("part2.pdf")?)
        .add(PdfDocument::open("part3.pdf")?)
        .build()?;

    merged.save("combined.pdf")?;

    println!("Merged {} pages into combined.pdf", merged.page_count());
    Ok(())
}

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

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

  1. Open each input document

    Open every source PDF with PdfDocument::open. The merger takes full PdfDocument values by move, so consume each input once.

    rust
    use pdfluent::prelude::*;
    
    let a = PdfDocument::open("invoice_jan.pdf")?;
    let b = PdfDocument::open("invoice_feb.pdf")?;
    let c = PdfDocument::open("invoice_mar.pdf")?;
  2. Build a PdfMerger and add inputs

    Create a PdfMerger, chain .add() for each document. Inputs are appended in the order they are added.

    rust
    let merger = PdfMerger::new()
        .add(a)
        .add(b)
        .add(c);
  3. Configure bookmark handling

    Choose a BookmarkMergeStrategy. Concat (the default) groups each source's bookmarks under a top-level entry; FlattenAll sequences them; Discard drops all bookmarks. In 1.0 Concat has dedicated treatment; FlattenAll and Discard fall back to the underlying concatenation.

    rust
    let merger = merger
        .with_bookmarks(BookmarkMergeStrategy::Concat)
        .with_page_labels(true); // 1.0: accepted but currently a no-op
  4. Build and save

    Call .build() to produce a merged PdfDocument, then save or serialise it. build() is the terminating step and consumes the merger.

    rust
    let merged = merger.build()?;
    merged.save("annual_report.pdf")?;
    
    println!("Total pages: {}", merged.page_count());
  • Named destinations from each input file are remapped so they remain valid in the merged document.
  • Page labels from source files are treated on a best-effort basis in 1.0; full preservation lands in 1.1.
  • Encrypted PDFs must be decrypted before merging — open them with OpenOptions::new().with_password("...") or call doc.decrypt("...") first.
  • The merger processes inputs in the order they were added. Page numbering in the output starts at 1 and increases sequentially.
  • Input bytes: open from memory with PdfDocument::from_bytes(&bytes) before adding to the merger — there is no separate add_bytes entry point.

Merge PDFs and generate a table of contents in Rust

Combine multiple PDF files into one and insert a generated table of contents page with clickable bookmark links.

rust
use pdfluent::{PdfDocument, PdfMerger, BookmarkMergeStrategy};

fn main() -> pdfluent::Result<()> {
    let merged = PdfMerger::new()
        .add(PdfDocument::open("intro.pdf")?)
        .add(PdfDocument::open("body.pdf")?)
        .with_bookmarks(BookmarkMergeStrategy::Concat)
        .with_page_labels(true)
        .build()?;
    merged.save("merged.pdf")?;
    Ok(())
}
  1. Prepare MergeInput entries

    Each MergeInput specifies a source file and an optional title used for the TOC entry and bookmark.

    rust
    use pdfluent::{PdfDocument, PdfMerger, BookmarkMergeStrategy};
    
    let merger = PdfMerger::new()
        .add(PdfDocument::open("section1.pdf")?)
        .add(PdfDocument::open("section2.pdf")?)
        .add(PdfDocument::open("appendix.pdf")?);
  2. Configure merge options

    Enable TOC generation and optionally configure the TOC page style, font, and bookmark depth.

    rust
    // Concatenate each source's bookmarks under a top-level entry (navigation TOC).
    // Page labels preserve each section's original numbering.
    let merger = merger
        .with_bookmarks(BookmarkMergeStrategy::Concat)
        .with_page_labels(true);
  3. Merge and get the document

    Document::merge_with_options returns a new Document. The TOC page is inserted at the position specified in the options.

    rust
    let merged = merger.build()?;
  4. Inspect the generated outlines

    The merge operation adds bookmark entries that match the TOC. Verify they are present.

    rust
    for bookmark in merged.outlines()? {
        println!("'{}' -> page {:?}", bookmark.title, bookmark.page);
    }
  5. Save the merged document

    Write the final merged file.

    rust
    merged.save("merged_with_toc.pdf")?;
  • The TOC page is generated using the page dimensions of the first page in the merge list. Override with MergeOptions::toc_page_size(PageSize::A4).
  • Existing bookmarks from each source document are preserved and nested under the top-level chapter bookmark.
  • If a source document is encrypted, decrypt it before passing to MergeInput or pass the password with MergeInput::with_password(pw).
  • Named destinations from each source document are re-scoped with a per-document prefix to avoid collisions.

Split a PDF by page range in Rust

Extract one or more page ranges from a PDF and write each range to a separate file. Useful for splitting chapters, invoices, or reports.

rust
use pdfluent::PdfDocument;

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("report.pdf")?;
    for (i, page) in doc.split_pages()?.into_iter().enumerate() {
        page.save(format!("page-{}.pdf", i + 1))?;
    }
    Ok(())
}
  1. Open the source document

    Load the PDF you want to split. PDFluent reads the file lazily, so opening a 500-page document uses minimal memory until pages are accessed.

    rust
    use pdfluent::PdfDocument;
    
    let doc = PdfDocument::open("quarterly_report.pdf")?;
    println!("Source has {} pages", doc.page_count());
  2. Define page ranges

    Create PageRange values for each segment. Pages are 1-indexed. Ranges can overlap if you need the same page in multiple output files.

    rust
    // Ranges to extract (0-based, end-exclusive)
    let ranges = [0..5usize, 5..18, 18..22];
  3. Split and write to separate files

    Pass the ranges to split_by_ranges(). Use {n} in the output pattern for the range index, or provide a Vec of explicit output paths.

    rust
    for (i, range) in ranges.iter().enumerate() {
        doc.extract_pages(range.clone())?.save(format!("segment_{}.pdf", i + 1))?;
    }
    // Produces: segment_1.pdf, segment_2.pdf, segment_3.pdf
  4. Use explicit output names

    When you need specific filenames, pass a slice of paths with the same length as the ranges slice.

    rust
    let names = ["cover.pdf", "body.pdf", "appendix.pdf"];
    for (range, name) in ranges.iter().zip(names) {
        doc.extract_pages(range.clone())?.save(name)?;
    }
  5. Split into in-memory buffers

    If you need to serve the split files over HTTP without touching disk, use to_bytes_vec() instead.

    rust
    for (i, range) in ranges.iter().enumerate() {
        let bytes = doc.extract_pages(range.clone())?.to_bytes()?;
        println!("Segment {}: {} bytes", i + 1, bytes.len());
    }
  • Page indices are 1-based. Passing 0 returns an error.
  • Ranges that extend past the last page are clamped to the last page automatically.
  • Bookmarks pointing to pages outside a range are dropped in that output file.
  • AcroForm fields on pages that fall within a range are included in the corresponding output file.

Split a PDF at each top-level bookmark

Use the PDF outline to split a document into sections automatically. Each top-level bookmark becomes its own output file.

rust
use pdfluent::PdfDocument;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = PdfDocument::open("manual.pdf")?;

    let results = doc
        .split_by_top_level_bookmarks()
        .write_files("{title}.pdf")?;

    for r in &results {
        println!("{} -> {} pages", r.filename, r.page_count);
    }
    Ok(())
}
  1. Open the PDF and inspect its outline

    Check that the document has top-level bookmarks before splitting. PDFluent exposes the full outline tree via outline().

    rust
    use pdfluent::PdfDocument;
    
    let doc = PdfDocument::open("manual.pdf")?;
    let outline = doc.outline()?;
    
    println!("Top-level sections: {}", outline.len());
    for item in &outline {
        println!("  {} -> page {}", item.title, item.destination_page);
    }
  2. Split at top-level bookmarks

    split_by_top_level_bookmarks() computes the page range for each bookmark automatically. The range ends where the next bookmark starts.

    rust
    let splitter = doc.split_by_top_level_bookmarks();
  3. Write output files using the bookmark title as filename

    Use {title} in the pattern to name each file after its bookmark. PDFluent sanitises the title to produce a valid filename.

    rust
    splitter.write_files("{title}.pdf")?;
    // "Introduction.pdf", "Chapter 1.pdf", "Chapter 2.pdf", ...
  4. Split by a specific outline depth

    To split at second-level bookmarks instead of the top level, set the depth parameter.

    rust
    use pdfluent::SplitDepth;
    
    doc.split_by_bookmarks(SplitDepth::Level(2))
        .write_files("section_{n}.pdf")?;
  5. Collect results for further processing

    If you need the split data in memory, use to_vec() to get a Vec of SplitSegment values without writing to disk.

    rust
    let segments = doc
        .split_by_top_level_bookmarks()
        .to_vec()?;
    
    for seg in segments {
        println!("{}: {} bytes", seg.title, seg.data.len());
        // upload seg.data to S3, etc.
    }
  • If the last bookmark has no following bookmark, its range extends to the last page of the document.
  • Bookmarks that point to the same page as the next bookmark produce a zero-page segment. PDFluent skips these by default.
  • Child bookmarks are included in the parent segment, not extracted separately, unless you use SplitDepth::Level(n).

Extract specific pages from a PDF in Rust

Pick individual pages or non-contiguous sets and write them to a new PDF. Works with page numbers, page labels, or a custom predicate.

rust
use pdfluent::PdfDocument;

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("report.pdf")?;
    let subset = doc.extract_pages(0..3)?;
    subset.save("first-three.pdf")?;
    Ok(())
}
  1. Open the source PDF

    Open the document you want to extract pages from. Page count is available immediately after opening.

    rust
    use pdfluent::PdfDocument;
    
    let doc = PdfDocument::open("source.pdf")?;
    println!("{} pages total", doc.page_count());
  2. Extract by page numbers

    Pass a slice of 1-based page numbers. Pages appear in the output in the order given, so you can reorder them freely.

    rust
    // Extract a contiguous page range (0-based, end-exclusive): pages 2-4
    let extracted = doc.extract_pages(1..4)?;
    extracted.save("pages_2_to_4.pdf")?;
  3. Extract by page label

    If the PDF uses custom page labels such as "i", "ii", "A-1", pass label strings instead of integers.

    rust
    // Split into single-page documents
    for (i, page) in doc.split_pages()?.into_iter().enumerate() {
        page.save(format!("page_{}.pdf", i + 1))?;
    }
  4. Extract with a filter predicate

    Use extract_pages_where() to filter programmatically. The closure receives a PageInfo struct with page number, label, width, height, and rotation.

    rust
    // Extract a range and read the bytes (e.g. to send over HTTP)
    let bytes = doc.extract_pages(0..3)?.to_bytes()?;
    println!("Extracted PDF is {} bytes", bytes.len());
  5. Save or return as bytes

    Call save() to write to disk, or to_bytes() to get the PDF data as a Vec<u8> for streaming or further processing.

    rust
    // Extract the final two pages by range and save
    let n = doc.page_count();
    doc.extract_pages(n.saturating_sub(2)..n)?.save("last_pages.pdf")?;
  • Pages are extracted in the order of the input slice. Duplicates are allowed and produce repeated pages.
  • Annotations and form fields on extracted pages are included.
  • If a page number is out of range, extract_pages returns an Err immediately before writing any output.

Drop pages from a PDF in Rust

Build a new document from the pages you keep. 1.0 removes a page by extracting the runs around it and merging them.

rust
use pdfluent::{PdfDocument, PdfMerger};

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("report.pdf")?;
    let total = doc.page_count();

    // Keep everything except page 5. Page numbers are 1-based and both ends of
    // the range are included -- and a range must not be empty, so the run after
    // page 5 is only added when there is one.
    let mut merger = PdfMerger::new().add(doc.extract_pages(1..=4)?);
    if total > 5 {
        merger = merger.add(doc.extract_pages(6..=total)?);
    }
    let trimmed = merger.build()?;

    trimmed.save("report_trimmed.pdf")?;
    println!("Remaining pages: {}", trimmed.page_count());
    Ok(())
}
  1. Open the PDF and count its pages

    The count is what the runs you keep are built from, and the source document is never modified.

    rust
    use pdfluent::{PdfDocument, PdfMerger};
    
    let doc = PdfDocument::open("draft.pdf")?;
    let total = doc.page_count();
    println!("Before: {} pages", total);
  2. Drop a page at the edge

    Dropping the first or the last page leaves a single run, so no merge is needed. extract_pages returns a new document.

    rust
    // Without the first page.
    let without_first = doc.extract_pages(2..=total)?;
    
    // Without the last page.
    let without_last = doc.extract_pages(1..=total - 1)?;
    without_last.save("draft_without_last.pdf")?;
  3. Drop a run in the middle

    Extract what is on either side of the run and concatenate the two with PdfMerger. Guard the second range the same way: a run that would start past the last page is an error, not an empty document.

    rust
    // Drop pages 3 to 6.
    let mut merger = PdfMerger::new().add(doc.extract_pages(1..=2)?);
    if total > 6 {
        merger = merger.add(doc.extract_pages(7..=total)?);
    }
    let trimmed = merger.build()?;
    println!("After the range: {} pages", trimmed.page_count());
  4. Drop pages that are not next to each other, and save

    Walk the page numbers you are dropping in ascending order and add the run that sits before each of them, then whatever is left at the end.

    rust
    // Drop pages 2, 5 and 8.
    let drop = [2usize, 5, 8];
    let mut merger = PdfMerger::new();
    let mut start = 1usize;
    
    for page in drop {
        if page > start {
            merger = merger.add(doc.extract_pages(start..=page - 1)?);
        }
        start = page + 1;
    }
    if start <= total {
        merger = merger.add(doc.extract_pages(start..=total)?);
    }
    
    merger.build()?.save("draft_cleaned.pdf")?;
  • PDFluent 1.0 has no in-place page removal. Removing a page is expressed as keeping the rest: extract the runs you want and concatenate them.
  • Page numbers are 1-based and ranges include both ends, so extract_pages(1..=4) is the first four pages.
  • An empty range is an error rather than an empty document, and so is a page number past the end. That is why the edge cases above are one run and not two.
  • Nothing is written until you call save on the document the merger built. The document you opened is unchanged on disk and in memory.
  • Extract and merge rebuild the page tree, and neither carries an outline or an internal GoTo destination across correctly: bookmarks and links into the document can end up pointing at the wrong page or at nothing. For a document whose navigation matters, check the result before shipping the pipeline.

Insert blank or existing pages into a PDF in Rust

Insert a blank page, a page from another PDF, or multiple pages at any position in an existing document.

rust
use pdfluent::{PdfDocument, PageSize};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut doc = PdfDocument::open("report.pdf")?;

    // Insert a blank A4 page after page 2 (at index 2)
    doc.insert_blank_page(2, PageSize::A4)?;

    doc.save("report_with_divider.pdf")?;
    println!("Page inserted. New count: {}", doc.page_count());
    Ok(())
}
  1. Open the target PDF

    Load the document you want to modify.

    rust
    use pdfluent::PdfDocument;
    
    let mut doc = PdfDocument::open("annual_report.pdf")?;
    println!("Current page count: {}", doc.page_count());
  2. Insert a blank page at a position

    insert_blank_page(index, size) inserts a new empty page before the page at that index. Use index = page_count() to append at the end.

    rust
    use pdfluent::PageSize;
    
    // Insert blank A4 page before the third page (index 2)
    doc.insert_blank_page(2, PageSize::A4)?;
    
    // Append a blank Letter page at the end
    doc.insert_blank_page(doc.page_count(), PageSize::Letter)?;
  3. Insert pages from another PDF

    Open a second document and copy pages from it into the target at a specific position.

    rust
    let source = PdfDocument::open("cover_page.pdf")?;
    
    // Insert the first page of source before page 0 (prepend)
    doc.insert_page_from(0, &source, 0)?;
    
    // Insert pages 1-3 from source after the current last page
    for i in 1..=3 {
        let pos = doc.page_count();
        doc.insert_page_from(pos, &source, i)?;
    }
  4. Save the modified document

    Write the result to disk.

    rust
    doc.save("report_expanded.pdf")?;
    println!("New page count: {}", doc.page_count());
  • Insertion indices are zero-based. Inserting at index 0 prepends the page before the current first page.
  • insert_page_from() copies the page content, resources, and annotations from the source document.
  • Bookmarks from the source document are not automatically carried over. Add them manually if needed.
  • PageSize::Custom(width, height) accepts dimensions in points for non-standard page sizes.

Change the page order of a PDF in Rust

Assemble a document in the page order you want by merging one-page extracts. Reverse, interleave, or move a single page.

rust
use pdfluent::{PdfDocument, PdfMerger};

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("input.pdf")?;
    let count = doc.page_count();

    // The page numbers, in the order you want them. Move the last to the front.
    let mut order: Vec<usize> = (1..=count).collect();
    order.rotate_right(1);

    let mut merger = PdfMerger::new();
    for page in &order {
        merger = merger.add(doc.extract_pages(*page..=*page)?);
    }

    merger.build()?.save("reordered.pdf")?;
    Ok(())
}
  1. Open the PDF

    Load the document and read the page count; the order you build is a permutation of those page numbers.

    rust
    use pdfluent::{PdfDocument, PdfMerger};
    
    let doc = PdfDocument::open("input.pdf")?;
    let count = doc.page_count();
  2. Build the new order

    1-based page numbers in the order you want to read them. Every page you want in the result has to appear in it.

    rust
    // Reverse the document.
    let order = (1..=count).rev().collect::<Vec<usize>>();
  3. Assemble the document in that order and save it

    Extract each page on its own and concatenate them. This is what 1.0 offers; there is no call that rewrites the page tree in place. The source file is untouched.

    rust
    use pdfluent::PdfMerger;
    
    let mut merger = PdfMerger::new();
    for page in &order {
        merger = merger.add(doc.extract_pages(*page..=*page)?);
    }
    
    merger.build()?.save("reordered.pdf")?;
  4. Move one page to another position

    Change the order vector rather than the document: take the page number out and put it back where you want it.

    rust
    let mut order = (1..=count).collect::<Vec<usize>>();
    // Move page 5 to second position.
    let page = order.remove(4);
    order.insert(1, page);
  • PDFluent 1.0 has no reorder_pages. Reordering is a merge of one-page extracts, in the order you choose.
  • OUTLINES AND INTERNAL LINKS DO NOT SURVIVE THIS. Each extract keeps the catalog of the document it was cut from and the merge imports the later pages under new object ids, so bookmarks and GoTo destinations end up pointing at pages that are no longer where they were. For a document whose navigation matters, 1.0 has no shape that reorders it -- that is what the page-order call on the backlog is for.
  • Page numbers are 1-based, which is also how they are printed on the page. extract_pages(3..=3) is the third page.
  • The order vector does not have to be a permutation of the whole document: leave a page number out and it is not in the result, put one in twice and it is in there twice. That is the one thing this shape does that an in-place reorder cannot.
  • One extract per page means one clone of the document per page, so for a document of hundreds of pages this costs real time. Sort out the pages you actually need first.

Rotate one or all pages of a PDF in Rust

Set page rotation to 90, 180, or 270 degrees. Rotate a single page, a range, or the whole document in a few lines of Rust.

rust
use pdfluent::{PdfDocument, Rotation};

fn main() -> pdfluent::Result<()> {
    let mut doc = PdfDocument::open("scan.pdf")?;
    for page in 1..=doc.page_count() {
        doc.rotate_page(page, Rotation::Clockwise90)?;
    }
    doc.save("rotated.pdf")?;
    Ok(())
}
  1. Open the PDF

    Load the file you want to rotate.

    rust
    use pdfluent::PdfDocument;
    
    let mut doc = PdfDocument::open("scanned_contract.pdf")?;
  2. Rotate a single page

    Call doc.rotate_page(page, rotation) with a 1-based page number. The Rotation enum has Clockwise90, Clockwise180 and Clockwise270; there is no variant for 0, because rotating by nothing is not an operation.

    rust
    use pdfluent::Rotation;
    
    // Rotate page 3 90 degrees clockwise. Pages are 1-based.
    doc.rotate_page(3, Rotation::Clockwise90)?;
    
    // Rotate page 4 upside-down
    doc.rotate_page(4, Rotation::Clockwise180)?;
  3. Rotate a page range

    Loop over a range of page indices to rotate a contiguous section.

    rust
    // Rotate pages 5 through 8
    for i in 5..=8 {
        doc.rotate_page(i, Rotation::Clockwise270)?;
    }
  4. Rotate all pages and save

    Iterate over all pages and apply the same rotation, then write the output.

    rust
    let count = doc.page_count();
    for i in 1..=count {
        doc.rotate_page(i, Rotation::Clockwise90)?;
    }
    
    doc.save("document_rotated.pdf")?;
    println!("Rotated {} pages", count);
  • rotate_page() sets the /Rotate entry in the page dictionary. This is the standard PDF rotation mechanism.
  • Rotation is additive if you call it multiple times on the same page. Use Rotation::None to reset to zero degrees.
  • Page content streams are not modified. The rotation is stored as metadata and applied by the PDF viewer.
  • To get the current rotation before changing it, call page.rotation() which returns a Rotation value.

Get the page count of a PDF in Rust

Read the total number of pages from a PDF file. PDFluent reads the page count from the document catalog without parsing every page's content stream.

rust
use pdfluent::PdfDocument;

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("file.pdf")?;
    println!("{} pages", doc.page_count());
    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 and call page_count()

    page_count() reads the /Count entry in the PDF page tree. It doesn't parse page content streams; for a well-formed PDF the call is effectively O(1).

    rust
    use pdfluent::prelude::*;
    
    let doc = PdfDocument::open("invoice.pdf")?;
    let count = doc.page_count();
    println!("This PDF has {} page(s)", count);
  2. Get page count from bytes

    If the PDF is already in memory (from a network response or database blob), use from_bytes() instead of open().

    rust
    let pdf_bytes: Vec<u8> = fetch_pdf_from_database()?;
    let doc = PdfDocument::from_bytes(&pdf_bytes)?;
    println!("Pages: {}", doc.page_count());
  3. Walk the page tree when /Count is suspect

    Some malformed PDFs have an incorrect /Count entry. If you need a physically-verified count, iterate doc.pages() and count as you go — each iteration step resolves the next page from the tree.

    rust
    let doc = PdfDocument::open("maybe_corrupt.pdf")?;
    let physical_count = doc.pages().count();
    let declared_count = doc.page_count();
    assert_eq!(physical_count, declared_count, "declared /Count != physical pages");
  4. Batch page counts for a directory of PDFs

    Loop over files and collect counts. PdfDocument opens in-memory, so drop each doc between iterations to keep allocations bounded.

    rust
    use pdfluent::prelude::*;
    use std::fs;
    
    for entry in fs::read_dir("./invoices")? {
        let path = entry?.path();
        if path.extension().map(|e| e == "pdf").unwrap_or(false) {
            match PdfDocument::open(&path) {
                Ok(doc) => println!("{}: {} pages", path.display(), doc.page_count()),
                Err(e) => eprintln!("{}: error - {}", path.display(), e),
            }
        }
    }
  • page_count() returns a usize. A valid PDF always has at least 1 page.
  • Encrypted PDFs require decryption before the page count is accessible. Open them with PdfDocument::open_with(path, OpenOptions::new().with_password("...")).
  • If you need a physically-accurate count against a potentially-malformed /Count entry, walk doc.pages().count() as shown in step 4.
  • Page indexing in the 1.0 SDK is 1-based throughout (RFC 0001 §1).

Get page width, height, and media box in Rust

Read the MediaBox, CropBox, and BleedBox from any PDF page. Convert between points and millimetres or inches for printing and layout workflows.

rust
use pdfluent::PdfDocument;

fn main() -> pdfluent::Result<()> {
    let doc = PdfDocument::open("file.pdf")?;
    for i in 1..=doc.page_count() {
        let (w, h) = doc.page(i)?.dimensions();
        println!("page {}: {} x {} pt", i, w, h);
    }
    Ok(())
}
  1. Open the PDF and access pages

    Open the document and iterate or index pages directly.

    rust
    use pdfluent::PdfDocument;
    
    let doc = PdfDocument::open("blueprint.pdf")?;
    let page = doc.page(1)?; // first page
  2. Read the MediaBox

    The MediaBox defines the full physical extent of the page in PDF user units (points). 1 point = 1/72 inch.

    rust
    let (width, height) = page.dimensions();
    println!("Width:  {:.2} pt", width);
    println!("Height: {:.2} pt", height);
  3. Read CropBox and BleedBox

    CropBox is the visible page area. BleedBox is for print bleed. Both fall back to the MediaBox if not set.

    rust
    // PDFluent exposes the effective page size via dimensions()
    let (width, height) = page.dimensions();
    println!("Page: {:.1} x {:.1} pt", width, height);
  4. Convert to millimetres and inches

    PDF points convert to mm with factor 25.4/72 and to inches with factor 1/72.

    rust
    fn pt_to_mm(pt: f64) -> f64 { pt * 25.4 / 72.0 }
    fn pt_to_inch(pt: f64) -> f64 { pt / 72.0 }
    
    let (w, h) = page.dimensions();
    println!(
        "Page size: {:.1} x {:.1} mm  ({:.3} x {:.3} in)",
        pt_to_mm(w), pt_to_mm(h), pt_to_inch(w), pt_to_inch(h),
    );
  • PDF units are always points (1/72 inch). There is no concept of DPI at the page dimension level.
  • A standard A4 page is 595 x 842 pt. Letter is 612 x 792 pt.
  • Pages in the same document can have different sizes. Always read dimensions per-page, not once for the document.
  • Rotate() entry affects display orientation. Use page.rotation() to read it and adjust width/height for display purposes.