Gruppo ECP Advpress Cyberpills.news Gruppo ECP Advpress Cyberpills.news Gruppo ECP Advpress Cyberpills.news Gruppo ECP Advpress Cyberpills.news Gruppo ECP Advpress Cyberpills.news Gruppo ECP Advpress Cyberpills.news

PDF generation with PHP using Adobe Document Generation APIs

Implementing the automatic creation of customized PDFs by integrating Adobe cloud services and PHP

The article explains how to create a PDF with PHP using the Adobe Document Generation APIs: a DOCX template with placeholders is uploaded, data is sent via JSON, the PDF is generated, and the download URL is received automatically through a callback.
This pill is also available in Italian language

Have you ever needed to generate PDF documents with your PHP scripts? If the answer is "Yes" and you still don’t know how to proceed, in this article we will guide you, step by step, to create a PHP script that will use the Adobe Document Generation APIs to generate a PDF document starting from a Microsoft Word document with DOCX extension. Simply put, this PHP script will perform the following tasks:

  1. Open the DOCX document as the template for the PDF document to be generated;
  2. Fill it with the desired information;
  3. Generate the PDF document;
  4. Obtain a copy of it;

Creating authentication credentials

In order to use Adobe Document Generation APIs, you first need to create a personal or business account on the Adobe Developer Website. This will give you access to the Adobe Developer Console where you can create a new project (API type) in the dedicated area. It is important at this stage to generate authentication credentials for using the REST APIs associated with the "PDF Services API" service of the "Document Cloud" product. At the end of this process, you will obtain a file named "pdfservices-api-credentials.json" which will be used by our PHP script to complete the authentication procedure required by Adobe's APIs.

Creating the DOCX template

Our PHP script must be able to access a DOCX document to be used as a template for generating the PDF document. Therefore, create a test Microsoft Word document named "template.docx". This document will contain custom placeholders, such as: "{{name}}", "{{surname}}", "{{date_of_birth}}", "{{address}}", which Adobe APIs will search for and replace with the corresponding information during the PDF generation process.

Creating the demonstration PHP script

Once the previous steps are completed, it is time to create our PHP script. Create a new file named "index.php", ensuring that you copy and paste the following content inside it:

