Developer Resources

Cracking the Code: Working with PDF Images in Modern C++

#Adobe PDF Library #Modern C++ #PDF Images #Code Samples #Tutorial
Published March 1, 2023 Updated June 19, 2026

Images in a PDF come in two fundamentally different forms: the rendered page itself, and the raster images embedded inside the page's content. Working with either one programmatically calls for a different approach, and the Adobe PDF Library's Modern C++ API provides clean, well-structured tools for both.

This tutorial walks through five image workflows you'll run into when building PDF processing systems: converting PDF pages to image files, extracting embedded images from page content, importing external images into a PDF, resampling images to reduce file size, and embedding ICC color profiles for color-managed output. All samples referenced here live in the Images folder of the Modern C++ samples repository on GitHub, and the API itself follows standard C++17 conventions throughout, including RAII initialization, range-based iteration, and standard exception handling.

TL;DR
  • Render PDF pages to TIFF, PNG, JPEG, BMP, or GIF files using PageImageParams and Page::get_image.
  • Recursively traverse page content with try_as to extract embedded raster images at native or resampled resolution.
  • Build new image-based PDFs from scratch by positioning an Image on a generated page.
  • Cut file size by resampling embedded images and saving with SaveFlags::CollectGarbage.
  • Produce color-managed CMYK or RGB TIFF output by attaching an ICC profile and rendering intent.

Who This Tutorial Is For

This is written for C++ developers building systems that process PDF images at scale. That includes document conversion pipelines that produce image derivatives of PDF pages, digital asset management systems that extract and catalog embedded images, print production workflows that require color-managed TIFF output, web applications that generate PDF thumbnails or previews, and archival systems that need to optimize embedded image resolution before storage.

Converting PDF Pages to Image Files

Use the DocToImages sample when you need to render PDF pages as standalone image files. Common applications include generating document thumbnails for a web interface, producing page previews for a document management system, creating print-quality rasters for review workflows, and converting PDF pages to TIFF for archival systems that don't accept PDF directly.

DocToImages is a full-featured command-line utility that accepts options for format, resolution, page range, compression, and rendering behavior. The core of the conversion is the PageImageParams structure combined with a call to Page::get_image. First, set up the rendering parameters:

cpp
PageImageParams pip;
pip.set_page_draw_flags(DrawFlags::UseAnnotFaces | DrawFlags::DoLazyErase);
pip.set_smoothing(options.smoothing);
pip.set_horizontal_resolution(options.hres);  // e.g., 300.0
pip.set_vertical_resolution(options.vres);

Then render each page and save it:

cpp
Page pg = doc.get_page(page_index);
Rect page_rect = pg.get_crop_box();
Image page_image = pg.get_image(page_rect, pip);

ImageSaveParams isp;
isp.set_compression(CompressionCode::LZW);  // For TIFF
page_image.save(output_path, ImageType::TIFF, isp);

The page_rect determines which part of the page gets rendered. get_crop_box() is the most common choice and returns the visible page area. get_media_box() returns the full physical page, get_trim_box() returns the trim-to area used in print production, and get_bleed_box() returns the pre-press bleed margins.

Supported Output Formats

FormatBest For
TIFF (LZW)Print-quality output
PNGWeb previews where lossless quality matters
JPEGThumbnails and display images where file size is the priority
BMP / GIFAlso supported by DocToImages

The sample also supports multipage TIFF output by collecting page images into an ImageCollection and saving once at the end.

Key Parameters

Resolution defaults to 300 DPI, which suits print-quality output. For web thumbnails, dropping to 72 or 96 DPI reduces file size significantly. The smoothing option (SmoothFlags::Text, SmoothFlags::LineArt, or the combined flag) applies anti-aliasing and noticeably improves quality at low resolutions. The asprinted flag adds DrawFlags::IsPrinting to the draw flags, which activates print-specific rendering behavior in the PDF content.

Expected output: one image file per page (or one multipage TIFF if the multi option is set), named using the base document name with a page counter suffix. A five-page document converted to PNG at 150 DPI produces five PNG files, one per page.

