As Steve Jobs said, "Design is not just what it looks like and feels like. Design is how it works."
Converting an image to PDF should work like this: open tool → upload image → get PDF. That is it. No backend calls, no third-party APIs, no file size limits from a server.
Here is how it actually works in the browser — and why TechMind.click built it this way.
The Core Approach - Canvas + jsPDF
The cleanest client-side image-to-PDF conversion uses the HTML5 Canvas API and jsPDF:
import { jsPDF } from "jspdf";
async function imageToPDF(imageFile) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
// A4 dimensions in mm
const pdf = new jsPDF({
orientation: img.width > img.height ? "landscape" : "portrait",
unit: "mm",
format: "a4"
});
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
// Calculate scaled dimensions preserving aspect ratio
const ratio = Math.min(
pageWidth / img.width,
pageHeight / img.height
);
const imgWidth = img.width * ratio;
const imgHeight = img.height * ratio;
// Center on page
const x = (pageWidth - imgWidth) / 2;
const y = (pageHeight - imgHeight) / 2;
pdf.addImage(
e.target.result,
"JPEG",
x, y,
imgWidth, imgHeight
);
resolve(pdf.output("blob"));
};
img.src = e.target.result;
};
reader.readAsDataURL(imageFile);
});
}
Handling Multiple Images
async function multipleImagesToPDF(imageFiles) {
const pdf = new jsPDF({ unit: "mm", format: "a4" });
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
for (let i = 0; i < imageFiles.length; i++) {
if (i > 0) pdf.addPage();
const dataUrl = await fileToDataURL(imageFiles[i]);
const dimensions = await getImageDimensions(dataUrl);
const ratio = Math.min(
pageWidth / dimensions.width,
pageHeight / dimensions.height
);
pdf.addImage(
dataUrl, "JPEG",
(pageWidth - dimensions.width * ratio) / 2,
(pageHeight - dimensions.height * ratio) / 2,
dimensions.width * ratio,
dimensions.height * ratio
);
}
return pdf.output("blob");
}
const fileToDataURL = (file) => new Promise((res) => {
const reader = new FileReader();
reader.onload = (e) => res(e.target.result);
reader.readAsDataURL(file);
});
const getImageDimensions = (src) => new Promise((res) => {
const img = new Image();
img.onload = () => res({ width: img.width, height: img.height });
img.src = src;
});
Why Client-Side Matters
As Alan Turing would appreciate — the most elegant solution solves the problem with the least complexity. Client-side conversion means:
Zero server costs — no file storage, no bandwidth for uploads
Privacy — user files never leave their device
Speed — no round-trip to a server
Offline capable — works without internet after initial page load
Quick Manual Fix
For non-developers or one-off conversions, TechMind.click has this built in — upload image, download PDF, done. Uses the same browser-based approach — no server, no storage.
What is your preferred client-side PDF library? jsPDF vs pdf-lib — drop it in the comments.
Top comments (0)