<?php
  set_time_limit( 0 );
  ignore_user_abort( TRUE );
  $credentials = 'pdfservices-api-credentials.json';
  $template = 'template.docx';
 
  function getAccessToken( $clientId, $clientSecret )
  {
   $ch = curl_init( 'https://ims-na1.adobelogin.com/ims/token/v3' );
   curl_setopt_array( $ch, [
    CURLOPT_POST => TRUE,
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_POSTFIELDS => http_build_query( [
     'client_id' => $clientId,
     'client_secret' => $clientSecret,
     'grant_type' => 'client_credentials',
     'scope' => 'openid AdobeID DCAPI'
    ] )
   ] );
   $response = curl_exec( $ch );
   $httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
   curl_close( $ch );
   if( $httpCode !== 200 )
    throw new Exception( "OAuth error ($httpCode): $response" );
   return json_decode( $response, TRUE )[ 'access_token' ];
  }
  function adobeCall( $url, $accessToken, $clientId, $method = 'POST', $body = NULL, array $headers = [] )
  {
   $ch = curl_init( $url );
   curl_setopt_array( $ch, [
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_HTTPHEADER => array_merge( [
     "Authorization: Bearer $accessToken",
     "x-api-key: $clientId"
    ], $headers )
   ] );
   if( $body !== NULL )
    curl_setopt( $ch, CURLOPT_POSTFIELDS, $body );
   $response = curl_exec( $ch );
   $httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
   curl_close( $ch );
   return [ $httpCode, $response ];
  }
  try
  {
   header( 'Content-Type: text/plain' );
 
   if( !array_key_exists( 'fieldsmap', $_GET ) || !json_validate( $_GET[ 'fieldsmap' ] ) )
   {
    http_response_code( 401 );
    exit( date( 'd/m/Y H:i:s' ) . " - Invalid or unspecified 'fieldsmap' parameter\n" );
   }
 
   // AUTHENTICATION
    if( !file_exists( $credentials ) )
     throw new Exception( "Credenziali non trovate" );
    echo date( 'd/m/Y H:i:s' ) . " - Authentication...\n";
    $credentials = json_decode( file_get_contents( $credentials ), TRUE );
    $accessToken = getAccessToken( $credentials[ 'client_credentials' ][ 'client_id' ], $credentials[ 'client_credentials' ][ 'client_secret' ] );
    echo date( 'd/m/Y H:i:s' ) . " - Authentication complete\n";
 
   // PRE-SIGNED URL REQUEST
    echo date( 'd/m/Y H:i:s' ) . " - Pre-signed URL request...\n";
    $templateData = file_get_contents( $template );
    $templateType = mime_content_type( $template );
    list( $code, $body ) = adobeCall(
     'https://pdf-services.adobe.io/assets',
     $accessToken,
     $credentials[ 'client_credentials' ][ 'client_id' ],
     'POST',
     json_encode( [ 'mediaType' => $templateType ] ),
     [ 'Content-Type: application/json' ]
    );
    if( $code < 200 || $code >= 300 )
     throw new Exception( "Pre-signed URL request error ($code): $body" );
    $info = json_decode( $body, TRUE );
    echo date( 'd/m/Y H:i:s' ) . " - Pre-signed URL request complete (assetID: " . $info[ 'assetID' ] . ")\n";
 
   // UPLOAD FILE
    echo date( 'd/m/Y H:i:s' ) . " - Upload file '$template'...\n";
    $ch = curl_init( $info[ 'uploadUri' ] );
    curl_setopt_array( $ch, [
     CURLOPT_RETURNTRANSFER => TRUE,
     CURLOPT_CUSTOMREQUEST => 'PUT',
     CURLOPT_HTTPHEADER => [ "Content-Type: $templateType" ],
     CURLOPT_POSTFIELDS => $templateData
    ] );
    $resp = curl_exec( $ch );
    $upCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
    curl_close( $ch );
    if( $upCode < 200 || $upCode >= 300 )
    throw new Exception( "Upload file error ($upCode): $resp" );
echo date( 'd/m/Y H:i:s' ) . " - Upload file complete\n";  
    
   // PDF DOCUMENT GENERATION   
    echo date( 'd/m/Y H:i:s' ) . " - PDF document generation...\n";   
    $jsonData = json_decode( $_GET[ 'fieldsmap' ], TRUE );   
    list( $code, $body ) = adobeCall(   
     'https://pdf-services.adobe.io/operation/documentgeneration',   
     $accessToken,   
     $credentials[ 'client_credentials' ][ 'client_id' ],   
     'POST',   
     json_encode( [   
      'assetID' => $info[ 'assetID' ],   
      'outputFormat' => 'pdf',   
      'jsonDataForMerge' => $jsonData,   
      'notifiers' => [ [   
       'type' => 'CALLBACK',   
       'data' => [   
        'url' => 'https://www.mysite.it/callback.php',   
        'headers' => [   
         'x-api-key' => $credentials[ 'client_credentials' ][ 'client_id' ],   
         'access-token' => $accessToken   
        ]   
       ]   
      ] ]   
     ] ),   
     [ 'Content-Type: application/json' ]   
    );   
    if( $code < 200 || $code >= 300 )   
     throw new Exception( "PDF document generation error ($code): $body" );   
    
   echo date( 'd/m/Y H:i:s' ) . " - PDF document generation complete\n";   
  }   
  catch( Exception $e )   
  {   
   http_response_code( 500 );   
   exit( date( 'd/m/Y H:i:s' ) . " - Error: " . $e->getMessage() . "\n" );   
  }   
 ?>

Detailed explanation

The "getAccessToken" function is responsible for obtaining an access token from Adobe's authentication APIs (IMS). This token is necessary for all subsequent calls to Adobe APIs. The "adobeCall" function is a wrapper for making generic calls to Adobe APIs.

The PHP script expects to receive a GET parameter called "fieldsmap" to function correctly. This parameter needs to be a valid JSON string that contains the mapping of DOCX template placeholders to the data to be inserted. Below is an example of the JSON string:

{
 "name": "Mario",
 "surname": "Rossi",
 "date_of_birth": "15/05/1980",
 "address": "Via Roma 123, 00100 Roma, RM"
}

Adobe PDF Services doesn't allow direct template file uploads. Instead, it first requires a pre-signed URL for the upload. The first call to the "adobeCall" function handles this very step. Adobe responds with an uploadUri (the pre-signed URL for the upload) and an assetID (an identifier for the uploaded file).

Once the pre-signed URL is obtained, the script uploads the DOCX file to that URL. In this specific case, it's not necessary to send the "Authorization" and "x-api-key" headers, so we don't use the "adobeCall" function (which automatically includes them). Instead, we handle this upload independently from all other calls.

