> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/get-started/samples/showcase-demo-extract-images-from-pdf.md).

# Extract Images from PDF Showcase Demo Code Sample

Add PDF image extraction capability to the viewer. This code is related to the showcase demo for PDF image extraction.

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="/web/get-started/readme.md" class="button primary">Web SDK</a><a href="https://showcase.apryse.com/extract-images-from-pdf" class="button primary">Live demo</a>
{% endhint %}

Easily add PDF image extraction capability to the viewer. Download each image separately or all images in a compressed ZIP file.

This demo allows you to:

* Load a PDF with annotations and images
* Edit the PDF
* Extract the images contained in the file

**Implementation steps** To add image extraction capability to WebViewer:

Step 1: Choose your [preferred web stack](/web/get-started/readme.md) Step 2: Add the ES6 JavaScript sample code provided in this guide

Once you generate your license key, it will automatically be included in your sample code below.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="compact" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// ES6 Compliant Syntax
// GitHub Copilot v1.0 - GPT-4o model - July 24, 2025
// File: index.js

import WebViewer from '@pdftron/webviewer';
import JSZip from 'jszip';
import saveAs from 'file-saver';

const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_WEBVIEWER_LICENSE_KEY"</code>';

// extracted images array
let extractedImages = [];

// Customize the WebViewer UI
const customizeUI = (instance) => {
  const { UI } = instance;

  // Set the toolbar group to the annotate tools
  UI.setToolbarGroup('toolbarGroup-Annotate');

  // Extract images button
  const extractImagesButton = new UI.Components.CustomButton({
    dataElement: 'extractImagesButton',
    className: 'custom-button-class',
    label: 'Extract Images',
    onClick: () => extractImages(instance), // extract images from the PDF
    style: {
      padding: '10px 10px',
      backgroundColor: 'blue',
      color: 'white',
    }
  });

  const defaultHeader = UI.getModularHeader('default-top-header');
  defaultHeader.setItems([...defaultHeader.items, extractImagesButton]);
};

// Extract images by traversing the display list for
// every page. With this approach it is possible to obtain
// image positioning information and DPI.
const extractImages = async (instance) => {
  // Clear previously extracted images
  extractedImages = [];

  // PDFNet is only available with full API enabled
  const { PDFNet, documentViewer } = instance.Core;
  let pageLimit = 5;
  try {
    // Start the full workers
    PDFNet.initialize().then(async () => {
      const document = documentViewer.getDocument();
      const doc = await document.getPDFDoc();
      doc.initSecurityHandler();

      const reader = await PDFNet.ElementReader.create();
      const itr = await doc.getPageIterator(1);

      // Read every page
      for (itr; (await itr.hasNext()) &#x26;&#x26; pageLimit > 0; await itr.next()) {
        const page = await itr.current();
        reader.beginOnPage(page);
        await extractImagesFromPage(reader, PDFNet);
        reader.end();
        pageLimit--;
      }

      extractedImagesDialogBox();
    });

  } catch (e) {
    console.error('❌ Failed to extract images:', e);
  }
}

// Recursive function to extract images from a page
const extractImagesFromPage = async (reader, PDFNet) => {
  let element;
  while ((element = await reader.next()) !== null) {
    switch (await element.getType()) {
      case PDFNet.Element.Type.e_image:
      case PDFNet.Element.Type.e_inline_image:
        if ((await element.getType()) == PDFNet.Element.Type.e_image) {
          const image = await PDFNet.Image.createFromObj(await element.getXObject());
          const filter = await PDFNet.Filter.createMemoryFilter(65536, false);
          const writer = await PDFNet.FilterWriter.create(filter);

          await image.exportAsPngFromStream(writer);
          await writer.flush();
          await filter.memoryFilterSetAsInputFilter();

          const reader = await PDFNet.FilterReader.create(filter);
          const pngBuffer = await reader.readAllIntoBuffer();

          const file = new Blob([pngBuffer], { type: 'image/png' });
          extractedImages.push(file);
        }
        break;
      case PDFNet.Element.Type.e_form: // Process form XObjects
        reader.formBegin();
        await extractImagesFromPage(reader, PDFNet);
        reader.end();
        break;
    }
  }
};

