This topic is locked

Convert MS Office to PDF document

4/8/2021 6:26:56 PM
PHPRunner Tips and Tricks
fhumanes author


Something I've always been looking for, I think I've managed to find it.

It is not perfect, since it only works on Windows, but the conversion it is perfect, since it uses the Microsoft software to make the conversion of any Microsoft Office document to PDF.

To make the conversion uses the MS products installed in a Windows and to run Microsoft software on Background we use the free product of the company Cognidox "Office_To_pdf.exe".

This product can be downloaded from https://github.com/cognidox/officetopdf

The documentation of your installation requirements and its parameters can be obtained in the same direction. Please review the installation requirements.

The example I have made consists of 2 parts:

  • server_pdf. PHPRunner application that manages a single table where the conversions that are made and with that data are recorded, is reported to the name of the file, size and time that has lasted the conversion.
    This is the part that has to be executed on a Windows machine with the MS Office product installed.


  • client_pdf. Application PHPRunner of a single table where "up" an MS Office file. Once it is uploaded, the conversion to PDF format is requested by button.
    It can be in Linux or any other S.O. where PHP runs.



Example only shows the conversion capacity of MS Office documents to PDF, now, each one that uses it may do so for functionally very different applications.

Demo: https://fhumanes.com/client_pdf.

It will only be usable when my PC is set with the "server_pdf" process, since these pages are housed in Linux machines.

User: "admin" / "admin"

As you will remember, we have several examples to quite easily build Word and Excel documents from PHPRunner applications, so those documents, once elaborated can be converted, through the functionality of this example, in pdf.

The fact of separating or uncoupling the PDF conversion service is very important, since this system requires a Windows machine (with MS Office installed) and is a very heavy process (requires many resources from the machine to make the conversion). Separating it causes the application that is serving the client does not "resose" with these file format conversion processes.

To communicate these 2 applications we have used the RestFULL API protocol and I show you the most relevant code of the client and server:

convert_pdf.php

<?php
/*
$data = $button->getCurrentRecord();

$id_record = $data['id_client_pdf_file']; //
// get information about files
$fileArray = my_json_decode($data["files"]);
if (count($fileArray) <> 1){ // To convert it is necessary that there is only one file
$result['error'] = "No se ha realizado conversión";
} else {
$result['error'] = "";
include "MyCode/convert_pdf.php"; // Convert file to PDF
}
*/
$file = $fileArray[0]['name'];
$name = $fileArray[0]['usrName'];
require_once __DIR__ . '/unirest_3.0.4/autoload.php'; // Load Library Unirest
$pathFile = __DIR__ . "/../".$file;

$document = file_get_contents($pathFile); // Charge the content of the file from its physical location

$headers = array(
'Authorization' => '3d524a53c110e4c22463b10ed32cef9d',
'Accept' => 'application/json'
);
$data = array('name' => $name, 'file' => base64_encode($document));
$body = Unirest\Request\Body::multipart($data);
// $body = Unirest\Request\Body::form($data);
$domainHost="http://localhost";
$response = Unirest\Request::post($domainHost.'/server_pdf/restapi/v1/document', $headers, $body);
$code = $response->code; // HTTP Status code
$headers = $response->headers; // Headers

$data_api = $response->raw_body; // Unparsed body
$data_api = json_decode($data_api,true);
if ($data_api['error'] == true || $code <> '201'){
$result['error'] = 'El servidor de conversión PDF ha dado error';
return;
}
$name = $data_api['document']['name'];
$document = base64_decode($data_api['document']['file']);
$part_path = pathinfo($file);
$file = $part_path['dirname'].'/'.$part_path['filename'].'.pdf';
$pathFile = __DIR__ . "/../".$file;
// Wirte new file PDF
file_put_contents($pathFile, $document);

$sizeFile = filesize($pathFile);
// Create data of new file
$fileArray[1]['name']=$file;
$fileArray[1]['usrName']=$name;
$fileArray[1]['size']=$sizeFile;
$fileArray[1]['type']='application/pdf';
$fileArray[1]['searchStr']=$name;

