ODT2PDF script can it be run in windows

is there a way to do this in windows
I would like it to convert to pdf so I don’t have to worry about stuff being off
so I made a invoice in word using odt
but I don’t want to convert it every time to pdf

Hello,

Natively in Dolibarr no it’s possible on windows.

how would you be able to do it

Its probably not the correct way but I created a php file down in /htdocs/core/modules/facture/doc directory that produces a pdf file for our invoices and similar for quotes etc. Then in the setup of the module you can enable the file. It doesn’t get over written by updates so it all seems to be good.

If you are not familiar with PHP I could possibly modify one for you. PM what you want your invoice etc to look like.

You need a LibreOfficePortable_24.2.3 install in ../dolibarr/htdocs/custom, before install libre office you go to ../dolibarr/htdocs/core/modules//doc/doc_generic__odt.modules.php now in the line where return the odt you put the function to change format odt to pdf. This line return de odt : $this->result = array(‘fullpath’ => $htmlContent);

in my case my function have this name:
require_once DOL_DOCUMENT_ROOT . ‘/custom/ophir/ophir.php’;
$htmlContent = libreofiiceodtpdf($file, $object);
$this->result = array(‘fullpath’ => $htmlContent);

check my function and in theory its work if you get a problem send me email to: esa@unboxerp.com

function libreofiiceodtpdf($filepath,$object) {
// Rutas de los archivos
global $conf;
$inputFile = $filepath; // Ruta del archivo de entrada

// Definir los parámetros necesarios
$objectref = dol_sanitizeFileName($object->ref);
$dir = $conf->propal->multidir_output[$object->entity].“/”.$objectref;
$date_file = dol_print_date(dol_now(), ‘dayxcard’);
//$file = $dir.“/”.$objectref.“.pdf”;
$file =“PROPOSAL_”.$objectref.“_”.$object->thirdparty->name.“_”.$date_file.“_”.date(“Hi”,dol_now()).“.pdf”;
if($object->element ==“propal”){
$file =“PROPOSAL_”.$objectref.“_”.$object->thirdparty->name.“_”.$date_file.“_”.date(“Hi”,dol_now()).“.pdf”;
$dir = $conf->propal->multidir_output[$object->entity] . “/” . $objectref;
}if($object->element ==“commande”){
$file =“SALES_ORDER_”.$objectref.“_”.$object->thirdparty->name.“_”.$date_file.“_”.date(“Hi”,dol_now()).“.pdf”;
$dir = $conf->commande->multidir_output[$object->entity] . “/” . $objectref;
}if($object->element ==“facture”){
$file =“INVOICE_”.$objectref.“_”.$object->thirdparty->name.“_”.$date_file.“_”.date(“Hi”,dol_now()).“.pdf”;
$dir = $conf->facture->multidir_output[$object->entity] . “/” . $objectref;
}if($object->element ==“shipping”){
$file =“SHIPMENT_”.$objectref.“_”.$object->thirdparty->name.“_”.$date_file.“_”.date(“Hi”,dol_now()).“.pdf”;
$dir = $conf->expedition->multidir_output[$object->entity] . “/sending”. “/” . $objectref;
}

$comando = DOL_DOCUMENT_ROOT . ‘\custom\LibreOfficePortable\App\Libreoffice\program\soffice’;
// Comando para ejecutar LibreOffice en modo headless
$command = ‘"’.$comando.‘" --headless --convert-to pdf --outdir "’ . $dir . ‘" "’ . $inputFile . ‘"’;

// Ejecutar el comando y capturar la salida y el código de retorno
exec($command . ’ 2>&1’, $output, $return_var);

// Agregar un retraso para asegurarse de que el archivo no esté en uso
usleep(500000); // 500 ms de retraso

// Verificar si el archivo está en uso antes de intentar eliminarlo
function isFileInUse($filePath) {
$fileHandle = @fopen($filePath, “r+”);
if ($fileHandle === false) {
return true; // El archivo está en uso
}
fclose($fileHandle);
return false; // El archivo no está en uso
}

/*
* alan
* */

$handle_path = DOL_DOCUMENT_ROOT . ‘\custom\ophir\Handle\handle.exe’; // Ajusta esta ruta

// Escapa la ruta del archivo
$filepath_escaped = escapeshellarg($filepath);
//$handle_cmd = “$handle_path $filepath_escaped”;
$handle_cmd = “$handle_path -accepteula $filepath_escaped”;

// Ejecuta handle.exe para ver qué proceso lo está usando
exec($handle_cmd, $output);

//print_r($output); // :left_arrow: Añade esto para depurar

foreach ($output as $line) {
// Busca líneas que contengan “pid:”
if (preg_match(‘/pid: (\d+)/i’, $line, $matches)) {
$pid = $matches[1];
// :warning: Forzar cierre del proceso que lo está usando
// exec(“taskkill /F /PID $pid”, $kill_output);
}
}
exec($handle_cmd, $output);
//print_r($output); // :left_arrow: Añade esto para depurar

// Intentar eliminar el archivo
if (file_exists($filepath)) {
// if (@unlink($filepath)) {
// echo “Archivo eliminado forzosamente: $filepath”;
// } else {
// echo “No se pudo eliminar el archivo después de matar el proceso.”;
// }
} else {
echo “El archivo ya no existe.”;
}

*/
// Verificar el resultado
if ($return_var === 0) {
if ($return_var === 0) {
if($object->element==‘facture’ ||$object->element ==“propal” ||$object->element ==“commande” ||$object->element ==“shipping” ){
$last_doc1= $object->element.“/” . $objectref.“/” . $file;
global $db;
$sql =“update “.MAIN_DB_PREFIX.”” .$object->element." set last_main_doc = ‘“.$last_doc1.”’ where rowid = ".$object->id;
$res=$db->query($sql);
$db->commit();
}

     // Buscar la línea que contiene la ruta del archivo PDF generado
     foreach ($output as $line) {
        // Utilizar una expresión regular para encontrar la ruta del archivo PDF
        if (preg_match('/-> (.\*\\.pdf)/', $line, $matches)) {
           $generatedFilePath = $matches\[1\]; // Ruta completa del archivo generado
           $generatedFileName = basename($generatedFilePath);

// $rename = rename($generatedFileName, $file);
// echo "El archivo se ha convertido correctamente a PDF y se ha guardado en: " . basename($generatedFilePath);
return $generatedFileName; // Retornar la ruta completa del archivo generado
}
}
}
} else {
echo “Error al convertir el archivo. Código de error: $return_var”;
print_r($output); // Mostrar la salida del comando para depuración
}

}

We ended up solving something similar by generating the PDF on a Linux service instead of trying to make Windows handle everything. It kept the ERP setup much simpler, especially once users started creating documents from different machines.

One thing I’d also suggest is thinking about where the PDFs will eventually be used. In our case, invoices and quotations are shared with B2B clients, so having a consistent PDF output mattered more than the actual operating system.

I actually started looking into this while helping a company that works with enterprise IT procurement. I came across Tech Distributor, a Fortinet distributor in Dubai, and it reminded me how common PDF-based quotations still are in that space.

If Windows is your only option, I’d probably look at a dedicated conversion service instead of relying on local scripts. It usually ends up being easier to maintain in the long run.