How to Convert Documents to Images

Eugene Musebe

Introduction

Document processors like Microsoft Office are the de facto standard for producing and formatting documents. When transmitting information such as statements or invoices, even its most passionate customers seek extra features and usefulness from online file converters to make PDFs. The reason for this is that word documents can be edited, whereas PDF documents cannot. The same may be said about how PDF documents maintain their formatting across numerous devices. In this article, you will be able to create a feature that distinguishes pdf pages as images and provide online storage for any page a user requires i.e for future reference.

Lets' begin writing

Codesandbox

Check the sandbox demo on Codesandbox.

You can also get the project github repo using Github.

Prerequisites

Entry-level javascript and React/Nextjs knowledge.

Setting Up the Sample Project

Create your new Nextjs app using npx create-next-app pdfconvert in your terminal. Head to your project root directory cd pdfconvert

In our Nextjs framework backend, we will have to integrate Cloudinary, which will be used to store our uploaded processed images.

Create your Cloudinary account using Link and log into it to access your dashboard which contains your environment variable keys necessary for the integration in your project.

In your project directory, include Cloudinary in your dependencies npm install cloudinary

Use the guide below to fill in your environment variables in the .env.local file from the Cloudinary dashboard.

1CLOUDINARY_CLOUD_NAME =
2
3CLOUDINARY_API_KEY =
4
5CLOUDINARY_API_SECRET =

Restart your project using npm run dev.

In the pages/api folder, create a new file named upload.js. Start by configuring the environment keys and libraries.

1var cloudinary = require("cloudinary").v2;
2
3cloudinary.config({
4 cloud_name: process.env.CLOUDINARY_NAME,
5 api_key: process.env.CLOUDINARY_API_KEY,
6 api_secret: process.env.CLOUDINARY_API_SECRET,
7});

Create a handler function to execute the POST request. The function will receive media file data and post it to the cloudinary website. It then captures the media file's cloudinary link and sends it back as a response.

1export default async function handler(req, res) {
2 if (req.method === "POST") {
3 let url = ""
4 try {
5 let fileStr = req.body.data;
6 const uploadedResponse = await cloudinary.uploader.upload_large(
7 fileStr,
8 {
9 resource_type: "video",
10 chunk_size: 6000000,
11 }
12 );
13 url = uploadedResponse.url
14 } catch (error) {
15 res.status(500).json({ error: "Something wrong" });
16 }
17
18 res.status(200).json({data: url});
19 }
20}

The code above concludes our backend.

Before you begin the front end, include the following in your environment variables: npm install pdfjs-dist

Create a directory, components/Converter.client.js, and include it in your pages/index directory.

1"pages/index"
2
3import Converter from "../components/Converter.client";
4
5export default function Home() {
6 return (
7 <Converter />
8 );
9}

In the components/Converter.client.js directory, start with including the necessary imports

1"components/Converter.client.js"
2
3
4import React, { useState, useRef, useEffect } from "react";
5
6import * as pdfjsLib from "pdfjs-dist/build/pdf";
7pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;
8
9const HTTP_SUCCESS = 200;

The HTTP_SUCCESS variablewill be used to determine successful responses from api calls.

Create a function named Converter and declare the following statehooks:

1"components/Converter.client.js"
2
3export default function Converter() {
4 const [img, setImg] = useState();
5 const [name, setName] = useState("");
6 const [selectedFile, setSelectedFile] = useState(null);
7 const [pages, setPages] = useState(0);
8 const [currentPage, setCurrentPage] = useState(1);
9 const [pdfFile, setPdfFile] = useState(null);
10 const pageRenderRef = useRef();
11
12 return (
13 <>
14 works
15 </>
16 )
17}

useState is a hook that lets you add React state to function components and the useRef hook allows you to persist values between renders. We will also add a useEffect hook to perform side effects in our functional component.

1useEffect(() => {
2 if (pdfFile && currentPage) {
3 pageRender();
4 }
5 }, [pdfFile, currentPage]);

The above hook will lookout for the pdf file and currentPage and render the page whenever changes are made in the two variables.

Create a function convert :

1const onConvert = async () => {
2 const uri = URL.createObjectURL(selectedFile);
3
4 const pdf = await pdfjsLib.getDocument({ url: uri });
5
6 await pdf.promise.then(
7 (_pdf) => {
8 const {
9 _pdfInfo: { numPages },
10 } = _pdf;
11 setPages(numPages);
12 setPdfFile(_pdf);
13 },
14 (error) => {
15 console.log("PDF error :", error);
16 }
17 );
18 };

The function above starts by creating a URL string object from the file object selectedFile, assigning it to the constant uri. The constant pdf will use pdfjsLib to capture the uri string and categorize it based on the files selected and number of pages the file contains using react useState hooks.

Create a pageRender function to capture the user's selected page to a canvas and assign it to the setImg hook as a string object

1const pageRender = () => {
2 pdfFile.getPage(currentPage).then((page) => {
3 {
4 const viewport = page.getViewport({ scale: 1 });
5 const canvas = pageRenderRef.current;
6 const context = pageRenderRef.current.getContext("2d");
7 canvas.height = viewport.height;
8 canvas.width = viewport.width;
9 const renderContext = {
10 canvasContext: context,
11 viewport: viewport,
12 enableWebGL: false,
13 };
14 page.render(renderContext);
15 setImg(canvas.toDataURL());
16 console.log(pageRenderRef.current);
17 }
18 });
19};

Once the image is captured, we will have to upload it to the backend for cloudinary upload. Note the HTTP_SUCCESS variable created earlier.

1const uploadVideo = async () => {
2 console.log(img);
3 try {
4 fetch("/api/upload", {
5 method: "POST",
6 body: JSON.stringify({ data: img }),
7 headers: { "Content-Type": "application/json" },
8 }).then((response) => {
9 console.log("Backend http status code : ", response.status);
10 if (response.status === HTTP_SUCCESS) {
11 response.json().then((result) => {
12 console.log(result);
13 });
14 }
15 });
16 } catch (error) {
17 console.error(error);
18 }
19};

Finally, include the following in your return statement to create your UI. The css files can be located in the Github repository

1return (
2 <div className="container">
3 <h1>Conversion of Documents to Images with Nextjs</h1>
4 <div className="row">
5 <div className="column">
6 <input
7 type="file"
8 accept="application/pdf"
9 onChange={(e) => setSelectedFile(e.target.files[0])}
10 />
11 {selectedFile ? <h3>You have selected: {selectedFile.name}</h3> : ""}
12 {selectedFile && <button onClick={onConvert}>Convert</button>}
13 </div>
14 <div className="column">
15 {pdfFile && (
16 <>
17 <input
18 type="number"
19 placeholder="Page number"
20 value={currentPage}
21 onChange={(e) => setCurrentPage(parseInt(e.target.value))}
22 min="1"
23 max={pages}
24 />{' '}
25 <button onClick={uploadVideo}>Upload Image</button><br /><br />
26 <canvas ref={pageRenderRef} width="100" height="200"></canvas>
27 </>
28 )}
29
30
31 </div>
32 </div>
33 </div>
34);

The UI should look like below

Thats it! Ensure to go through the article to enjoy the experience.

Eugene Musebe

Software Developer

I’m a full-stack software developer, content creator, and tech community builder based in Nairobi, Kenya. I am addicted to learning new technologies and loves working with like-minded people.