Nuxt.js Image to Pdf Converter

Eugene Musebe

Introduction

We may need to switch formats whenever we need to deliver image content to our users, depending on business requirements and the user’s device configurations. This is especially true for image/PDF formats. In this article, we review how we can easily achieve this using Cloudinary and Nuxt.Js.

The complete project can be found on the CodeSandbox, you can fork it and add the following environment variables to your sandbox server to make it work.

1NUXT_ENV_CLOUDINARY_CLOUD_NAME=
2CLOUDINARY_API_KEY=
3CLOUDINARY_API_SECRET=

CodeSandbox

The complete codebase can also be found on Github

Prerequisites

We will be using beginner-level Nuxt.Js and Express.Js in this tutorial. Thus we recommend previous knowledge and experience in JavaScript and Vue.Js.

Node.Js, Express.Js, and Nuxt.Js knowledge and experience are not a requirement.

Installation

Nuxt.Js Project

Ensure Node.Js is installed on your system. Npx also needs to be installed as well. It is shipped by default since npm 5.2.0, npm v6.1, or yarn.

Run the following command to create the project

1yarn create nuxt-app nuxtjs-image-pdf-converter

We recommend installing axios during the prompt as we will use it to interact with the Express.Js server.

Since we will have both client and server-side operations, we recommend selecting the Universal (SSR/SSG) rendering mode and Server (Node.Js hosting) deployment mode.

Here is a compilation of our recommended setup:

  • Project Name: nuxtjs-image-pdf-converter

  • Programming Language -> JavaScript

  • Package manager -> Yarn

  • UI Framework -> TailwindCSS

  • Nuxt.Js modules -> Axios

  • Linting tools -> None

  • Rendering mode -> Universal (SSR/SSG)

  • Deployment target -> Server (Node.Js hosting)

  • Development tools -> None

  • Continuous integration -> None

  • Version control -> Git

Once the above setup is complete, Enter the project folder with the following command:

1cd nuxtjs-image-pdf-converter

Cloudinary Nuxt

Cloudinary doesn't come installed by default in Nuxt.Js, thus we need to install the recommended package using the following command

1yarn add @nuxtjs/cloudinary
2
3# OR
4
5npm install @nuxtjs/cloudinary

Once the dependency has been added to our project, let's add @nuxt/cloudinary as a module in our nuxt.config.js modules section. This registers the package in our Nuxt.Js project.

1// nuxt.config.js
2
3export default {
4
5modules: [
6
7'@nuxtjs/cloudinary'
8
9]
10
11}

To configure our Cloudinary instance, we'll add a cloudinary section to our nuxt.config.js

1// nuxt.config.js
2
3cloudinary: {
4
5cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
6
7secure: true
8
9}

We do not want our cloud name exposed in our project code. For this reason, we'll create a .env file. By default, this file is ignored by git and will not be committed to our remote code repository.

1// .env
2
3NUXT_ENV_CLOUDINARY_CLOUD_NAME=my-cloudinary-cloud-name

Express Server

Nuxt.Js allows us to add our own custom middleware. We are going to use this to create an Express.Js API without creating our own dedicated external server.

We are first going to install Express.Js

1yarn add express
2
3# OR
4
5npm install express

We will create a file server-middleware/api.js to handle our API requests:

1// server-middleware/api.js
2
3const app = require('express')()
4
5app.all('/generate-pdf', async (req, res) => {
6
7res.json({ data:'data' })
8
9})
10
11module.exports = app

The above code enables our API to handle all api/generate-pdf requests.

To register our API with Nuxt.Js we will need to register it in the serverMiddleware section of nuxt.config.js

1// nuxt.config.js
2
3serverMiddleware: [
4
5{ path: "/api", handler: "~/server-middleware/api.js" },
6
7],

This will ensure that all /api requests are routed to our API.

Node.Js Cloudinary

To interact with Cloudinary from our Express.Js server, we'll need to install the Node.Js SDK as well. We'll use the following command to install it:

1yarn add cloudinary
2
3# OR
4
5npm install cloudinary

Once installed, we'll need to load and configure the SDK in our api.js

1// server-middleware/api.js
2
3require('dotenv').config()
4
5const cloudinary = require('cloudinary');
6
7cloudinary.config({
8
9cloud_name: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
10
11api_key: process.env.CLOUDINARY_API_KEY,
12
13api_secret: process.env.CLOUDINARY_API_SECRET,
14
15secure: true
16
17});
18
19...

We do not want our API Key and Secret exposed to our code repository. We will thus add them to our .env file

1// .env
2
3...
4
5CLOUDINARY_API_KEY=secret-api-key
6
7CLOUDINARY_API_SECRET=secret-api-secret

Converting images to PDF

Let us render a simple form allowing our users to upload images.

1<!-- pages/images-to-pdf.vue -->
2
3<form @submit.prevent="submit">
4
5<input
6
7multiple
8
9accept=".jpeg,.jpg,.png,image/jpeg,image/png"
10
11type="file"
12
13name="images"
14
15/>
16
17<button type="submit">
18
19Convert
20
21</button>
22
23</form>

To convert images to PDF, we'll first upload them to cloudinary. During the upload process, we'll assign them a unique tag. We will then use this tag to generate the PDF.

We'll use a Promise.all to ensure all the images are uploaded. Once the images are uploaded, we then call the api/generate-pdf endpoint with the new tag as a query parameter. This request should return an instance object containing the secure_url to our PDF document.

1// pages/images-to-pdf.vue
2
3export default {
4
5data() {
6
7return {
8
9pdfUrl: null
10
11};
12
13},
14
15methods: {
16
17async submit(e) {
18
19const randomTag = Math.random().toString(36).substring(7);
20
21this.uploading = true;
22
23const files = e.target.images.files;
24
25const uploadedFiles = await Promise.all(
26
27Array.from(files).map(async (file) => {
28
29/* create a reader */
30
31const readData = (f) =>
32
33new Promise((resolve) => {
34
35const reader = new FileReader();
36
37reader.onloadend = () => resolve(reader.result);
38
39reader.readAsDataURL(f);
40
41});
42
43
44/* Read data */
45
46const data = await readData(file);
47
48
49/* upload the converted data */
50
51return await this.$cloudinary.upload(data, {
52
53upload_preset: "nuxtjs-image-pdf-converter",
54
55folder: `nuxtjs-image-pdf-converter/images/${randomTag}`,
56
57tags: [randomTag],
58
59});
60
61})
62
63);
64
65const resp = await this.$axios.get(`/api/generate-pdf?tag=${randomTag}`);
66
67
68
69this.pdfUrl = resp.data.pdf.secure_url;
70
71
72
73this.uploading = false;
74
75},
76
77},
78
79};

Within our api.js, we will call on the cloudinary multi method. This is the method we will use to create a single PDF from all the uploaded images.

To do this, we will update the configuration we created for the route /generate.pdf. This is the code contained in the app.all(..) method we created earlier.

1//server-middleware/api.js
2
3app.all('/generate-pdf', async (req, res) => {
4
5const pdf = await cloudinary.v2.uploader.multi(
6
7req.query.tag,
8
9{ format: "pdf" },
10
11);
12
13
14
15res.json({ pdf: pdf })
16
17})

We will call the method requesting cloudinary to create a new pdf. We will then send back the response to our front-end.

We will then render a link on the front-end allowing our users to view the newly created PDF.

1<!-- pages/images-to-pdf.vue -->
2
3<a
4
5:href="pdfUrl"
6
7target="_blank"
8
9>
10
11View PDF
12
13</a>

Converting PDFs to Images

We will first ensure that only PDF file types can be uploaded by our users. This can be done using simple HTML

1<!-- pages/pdf-to-images.vue -->
2
3<form @submit.prevent="submit">
4
5<input
6
7type="file"
8
9accept="application/pdf"
10
11name="pdf"
12
13/>
14
15<button>Convert</button>
16
17</form>

On upload, we will upload the PDF document to cloudinary. We will receive an object containing the public_id and the pages in the document on upload. We will then use this data to generate URLs rendering each page as an image.

To achieve this, let us add the following code to the section of our pages/pdf-to-images.vue file. You can find the whole file on github here.

1// pages/pdf-to-images.vue
2
3export default {
4
5data() {
6
7return {
8
9images: null,
10
11};
12
13},
14
15methods: {
16
17async submit(e) {
18
19this.uploading = true;
20
21const file = e.target.pdf.files[0];
22
23/* create a reader */
24
25const readData = (f) =>
26
27new Promise((resolve) => {
28
29const reader = new FileReader();
30
31reader.onloadend = () => resolve(reader.result);
32
33reader.readAsDataURL(f);
34
35});
36
37/* Read data */
38
39const data = await readData(file);
40
41/* upload the converted data */
42
43const upload = await this.$cloudinary.upload(data, {
44
45upload_preset: "nuxtjs-image-pdf-converter",
46
47folder: `nuxtjs-image-pdf-converter/PDFs`,
48
49});
50
51this.images = [];
52
53for (let page = 1; page <= upload.pages; page++) {
54
55this.images.push({
56
57page,
58
59url: this.$cloudinary.image.url(upload.public_id, {
60
61page,
62
63}),
64
65});
66
67}
68
69this.uploading = false;
70
71},
72
73},
74
75};

Once the images array has been filled with the links, we can then render them for our users to view:

1<!-- pages/pdf-to-images.vue -->
2
3<ul>
4
5<li v-for="(image, index) in images" :key="index">
6
7<a
8
9:href="image.url"
10
11target="_blank"
12
13>
14
15Page {{ image.page }}
16
17</a>
18
19</li>
20
21</ul>

Running the project

To run the project locally, use the following command in the nuxtjs-image-pdf-converter project folder:

1yarn dev
2
3# OR
4
5npm run dev

Conclusion

In the above article, we have reviewed how to convert PDFs to images and vice versa with the help of Cloudinary. There are additional formats we can convert between with additional options. Feel free to review the resources to read on what's possible.

Refference

Cloudinary Nuxt

Express.Js

Vue.Js

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.