How to Merge Images into PDF in Salesforce

In Salesforce, the prestigious CRM system, we may often attach images in records, like expenses and invoices, and need to merge all the images into a PDF file for printing and archiving. Since Salesforce does not provide such a feature by default, in this article, we are going to build a lightning web component to merge images to PDF.

Dynamsoft Document Viewer is used as the SDK for image editing and PDF generation. The SDK compiles several PDF and image processing libraries as WASM, which cannot be directly used in a lightning web component, but we can use iframe to bypass this restriction.

You can learn about how to start a lightning web component from scratch in the previous blog and the usage of Dynamsoft Document Viewer to merge images to PDF in another blog. We are going to focus on the implementation of the component.

Screenshot of the final result:

document viewer

Merge Images to PDF in Lightning Web Component

Let’s do this in steps.

Write an HTML5 Page to Merge Images to PDF

  1. Create a new HTML file with the following template:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <title>DDV - HelloWorld</title>
    </head>
    <style>
        html,body {
            width: 100%;
            height: 100%;
            margin:0;
            padding:0;
            overscroll-behavior-y: none;
            overflow: hidden;
        }
    
        #container {
            width: 100%;
            height: 100%;
        }
    </style>
    <body>
        <div id="container"></div>
    </body>
    <script type="module">
    </script>
    </html>
    
  2. Include dependencies.

    Include Dynamsoft Document Viewer in the page.

    <script src="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/ddv.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/ddv.css">
    
  3. Initialize Dynamsoft Document Viewer. You can apply for a license here.

    (async () => {
        // Public trial license which is valid for 24 hours
        // You can request a 30-day trial key from https://www.dynamsoft.com/customer/license/trialLicense/?product=mwc
        Dynamsoft.DDV.Core.license = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==";
        Dynamsoft.DDV.Core.engineResourcePath = "https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/engine";// Lead to a folder containing the distributed WASM files
        await Dynamsoft.DDV.Core.init();
        Dynamsoft.DDV.setProcessingHandler("imageFilter", new Dynamsoft.DDV.ImageFilter());
    })();
    
  4. Create an EditViewer. We can add images into it.

    const editViewer = new Dynamsoft.DDV.EditViewer({
        container: "container",
    });
    
  5. Add an event listening to messages from another window. If it receives a message, save the images as PDF and post it as dataURL for the parent window.

    window.addEventListener(
      "message",
      async (event) => {
        console.log("received message in iframe");
        console.log(event);
        let pdf = await editViewer.currentDocument.saveToPdf();
        let dataURL = await blobToDataURL(pdf);
        window.parent.postMessage(dataURL);
      },
      false,
    );
    
    function blobToDataURL(blob) {
      return new Promise((resolve,reject)=>{
        var reader = new FileReader();
        reader.readAsDataURL(blob);
        reader.addEventListener("error",reject);
        reader.onload = function (e) {
          resolve(e.target.result);
        }
      })
    }
    

Include the HTML5 Page as Static Resources of Lightning Web Component

  1. Create a ddv folder under staticresources.

  2. Add ddv.resource-meta.xml as the meta file under staticresources.

    <?xml version="1.0" encoding="UTF-8"?>
    <StaticResource xmlns="http://soap.sforce.com/2006/04/metadata">
        <cacheControl>Private</cacheControl>
        <contentType>application/zip</contentType>
    </StaticResource>
    
  3. Add the HTML page as index.html in the ddv folder.

Use the HTML5 Page in a Lightning Web Component

  1. Create a new lightning web component.

  2. In the HTML, add a button to start the merging, a container for the HTML5 page and an anchor to store the PDF dataURL.

    <template>
      <lightning-card title="Dynamsoft Document Viewer" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
          <lightning-button label="Merge into PDF" onclick={merge}></lightning-button>
          <div>
            <label>
              Received dataURL:&nbsp;
              <a target="_blank" href={dataURL}>{dataURLLabel}</a>
            </label>
          </div>
        </div>
        <div class="ddvViewer" id='ddvViewer' style="height:500px;width:400px;"></div>
      </lightning-card>
    </template>
    
  3. In the renderedCallback lifecycle event, append an iframe with its src set to the HTML5 page we wrote. We also add an event listening to dataURL messages from the iframe.

    import { LightningElement } from 'lwc';
    import ddv from '@salesforce/resourceUrl/ddv';
    export default class DocumentManager extends LightningElement {
      ddvInitialized = false;
      ddvFrame;
      dataURL = "";
    
      get dataURLLabel() {
        if (this.dataURL.length > 15) {
          return this.dataURL.substring(0,10) + "...";
        }else{
          return this.dataURL;
        }
      }
         
      renderedCallback() {
          if (this.ddvInitialized) {
              return;
          }
          this.ddvInitialized = true;
          window.addEventListener(
            "message",
            (event) => {
              this.dataURL = event.data;
            },
            false,
          );
          this.ddvFrame = document.createElement('iframe');
          this.ddvFrame.src = ddv + "/index.html";
          // div tag in which iframe will be added should have id attribute with value myDIV
          this.template.querySelector("div.ddvViewer").appendChild(this.ddvFrame);
    
          // provide height and width to it
          this.ddvFrame.setAttribute("style","height:100%;width:100%;");
      }
    }
    
  4. The merge function which is called when the merge images to PDF button is pressed.

    merge(){
      this.ddvFrame.contentWindow.postMessage("merge");
    }
    

All right, we’ve completed the component. We can take a step further to use APEX to get all the files on Salesforce. We may discuss this in the future.

Source Code

https://github.com/tony-xlh/Merge-Images-to-PDF