Putting Photos Into a PDF Without Re-Compressing Them

An expense system wants one PDF. You have eleven photos of receipts on your phone, two of them sideways, and one of a long receipt that’s taller than any page. That’s the situation I built ToolOnTap’s Image to PDF tool for. Putting a picture on a page sounds trivial, and mostly it is. The details are in page sizes, orientation, and not damaging the photos on the way in.

One page per image, in your order

The tool makes one page per image, in the order of the list. Files are added in the order your device hands them over, and from there you move them by dragging, or with the up and down buttons on each row. The buttons work with a keyboard, and after each move a screen reader hears where the page went, for example “receipt-04.jpg moved to page 2 of 11”.

Ordering matters more than it looks. The order a file picker hands over isn’t always the order you took the photos in, so the list shows a thumbnail and a number next to every page. You check the order before you create the PDF, instead of after.

Page sizes are measured in points

PDF measures everything in points, and a point is 1/72 of an inch. The two standard paper sizes in the tool are stored in points:

Page size Points Paper
A4 595.28 × 841.89 210 × 297 mm
US Letter 612 × 792 8.5 × 11 in

Letter comes out in round numbers because it’s defined in inches (8.5 × 72 = 612). A4 is defined in millimeters, so its points aren’t whole. The margins are points too: None is 0, Small is 18 (about 6 mm) and Large is 36 (about 13 mm).

The third option, “Same as image”, has no fixed size. Each page is made exactly as big as its picture, plus the margin. Images are measured in pixels, so the tool needs a conversion, and it uses the CSS one: 96 pixels per inch. An 800 × 600 screenshot becomes a 600 × 450 point page, which a PDF viewer at 100% zoom shows at about the size the screenshot had on screen.

Turning the page for wide images

On A4 and Letter, every image is scaled to fit inside the margins and centered on its page. Here’s the whole placement function:

export function placeImage(image: Size, size: PageSize, margin: PageMargin): PagePlacement {
  const m = MARGINS[margin]
  if (size === 'fit') {
    const width = image.width * PX_TO_PT
    const height = image.height * PX_TO_PT
    return { page: { width: width + 2 * m, height: height + 2 * m }, image: { x: m, y: m, width, height } }
  }
  const base = PAGE_SIZES[size]
  const landscape = image.width > image.height
  const page = landscape ? { width: base.height, height: base.width } : { ...base }
  const scale = Math.min((page.width - 2 * m) / image.width, (page.height - 2 * m) / image.height)
  const width = image.width * scale
  const height = image.height * scale
  return { page, image: { x: (page.width - width) / 2, y: (page.height - height) / 2, width, height } }
}

The landscape line is the decision that matters. If an image is wider than it is tall, its page turns sideways. Without that, a landscape scan of a two-page spread would be shrunk to fit the width of a tall sheet, with empty space above and below. A square image counts as portrait.

The Math.min picks whichever side hits the margin first, so the image always fits without being cropped or stretched. A 3000 × 4000 phone photo on A4 with Small margins comes out about 559 × 746 points, and it touches the side margins with a little space above and below.

Two honest consequences of this:

  • Small images are scaled up to fill the page, because the scale factor can be bigger than 1. A small screenshot on A4 will look soft. “Same as image” is the better choice for screenshots, and the tool page says so.
  • Each page decides for itself. In a mixed set, landscape photos get landscape pages and portrait photos get portrait pages in the same PDF. PDF viewers show each page the right way up, but it surprises people the first time.

My long receipt from the opening ends up as a narrow strip in the middle of a portrait page, because its height is what hits the margin. It’s readable, and you can zoom in, but if it’s very long, crop it into two images first.

Keeping JPGs untouched

The PDF itself is written by pdf-lib. It’s only imported when you click “Create PDF”, so the page loads without it.

pdf-lib can embed JPG and PNG files directly. A JPG goes into the PDF byte for byte, compressed exactly as it was, so there’s no second round of JPG compression and no quality loss. That’s the path the tool takes whenever it can:

const embedAsIs = file.type === 'image/png' || (file.type === 'image/jpeg' && jpegOrientation(bytes) === 1)

The second half of that line is about phone photos. Phones often save a photo with its pixels sideways and a small EXIF tag that says how to turn it. Browsers and gallery apps read that tag. PDF viewers don’t, because inside a PDF the image is just pixels. Embed one of those JPGs as it is, and the page shows it on its side.

So before embedding a JPG, the tool reads its orientation tag. It walks the JPG’s markers from the start of the file, finds the APP1 block that starts with “Exif”, and reads tag 0x0112, handling both byte orders a camera can use. If it finds no tag, or the value is 1 (upright), the JPG goes in untouched. If not, it’s drawn onto a canvas (which applies the rotation) and saved again as a JPG at 92% quality, then embedded.

Other formats take the same path. WebP and GIF can’t be embedded directly by pdf-lib, so they’re re-encoded as JPG too. That canvas step has the same 16,777,216-pixel cap the other image tools use, the largest canvas iOS Safari will draw, so a very large non-JPG image is scaled down to fit under it. HEIC photos are sent to the HEIC to JPG converter first, because only Safari can decode them.

How big the PDF gets

Because JPGs go in as they are, the PDF ends up roughly as big as your images added together, plus a little for the page structure. Eleven 3 MB phone photos make a PDF of about 33 MB, which is too big to attach to many emails. Compress the photos first with the image compressor, then make the PDF. The tool won’t do that silently, because it would mean changing your images without asking.

Each image can be up to 50 MB, and there’s no fixed page limit. The real limit is your device’s memory, since the whole PDF is built in memory before you download it, on your own device, with nothing uploaded. A single image gives you a PDF named after it (receipt.jpg becomes receipt.pdf), and several give you images.pdf.

Try it

Image to PDF handles receipts, signed forms and screenshots. Use A4 or US Letter for anything that will be printed, and “Same as image” for screenshots. Once you have a PDF, the PDF tools can merge it with others, rotate pages or put them in a new order.

Tools in this article

  • Convert images to PDF

    Combine JPG, PNG and other images into one PDF in your browser. Reorder pages, pick A4 or Letter, no upload and no watermark.

More articles