AWS Partner Network (APN) Blog

Reimagining Developer and Document Experiences with the Adobe PDF Services API

By Ben Vanderburg, Principal Product Evangelist – Adobe
By Raymond Camden, Sr. Developer Evangelist – Adobe

By Kenny Rajan, Vikram Mehto, John Chao, Solutions Architects – AWS

Adobe-AWS-Partners

Businesses today manage many types of agreements, from employer and customer contracts to partnership and vendor agreements.

When these agreements are created, they must be customized with elements like specific terms and company branding before they can move to the review and approval stage, and ultimately sent out for signature.

Generating agreements can be tedious, manually intensive work. Automation can solve some of the challenges, but not all. For starters, it’s difficult to automate custom clauses and insert data based on conditional elements such as those required for sales proposals and legal contracts:

  • Sales proposals and contracts: While much proposal content is reusable (firm capabilities, team profiles, statement of work terms) important deal specifics, such as customer information, project scope, and pricing, need to be added. Manually finding and replacing content, as well as highlighting signature fields, is time consuming and monotonous.
    .
  • Legal contracts: Legal contracts require inputs from multiple stakeholders, such as agents, legal resources, and business managers. In many cases, these contracts require company branding, conditions and clauses, and data from various sources, such as the custom pricing and terms.

Once agreements are drafted, they often undergo a review and approval process via multiple channels (email, Slack, text, phone) before being emailed out for signature. The end-to-end process can be extremely cumbersome, even with the solutions available in the market today.

AWS ISV Partner Adobe offers a variety of services to help automate and streamline the end-to-end process of document generation, approval and e-signature.

To start, Adobe Document Generation allows you to create PDF and Microsoft Word documents from templates and JSON data. This allows businesses to automate the creation of legal contracts and sales proposals with support for conditional logic, dynamic fields, and more for end-to-end workflow automation.

The Document Generation API is available as part of Adobe PDF Services APIs. Developers can subscribe for free through AWS Marketplace. There is a two-week evaluation period with up to 500 document transactions, allowing you to try out all of the services. See the AWS Marketplace pricing information to understand the price per unit of Adobe Service API transactions after the free evaluation period.

There are over 15 different PDF and document manipulation APIs to build custom end-to-end agreements, content publishing, data analysis workflow experiences, and more. These integrate with Adobe Sign to quickly and easily complete these agreement workflows with signatures.

The PDF Services API can also be paired with Adobe PDF Embed API, a free client-side PDF viewer, to streamline review and approval within the broader process.

Solution Overview

You can build an API-powered application that uses a custom template with dynamic fields that can be mapped to data inputs to be merged at runtime.

Using the Adobe Document Generation Tagger add-in for Word, you can quickly customize any document template with branding and the agreement-specific legal elements. Within the application layer, Adobe Document Generation API can be called, sending along the custom template and JSON data to be merged.

After the PDF has been dynamically created using the Document Generation API, developers can use Adobe PDF Embed API to embed the generated document into their application. PDF Embed API has commenting and annotation capabilities, so it can be used for stakeholder review, redlines, and approval.

High-Level Document Workflow

  1. A JSON data model will be built within the application using available data from a database.
  2. Using the JSON data model, developers can upload it to the legal contract template using the Adobe Document Tagger to create custom data fields.
  3. The data fields that are uploaded will be used to map the developer’s custom data fields to the template.
  4. Conditional logic, calculations, and repeating elements will be placed in areas of the template to create further customization based on data inputs and logic.
  5. Insert Adobe Sign text tags in the template to generate document output ready to be consumed by Adobe Sign.
  6. Developers send both the Word template and JSON data input file to Adobe Document Generation API and receives a PDF file back.
  7. The PDF file can be embedded in the application for review and approval with Adobe PDF Embed API.

Once approved, the agreement is sent out for signature using Adobe Sign.

Setting Up Adobe PDF Services API

In this post, we’ll walk you through the steps for developers to subscribe to PDF Services API in AWS Marketplace and use a sample node.js application project to digitize your document process. After you’re familiar with the APIs, you can leverage the samples in your own cloud-powered applications.

