How to Convert PNG Images to PDF with Code
Convert one or many PNG files to PDF with Pillow, img2pdf, ReportLab, or PDFKit, with guidance on quality, transparency, layout, and errors.
Direct answer: use Pillow for a quick single-image or small multi-page conversion, img2pdf when preserving source pixels matters, ReportLab when you need controlled page layout and additional drawn content, and PDFKit for Node.js or browser applications. Convert images to RGB when transparency is not required, choose a deliberate page size and ordering, and verify the resulting PDF in the viewer where it will be used.
Choose the right conversion method
| Need | Best fit | Why |
|---|---|---|
| One PNG to one PDF page | Pillow | Very small amount of Python code |
| Several PNGs combined with minimal processing | img2pdf | Designed for image-to-PDF conversion and can preserve pixels without re-encoding where supported |
| Captions, headers, margins, shapes, or fixed page templates | ReportLab | Canvas API gives explicit control over page drawing |
| Node.js or browser JavaScript | PDFKit | PDF document generation for Node and browser runtimes |
Python: convert one PNG with Pillow
Pillow’s official image-format documentation states that it can write PDF (Acrobat) images. Install it with:
python -m pip install Pillow
Then run this complete example:
from PIL import Image
image = Image.open("input.png")
image.save("output.pdf", "PDF")
This writes one PDF page using the PNG’s pixel dimensions and metadata. The physical size can therefore differ between files with different resolution metadata.
Combine multiple PNG files with Pillow
from pathlib import Path
from PIL import Image
paths = [
Path("page-1.png"),
Path("page-2.png"),
Path("page-3.png"),
]
# RGB is broadly compatible when an alpha channel is not needed.
images = [Image.open(path).convert("RGB") for path in paths]
if not images:
raise ValueError("No PNG files supplied")
images[0].save(
"output.pdf",
"PDF",
save_all=True,
append_images=images[1:],
)
# Release file handles after writing.
for image in images:
image.close()
The list order is the page order. Build it deterministically rather than relying on filesystem enumeration order.
Pillow image modes and transparency
Pillow documents binary PDF 1.4 output. Its encoding depends on image mode: 1-bit images use TIFF or a JPEG fallback, L/RGB/CMYK images use JPEG, palette images use hexadecimal encoding, and LA/RGBA images use JPEG2000. Converting to RGB is a practical compatibility step when you do not need transparency. If alpha matters, test the output in your target PDF viewer because transparent PNG pixels must be composited or encoded in a way the viewer supports.
For an explicit white background while flattening alpha:
from PIL import Image
source = Image.open("transparent.png").convert("RGBA")
background = Image.new("RGB", source.size, "white")
background.paste(source, mask=source.getchannel("A"))
background.save("flattened.pdf", "PDF")
Python: lossless-oriented conversion with img2pdf
img2pdf describes itself as a Python program that converts image files to PDF. Its documented inputs include PNG, JPG/JPEG, WebP, BMP, GIF, and TIFF. Install it with:
python -m pip install img2pdf
Convert one or more files from the command line:
img2pdf --output out.pdf page1.png page2.png page3.png
You can also call it from Python:
import img2pdf
paths = ["page1.png", "page2.png", "page3.png"]
with open("out.pdf", "wb") as output:
output.write(img2pdf.convert(paths))
The project documents lossless conversion for PNG, JPEG, and JPEG2000 inputs where supported, adding PDF container overhead without re-encoding the image data. This is a strong choice for scans and screenshots when preserving source pixels matters more than adding page content.
Python: compose a designed PDF with ReportLab
Use ReportLab when each PNG is part of a composed page that also contains captions, headers, footers, shapes, or other generated content. Install the dependencies:
python -m pip install reportlab Pillow
This example creates a page exactly as large as the source image:
from PIL import Image
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas
image = Image.open("input.png")
width, height = image.size
pdf = canvas.Canvas("output.pdf", pagesize=(width, height))
pdf.drawImage(ImageReader(image), 0, 0, width=width, height=height)
pdf.showPage()
pdf.save()
image.close()
Fit a PNG inside a fixed page without distortion
from PIL import Image
from reportlab.lib.pagesizes import A4
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas
image = Image.open("input.png")
source_w, source_h = image.size
page_w, page_h = A4
margin = 36 # points; 72 points = 1 inch
available_w = page_w - 2 * margin
available_h = page_h - 2 * margin
scale = min(available_w / source_w, available_h / source_h)
draw_w = source_w * scale
draw_h = source_h * scale
x = (page_w - draw_w) / 2
y = (page_h - draw_h) / 2
pdf = canvas.Canvas("a4-output.pdf", pagesize=A4)
pdf.drawImage(ImageReader(image), x, y, width=draw_w, height=draw_h, preserveAspectRatio=True)
pdf.showPage()
pdf.save()
image.close()
ReportLab measures page coordinates in points. Production code can calculate dimensions from DPI, apply margins, add a background, and draw text or vector elements before saving.
JavaScript: convert PNG to PDF with PDFKit
PDFKit is a PDF document generation library for Node and the browser. Install it in Node.js:
npm install pdfkit
Complete Node.js example:
const PDFDocument = require('pdfkit');
const fs = require('node:fs');
const doc = new PDFDocument({ autoFirstPage: false });
doc.pipe(fs.createWriteStream('output.pdf'));
doc.addPage();
doc.image('input.png', 0, 0, { fit: [595, 842] });
doc.end();
The fit box above is approximately A4-sized in points. To preserve the image’s aspect ratio while centering it, calculate the scale from the source width and height, then pass the resulting x, y, width, and height values to doc.image().
PDFKit in a browser
Browser builds cannot read a local filesystem path directly. PDFKit’s browser pattern uses a stream or blob collection, and image bytes or registered data must be supplied by the application. Fetch the PNG as an ArrayBuffer, convert it to the format expected by your bundler’s PDFKit build, add the image, and collect the generated chunks into a Blob for download.
cURL: convert an existing PNG with a service
If your application already stores PNG bytes and you need a hosted conversion endpoint, use the provider’s documented API. For a screenshot that should become a PDF, ScreenshotNeo can capture the source URL directly and return PDF output. See the ScreenshotNeo API documentation for all parameters and response details.
Or skip the browser setup
For a web page that you want as a PDF, ScreenshotNeo handles the capture step before your PDF workflow:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d format=pdf \
-o page.pdf
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://stripe.com",
"format": "pdf",
},
timeout=90,
)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com',
format: 'pdf',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
// Write bytes to your storage or return them from your application.
Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Page size, resolution, and image quality
- Pixels versus physical size: a PNG is pixel-based; a PDF page is measured in points. Decide whether the page should match the pixel dimensions, a standard paper size, or a DPI-derived physical size.
- Aspect ratio: scale uniformly and center the image, or deliberately crop it. Never stretch width and height independently when the image contains text or diagrams.
- Compression: img2pdf is appropriate when avoiding additional image re-encoding matters. Pillow’s mode-dependent PDF encoding can change how data is stored.
- Color: keep RGB for ordinary screen images. Preserve CMYK only when the downstream PDF workflow requires it and has been validated.
- Transparency: choose whether to preserve alpha or flatten onto a known background. A white background is usually safest for printing and broad viewer compatibility.
- Large images: resize before conversion when the source resolution is far beyond the required display or print size.
Batch conversion checklist
- Collect only supported files and sort them with a deterministic key, such as a numeric page number.
- Validate that every file opens before starting the PDF.
- Choose one policy for alpha, color mode, page size, and margins.
- Process one image at a time or use a bounded batch when memory is limited.
- Write to a temporary output and rename it only after the PDF closes successfully.
- Open the result with a PDF parser or target viewer and verify page count and ordering.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
cannot write mode RGBA as PDF or viewer compatibility errors |
Alpha or an unsupported mode is being passed through | Flatten onto a background or convert to RGB before saving |
| Pages appear in the wrong order | Filesystem order is not guaranteed | Sort paths explicitly using a numeric or natural sort key |
| Image is stretched | Width and height were scaled independently | Use one uniform scale calculated from the available page box |
| Image is tiny or huge on paper | PNG resolution metadata differs or was ignored | Set page dimensions explicitly, or calculate points from the intended DPI |
| Output is much larger than expected | Very large source pixels or embedded data | Resize to the required resolution, remove unnecessary metadata, and compare Pillow with img2pdf |
| ReportLab cannot place a PNG | Image support dependencies are missing | Install Pillow and pass an ImageReader |
| PDFKit cannot find the image in a browser | Browser code cannot access a local path | Fetch the bytes and provide browser-compatible image data |
| Memory usage spikes during a batch | All decoded images remain resident | Process in bounded batches, close images promptly, or use img2pdf for direct conversion |
| PDF opens but has a blank page | Canvas/page lifecycle was not completed | Call showPage()/save() in ReportLab or end() in PDFKit |
Performance, reliability, and cost considerations
There are no directly comparable conversion benchmarks in the source documentation, so choose based on workflow requirements rather than claimed timings. Decoding large PNGs is memory-intensive; resizing and bounded processing usually matter more than micro-optimizing the PDF call. For reliable jobs, validate inputs, write atomically, retain the original PNGs until verification succeeds, and record page count and ordering in your job metadata.
Local libraries have no per-document API charge, but your process pays for CPU, memory, storage, and operational maintenance. A hosted capture service adds an API cost but removes browser automation and page-rendering setup. ScreenshotNeo bills only clean shots; failed loads, bot checks, blank pages, timeouts, and cache hits are not billed, with verdict and billing headers returned for auditability.
FAQ
Can I combine PNG and JPEG files in one PDF?
Yes. img2pdf accepts several image formats, and Pillow, ReportLab, and PDFKit can add different image files as pages. Apply the same page-size and color policy to every input.
Will converting a PNG make its text searchable?
No. These methods place pixels into a PDF. Add an OCR stage if you need a searchable text layer.
Which method preserves transparency best?
Test the exact viewer and workflow. If broad compatibility matters more than alpha, flatten to RGB on a deliberate background. If pixel preservation is the priority, evaluate img2pdf for the specific PNG mode.
How do I create one PDF page per screenshot?
Pass the images as an ordered list to Pillow or img2pdf, call addPage() for each image in PDFKit, or create and draw one ReportLab page per image.
Can a web page be captured directly as a PDF?
Yes. ScreenshotNeo’s format=pdf request captures a URL and returns a PDF, with controls for paper size, margins, landscape mode, page ranges, waiting, custom CSS and JavaScript, headers, cookies, and other capture options documented at its API documentation.
Sources: Pillow PDF format documentation; img2pdf documentation; ReportLab graphics guide; PDFKit documentation.


