How to Build an Online Image-to-PDF Converter with HTML, CSS, JS, and NodeJS

Have you ever needed to combine multiple images into a single PDF document? While you could use a desktop program like Adobe Acrobat or an online service, it‘s actually not too difficult to build your own web-based image-to-PDF converter.

In this tutorial, we‘ll walk through how to create an online tool that allows users to upload a set of images, rearrange them into the desired order, and generate a PDF file – all using HTML, CSS, JavaScript, and NodeJS. This can be handy for consolidating multiple scanned pages or exporting your photography.

Here‘s an overview of the key technologies we‘ll use:

  • HTML and CSS for the frontend interface
  • JavaScript for interactive features like reordering images and initiating PDF generation
  • NodeJS and ExpressJS for the backend server
  • Multer middleware to handle multi-file uploads
  • PDFKit library to construct the PDF document

Our basic flow will be:

  1. Present an HTML form where the user can select one or more image files
  2. On form submission, upload the files to the server
  3. Display the uploaded images and allow reordering via drag-and-drop
  4. Convert the images to a PDF when the user clicks a "Generate PDF" button
  5. Provide the PDF as a download link

Let‘s get started!

Project Setup

First, create a new directory for the project and initialize it as a Node project:

mkdir img2pdf
cd img2pdf
npm init -y

Next, install the necessary dependencies:

npm install express multer pdfkit

We‘ll also want a convenient way to restart the server automatically when we make changes during development. For this, we can use nodemon:

npm install --save-dev nodemon

Open up the package.json file and add the following script:

"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
},

Now we can run the development server using:

npm run dev

HTML Form

Let‘s build our frontend starting with the HTML form. Create an index.html file in the project root:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Image to PDF Converter</title>
  <link rel="stylesheet" href="/css/style.css">
</head>
<body>
  <header>

  </header>

  <main>
    <form id="upload-form" action="/upload" method="POST" enctype="multipart/form-data">
      <div>
        <label for="images">Select images:</label>
        <input type="file" id="images" name="images" accept="image/*" multiple required>
      </div>
      <button type="submit">Upload</button>
    </form>

    <div id="uploaded-images"></div>

    <button id="generate-pdf">Generate PDF</button>

    <div id="pdf-link"></div>
  </main>

  <script src="https://unpkg.com/[email protected]/Sortable.min.js"></script>
  <script src="/js/main.js"></script>
</body>
</html>

Here we have a simple form with a file input that accepts multiple image files. We‘ve included the SortableJS library which we‘ll use later to enable drag-and-drop reordering of the uploaded images.

Server Setup

Now let‘s set up our server. Create a server.js file:

const express = require(‘express‘);
const multer  = require(‘multer‘);
const PDFDocument = require(‘pdfkit‘);

const app = express();
const upload = multer({ dest: ‘uploads/‘ });

app.use(express.static(‘public‘));

app.post(‘/upload‘, upload.array(‘images‘), (req, res) => {
  const imageFiles = req.files;
  const imageFilenames = imageFiles.map(file => file.filename);
  res.json({ imageFilenames });
});

app.post(‘/generate-pdf‘, (req, res) => {
  const imageFilenames = req.body.imageFilenames;

  const doc = new PDFDocument();
  doc.pipe(res);

  imageFilenames.forEach((filename, idx) => {
    if (idx > 0) doc.addPage();
    doc.image(`uploads/${filename}`, 0, 0, { fit: [600, 800] }); 
  });

  doc.end();

  res.setHeader(‘Content-Type‘, ‘application/pdf‘);
  res.setHeader(
    ‘Content-Disposition‘, 
    `attachment; filename="combined_images_${Date.now()}.pdf"`
  ); 
});

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Server running on port ${port}`));

We first configure multer to store uploaded files in an uploads directory.

The /upload route accepts the uploaded images, saves them, and returns the filenames as JSON.

The /generate-pdf route expects an array of filenames in the request body. It creates a new PDF document using PDFKit, adds each image to a new page of the document, and pipes the PDF to the response.

Frontend JavaScript

Finally, let‘s wire everything up with some frontend JavaScript. Create a public/js/main.js file:

const uploadForm = document.getElementById(‘upload-form‘);
const uploadedImagesDiv = document.getElementById(‘uploaded-images‘);
const generatePdfBtn = document.getElementById(‘generate-pdf‘);
const pdfLinkDiv = document.getElementById(‘pdf-link‘);

let uploadedImageFilenames = [];

uploadForm.addEventListener(‘submit‘, async (e) => {
  e.preventDefault();

  const formData = new FormData(uploadForm);
  const response = await fetch(‘/upload‘, {
    method: ‘POST‘,
    body: formData,
  });
  const data = await response.json();

  uploadedImageFilenames = data.imageFilenames;
  renderUploadedImages();
});

function renderUploadedImages() {
  uploadedImagesDiv.innerHTML = ‘‘;

  uploadedImageFilenames.forEach(filename => {
    const img = document.createElement(‘img‘);
    img.src = `/uploads/${filename}`;
    img.width = 100;
    uploadedImagesDiv.append(img);
  });

  new Sortable(uploadedImagesDiv, {
    animation: 150,
    ghostClass: ‘blue-background-class‘,
  });
}

generatePdfBtn.addEventListener(‘click‘, async () => {
  const response = await fetch(‘/generate-pdf‘, {
    method: ‘POST‘,
    headers: {
      ‘Content-Type‘: ‘application/json‘
    },
    body: JSON.stringify({ imageFilenames: uploadedImageFilenames }),
  });
  const pdfBlob = await response.blob();
  const pdfUrl = URL.createObjectURL(pdfBlob);

  pdfLinkDiv.innerHTML = `<a href="${pdfUrl}" download="combined_images.pdf">Download PDF</a>`;
});

When the form is submitted, we send a POST request to /upload with the selected files. The response contains the filenames of the saved images, which we store in the uploadedImageFilenames array.

We then render the uploaded images and use SortableJS to allow the user to reorder them by dragging and dropping.

When the "Generate PDF" button is clicked, we send the image filenames to the /generate-pdf endpoint. The server returns the PDF file, which we convert to a URL and display as a download link.

Enhancements

There are many potential improvements we could make, such as:

  • Input validation to ensure only image files are uploaded
  • A preview of the PDF before downloading
  • Options to select paper size, orientation, margins, etc.
  • A progress indicator during PDF generation
  • Persist image uploads and PDF files rather than storing in memory
  • Support for additional image formats like TIFF or WebP
  • OCR to convert images containing text to a searchable PDF
  • Integration with cloud storage services like Dropbox or Google Drive

Feel free to experiment and adapt this code to fit your specific needs!

Conclusion

In this tutorial, we built a simple but functional web app for combining multiple images into a PDF file. With some HTML, CSS, and JavaScript on the frontend, and NodeJS with the help of libraries like Express, Multer, and PDFKit on the backend, we were able to create a useful tool in relatively few lines of code.

This demonstrates the power of web technologies for building interactive applications. I hope this inspires you to try creating your own web projects!

You can find the complete source code for this example on GitHub. If you have any questions or suggestions for improvement, feel free to open an issue or submit a pull request.

Happy coding!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *