Home > Net >  Next.js API: Watermark PDF using query parameters in URL
Next.js API: Watermark PDF using query parameters in URL

Time:09-25

this is my first time testing out Next.js API, I am also quite new to the whole Next.js/React world so bear with me.

The goal for this API route is to trigger an automatic download of a PDF with a custom watermark generated from API URL query parameters like this: /api/PDFWatermark?id=123&firstname=John&lastname=Doe

In order to create the watermark I am using pdf-lib and I am using a modified version of this code to watermark my PDF. To generate a modified and downloadable version of the original PDF pdfDoc I have tried to create a blob using the pdfBytes after the watermarking. After the blob is created I thought I could add it to an anchor attached to the DOM.

When commenting out the blob and anchor code, two errors occur:

  1. ReferenceError: Blob is not defined
  2. ReferenceError: document is not defined (possibility because there is no DOM to attach the anchor link)

At this point I am only able to print the pdfBytes as json, I am not able to create and download the actual watermarked PDF file.

Is there a way to auto download the pdfBytes as a PDF file when the API is called?

UPDATE

Working code below after changing modifyPDF to return a buffer:

const pdfBytes = await pdfDoc.save();
return Buffer.from(pdfBytes.buffer, 'binary');

And:

export default async function handler(req, res) {
  const filename = "test.pdf";
  const {id, firstname, lastname} = req.query;
  const pdfBuffer = await modifyPDF(firstname, lastname, id);
  res.status(200); 
  res.setHeader('Content-Type', 'application/pdf');  // Displsay
  res.setHeader('Content-Disposition', 'attachment; filename=' filename);
  res.send(pdfBuffer);
}

WORKING:

import {PDFDocument, rgb, StandardFonts  } from 'pdf-lib';

export async function modifyPDF(firstname, lastname, id) {


    const order_id = id; 
    const fullname = firstname   " "   lastname;

    const existingPdfBytes = await fetch("https://pdf-lib.js.org/assets/us_constitution.pdf").then((res) => res.arrayBuffer());
    const pdfDoc = await PDFDocument.load(existingPdfBytes);
    const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);


    const watermark = fullname   " (OrderID: "   id   ")";

    
     // Set Document Metadata
     pdfDoc.setSubject(watermark);

     // Get pages
     const pages = pdfDoc.getPages();
 
     // Iterate every page, skip first
     //pages.slice(1).forEach(page => {
     pages.forEach(page => {
   
       
       // Get the width and height of the page
       const {
         width,
         height
       } = page.getSize()
 
       // Watermark the page
       page.drawText(watermark, {
             x: 70,
             y: 8,
             size: 10,
             font: helveticaFont,
             color: rgb(0.95, 0.1, 0.1),
       })
     })
  
    const pdfBytes = await pdfDoc.save();
    return Buffer.from(pdfBytes.buffer, 'binary');
 

}


export default async function handler(req, res) {
  const filename = "test.pdf";
  const {id, firstname, lastname} = req.query;
  const pdfBuffer = await modifyPDF(firstname, lastname, id);
  res.status(200); 
  res.setHeader('Content-Type', 'application/pdf');  // Displsay
  res.setHeader('Content-Disposition', 'attachment; filename=' filename);
  res.send(pdfBuffer);
}

CodePudding user response:

Change your modifyPDF to return a buffer

[...]
    const pdfBytes = await pdfDoc.save();
    return Buffer.from(pdfBytes.buffer, 'binary');
[...]

Let the API return the PDF to the browser through the handler:

export default async function handler(req, res) {

    const {id, firstname, lastname} = req.query;
    const pdfBuffer = await modifyPDF(firstname, lastname, id);
    res.status(200); 
    
    res.setHeader('Content-Type', 'application/pdf'); 
    res.setHeader('Content-Disposition', 'attachment; filename=' filename); 
    // Edited as the linked example reports: 
    // res.type('pdf'); // and might not work
    res.send(pdfBuffer);
}

Untested but you should get the gist.

Here's the full example from the library itself

  • Related