Next, with the second and final call to the "adobeCall" function, we instruct the Adobe endpoint to access the previously uploaded DOCX template (assetID), populate it with the data specified in the "fieldsmap" JSON string, and notify a second PHP script, named "callback.php" and accessible at the URL "www.mysite.it", that the PDF document has been created so it can be processed.

Creating a callback script

At this point, all that's left is to handle Adobe's callback request. This will inform our listening script about the outcome of the PDF document creation request and provide a temporary URL, if available, from which to download it. To do this, we'll create a second PHP script called "callback.php". Be sure to copy and paste the following content into it:

<?php
  set_time_limit( 0 );
  ignore_user_abort( TRUE );
  header( 'Content-Type: text/plain' );
 
  if( ( $_SERVER[ 'REQUEST_METHOD' ] ?? '' ) !== 'POST' )
  {
   http_response_code( 405 );
   exit( date( 'd/m/Y H:i:s' ) . " - Method not allowed\n" );
  }
 
  $body = file_get_contents( 'php://input' );
  $body = json_decode( $body, TRUE );
  if( json_last_error() !== JSON_ERROR_NONE || !array_key_exists( 'statusResponse', $body ) || !array_key_exists( 'status', $body[ 'statusResponse' ] ) )
  {
   http_response_code( 400 );
   exit( date( 'd/m/Y H:i:s' ) . " - Invalid payload\n" );
  }
 
  echo date( 'd/m/Y H:i:s' ) . " - Document availability notification\n";
  switch( $body[ 'statusResponse' ][ 'status' ] )
  {
   case 'done':
    if( !array_key_exists( 'asset', $body[ 'statusResponse' ] ) )
    {
     http_response_code( 400 );
     exit( date( 'd/m/Y H:i:s' ) . " - Missing 'asset' parameter\n" );
    }
    if( !array_key_exists( 'downloadUri', $body[ 'statusResponse' ][ 'asset' ] ) )
    {
     http_response_code( 400 );
     exit( date( 'd/m/Y H:i:s' ) . " - Missing 'downloadUri' parameter\n" );
    }
    echo date( 'd/m/Y H:i:s' ) . " - Initiating document download '" . $body[ 'statusResponse' ][ 'asset' ][ 'downloadUri' ] . "'...\n";
    $ch = curl_init( $body[ 'statusResponse' ][ 'asset' ][ 'downloadUri' ] );
    curl_setopt_array( $ch, [
     CURLOPT_RETURNTRANSFER => TRUE,
     CURLOPT_FOLLOWLOCATION => TRUE,
     CURLOPT_CONNECTTIMEOUT => 60,
     CURLOPT_TIMEOUT => 0,
     CURLOPT_SSL_VERIFYPEER => TRUE,
     CURLOPT_SSL_VERIFYHOST => 2
    ] );
    $document = curl_exec( $ch );
    $httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
    $error = curl_error( $ch );
    curl_close( $ch );
    if( $document === FALSE || $httpCode < 200 || $httpCode >= 300 )
    {
     http_response_code( 500 );
     exit( date( 'd/m/Y H:i:s' ) . " - Download error: " . ( $error ?: "HTTP $httpCode" ) . "\n" );
    }
    if( @file_put_contents( 'document.pdf', $document ) === FALSE )
    {
     http_response_code( 500 );
     exit( date('d/m/Y H:i:s') . " - Error: document save failed\n" );
    }
    http_response_code( 200 );
    exit( date( 'd/m/Y H:i:s' ) . " - Document download complete\n" );
    break;
   case 'failed':
    http_response_code( 200 );
    exit( date( 'd/m/Y H:i:s' ) . " - Error: " . $body[ 'statusResponse' ][ 'error' ] . "\n" );
    break;
   default:
    http_response_code( 200 );
    exit( date( 'd/m/Y H:i:s' ) . " - Error: unhandled event\n" );
  }
 ?>

Recommendations

This PHP script is highly simplified to help you quickly understand how to correctly interface with Adobe APIs. We recommend you adequately protect the "pdfservices-api-credentials.json" file, as it contains sensitive access credentials. Only allow your script to access it, or store its content within a PHP variable.

Callback URLs should not contain query strings or credentials. Any additional parameters can be forwarded through the headers. Headers can later be retrieved using the "getallheaders" function.

06/16/2025 17:46

Marco Verro

Don’t miss the most important news
Enable notifications to stay always updated