To get started, set up credentials to use PDF Services API. We can register an account and use the Node.js Quick Start to verify our credentials work before integrating the functionality into a larger application.

  • Adobe PDF Service API are available in two subscriptions:
  • In the subsequent AWS Marketplace page, click Subscribe to set up your account.
  • This step will redirect you to the Adobe site to register your API key through a custom Adobe onboarding workflow.
  • Click Get Started in the welcome page for PDF Services API.
  • Enter your Adobe credentials or create account.
  • Enter a name and description for your credentials. Select Create personalized code sample to download a code sample with your credentials. Also check Node.js as programing language for this exercise. Click Create Credentials.

Adobe-PDF-Services-1

Figure 1 – Creating Adobe API credentials.

  • On the next screen, a sample code zip file (PDFServicesSDK-Node.jsSamples.zip) will be downloaded to your local system. Save the samples in your local system or in your application securely, as the sample code includes Adobe API credentials. You can also use the same screen to go back to the AWS Marketplace subscription, Adobe documentation, Adobe forums, or other code samples.

Adobe-PDF-Services-2

Figure 2 – Adobe documentation and code samples section.

Adobe-PDF-Services-3

Figure 3 – AWS console section to manage subscriptions.

Samples for the PDF Services Node.js SDK

This sample project helps you get started with the PDF Services Node.js SDK. For this tutorial, you should have the following prerequisites:

  • The sample JavaScript files illustrate how to perform PDF-related actions (such as converting to and from the PDF format) using the SDK. Please note the PDF Services Node.js SDK supports only server-side use cases.
  • An AWS account with the necessary permissions to configure AWS Cloud9 and Amazon Simple Storage Service (Amazon S3).
  • Install Node.js : Version 10.13 or above. Check out the Node installation instructions.

Running Samples with AWS Cloud9

For the purpose of this post, we’ll use AWS Cloud9, a cloud-based integrated development environment (IDE) that lets you write, run, and debug your code with just a browser. It includes a code editor, debugger, and terminal. See AWS Cloud9 documentation for more information.

Since your Cloud9 IDE is cloud-based, you can work on your projects from your office, home, or anywhere using an internet-connected machine. There is no additional charge for AWS Cloud9.

If you use an Amazon Elastic Compute Cloud (Amazon EC2) instance for your AWS Cloud9 development environment, you pay only for the compute and storage resources (an EC2 instance or Amazon EBS volume, for example) that are used to run and store your code. See AWS Cloud9 pricing documentation for more information.

Setting Up AWS Cloud9

  • Create an AWS Cloud9 environment; see the AWS Cloud9 documentation for instructions.
  • Go to file menu from your AWS Cloud9 home page and click Upload Local Files to upload the sample Node.js project you downloaded previously.
  • In AWS Cloud9, access the sample project direct folder you uploaded in the previous step. Run the npm install command.
  • The sample code is available under the src folder. The Readme section of the project provides many examples to convert files of various formats to PDF. You can also see Adobe SDK documentation sub-sections on to how to run the samples.
  • Test files used by the samples can be found in the resources folder. When executed, all samples create an output child folder under the project root directory to store their results.
  • We’ll also demonstrate in this post how to integrate the files with Amazon S3, a simple web services interface you can use to store and retrieve any amount of data, at any time, from anywhere on the web.

Create a PDF File with Adobe PDF Services API

Agreements and proposals are often written in a productivity application like Word. We can add functionality to your application to accept documents in this format and convert the document to PDF.

The sample code you downloaded earlier contains a script named create-pdf-from-docx.js in the src folder, which can be executed to convert a Word document to PDF. The following example will illustrate how to use the PDF Services SDK to convert a DOCX file to PDF.

The following function, convertDocumentToPDF, takes an uploaded document and converts it to a PDF in a different folder:

  • In AWS Cloud9, access the sample project direct folder you uploaded in the previous step. Run the npm install aws-sdk command.
  • Execute the following node command to create a PDF file from a docx file stored in the resource directory of the sample:

node src/createpdf/create-pdf-from-docx.js

  • The following script, convertDocumentToPDF, uses a docx file from the resources folder and converts it to a PDF in the output directory:
try {
    // Initial setup, create credentials instance.
    const credentials =  PDFServicesSdk.Credentials
        .serviceAccountCredentialsBuilder()
        .fromFile("pdftools-api-credentials.json")
        .build();

    // Create an ExecutionContext using credentials and create a new operation instance.
    const executionContext = PDFServicesSdk.ExecutionContext.create(credentials),
        createPdfOperation = PDFServicesSdk.CreatePDF.Operation.createNew();

    // Set operation input from a source file.
    const input = PDFServicesSdk.FileRef.createFromLocalFile('resources/createPDFInput.docx');
    createPdfOperation.setInput(input);

    // Execute the operation and Save the result to the specified location.
    createPdfOperation.execute(executionContext)
        .then(result => result.saveAsFile('output/createPDFFromDOCX.pdf'))
        .catch(err => {
            if(err instanceof PDFServicesSdk.Error.ServiceApiError
                || err instanceof PDFServicesSdk.Error.ServiceUsageError) {
                console.log('Exception encountered while executing operation', err);
            } else {
                console.log('Exception encountered while executing operation', err);
            }
        });
} catch (err) {
    console.log('Exception encountered while executing operation', err);

}
  • You may notice a general pattern with the code: it builds a credentials object and an execution context, initializes some operation, and then executes the operation with the execution context. You’ll see this pattern throughout the sample code.

In this example, we have used the files from PDF Services API samples uploaded in the AWS Cloud9 environment.

In a real-world use case, you can make a few additions to the upload function within your application to call this function and automatically convert the Word documents to a PDF.

The following code builds the destination path for the converted PDF and initiates the conversion:

const documentFolder = path.join(__dirname, "../ docs");
var extPosition = req.files.source.name.lastIndexOf('.') - 1; 
if(extPosition < 0 ) {
extPosition = req.files.source.name.length 
}
const destinationName = path.join(documentFolder, 
req.files.source.name.substring(0, extPosition) + '.pdf'); console.log(destinationName); 
logger.info('converting to ${destinationName}') convertDocumentToPDF(uploadPath, destinationName
  • Next, access the output folder in AWS cloud9 to validate the results.

Adobe-PDF-Services-4

Figure 4 – Converted Word to PDF document sample.

Converting Other File Types to PDF

The document toolkit converts other formats to PDF, such as static HTML, another common document type. The toolkit accepts HTML documents packaged as a .zip file with all resources referenced by the document (CSS files, images, and other files) in the same .zip file.

  • The HTML document itself must be named index.html and placed in the root of the .zip file. If we have a .zip file containing HTML we want to convert, we can use the following code:
//Create an HTML to PDF operation and provide the source file to it htmlToPDFOperation = PDFServicesSdk.CreatePDF.Operation.createNew(); const input = PDFServicesSdk.FileRef.createFromLocalFile(sourceZipFile); htmlToPDFOperation.setInput(input); 
// custom function for setting options setCustomOptions(htmlToPDFOperation); 
// Execute the operation and Save the result to the specified location. htmlToPDFOperation.execute(executionContext) 
.then(result => result.saveAsFile(destinationPdfFile)) .catch(err => { 
logger.error('Exception encountered while executing operation'); }); 
The function setCustomOptions specifies other PDF settings, such as the page size. Here, you can see the function sets the page size to 11.5 by 11 inches: 
const setCustomOptions = (htmlToPDFOperation) => {
const pageLayout = new PDFServicesSdk.CreatePDF.options.html.PageLayout(); pageLayout.setPageSize(11.5, 8); 
const htmlToPdfOptions =
new PDFServicesSdk.CreatePDF.options.html.CreatePDFFromHtmlOptions.Builder() .includesHeaderFooter(true) 
    .withPageLayout(pageLayout)
.build(); htmlToPDFOperation.setOptions(htmlToPdfOptions); 
};
  • Opening an HTML document containing some terms, we get the following within the browser:

Adobe-PDF-Services-5

Figure 5 – Sample HTML page for document conversation.

  • The source for this document is composed of a CSS file and an HTML file:

Adobe-PDF-Services-6

Figure 6 – Cascading Style Sheet for Static HTML content.

  • After processing the HTML file, we have the same text in PDF format:

Adobe-PDF-Services-7

Figure 7 – Converted html to pdf document sample.

Converting HTML Documents Stored in Amazon S3

The sample below creates a PDF file from a static HTML file. Since HTML web pages typically contain external assets, the input file must be a .zip file containing an index.html at the top level of the archive, as well as any dependencies such as images, CSS files, and so on.

In this example, we’ll upload the .zip test file containing HTML content to an Amazon S3 bucket. This service provides a highly durable storage infrastructure designed for mission-critical and primary data storage. Your applications can use S3 to store the any input documents such as proposals and agreements.

With Amazon S3 default encryption, you can set the default encryption behavior for a bucket so that all new objects are encrypted when they are stored there. See the Amazon S3 documentation to learn more about the encryption behavior.

Adobe-PDF-Services-8

Figure 8 – High-level architecture – API flow between application and S3.

Amazon S3 with Adobe PDF Services API Integration

  • Create Amazon S3 bucket. See the S3 documentation for steps.
  • Create an input folder and upload the resources/createPDFFromStaticHtmlInput.zip test file from your sample package.
  • Create an output folder in the same S3 bucket to the results. The sample script “`create-pdf-from-static-html.js“` creates a PDF file from a .zip file containing the input HTML file and its resources. See Adobe API SDK documentation of create-pdf-operation.js to see instructions on the structure of the zip file.
  • From your AWS Cloud9 console adobe-dc-pdf-tools-sdk-node-samples, execute the below command to create an input folder to temporarily store the input files from S3. Input documents stored in the input files will be deleted after the document conversation is complete and stored in S3.

$ mkdir input

  • From the AWS Cloud9 console, access the file create-pdf-from-static-html.js’ in /src/createpdf directory path. Replace the current script with the below code (copy and paste).
/*
 * Copyright 2019 Adobe
 * All Rights Reserved.
 *
 * NOTICE: Adobe permits you to use, modify, and distribute this file in
 * accordance with the terms of the Adobe license agreement accompanying
 * it. If you have received this file from a source other than Adobe,
 * then your use, modification, or distribution of it requires the prior
 * written permission of Adobe.
 */
 
const PDFServicesSdk = require('@adobe/documentservices-pdftools-node-sdk');
const AWS = require('aws-sdk');
const fs = require('fs');
 
const BUCKET_NAME = "pdf-node"
const inputFileName = 'createPDFFromStaticHtmlInput.zip'; //name of the file to convert and upload
const outputFileName = 'createPDFFromStaticHtmlInput.pdf'; //output file name
 
const AWS_API_VERSION = '2006-03-01';

const PDF_CREDENTIALS_PATH = "./../../pdftools-api-credentials.json";
const inputFilePath = './../../input/' + inputFileName;
const outputFilePath = './../../output/' + outputFileName

function configureAWS() {
    // return a promise so you can wait for this to complete
    return new Promise((resolve, reject) => {
        AWS.config.getCredentials(function (err) {
            if (err) {
                console.log("Error when getting AWS credentials", err.stack);
                reject(err);
            }
            else {
                const { accessKeyId } = AWS.config.credentials
                console.log("Access key:", accessKeyId);
                resolve(accessKeyId)
            }
        });
    })
}

function downloadDocFromS3(params) {
    return new Promise((resolve, reject) => {
        new AWS.S3({ apiVersion: AWS_API_VERSION }).getObject(params, function (err, data) {
            if (err) {
                reject(err)
            }
 
            // Download the Doc file in the input folder in local
            fs.writeFileSync(inputFilePath, data.Body);
            console.log('file downloaded successfully');
            resolve()
        });
    })
}
 

/**
 * This sample illustrates how to convert an HTML file to PDF. The HTML file and its associated dependencies must be
 * in a single ZIP file.
 * <p>
 * Refer to README.md for instructions on how to run the samples.
 */

/**
 * Sets any custom options for the operation.
 *
 * @param htmlToPDFOperation operation instance for which the options are provided.
 */

function transformDocToPDF({ locationPath, outputPath, credentialsPath }) {
    const credentials = PDFServicesSdk.Credentials
        .serviceAccountCredentialsBuilder()
        .fromFile(credentialsPath)
        .build();

    const setCustomOptions = (htmlToPDFOperation) => 
    {
    // Define the page layout, in this case an 8 x 11.5 inch page (effectively portrait orientation).
    const pageLayout = new PDFServicesSdk.CreatePDF.options.PageLayout();
    pageLayout.setPageSize(8, 11.5);

    // Set the desired HTML-to-PDF conversion options.
    
    const htmlToPdfOptions = new PDFServicesSdk.CreatePDF.options.html.CreatePDFFromHtmlOptions.Builder()
        .includesHeaderFooter(true)
        .withPageLayout(pageLayout)
        .build();
    htmlToPDFOperation.setOptions(htmlToPdfOptions);};
        
    // Create an ExecutionContext using credentials and create a new operation instance.
    const executionContext = PDFServicesSdk.ExecutionContext.create(credentials),
        htmlToPDFOperation = PDFServicesSdk.CreatePDF.Operation.createNew();

    // Set operation input from a source file.
    const input = PDFServicesSdk.FileRef.createFromLocalFile('./../../input/' + inputFileName);
    htmlToPDFOperation.setInput(input);

    // Provide any custom configuration options for the operation.
    setCustomOptions(htmlToPDFOperation);
    
    // Execute the operation and Save the result to the specified location.
    
    return new Promise((resolve, reject) => {
        // Execute the operation and Save the result to the specified location.
        htmlToPDFOperation.execute(executionContext)
            //save the converted file in the output folder
            .then(result => result.saveAsFile(outputPath)).then(() => {
                resolve();
            })
            .catch(err => {
                if (err instanceof PDFServicesSdk.Error.ServiceApiError ||
                    err instanceof PDFServicesSdk.Error.ServiceUsageError) {
                    console.log('Exception encountered while executing operation', err);
                }
                else {
                    console.log('Exception encountered while executing operation', err);
                }
                reject(err);
            });
    });
}    
    
function uploadToS3(filePath) {
    //Upload the file in the output folder of S3 bucket
    const uploadParams = {
        Key: 'output/' + outputFileName,
        Body: fs.readFileSync(filePath),
        Bucket: BUCKET_NAME
    };
    return new Promise((resolve, reject) => {
        new AWS.S3().upload(uploadParams, function (err, data) {
            if (err) {
                reject(err)
            }
            console.log(`File uploaded successfully. ${data.Location}`);
            resolve();
        })
 
    });
}
 
// main function

async function convertPdf() {
    try {
        await configureAWS();
 
        const params = {
            Key: 'input/' + inputFileName,
            Bucket: BUCKET_NAME
        };
 
        const docPath = inputFilePath;
        const pdfPath = outputFilePath;
 
        await downloadDocFromS3(params);
        await transformDocToPDF({
            locationPath: docPath,
            outputPath: pdfPath,
            credentialsPath: PDF_CREDENTIALS_PATH
        });
        await uploadToS3(pdfPath);
   
        fs.unlinkSync(docPath);
        fs.unlinkSync(pdfPath);
    } catch(err) {
        console.log("Error in the PDF convert process",err)
    }
}

convertPdf();
  • Modify the above to code by providing your S3 bucket for the const BUCKET_NAME = “<S3 bucket name>”. For example, BUCKET_NAME = “my-pdf-bucket”.

Note that the AWS SDK for JavaScript provides a JavaScript API for AWS services. We have used the JavaScript API to call Amazon S3. To access the S3 files, you can keep your AWS credentials data in a shared file used by SDKs and the command line interface.

When the SDK for JavaScript loads, it automatically searches the shared credentials file, which is named “credentials”. See the AWS documentation for Loading Credentials in Node.js.

  • Click Run from the Cloud9 console.

Adobe-PDF-Services-9

Figure 9 – AWS Cloud9 run feature.

  • Upon successful execution, AWS Cloud9 will display message as “File uploaded successfully.”
  • Access your output folder of your S3 bucket to validate the file.
  • Download the object from S3 to validate the file. See the Amazon S3 documentation to download from AWS console.

Using an S3 Trigger to Invoke a Lambda Function

See the AWS Lambda documentation to invoke the Lambda functions in response to events from Amazon S3. You can incorporate an event-based trigger and build extensions within your document service applications.

Using AWS Secrets Manager to Protect Secrets

You can use AWS Secrets Manager along with AWS Lambda and Amazon S3 to securely store Adobe API credentials. Secrets Manager helps you protect secrets needed to access your applications, services, and IT resources. Users and applications retrieve secrets with a call to Secrets Manager APIs, eliminating the need to hardcode sensitive information in plain text. See AWS Secrets Manager documentation to learn more.

Cleanup

Don’t forget to clean up your AWS account to avoid ongoing charges for resources you created in this post. Simply follow the instructions outlined in the AWS Cloud9 documentation and Amazon S3 documentation.

Appending Pages

Another common operation with PDF files is appending pages to the end that may have standard text, such as a list of terms. The document toolkit can combine several PDF documents into a single document.

If we have a list of document paths (here in sourceFileList), we can add each file’s file references to an object for a combine operation.

When the combine operation executes, it provides a single file containing all of the source content. We can use saveAsFile on the object to persist the file to storage.

```
const executionContext = PDFServicesSDK.ExecutionContext.create(credentials); var combineOperation = PDFServicesSDK.CombineFiles.Operation.createNew(); 
sourceFileList.forEach(f => {
var combinedSource = PDFServicesSDK.FileRef.createFromLocalFile(f); console.log(f);
combineOperation.addInput(combinedSource); 
}); 
combineOperation.execute(executionContext) .then(result=>result.saveAsFile(destinationFile)) .catch(err => { 
logger.error(err.message);
});

```

Displaying PDF Documents

We have performed several operations on PDF files, but ultimately your user needs to view the documents. You can easily embed the document into a webpage using Adobe’s PDF Embed API.

On the page that will display the PDF, add a <div /> element to hold the document and give it an ID; we’ll use this ID shortly. In the web page, include a <script /> reference to PDF Embed JavaScript library:

<script src=”https://documentcloud.adobe.com/view-sdk/main.js”></script>

The last bit of code you need is a function that displays the document once the Adobe Document Services JavaScript is loaded. We receive a notification the script is loaded through an adobe_dc_view_sdk.ready event, then create a new AdobeDC.View object.

This object needs your Client ID and ID of the element we created earlier. Find your client ID in the Adobe Developer Console. When you view the settings for the application you created when generating credentials earlier, the Client ID will display there.

Adobe-PDF-Services-10.1

Figure 10 – Adobe Developer Console.

Here’s an example of how the template could look with both the HTML for displaying the PDF as well as the code:

<div id="adobe-dc-view" style="height: 360px; width: 500px;">
</div>

<script src="https://documentcloud.adobe.com/view-sdk/main.js"></script>
<script type="text/javascript">
  document.addEventListener("adobe_dc_view_sdk.ready", function(){
    var adobeDCView = new AdobeDC.View({clientId: "<YOUR_CLIENT_ID>", divId: "adobe-dc-view"});
    adobeDCView.previewFile({
      content:{ location: 
        { url: "<URL TO YOUR PDF>"}},
      metaData:{fileName: "<NAME OF YOUR PDF>"}
    },
    {
      embedMode: "SIZED_CONTAINER"
    });
  });
</script>

Other PDF Options

Adobe’s PDF Embed API demo enables you to preview the various other options for embedding PDF documents.

Adobe-PDF-Services-11

Figure 11 – Adobe Viewer components.

You can turn various options on and off and immediately see how they render. When you find a combination you like, click the </> Generate Code button to generate the actual HTML code using those options.

Adobe-PDF-Services-12

Figure 12 – Adobe generate HTML code feature.

Next Steps

As you can see from the quick starts and the provided code we shared in this post, it’s easy to implement digital document generation and approval processes using Node with Adobe PDF Services API. Adobe’s APIs integrate into your existing client applications seamlessly.

To discover the required scopes for a call, or to see how the call is built, you can build sample calls from the documentation. The quick starts also demonstrate other functionality and file formats PDF Services APIs process.

If you need to add digital signatures and security, Adobe Sign can be used to process digital signatures for approval. See the Adobe Sign documentation to learn more.

You can also add a multitude of PDF capabilities to your applications, enabling your users to quickly and easily view and sign their documents and much more. To start, subscribe to the free evaluation.

.
Adobe-APN-Blog-CTA-1
.


Adobe – AWS Partner Spotlight

Adobe is an AWS ISV Partner that helps customers turn ordinary interactions into more valuable digital experiences every day, across media and devices, anywhere, anytime.

Contact Adobe | Partner Overview | AWS Marketplace

*Already worked with Adobe? Rate the Partner

*To review an AWS Partner, you must be a customer that has worked with them directly on a project.