Generazione di PDF con PHP usando le API Document Generation di Adobe
Implementare la creazione automatica di PDF personalizzati integrando servizi cloud Adobe e PHP
Avete mai avuto la necessità di generare documenti PDF con i vostri script PHP? Se la risposta è "Si" e non sapete ancora come procedere, in questo articolo vi guideremo, passo dopo passo, alla creazione di uno script PHP che sfrutterà le API Document Generation di Adobe per generare un documento PDF partendo da un documento Microsoft Word avente estensione DOCX. Più semplicemente, questo script PHP svolgerà le seguenti attività:
- Aprire il documento DOCX come template del documento PDF da generare;
- Compilarlo con le informazioni desiderate;
- Generare il documento PDF;
- Ottenere una sua copia;
Creazione delle credenziali di autenticazione
Per poter sfruttare le API Document Generation di Adobe è necessario prima di tutto creare un account personale o aziendale sul sito web Adobe Developer Website. Avrete così modo di accedere alla Adobe Developer Console dove creare un nuovo progetto (di tipo API) nell'apposita area. È importante in questa fase arrivare a generare delle credenziali di autenticazione per l'impiego delle API REST associate al servizio "PDF Services API" del prodotto "Document Cloud". Al termine di questa procedura, otterrete un file denominato "pdfservices-api-credentials.json" che verrà impiegato dal nostro script PHP per portare a termine la procedura di autenticazione prevista dalle API di Adobe.
Creazione del template DOCX
Il nostro script PHP dovrà poter accedere ad un documento DOCX da impiegare come template per la generazione del documento PDF. Creare quindi un documento Microsoft Word di prova il cui nome sarà "template.docx". Questo documento conterrà dei segnaposti personalizzati, come ad esempio: "{{nome}}", "{{cognome}}", "{{data_di_nascita}}", "{{indirizzo_di_residenza}}", che le API di Adobe ricercheranno e sostituiranno con le informazioni corrispondenti durante la procedura di generazione del documento PDF.
Creazione dello script PHP dimostrativo
Completati i passaggi precedenti, è giunto il momento di creare il nostro script PHP. Creare quindi un nuovo documento denominato "index.php", avendo premura di copiare e incollare al suo interno il seguente contenuto:
<?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' ) . " - Parametro 'fieldsmap' non valido o non specificato\n" );
}
// AUTENTICAZIONE
if( !file_exists( $credentials ) )
throw new Exception( "Credenziali non trovate" );
echo date( 'd/m/Y H:i:s' ) . " - Autenticazione...\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' ) . " - Autenticazione completata\n";
// RICHIESTA URL PREFIRMATO
echo date( 'd/m/Y H:i:s' ) . " - Richiesta URL prefirmato...\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( "Errore richiesta URL prefirmato ($code): $body" );
$info = json_decode( $body, TRUE );
echo date( 'd/m/Y H:i:s' ) . " - Richiesta URL prefirmato completata (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( "Errore upload file ($upCode): $resp" );
echo date( 'd/m/Y H:i:s' ) . " - Upload file completato\n";
// GENERAZIONE DOCUMENTO PDF
echo date( 'd/m/Y H:i:s' ) . " - Generazione documento PDF...\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.miosito.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( "Errore generazione documento PDF ($code): $body" );
echo date( 'd/m/Y H:i:s' ) . " - Generazione documento PDF completata\n";
}
catch( Exception $e )
{
http_response_code( 500 );
exit( date( 'd/m/Y H:i:s' ) . " - Errore: " . $e->getMessage() . "\n" );
}
?>
Spiegazione dettagliata
La funzione "getAccessToken" è responsabile dell'ottenimento di un token di accesso dalle API di autenticazione di Adobe (IMS). Questo token è necessario per tutte le chiamate successive alle API di Adobe. La funzione "adobeCall" è un wrapper per effettuare chiamate generiche alle API di Adobe.
Lo script PHP, per poter funzionare, si aspetta di ricevere un parametro GET denominato "fieldsmap". Questo parametro deve essere una stringa JSON valida che contiene la mappatura dei segnaposto del template DOCX con i dati da inserire. Di seguito un esempio della stringa JSON:
{
"nome": "Mario",
"cognome": "Rossi",
"data_di_nascita": "15/05/1980",
"indirizzo_di_residenza": "Via Roma 123, 00100 Roma, RM"
}
Adobe PDF Services non consente un upload diretto del file template, ma richiede prima un URL pre-firmato per il caricamento. La prima chiamata alla funzione "adobeCall" si occupa perciò di fare proprio questo. Adobe risponde con un uploadUri (l'URL pre-firmato per l'upload) e un assetID (un identificatore per il file caricato).
Una volta ottenuto l'URL pre-firmato, lo script carica il file DOCX su tale URL. Non essendo necessario in questo specifico caso inoltrare anche le intestazioni "Authorization" e "x-api-key", non ricorriamo alla funzione "adobeCall" (che le include automaticamente) ma la gestiamo in modo indipendente da tutte le altre.
Successivamente, con la seconda e ultima chiamata alla funzione "adobeCall", invitiamo l'endpoint Adobe ad accedere al template DOCX caricato in precedenza (assetID), compilarlo con i dati specificati nella stringa JSON "fieldsmap" e comunicare ad un secondo script PHP, denominato "callback.php" e raggiungibile all'url "www.miosito.it", l'avvenuta creazione del documento PDF, affinché possa processarlo.
Creazione di uno script di callback
A questo punto, non resta che gestire la richiesta callback di Adobe che comunicherà al nostro script in ascolto l'esito della richiesta di creazione del documento PDF e l'eventuale URL temporaneo dalla quale scaricarlo. Per fare ciò, creiamo un secondo script PHP denominato "callback.php", avendo premura di copiare e incollare al suo interno il seguente contenuto:
<?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' ) . " - Metodo non consentito\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' ) . " - Payload non valido\n" );
}
echo date( 'd/m/Y H:i:s' ) . " - Notifica disponibilità documento\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' ) . " - Parametro 'asset' mancante\n" );
}
if( !array_key_exists( 'downloadUri', $body[ 'statusResponse' ][ 'asset' ] ) )
{
http_response_code( 400 );
exit( date( 'd/m/Y H:i:s' ) . " - Parametro 'downloadUri' mancante\n" );
}
echo date( 'd/m/Y H:i:s' ) . " - Inizio download documento '" . $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' ) . " - Errore download: " . ( $error ?: "HTTP $httpCode" ) . "\n" );
}
if( @file_put_contents( 'document.pdf', $document ) === FALSE )
{
http_response_code( 500 );
exit( date('d/m/Y H:i:s') . " - Errore: salvataggio documento non riuscito\n" );
}
http_response_code( 200 );
exit( date( 'd/m/Y H:i:s' ) . " - Download documento completato\n" );
break;
case 'failed':
http_response_code( 200 );
exit( date( 'd/m/Y H:i:s' ) . " - Errore: " . $body[ 'statusResponse' ][ 'error' ] . "\n" );
break;
default:
http_response_code( 200 );
exit( date( 'd/m/Y H:i:s' ) . " - Errore: evento non gestito\n" );
}
?>
Raccomandazioni
Questo script PHP è estremamente semplificato per permettervi di comprendere rapidamente come interfacciarsi con le API di Adobe nel modo corretto. Vi raccomandiamo di proteggere adeguatamente il file "pdfservices-api-credentials.json" contenente credenziali di accesso riservate, consentendone l'accesso unicamente al vostro script o affidando il suo contenuto ad una variabile PHP.
Le URL di callback non devono contenere query string o credenziali. L'inoltro di eventuali parametri supplementari è possibile attraverso le intestazioni. Sarà possibile in seguito recuperare le intestazioni mediante la funzione "getallheaders".
06/16/2025 17:46
Marco Verro