Skip to content

Instantly share code, notes, and snippets.

@Ebrahim-Ramadan
Created August 20, 2023 16:20
Show Gist options
  • Save Ebrahim-Ramadan/c4a6a040ef6233923af76996ca1137fd to your computer and use it in GitHub Desktop.
Save Ebrahim-Ramadan/c4a6a040ef6233923af76996ca1137fd to your computer and use it in GitHub Desktop.
Images to PDF Next App with jspdf
export class CustomImage extends Image {
constructor(public mimeType: string) {
super();
}
get imageType(): string {
return this.mimeType.split("/")[1];
}
}
import jsPDF from "jspdf";
import { CustomImage } from "./custom-image";
const A4_PAPER_DIMENSIONS = {
width: 210,
height: 297,
};
const A4_PAPER_RATIO = A4_PAPER_DIMENSIONS.width / A4_PAPER_DIMENSIONS.height;
interface ImageDimension {
width: number;
height: number;
}
export const imageDimensionsOnA4 = (dimensions: ImageDimension) => {
const isLandscapeImage = dimensions.width >= dimensions.height;
if (isLandscapeImage) {
return {
width: A4_PAPER_DIMENSIONS.width,
height:
A4_PAPER_DIMENSIONS.width / (dimensions.width / dimensions.height),
};
}
const imageRatio = dimensions.width / dimensions.height;
if (imageRatio > A4_PAPER_RATIO) {
const imageScaleFactor =
(A4_PAPER_RATIO * dimensions.height) / dimensions.width;
const scaledImageHeight = A4_PAPER_DIMENSIONS.height * imageScaleFactor;
return {
height: scaledImageHeight,
width: scaledImageHeight * imageRatio,
};
}
return {
width: A4_PAPER_DIMENSIONS.height / (dimensions.height / dimensions.width),
height: A4_PAPER_DIMENSIONS.height,
};
};
export const fileToImageURL = (file: File): Promise<CustomImage> => {
return new Promise((resolve, reject) => {
const image = new CustomImage(file.type);
image.onload = () => {
resolve(image);
};
image.onerror = () => {
reject(new Error("Failed to convert File to Image"));
};
image.src = URL.createObjectURL(file);
});
};
export const generatePdfFromImages = (images: CustomImage[]) => {
const doc = new jsPDF();
doc.deletePage(1);
images.forEach((image) => {
const imageDimensions = imageDimensionsOnA4({
width: image.width,
height: image.height,
});
doc.addPage();
doc.addImage(
image.src,
image.imageType,
(A4_PAPER_DIMENSIONS.width - imageDimensions.width) / 2,
(A4_PAPER_DIMENSIONS.height - imageDimensions.height) / 2,
imageDimensions.width,
imageDimensions.height
);
});
const pdfURL = doc.output("bloburl");
window.open(pdfURL as any, "_blank");
};
import React, { ChangeEventHandler } from "react";
import Image from 'next/image'
import * as Helpers from "./helpers";
import { CustomImage } from "./custom-image";
import './style.css'
export function MainFunc() {
const [uploadedImages, setUploadedImages] = React.useState<CustomImage[]>([]);
const handleImageUpload = React.useCallback<
ChangeEventHandler<HTMLInputElement>
>
(
(event) => {
const fileList = event.target.files;
const fileArray = fileList ? Array.from(fileList) : [];
const fileToImagePromises = fileArray.map(Helpers.fileToImageURL);
Promise.all(fileToImagePromises).then(setUploadedImages);
},
[setUploadedImages]
);
const cleanUpUploadedImages = React.useCallback(() => {
setUploadedImages([]);
uploadedImages.forEach((image) => {
URL.revokeObjectURL(image.src);
});
}, [setUploadedImages, uploadedImages]);
const generatePdfFromImages = React.useCallback(() => {
Helpers.generatePdfFromImages(uploadedImages);
cleanUpUploadedImages();
}, [uploadedImages, cleanUpUploadedImages]);
return (
<>
<h1>Convert images to PDFs</h1>
<div className="images-container">
{uploadedImages.length > 0 ? (
uploadedImages.map((image) => (
<Image
width={400}
height={400}
key={image.src} src={image.src} className="uploaded-image" alt="uploaded-image" />
))
) : (
<p>You Can Upload some images Now...</p>
)}
</div>
<div className="buttons-container">
<label htmlFor="file-input">
<span className="button">Upload images</span>
<input
id="file-input"
type="file"
accept="image/*"
onChange={handleImageUpload}
style={{ display: "none" }}
multiple
/>
</label>
<button
onClick={generatePdfFromImages}
className="button"
disabled={uploadedImages.length === 0}
>
Generate PDF
</button>
</div>
</>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment