| name | pdfium-impl-output-formats |
| description | Use when turning a rendered pdfium-render PdfBitmap into a PNG or JPEG file, into an image crate DynamicImage, or into raw pixel bytes, and when the exported colors look wrong with red and blue swapped. Prevents the BGRA-versus-RGBA channel-swap bug and the removed as_bytes method, and prevents image-crate version mismatches. Covers as_image, as_rgba_bytes, as_raw_bytes, bytes_required_for_size, saving via DynamicImage with ImageFormat, and the image_025 feature coupling. Keywords: pdfium-render save PDF as PNG, export PDF page to JPEG, PdfBitmap as_image, as_rgba_bytes, as_raw_bytes, DynamicImage, image crate, ImageFormat, save_with_format, BGRA RGBA channel swap, red and blue swapped, colors wrong, image looks blue, as_bytes removed, how do I export a PDF page as an image
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires pdfium-render 0.8,0.9. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
pdfium-impl-output-formats
Rendering a page produces a PdfBitmap. This skill turns that bitmap into a
deliverable: a PNG or JPEG file, an image crate DynamicImage, or raw pixel
bytes for a downstream pipeline. The one trap that dominates this area is byte
order: a PDFium bitmap is BGRA-family, the image crate expects RGBA, and
mixing them swaps red and blue.
Scope: bitmap-to-output conversion. For producing the PdfBitmap (the
PdfRenderConfig builder, render_with_config) see pdfium-syntax-rendering.
Quick Reference
The output API (0.9.x)
| You want | Call | Returns |
|---|
an image crate image | bitmap.as_image() | Result<DynamicImage, PdfiumError> |
| normalized RGBA8 bytes | bitmap.as_rgba_bytes() | Vec<u8> |
| native-order raw bytes | bitmap.as_raw_bytes() | Vec<u8> |
| pixel width / height | bitmap.width() / bitmap.height() | Pixels |
| the source pixel format | bitmap.format() | Result<PdfBitmapFormat, _> |
| buffer size for a size | PdfBitmap::bytes_required_for_size(w, h) | usize |
The byte-order rule
A native PdfBitmap buffer is BGRA-ordered (PdfBitmapFormat::BGRA, BGRx,
BGR, or Gray). The image crate expects RGBA / RGB order.
- ALWAYS use
as_image() to get a DynamicImage: it performs the correct
channel conversion internally.
- ALWAYS use
as_rgba_bytes() when you need raw bytes for the image crate
or any RGBA consumer: it normalizes to RGBA8 regardless of source format.
- NEVER feed
as_raw_bytes() output to an RGBA consumer: those bytes are in
the bitmap's native BGRA order, so red and blue come out swapped (issue #50).
- Use
as_raw_bytes() only when the consumer expects the exact native format
reported by bitmap.format().
Core rules
- NEVER call
as_bytes(): it was removed in 0.9.0. Use as_raw_bytes() or
as_rgba_bytes().
- ALWAYS keep the
pdfium-render image_* feature matched to the image
crate version in Cargo.toml, or as_image() returns a DynamicImage your
code cannot use.
- ALWAYS convert to RGB with
into_rgb8() before saving JPEG: JPEG has no
alpha channel.
Decision Trees
Which accessor do I use?
What does the consumer expect?
An image file (PNG / JPEG) -> as_image(), then save (see Patterns)
An image crate DynamicImage -> as_image()
Raw RGBA8 bytes (web canvas, GPU,
another RGBA library) -> as_rgba_bytes()
Raw bytes in the exact native
PDFium format -> as_raw_bytes() + check format()
Which file format do I save?
Does the output need transparency or lossless pixels?
YES -> PNG. save_with_format(path, ImageFormat::Png). Alpha is preserved.
NO, it is a photo-like page and size matters
-> JPEG. into_rgb8() first (drops alpha), then
save_with_format(path, ImageFormat::Jpeg).
Patterns
These examples assume bitmap is a PdfBitmap from
page.render_with_config(&config)? (see pdfium-syntax-rendering). Functions
return Result<(), Box<dyn std::error::Error>> because saving mixes
PdfiumError and the image crate's ImageError.
Pattern 1: Save a page as PNG
use pdfium_render::prelude::*;
fn save_png(bitmap: &PdfBitmap, path: &str)
-> Result<(), Box<dyn std::error::Error>> {
bitmap.as_image()?.save_with_format(path, image::ImageFormat::Png)?;
Ok(())
}
as_image() returns a Result<DynamicImage, PdfiumError>. The ? propagates
the error; DynamicImage::save_with_format handles PNG encoding.
Pattern 2: Save a page as JPEG
use pdfium_render::prelude::*;
fn save_jpeg(bitmap: &PdfBitmap, path: &str)
-> Result<(), Box<dyn std::error::Error>> {
bitmap
.as_image()?
.into_rgb8()
.save_with_format(path, image::ImageFormat::Jpeg)?;
Ok(())
}
This is the canonical shape from the pdfium-render export.rs example.
into_rgb8() is required for JPEG; skipping it on an image with alpha is an
error.
Pattern 3: Raw RGBA bytes for a pipeline
use pdfium_render::prelude::*;
fn rgba_pixels(bitmap: &PdfBitmap) -> (Vec<u8>, Pixels, Pixels) {
let bytes = bitmap.as_rgba_bytes();
(bytes, bitmap.width(), bitmap.height())
}
For a web canvas, a GPU texture upload, or another RGBA library, this is the
correct accessor. NEVER substitute as_raw_bytes() here.
Pattern 4: Native bytes and buffer sizing
use pdfium_render::prelude::*;
fn native_buffer(bitmap: &PdfBitmap)
-> Result<(Vec<u8>, PdfBitmapFormat), PdfiumError> {
let format = bitmap.format()?;
let bytes = bitmap.as_raw_bytes();
Ok((bytes, format))
}
fn expected_size(width: Pixels, height: Pixels) -> usize {
PdfBitmap::bytes_required_for_size(width, height)
}
Use as_raw_bytes() only when the consumer is told the exact
PdfBitmapFormat; otherwise the BGRA order will be misread.
Pattern 5: Match the image crate feature
Cargo.toml must keep the image crate version and the pdfium-render
image_* feature in step:
[dependencies]
pdfium-render = { version = "0.9", features = ["image_025"] }
image = "0.25"
image_025 pairs with image 0.25, image_024 with 0.24, image_023 with
0.23. image_latest tracks the newest. A mismatch makes as_image() return a
DynamicImage from a different image version than the one your code imports,
which is a distinct, incompatible type.
Version Notes: 0.8.x vs 0.9.x
PdfBitmap::as_bytes() was deprecated in 0.8.16 and REMOVED in 0.9.0.
Replace it with as_raw_bytes() (native order) or as_rgba_bytes()
(normalized RGBA8).
as_rgba_bytes() was added in 0.8.16. On older versions only as_raw_bytes
and the now-removed as_bytes existed.
- The
image_023 / image_024 / image_025 feature family was introduced in
0.8.26. Before that the image crate version was fixed by the crate.
- 0.9.2 changed
PdfBitmap::empty() and from_bytes(): the bindings
argument was dropped, from_bytes() became safe, and an unsafe
from_bytes_unchecked() was added. On 0.9.1 and earlier from_bytes() is
unsafe and both take a bindings argument.
Anti-Patterns (summary)
| Anti-pattern | Why it fails | Fix |
|---|
as_raw_bytes() into an RGBA consumer | native bytes are BGRA: red and blue swap | as_rgba_bytes() or as_image() |
as_bytes() | removed in 0.9.0 | as_raw_bytes() / as_rgba_bytes() |
saving an RGBA image as JPEG without into_rgb8() | JPEG has no alpha channel | into_rgb8() before save_with_format |
image crate version not matching the image_* feature | as_image() yields an incompatible DynamicImage type | align Cargo.toml |
Full failure transcripts and fixes are in references/anti-patterns.md.
Reference Files
references/methods.md: exact verified signatures for PdfBitmap,
PdfBitmapFormat, and the image crate methods used here.
references/examples.md: complete, verified Rust programs for PNG export,
JPEG export, raw-byte handoff, and feature configuration.
references/anti-patterns.md: each output failure with the cause and the
corrected code.
Related Skills
pdfium-syntax-rendering: producing the PdfBitmap with PdfRenderConfig
and render_with_config.
pdfium-errors-runtime: the PdfiumError variants, including the
byte-order bug class.
pdfium-core-coordinates: Pixels versus PdfPoints and rendered raster
dimensions.