$field = my_json_encode($fileArray);

$dataDB = array();
$keyvalues = array();
$dataDB["files"] = $field;
$keyvalues["id_client_pdf_file"] = $id_record;
DB::Update("client_pdf_file", $dataDB, $keyvalues );

?>

index.php

<?php
/**
*
* @About: API Interface
* @File: index.php
* @Date: $Date:$ Nov-2015
* @Version: $Rev:$ 1.0
* @Developer: Federico Guzman (federicoguzman@gmail) and modified Fernando Humanes (frnandohumanes@gmail.com)

/* Los headers permiten acceso desde otro dominio (CORS) a nuestro REST API o desde un cliente remoto via HTTP
* Removiendo las lineas header() limitamos el acceso a nuestro RESTfull API a el mismo dominio
* Nótese los métodos permitidos en Access-Control-Allow-Methods. Esto nos permite limitar los métodos de consulta a nuestro RESTfull API
* Mas información: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
**/
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS');
header("Access-Control-Allow-Headers: X-Requested-With");
header('Content-Type: text/html; charset=utf-8');
header('P3P: CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"');

include_once '../include/Config.php';

require_once("../../include/dbcommon.php"); // DataBase PHPRunner

/* Puedes utilizar este file para conectar con base de datos incluido en este demo;
* si lo usas debes eliminar el include_once del file Config ya que le mismo está incluido en DBHandler
**/
//require_once '../include/DbHandler.php';

require '../libs/Slim/Slim.php';
\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
/* Usando POST para convertir fichero */

$app->post('/document', 'authenticate', function() use ($app) {
// check for required params
$startConvert = date("Y-m-d H:i:s");; // marcar fecha de inicio
verifyRequiredParams(array('name', 'file'));

$BlobInput=base64_decode($app->request->post('file')); // Convert Base64
// $BlobInput=$app->request->post('file'); // ile binary
$part_file = explode(".", $app->request->post('name'));
$ExtensionFile = $part_file[(count($part_file)-1)]; // Tipo file
$new_file = substr( $app->request->post('name'),0,(strlen($app->request->post('name'))-strlen($ExtensionFile)-1)).".pdf";
// -------------------- for create temporaly file --------------------------
$temp_file = tempnam(sys_get_temp_dir(), 'temp');
$part_path = pathinfo($temp_file);
$temp_file1 = $part_path['dirname'].'/'.$part_path['filename'].'.'.$ExtensionFile;

$phpLog = $part_path['dirname'].'/phplog.txt'; // Trace of Debug code

$fp = fopen($temp_file1, 'w');
$fwrite = fwrite($fp, $BlobInput);
fclose($fp);

$sizeFile = filesize($temp_file1);

$temp_file2 = $part_path['dirname'].'/'.$part_path['filename'].'.pdf';

$fileLock = $part_path['dirname'].'/lock.txt';
if (!file_exists($fileLock)) { // Si no existe, se crea
$fpLock = fopen("$fileLock", "w");
fclose($fpLock);
}
$fp = fopen($fileLock, "r+"); // Fcihero de bloqueos para que sólo haya una única ejecución
do { // Bucle de conversión copn control de que sólo un proceso puede estar en ejecución

if (flock($fp, LOCK_EX)) { // adquirir un bloqueo exclusivo

// Convert to PDF
// $result = shell_exec("..\\Office_to_PDF\\OfficeToPDF.exe $temp_file1 $temp_file2");
$status = exec("..\\Office_to_PDF\\OfficeToPDF.exe /readonly /noquit /excel_active_sheet $temp_file1 $temp_file2", $outputCommand, $result);

if ($result <> 0) {
// LOG
$a = "Resultado de conversion: ".$result."\n".var_export($outputCommand, true)." \n";
file_put_contents($phpLog, $a, FILE_APPEND | LOCK_EX);
}
flock($fp, LOCK_UN); // libera el bloqueo
break;
} else {
sleep(1);
}

} while (true);
fclose($fp);

// ------------------ Operation with file result -------------------------------------------
$document = file_get_contents($temp_file2);
unlink($temp_file); // delete file tmp
unlink($temp_file1); // delete file tmp
unlink($temp_file2); // delete file tmp

$response = array();
//capturamos los parametros recibidos y los almacxenamos como un nuevo array
$param['name'] = $new_file;
$param['file'] = base64_encode($document);
if ( is_array($param) ) {
$response["error"] = false;
$response["message"] = "Documento convertido satisfactoriamente!";
$response["document"] = $param;
} else {
$response["error"] = true;
$response["message"] = "Error al crear auto. Por favor intenta nuevamente.";
}
echoResponse(201, $response);

// Write LOG
$data = array();
$data["nameFile"] = $new_file;
$data["sizeFile"] = $sizeFile;
$data["startConvert"] = $startConvert;
$data["endConvert"] = date("Y-m-d H:i:s");
DB::Insert("server_pdf_log", $data );
});
/* corremos la aplicación */
$app->run();

