HTML to PDF conversion

  • Goal:
    Hi, First of all let me tell you that I am new to Retool, I am using an external API which fetch the invoice in HTML format, so I want the response of Invoice query to be converted into PDF format and download it when I run the JavaScript code.

I tried using different suggestions but looks like there is not success. Can someone please help me to know if this is possible in retool to "Convert the response of the from HTML to pdf and download the PDF file" ? If, yes how to do it

Hi @Shakeel_Ahmed, there are a number of ways to accomplish this. Here's one:

If you're fetching your invoice from an external API, you can use library like turndown JS to convert the HTML to Markdown, then use Retool's PDF Exporter resource.

Steps:

  1. Settings > Libraries > Add TurndownJS
  2. Use the turndown library in a JS query
// Step 1: Get your HTML content (assuming you have it from an API response)
var htmlContent = query1.data.htmlBlock; // Replace this with your actual HTML content

//  Step 2: Create a new instance of Turndown
var turndownService = new TurndownService();

// Step 3: Convert HTML to Markdown
var markdown = turndownService.turndown(htmlContent);

// Step 4: Display or use the Markdown content
return markdown;
  1. Create a PDF Exporter Resource and reference the markdown returned in the previous step

Let me know if that worked for you!

Hi @mroknich Thank you so much, it is really nice help. I succeed to export the PDF now but the problem now is the layout of the PDF is changed. My Invoice is one page only while the retool PDF exporter converted the data into 4 pages which means it doesn't look like a invoice.

Here I am sharing the screenshot of my invoice of A4 size PDF and the screenshot from retool exported PDF.
Can you please help me to know how to export the PDF into original layout ? I want to export the PDF in the same view as it is showing in the screenshot invoice.png


Hii @Shakeel_Ahmed ,

For downloading TAX INVOICE, use the following JavaScript code to generate and download the invoice.
dom-to-image : <script src="https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js"></script>
pdf-library: <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script>

const generatePDF = async () => {
                try {
                    if (!Details) {
                        console.error('Details are not set properly');
                        return;
                    }

                    const payslipElement = document.getElementById('invoice');
                    if (!payslipElement) {
                        console.error('Payslip element not found in the DOM');
                        return;
                    }
                    payslipElement.style.visibility = 'visible';
                    const clone = payslipElement.cloneNode(true);
                    clone.style.width = 'auto';
                    document.body.appendChild(clone);
                    const imgData = await domtoimage.toPng(clone, {
                        quality: 1.0,
                        bgcolor: '#ffffff',
                        width: clone.offsetWidth,
                        height: clone.offsetHeight,
                    });
                    document.body.removeChild(clone);
                    const { PDFDocument } = PDFLib;
                    const pdfDoc = await PDFDocument.create();
                    const page = pdfDoc.addPage([842, 695]); // set the page size as per requirements
                    const img = await pdfDoc.embedPng(imgData);
                    const imgWidth = img.width;
                    const imgHeight = img.height;
                    const pdfWidth = page.getWidth();
                    const pdfHeight = page.getHeight();
                    let scaleFactor = 1;
                    if (imgWidth > pdfWidth || imgHeight > pdfHeight) {
                        const widthScaleFactor = pdfWidth / imgWidth;
                        const heightScaleFactor = pdfHeight / imgHeight;
                        scaleFactor = Math.min(widthScaleFactor, heightScaleFactor);
                    }
                    const newWidth = imgWidth * scaleFactor;
                    const newHeight = imgHeight * scaleFactor;
                    const xOffset = (pdfWidth - newWidth) / 2;
                    const yOffset = (pdfHeight - newHeight) / 2;
                    page.drawImage(img, {
                        x: xOffset,
                        y: pdfHeight - newHeight - yOffset,
                        width: newWidth,
                        height: newHeight,
                    });
                    const pdfBytes = await pdfDoc.save();
                    const blob = new Blob([pdfBytes], { type: 'application/pdf' });
                    const link = document.createElement('a');
                    link.href = URL.createObjectURL(blob);
                    link.download = 'Invoice.pdf';
                    link.click();
                    payslipElement.style.visibility = 'hidden';
                } catch (error) {
                    console.error('Error generating PDF:', error);
                }
            };

I hope this information is useful. If you have any questions, feel free to ask.

1 Like

Hi @ZeroCodez Thank you!
Which data I have to pass in this code ?

  1. Response from API "Invoice"?
    OR
  2. Response from JS suggested by @mroknich ?

Hello @Shakeel_Ahmed ,

Pass the response from API "Invoice".
Get the HTML component ID, and set it to the above given code in paySlipElement

1 Like

Hi @ZeroCodez Something like this ?
const payslipElement = Invoice.data.d.HTMLInvoice;
or
const payslipElement = document.getElementById(Invoice.data.d.HTMLInvoice);
I tried like this but it is not downloading any PDF, while the Code runs successfully without any error in console.
BTW, I was looking something simpler explained by @mroknich ,

1 Like

Hi @ZeroCodez ,
Here is the path of the API "Invoice" response: Invoice.data.d.HTMLInvoice (This returns the HTML Data)
Can you please help me to understand how exactly I have to set this in your suggested code ?

@ZeroCodez @mroknich Hi,

Any update over this topic ?
I am looking for an option which can download the pdf file in the original page format.

Hi @Shakeel_Ahmed, I haven't implemented @ZeroCodez's approach but here are two alternatives:

2 Likes