Created
August 20, 2023 16:20
-
-
Save Ebrahim-Ramadan/c4a6a040ef6233923af76996ca1137fd to your computer and use it in GitHub Desktop.
Images to PDF Next App with jspdf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class CustomImage extends Image { | |
constructor(public mimeType: string) { | |
super(); | |
} | |
get imageType(): string { | |
return this.mimeType.split("/")[1]; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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