Sample: doc_to_images.cpp on GitHub.

Extracting Embedded Images From a PDF

Use ImageExtraction when you need to retrieve the raster images embedded within a PDF's content, as distinct from rendering the page itself as an image. Pulling product photos from a catalog PDF, extracting diagrams from a technical document, and harvesting images for a digital asset management system are typical use cases. Note that vector graphics are not exported by this sample — only raster images.

The sample traverses the page content tree using try_as to identify Image elements. When one is found, it's saved directly at its native resolution and also saved at a resampled 500 DPI version. The traversal is recursive, since images can be nested inside Container, Group, and Form elements:

cpp
static void extract_images(Content& content) {
    for (int i = 0; i < content.get_num_elements(); ++i) {
        auto elem = content.get_element(i);

        if (auto* img = elem->try_as<Image>()) {
            img->save("ImageExtraction-extract-out" + std::to_string(next_index) + ".png",
                      ImageType::PNG);

            Image resampled = img->change_resolution(500);
            resampled.save("ImageExtraction-extract-Resolution-500-out" +
                          std::to_string(next_index) + ".png", ImageType::PNG);
            ++next_index;
        }
        else if (auto* grp = elem->try_as<Group>()) {
            auto sub = grp->get_content();
            if (sub) extract_images(*sub);
        }
        // Similarly for Container and Form types
    }
}

The try_as pattern is idiomatic Modern C++ and avoids the unsafe casts required by the classic API. If the element isn't the requested type, try_as returns nullptr and the branch is skipped cleanly. The change_resolution(500) call also shows that extracted images can be resampled immediately before saving — useful when the source PDF contains very high-resolution images that need normalizing to a target DPI for downstream processing.

Expected output: two PNG files per image found in the document — one at the original embedded resolution and one at 500 DPI — numbered sequentially starting from 0.

Sample: image_extraction.cpp on GitHub.

Importing an External Image Into a PDF

Use ImageImport when you need to add an external image file to a PDF document. This is useful for programmatically assembling PDFs from image assets, stamping a logo or signature image onto existing documents, building image-heavy PDFs from a collection of raster files, and creating new PDF documents where the content is primarily or entirely image-based.

The sample creates a new Document, loads an image from a file path, creates a page sized to fit the image with a one-inch margin on all sides, positions the image on the page, and saves the result. Image placement uses PDF points, where 72 points equal one inch:

cpp
Document doc;
Image new_image(input_path, doc);  // Associate the image with the document

// Create a page 1 inch larger than the image on all sides
Matrix img_matrix = new_image.get_matrix();
Rect page_rect{0, 0, img_matrix.a + 144, img_matrix.d + 144};
Page doc_page = doc.create_page(Document::before_first_page, page_rect);

// Center the image (72 points = 1 inch margin)
new_image.translate(72, 72);

Content content = doc_page.get_content();
content.add_element(new_image);
doc_page.update_content();

doc.save(SaveFlags::Full, output_path);

get_matrix() returns the image's transformation matrix. The a and d fields of a PDF matrix are the horizontal and vertical scaling factors, which for an unrotated image correspond to the image's width and height in points. Adding 144 (two inches at 72 points per inch) to each dimension creates a one-inch border on each side.

Don't Skip update_content()
update_content() must be called after modifying page content, or the changes won't be written to the document structure. Forgetting this step is a common source of empty output pages.

Expected output: a new PDF file named ImageImport-out1.pdf containing a single page with the imported image centered on a white background with one-inch margins.

Sample: image_import.cpp on GitHub.

Resampling Images to Reduce File Size

Use ImageResampling when a PDF contains embedded images at a resolution higher than needed for the intended output. A PDF prepared for print may contain images at 600 DPI or higher; if that PDF is being archived or delivered for screen viewing, those high-resolution images are unnecessary overhead. Resampling reduces image resolution and file size while keeping the document visually correct at the target viewing resolution.

The sample traverses the page content tree — using the same recursive pattern as ImageExtraction — finds each Image element, calls change_resolution to produce a resampled version at 400 DPI, and replaces the original in the content tree. The replacement uses add_element followed by remove_element to swap positions:

cpp
if (auto* img_ptr = elem->try_as<Image>()) {
    Image new_img = img_ptr->change_resolution(400);

    content.add_element(new_img, i - 1);   // Insert resampled at position i
    content.remove_element(i + 1);          // Remove original from i+1
    ++num_replaced;
}

After all images on a page are replaced, update_content() is called on the page to commit the changes. The final save uses SaveFlags::CollectGarbage in addition to SaveFlags::Full, which removes the now-unreferenced original image data from the output file — and is what actually produces the smaller file size:

cpp
doc.save(SaveFlags::Full | SaveFlags::CollectGarbage, output_path);
CollectGarbage Is Required
Omitting SaveFlags::CollectGarbage produces an output file that contains both the original and resampled image data, defeating the purpose of resampling. Always include it when the goal is file size reduction.

Expected output: a new PDF named ImageResampling-out.pdf with all raster images resampled to 400 DPI. Visual appearance is unchanged at normal viewing zoom levels; file size reduction varies with the original image resolutions but can be substantial for print-optimized source documents.

Sample: image_resampling.cpp on GitHub.

Embedding ICC Color Profiles for Color-Managed Output

Use ImageEmbedICCProfile when rendering PDF pages to image files for color-critical applications such as print production, color proofing, or archival systems where accurate color reproduction is required. An ICC profile defines the color characteristics of the output device — rendering with a matching profile ensures colors in the output image match what the device will actually produce.

The sample renders each page of a PDF to a CMYK TIFF using a specified ICC profile. Four output files are generated per page, one for each of the four standard rendering intents defined in the ICC specification, which controls how out-of-gamut colors in the PDF are mapped to the output color space:

cpp
PageImageParams pip;
pip.set_icc_profile_custom_path(profile_path);
pip.set_image_color_space(NamedColorSpace::device_cmyk());

pip.set_render_intent(RenderIntent::Saturation);
Image img_sat = pg.get_image(pg.get_crop_box(), pip);
img_sat.save("ImageEmbedICCProfile-out_sat0.tif", ImageType::TIFF, isp);

pip.set_render_intent(RenderIntent::Perceptual);
Image img_per = pg.get_image(pg.get_crop_box(), pip);
img_per.save("ImageEmbedICCProfile-out_per0.tif", ImageType::TIFF, isp);

// AbsColorimetric and RelColorimetric variants follow the same pattern

Understanding Rendering Intents

IntentUse Case
PerceptualPhotographs and continuous-tone images; compresses the source gamut to fit the output gamut while preserving relative color relationships
RelColorimetricMaps in-gamut colors exactly and clips out-of-gamut colors
AbsColorimetricSame as RelColorimetric, but also adjusts for the output medium's white point — matters for proofing
SaturationMaximizes color vividness; used for business graphics and charts rather than photographic content

The sample uses a CMYK ICC profile (Probev1_ICCv2.icc), appropriate for offset printing workflows. Substituting an sRGB or AdobeRGB profile with DeviceRGB color space produces RGB TIFF output suitable for digital publishing.

Expected output: four TIFF files per page, named with suffixes _sat, _abs, _per, and _rel to identify the rendering intent used. All four are CMYK TIFFs with LZW compression, suitable for color comparison and print proofing.

Sample: image_embed_icc_profile.cpp on GitHub.

Additional Image Capabilities

The Modern C++ Images folder includes a few additional samples not covered above. DrawToBitmap renders pages to in-memory bitmap buffers rather than files, which is useful for server-side thumbnail generation. DrawSeparations and GetSeparatedImages handle color separation workflows for commercial print. OutputPreview simulates how a page will look with specific colorants turned off, a standard pre-press proofing operation. All are available in the Modern C++ samples repository.

Next Steps

Whether you're rendering pages, extracting embedded images, or building color-managed output, the Modern C++ API gives you a consistent, type-safe way to work with PDF images at scale.