The Best Way to Edit a PDF Document in Salesforce

PDF documents/reports are frequently used in Salesforce. There is often a case when we need to have some customization done to the existing documents. In this article, we’ll explore various use cases and how to implement them within Salesforce.

PDF.co is a REST-based API providing various PDF-related features. It’s super easy to use and integrates easily into the project, which makes it one of the best ways to edit PDF documents in Salesforce. Here, we’ll be using PDF.co endpoints with Salesforce Apex.

Let’s get started with it!

How PDF.co API Works

PDF.co API Endpoints do the heavy lifting work when processing PDF documents. In order to use PDF.co API, it’s necessary to pass the API Key in the request header.

For example, consider the following code snippet.

HttpRequest req = new HttpRequest();
req.setBody(jsonPayload);
req.setHeader('x-api-key', API_KEY);

Here, we’re first creating an HttpRequest object. Next, with the use of the “setHeader” method, the new header key “x-api-key” is set with the PDF.co API key value.

This API key is necessary for request authentication on the PDF.co server side. One can retrieve this API key by signing up to PDF.co.

Source Code Demonstrating How PDF.co Works in Apex

Let’s take a scenario where we need to add Text to an existing PDF. Here, we’ll look into the source code first, and then we’ll analyze it.

public class Program {
   
    public static void fetchDataAndCreatePDF()
    {
        String API_KEY = '*****************************************';      
        string SourceFileUrl = 'https://bytescout-com.s3.amazonaws.com/files/demo-files/cloud-api/pdf-edit/sample.pdf';
        string Pages = '';
        string Password = '';
        String DestinationFile = 'result';
        string Type = 'annotation';
        Integer X = 400;
        Integer Y = 600;
        string Text = 'APPROVED';
        string FontName = 'Times New Roman';
        Decimal FontSize = 24;
        string FontColor = 'FF0000';
        Map<string, Object> parameters = new Map<string, Object>();
        parameters.put('name', DestinationFile);
        parameters.put('password', Password);
        parameters.put('pages', Pages);
        parameters.put('url', SourceFileUrl);
        parameters.put('type', Type);
        parameters.put('x', X);
        parameters.put('y', Y);
        parameters.put('text', Text);
        parameters.put('fontname', FontName);
        parameters.put('size', FontSize);
        parameters.put('color', FontColor);
       
        string jsonPayload = Json.serialize(parameters);
        try
        {
            string url = 'https://api.pdf.co/v1/pdf/edit/add';
            HttpRequest req = new HttpRequest();
            req.setBody(jsonPayload);
            req.setHeader('x-api-key', API_KEY);
            req.setHeader('Content-Type', 'application/json');
            req.setEndpoint(url);
            req.setMethod('POST');
            req.setTimeout(60000);
            Http http = new Http();
            HTTPResponse res = http.send(req);
            if(res.getStatusCode() == 200)
            {
                System.Debug('res ' + res);
                Map<String, Object> deserializedBody =  (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
                String urlVal = (String)deserializedBody.get('url');
                downloadPDFAndStore(urlVal, DestinationFile);
            }
            else
            {
                System.debug('Success Response ' + res.getBody());
                System.Debug(' Status ' + res.getStatus());
                System.Debug(' Status Code' + res.getStatusCode());
                System.Debug(' Status String' + res.toString());
            }
           
        }
        catch(Exception ex)
        {
            String errorBody = 'Message: ' + ex.getMessage() + ' -- Cause: ' + ex.getCause() + ' -- Stacktrace: ' + ex.getStackTraceString();
            System.Debug(errorBody);
        }
    }
 
    @TestVisible
    private static void downloadPDFAndStore(String extFileUrl, String DestinationFile)
    {
        try
        {
            Http h = new Http();
            HttpRequest req = new HttpRequest();
            extFileUrl = extFileUrl.replace(' ', '%20');
            req.setEndpoint(extFileUrl);
            req.setMethod('GET');
            req.setHeader('Content-Type', 'application/pdf');
            req.setCompressed(true);
            req.setTimeout(60000);
            //Now Send HTTP Request
            HttpResponse res  = h.send(req);
            if(res.getStatusCode() == 200)
            {
                blob fileContent = res.getBodyAsBlob();
                ContentVersion conVer = new ContentVersion();
                conVer.ContentLocation = 'S'; // to use S specify this document is in Salesforce, to use E for external files
                conVer.PathOnClient = DestinationFile + '.pdf'; // The files name, extension is very important here which will help the file in preview.
                conVer.Title = DestinationFile; // Display name of the files
                conVer.VersionData = fileContent;
                insert conVer;
                System.Debug('Success');
            }
            else
            {
                System.debug('Success Response ' + res.getBody());
                System.Debug(' Status ' + res.getStatus());
                System.Debug(' Status Code' + res.getStatusCode());
                System.Debug(' Status String' + res.toString());
            }
        }
        catch(Exception ex)
        {
            String errorBody = 'Message: ' + ex.getMessage() + ' -- Cause: ' + ex.getCause() + ' -- Stacktrace: ' + ex.getStackTraceString();
            System.Debug(errorBody);
        }
    }
}

Now that we’ve gone through the full source code, let’s analyze its main snippets.

PDF Editing – Source Code Analysis

To begin with, Inside the main class named “Program” we have a static method named “fetchDataAndCreatePDF”. At the beginning of this method, we’re preparing all variables. In other terms, we’re preparing all useful information such as PDF.co API key, Input PDF URL, what text to be added, font and text placement information, etc.

String API_KEY = '*****************************************';      
        string SourceFileUrl = 'https://bytescout-com.s3.amazonaws.com/files/demo-files/cloud-api/pdf-edit/sample.pdf';
        string Pages = '';
        string Password = '';
        String DestinationFile = 'result';
        string Type = 'annotation';
        Integer X = 400;
        Integer Y = 600;
        string Text = 'APPROVED';
        string FontName = 'Times New Roman';
        Decimal FontSize = 24;
        string FontColor = 'FF0000';

Next, we’re preparing a Map object for request parameters. Later, these parameters will be JSON serialized.

Now, we have all information ready and ready to make a call to PDF.co.

string url = 'https://api.pdf.co/v1/pdf/edit/add';
            HttpRequest req = new HttpRequest();
            req.setBody(jsonPayload);
            req.setHeader('x-api-key', API_KEY);
            req.setHeader('Content-Type', 'application/json');
            req.setEndpoint(url);
            req.setMethod('POST');
            req.setTimeout(60000);
            Http http = new Http();
            HTTPResponse res = http.send(req);

In the end, the Response of this is processed and the URL is saved to a specific location using “downloadPDFAndStore” method.

Useful Resources to Implement Other PDF Functionality

PDF.co Related Links

Adding Text to PDF

Adding Image to PDF

Filling PDF Forms

Password-Protecting PDF

Merging PDF

Lastly, I hope this article will be helpful to you to implement PDF-related features with ease in your Salesforce application. There are other ways to transform PDFs like PDF editing in PHP, or scanned file editing using Make integration.

To learn about all the options, visit PDF Editor API page.