/*********************** USEFULL FUNCTIONS **************************************/

/**
* Verificando los parametros requeridos en el metodo o endpoint
*/
function verifyRequiredParams($required_fields) {
$error = false;
$error_fields = "";
$request_params = array();
$request_params = $_REQUEST;
// Handling PUT request params
if ($_SERVER['REQUEST_METHOD'] == 'PUT') {
$app = \Slim\Slim::getInstance();
parse_str($app->request()->getBody(), $request_params);
}
foreach ($required_fields as $field) {
if (!isset($request_params[$field]) || strlen(trim($request_params[$field])) <= 0) {
$error = true;
$error_fields .= $field . ', ';
}
}

if ($error) {
// Required field(s) are missing or empty
// echo error json and stop the app
$response = array();
$app = \Slim\Slim::getInstance();
$response["error"] = true;
$response["message"] = 'Required field(s) ' . substr($error_fields, 0, -2) . ' is missing or empty';
echoResponse(400, $response);

$app->stop();
}
}
/**
* Mostrando la respuesta en formato json al cliente o navegador
* @param String $status_code Http response code
* @param Int $response Json response
*/
function echoResponse($status_code, $response) {
$app = \Slim\Slim::getInstance();
// Http response code
$app->status($status_code);

// setting response content type to json
$app->contentType('application/json');

echo json_encode($response);
}

/**
* Adding an intermediate layer of authentication for one or all methods, use as needed
* Revisa si la consulta contiene un Header "Authorization" para validar
*/
function authenticate(\Slim\Route $route) {
// Getting request headers
$headers = apache_request_headers();
// LOG
// $a = var_export($headers, true);
// file_put_contents("C:/tmp/php/error/php.log", $a, FILE_APPEND | LOCK_EX);

$response = array();
$app = \Slim\Slim::getInstance();

// Verifying Authorization Header
if (isset($headers['Authorization'])) {
//$db = new DbHandler(); //utilizar para manejar autenticacion contra base de datos

// get the api key
$token = $headers['Authorization'];

// validating api key
if (!($token == API_KEY)) { //API_KEY declarada en Config.php

// api key is not present in users table
$response["error"] = true;
$response["message"] = "Acceso denegado. Token inválido";
echoResponse(401, $response);

$app->stop(); //Detenemos la ejecución del programa al no validar

} else {
//procede utilizar el recurso o metodo del llamado
}
} else {
// api key is missing in header
$response["error"] = true;
$response["message"] = "Falta token de autorización";
echoResponse(400, $response);

$app->stop();
}
}
?>

In my portal I leave the two projects (phprunner 10.4) so you can try them on your pc's and as always, for any doubt you have, you can contact me at fernandhumanes@gmail.com

fhumanes author 4/8/2021

I'm sorry, I've left a "maquette" from the disastrous article.

I still have not learned to use the new Forum well and I do not know how to leave it better.

Greetings,
fernando