// Dialog box for displaying extracted images
const extractedImagesDialogBox = async () => {
  // Create overlay
  const overlay = document.createElement('div');
  overlay.style.position = 'fixed';
  overlay.style.top = '0';
  overlay.style.left = '0';
  overlay.style.width = '100%';
  overlay.style.height = '100%';
  overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
  overlay.style.zIndex = '999';
  overlay.style.display = 'flex';
  overlay.style.justifyContent = 'center';
  overlay.style.alignItems = 'center';

  // Create dialog box
  const dialogBox = document.createElement('div');
  dialogBox.style.backgroundColor = 'white';
  dialogBox.style.padding = '20px';
  dialogBox.style.border = '2px solid #ccc';
  dialogBox.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)';
  dialogBox.style.width = '300px';
  dialogBox.style.textAlign = 'center';
  dialogBox.style.borderRadius = '8px';

  // Message
  const message = document.createElement('p');
  message.style.fontFamily = 'Arial';
  message.style.fontWeight = 'bold';
  message.textContent = 'Extracted Images';
  dialogBox.appendChild(message);

  // Note
  const note = document.createElement('p');
  note.style.marginTop = "20px";
  note.style.fontFamily = 'Arial';
  note.textContent = 'For demo purposes, only the first 5 pages of the PDF will be processed.';
  dialogBox.appendChild(note);

  // Listbox (select element)
  const listBox = document.createElement('select');
  listBox.size = 6;
  listBox.id = 'listBox';
  listBox.style.marginTop = "20px";
  listBox.style.alignItems = 'center';
  listBox.style.border = '2px solid blue';
  listBox.style.borderRadius = '4px';

  // Add images to the listbox dynamically
  extractedImages.forEach((image, index) => {
    const imageFileName = `image${index + 1}.png`;
    const item = document.createElement('option');
    item.value = index;
    item.textContent = `Download ${imageFileName}`;
    item.style.color = 'blue';
    item.style.cursor = 'pointer';
    item.onclick = () => saveAs(image, imageFileName); // Download the image when clicked
    listBox.appendChild(item);
  });
  dialogBox.appendChild(listBox);

  // Download all images button
  const downloadButton = document.createElement('button');
  downloadButton.textContent = 'Download All Images (.zip)';
  downloadButton.style.marginTop = "20px";
  downloadButton.style.backgroundColor = 'blue';
  downloadButton.style.color = 'white';
  downloadButton.style.border = '1px solid blue';
  downloadButton.style.borderRadius = '4px';
  downloadButton.style.cursor = 'pointer';
  downloadButton.onclick = () => downloadAllImagesZip();
  dialogBox.appendChild(downloadButton);

  // Close button
  const closeButton = document.createElement('button');
  closeButton.textContent = 'Close';
  closeButton.style.marginLeft = '10px';
  closeButton.style.color = 'blue';
  closeButton.style.border = '1px solid blue';
  closeButton.style.borderColor = 'blue';
  closeButton.style.borderRadius = '4px';
  closeButton.style.cursor = 'pointer';
  closeButton.onclick = () => document.body.removeChild(overlay);
  dialogBox.appendChild(closeButton);

  // Append dialog to overlay and overlay to body
  overlay.appendChild(dialogBox);
  document.body.appendChild(overlay);
};

const downloadAllImagesZip = () => {
  const zip = new JSZip();
  extractedImages.forEach((image, index) => {
    zip.file(`image${index + 1}.png`, image);
  });

  zip.generateAsync({ type: 'blob' }).then(function (content) {
    saveAs(content, 'extracted.zip');
  });

  // Close dialog box after download
  const overlay = document.body.querySelector('div[style*="position: fixed"]');
  document.body.removeChild(overlay);
};

WebViewer(
  {
    path: '/lib',
    initialDoc: 'https://apryse.s3.us-west-1.amazonaws.com/public/files/samples/lexpress.pdf',
    fullAPI: true, // Enable full API access. This will make the PDFNet namespace available on the WebViewer instance and allow accessing a PDFDoc instance from the WebViewer document object.
    enableFilePicker: true, // Enable file picker to open files
    licenseKey: licenseKey, 
  },
  document.getElementById('viewer')
).then((instance) => {

  // customize WebViewer UI
  customizeUI(instance);

  console.log('✅ WebViewer loaded successfully.');
}).catch((error) => {
  console.error('❌ Failed to initialize WebViewer:', error);
});
</code></pre>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/get-started/samples/showcase-demo-extract-images-from-pdf.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
