Categories > [old] TbsOoo & TinyDoc >

Génération de document pdf

The forum is closed. Please use Stack Overflow for submitting new questions. Use tags: tinybutstrong , opentbs
By: Bruno
Date: 2007-05-21
Time: 22:54

Génération de document pdf

Bonsoir,

Peut-on générer des documents PDF directement en faisant un publipostage à partir d'un document openoffice.

Ceci permettant à certains utilisateurs de ne pas avoir openoffice sur le poste client.

Merci d'avance
By: Olivier
Date: 2007-05-24
Time: 13:11

Re: Génération de document pdf

Pour transformer en document PDF j'utilise une technique qui est de faire tourner OpenOffice sur le serveur Linux sans X, et ensuite par un script en Python qui est sur le serveur, je l'appelle en ligne de commande par PHP et cela me permet de convertir le document OpenOffice en PDF ou Excel ou Word

Ce n'est pas évident à faire fonctionner et il faut mettre les mains dans Linux et maitriser l'installation du serveur.

Peut être qu'il doit exister une classe PHP pour faire ca sans cette usine à gaz.

Olivier
By: Bruno
Date: 2007-05-24
Time: 13:25

Re: Génération de document pdf

Merci Olivier.

Je suis chez ovh en mutualisé, donc je ne peux pas mettre les mains dans le sytème d'exploitation.

Je vais voir si une classe existe et si oui, je la mettrai ici.
By: bruno
Date: 2007-05-25
Time: 10:11

Re: Génération de document pdf

Bon il existe une solution qui semble alléchante ici : "http://www.artofsolving.com/opensource/jodconverter"

Mais elle impose d'avoir openoffice sur le serveur.

Je continue de creuser.
By: golivier
Date: 2007-06-06
Time: 08:48

Re: Génération de document pdf

Bonjour,

Je suis interessé par cette solution. Peux tu développer un peu ?

Olivier
By: Bruno
Date: 2007-06-06
Time: 09:43

Re: Génération de document pdf

Bonjour olivier,

Après recherche et discussion, j'ai trouvé dans OOoConv (http://oooconv.free.fr/oooconv/oooconv_fr.html) la partie de code qui m'intéresse pour la bascule :

$execution=" HOME=\"/home/httpd\" ".$oooconv_home."transforme.sh ".
$racine.$dest . " " . $type_doc . " " . $code ; $execution = $execution . " 2>&1 > /tmp/test.txt"; exec( $execution );

Je n'ai pas encore pris le temps de l'intégrer dans mon php, mais je vais le faire rapidement.

Je te tiens au courant.

Bruno
By: golivier
Date: 2007-06-06
Time: 10:49

Re: Génération de document pdf

Merci.

L'intall de Ooo sous linux sans serveur X, c'est simple ? ou y a t'il des piège à éviter ?

Niveau stabilité, c'est comment ?

Olivier
By: Olivier
Date: 2007-06-06
Time: 12:20

Re: Génération de document pdf

Bonjour,

Voici quelques informations pour l'installation d'OpenOffice sous Linux sans X (tests effectués sur une Fedora).
Le code date de 2 ans et c'etait OpenOffice 1.1
J'ai fais une petite compilation de la procedure pour l'installer.

*** Votre mission sera de la faire refonctionner et de la partager aux autres ***

Pour information je suis parti de l'exemple de OOconvert
http://delta-flyer.musc.edu/ooconvert/
Mais je n'ai ni besoin du Perl, ni besoin du Java.

PHP fait une requete au service de conversion
Ce service de conversion est lancé via OpenOffice et le script Python associé



1/---> Installation de XVFB

Xorg doit être installé.
Vérifier que xvfb est installé avec « up2date -i xorg-x11-Xvfb»
Télécharger xvfb-run (--nodeps) à partir de http://distro.ibiblio.org/pub/linux/distributions/altlinux/Sisyphus/noarch/RPMS.master/xvfb-run-1.2-alt1.noarch.rpm
Les dépendances requises sont résolues mais les noms de packages ne sont pas les même. Installer avec –nodeps.


2/---> Installation d’Open Office

up2date -i openoffice.org (ATTENTION, plutot utiliser yum)
Telecharger si besoin le package openoffice.org-pyuno-2.0.1-1.i586.rpm pour supporter le langage python dans OpenOffice


3/---> Creation d'un utilisateur, par exemple oobatch

groupadd –g 5000 oobatch
useradd –c "Utilisateur Batch conversion OOCONVERT" –d /home/oobatch –g 5000 –m –s /bin/bash –u 5000 oobatch
cd /home/oobatch/
faire ls –al /home/oobatch et vérifier la presence de fichier .openoffice* (Si OpenOffice a déjà été lancé avec le user oobatch)


4/---> Installation du service de conversion

A la fin de /etc/rc.local, ajouter /usr/local/bin/ooconvert.daemon.sh.

Contenu du fichier /usr/local/bin/ooconvert.daemon.sh
#!/bin/bash
su - oobatch -c 'nohup xvfb-run --server-args=":99 -fbdir /tmp" /usr/lib/ooo-1.1/program/soffice "-accept=socket,host=localhost,port=2002;urp;">/dev/null &'

Faire chmod 755 /usr/local/bin/ooconvert.daemon.sh
Lancer le script /usr/local/bin/ooconvert.daemon.sh

Faire ps –ax. La sortie devrait contenir quelque chose de ce genre
4757 pts/0    S      0:00 /bin/sh /usr/bin/xvfb-run --server-args=:99 -fbdir /tmp /usr/local/openoffice/program/soffice -acc
4764 pts/0    S      0:00 Xvfb :99 :99 -fbdir /tmp -nolisten tcp
4767 pts/0    S      0:03 /usr/local/openoffice/program/soffice.bin -accept=socket,host=localhost,port=2002;urp;


5/---> Installation des scripts Python

Mettez les 2 fichiers en Python qq part sur votre serveur, dans l'arborescence web par exemple
Creer un repertoire "python"
Mettre les 2 fichiers convooo_calc.py et convooo_writer.py
Attention ils devront être executable par le user php (apache en general). A verifier pour l'utilisateur oobatch

NOTE : attention a ne pas modifier les tabulations et espaces des fichiers Python, car cela en modifie l'interprétation.
Ce n'est pas comme le PHP ou l'on peut justifier comme l'on veut et le début et la fin des instructions sont données en général par les accolades { }
Si vous avez un probleme, je pourrais vous envoyer les fichiers directement, ou je demanderais à Skrol de les mettre sur le site.


contenu du fichier : convooo_calc.py
#!/usr/local/openoffice/program/python

import getopt,sys
import uno
from unohelper import Base,systemPathToFileUrl, absolutize
from os import getcwd

from com.sun.star.beans import PropertyValue
from com.sun.star.beans.PropertyState import DIRECT_VALUE
from com.sun.star.uno import Exception as UnoException
from com.sun.star.io import IOException,XInputStream, XOutputStream

class OutputStream( Base, XOutputStream ):
    def __init__( self ):
        self.closed = 0
         
    def closeOutput(self):
        self.closed = 1

    def writeBytes( self, seq ):
        sys.stdout.write( seq.value )

    def flush( self ):
        pass

def main():
    retVal = 0
    doc = None

    try:
        opts, args = getopt.getopt(sys.argv[1:], "hc:",["help", "connection-string=" , "csv", "dbf", "dif", "html", "pdf", "excel", "ps", "sylk"])
        format = None
        url = "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext"
        filterName = "Text (Encoded)"
        for o, a in opts:
            if o in ("-h", "--help"):
                usage()
                sys.exit()
            if o in ("-c", "--connection-string" ):
                url = "uno:" + a + ";urp;StarOffice.ComponentContext"
            if o == "--cvs":
                filterName = "Text - txt - csv (StarCalc)"
            if o == "--dbf":
                filterName = "dBase"
            if o == "--dif":
                filterName = "DIF"
            if o == "--html":
                filterName = "HTML (CalcWriter)"
            if o == "--pdf":
                filterName = "calc_pdf_Export"
            if o == "--excel":
                filterName = "MS Excel 97"           
            if o == "--ps":
                filterName = "calc_ps_Export"
            if o == "--sylk":
                filterName = "SYLK"
        #print filterName
        if not len( args ):
            usage()
            sys.exit()
             
        ctxLocal = uno.getComponentContext()
        smgrLocal = ctxLocal.ServiceManager

        resolver = smgrLocal.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", ctxLocal )
        ctx = resolver.resolve( url )
        smgr = ctx.ServiceManager

        desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx )

        cwd = systemPathToFileUrl( getcwd() )
        outProps = (
            PropertyValue( "FilterName" , 0, filterName , 0 ),
            PropertyValue( "OutputStream",0, OutputStream(),0))
        inProps = PropertyValue( "Hidden" , 0 , True, 0 ),
        for path in args:
            try:
                fileUrl = uno.absolutize( cwd, systemPathToFileUrl(path) )
                doc = desktop.loadComponentFromURL( fileUrl , "_blank", 0,inProps)

                if not doc:
                    raise UnoException( "Couldn't open stream for unknown reason", None )

                doc.storeToURL("private:stream",outProps)
            except IOException, e:
                sys.stderr.write( "Error during conversion: " + e.Message + "\n" )
                retVal = 1
            except UnoException, e:
                sys.stderr.write( "Error ("+repr(e.__class__)+") during conversion:" + e.Message + "\n" )
                retVal = 1
            if doc:
                doc.dispose()

    except UnoException, e:
        sys.stderr.write( "Error ("+repr(e.__class__)+") :" + e.Message + "\n" )
        retVal = 1
    except getopt.GetoptError,e:
        sys.stderr.write( str(e) + "\n" )
        usage()
        retVal = 1

    sys.exit(retVal)

def usage():
    sys.stderr.write( "usage: ooextract.py --help |\n"+
        "       [-c <connection-string> | --connection-string=<connection-string>\n"+
        "       file1 file2 ...\n"+
        "\n" +
        "Extracts plain text from documents and prints it to stdout.\n" +
        "Requires an OpenOffice.org instance to be running. The script and the\n"+
        "running OpenOffice.org instance must be able to access the file with\n"+
        "by the same system path.\n"
        "\n"+
        "-c <connection-string> | --connection-string=<connection-string>\n" +
        "        The connection-string part of a uno url to where the\n" +
        "        the script should connect to in order to do the conversion.\n" +
        "        The strings defaults to socket,host=localhost,port=2002\n"
        "--html \n"
        "        Instead of the text filter, the writer html filter is used\n"
        )

main()

contenu du fichier : convooo_writer.py
#!/usr/local/openoffice/program/python

import getopt,sys
import uno
from unohelper import Base,systemPathToFileUrl, absolutize
from os import getcwd

from com.sun.star.beans import PropertyValue
from com.sun.star.beans.PropertyState import DIRECT_VALUE
from com.sun.star.uno import Exception as UnoException
from com.sun.star.io import IOException,XInputStream, XOutputStream

class OutputStream( Base, XOutputStream ):
    def __init__( self ):
        self.closed = 0
         
    def closeOutput(self):
        self.closed = 1

    def writeBytes( self, seq ):
        sys.stdout.write( seq.value )

    def flush( self ):
        pass

def main():
    retVal = 0
    doc = None

    try:
        opts, args = getopt.getopt(sys.argv[1:], "hc:",["help", "connection-string=" , "html", "pdf", "rtf", "text", "word"])
        format = None
        url = "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext"
        filterName = "Text (Encoded)"
        for o, a in opts:
            if o in ("-h", "--help"):
                usage()
                sys.exit()
            if o in ("-c", "--connection-string" ):
                url = "uno:" + a + ";urp;StarOffice.ComponentContext"
            if o == "--html":
                filterName = "HTML (StarWriter)"
            if o == "--pdf":
                filterName = "writer_pdf_Export"
            if o == "--rtf":
                filterName = "Rich Text Format"       
            if o == "--text":
                filterName = "Text"
            if o == "--word":
                filterName = "MS Word 97"       
        #print filterName
        if not len( args ):
            usage()
            sys.exit()
             
        ctxLocal = uno.getComponentContext()
        smgrLocal = ctxLocal.ServiceManager

        resolver = smgrLocal.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", ctxLocal )
        ctx = resolver.resolve( url )
        smgr = ctx.ServiceManager

        desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx )

        cwd = systemPathToFileUrl( getcwd() )
        outProps = (
            PropertyValue( "FilterName" , 0, filterName , 0 ),
            PropertyValue( "OutputStream", 0, OutputStream(),0))
        inProps = PropertyValue( "Hidden" , 0 , True, 0 ),
        for path in args:
            try:
                fileUrl = uno.absolutize( cwd, systemPathToFileUrl(path) )
                doc = desktop.loadComponentFromURL( fileUrl , "_blank", 0,inProps)

                if not doc:
                    raise UnoException( "Couldn't open stream for unknown reason", None )

                doc.storeToURL("private:stream",outProps)
            except IOException, e:
                sys.stderr.write( "Error during conversion: " + e.Message + "\n" )
                retVal = 1
            except UnoException, e:
                sys.stderr.write( "Error ("+repr(e.__class__)+") during conversion:" + e.Message + "\n" )
                retVal = 1
            if doc:
                doc.dispose()

    except UnoException, e:
        sys.stderr.write( "Error ("+repr(e.__class__)+") :" + e.Message + "\n" )
        retVal = 1
    except getopt.GetoptError,e:
        sys.stderr.write( str(e) + "\n" )
        usage()
        retVal = 1

    sys.exit(retVal)

def usage():
    sys.stderr.write( "usage: ooextract.py --help |\n"+
        "       [-c <connection-string> | --connection-string=<connection-string>\n"+
        "       file1 file2 ...\n"+
        "\n" +
        "Extracts plain text from documents and prints it to stdout.\n" +
        "Requires an OpenOffice.org instance to be running. The script and the\n"+
        "running OpenOffice.org instance must be able to access the file with\n"+
        "by the same system path.\n"
        "\n"+
        "-c <connection-string> | --connection-string=<connection-string>\n" +
        "        The connection-string part of a uno url to where the\n" +
        "        the script should connect to in order to do the conversion.\n" +
        "        The strings defaults to socket,host=localhost,port=2002\n"
        "--html \n"
        "        Instead of the text filter, the writer html filter is used\n"
        "--pdf \n"
        "        Instead of the text filter, the writer pdf filter is used\n"
        )

main()



6/---> Installation de la bibliothèque PHP

Mettez le fichier ci-dessous avec vos bibliothèques

contenu de : convooo_class.php
<?php
/*
********************************************************
ConvOOo
********************************************************
Author   : Olivier LOYNET (tbsooo@free.fr)
Version  : 0.1.5
Require  : PHP >= 4.0.6
Date     : 2005-09-24
Web site : www.tinybutstrong.com
********************************************************
Released under the GNU GPL license
http://www.gnu.org/copyleft/gpl.html
********************************************************
Inspired and enhanced from :
http://delta-flyer.musc.edu/ooconvert/
Don't have to use the Perl scripts in /cgi-bin
********************************************************
*/

class clsConvOOo
{
  // ===== public properties =====
 
  var $source_filename       = '';
  var $target_type           = '';
  var $target_filename       = '';
  var $error_message         = '';

  // ===== private properties =====

  var $_python_bin           = '';
  var $_python_script_dir    = '';
  var $_python_script_calc   = 'convooo_calc.py';
  var $_python_script_writer = 'convooo_writer.py';

  // ===== constructor =====

  function clsConvOOo()
  {
  }

  // ===== public method =====

  function SetPythonBin($binary)
  {
    // set the binary for 'Python scripts'
    if ($binary == '') {
      $this->error_message = 'Python binary not set';
      return false;
    }
    $this->_python_bin = $this->_BinaryQuote($binary);
    return true;
  }

  function SetPythonScriptDir($path)
  {
    // set the path Python script directory
    $this->_python_script_dir = $path.($path != '' && substr($path, -1, 1) != '/' ? '/' : '');
  }

  function SetPythonScriptCalc($script)
  {
    // set the Python script calc
    if ($script == '') {
      $this->error_message = 'Python script to convert calc is not set';
      return false;
    }
    if (!file_exists($this->_python_script_dir.$script)) {
      $this->error_message = 'Python script \''.$script.'\' not found in dir \''.$this->_python_script_dir.'\'';
      return false;
    }
    if (!is_executable($this->_python_script_dir.$script)) {
      $this->error_message = 'Python script \''.$script.'\' can\'t be executed';
      return false;
    }
    $this->_python_script_calc = $script;
    return true;
  }

  function SetPythonScriptWriter($script)
  {
    // set the Python script writer
    if ($script == '') {
      $this->error_message = 'Python script to convert writer is not set';
      return false;
    }
    if (!file_exists($this->_python_script_dir.$script)) {
      $this->error_message = 'Python script \''.$script.'\' not found in dir \''.$this->_python_script_dir.'\'';
      return false;
    }
    if (!is_executable($this->_python_script_dir.$script)) {
      $this->error_message = 'Python script \''.$script.'\' can\'t be executed';
      return false;
    }
    $this->_python_script_writer = $script;
    return true;
  }

  function Convert($source_filename, $target_type)
  {
    $this->source_filename = $source_filename;
    $this->target_type     = strtolower($target_type);
    $this->target_filename = '';

    $shell_command = $this->_GetPythonShellCommand();
    if ($shell_command === false) {
      return false;
    } else {
      // execute shell command
      return shell_exec($shell_command);
    }
  }

  function ConvertToFile($source_filename, $target_type, $target_filename = '')
  {
    $this->source_filename = $source_filename;
    $this->target_type     = strtolower($target_type);
    $this->target_filename = ($target_filename == '' ? $this->source_filename.'.'.$this->target_type : $target_filename);

    $shell_command = $this->_GetPythonShellCommand();

    if ($shell_command === false) {
      return false;
    } else {
      exec($shell_command);
      // return target filename
      return $this->target_filename;
    }
  }

  function RemoveDoc()
  {
    if ($this->target_filename != '' && file_exists($this->target_filename)) {
      unlink ($this->target_filename);
    }
  }

  // ===== private method =====

  function _GetPythonShellCommand()
  {
    // define the python script depends on the OOo document type
    $script_name = array();
    $script_name['sxc'] = $this->_python_script_calc;
    $script_name['stc'] = $this->_python_script_calc;
    $script_name['sdc'] = $this->_python_script_calc;
    $script_name['ods'] = $this->_python_script_calc;   // OpenDocument Spreadsheet
    $script_name['ots'] = $this->_python_script_calc;   // OpenDocument Spreadsheet Template
    $script_name['sxw'] = $this->_python_script_writer;
    $script_name['stw'] = $this->_python_script_writer;
    $script_name['sdw'] = $this->_python_script_writer;
    $script_name['odt'] = $this->_python_script_writer; // OpenDocument Text
    $script_name['ott'] = $this->_python_script_writer; // OpenDocument Text Template

    // define the convert parameter depend on the OOo document type and the python script
    $convert_type = array();
    $convert_type[$this->_python_script_calc]['csv']   = '--csv';
    $convert_type[$this->_python_script_calc]['dbf']   = '--dbase';
    $convert_type[$this->_python_script_calc]['dif']   = '--dif';
    $convert_type[$this->_python_script_calc]['htm']   = '--html';
    $convert_type[$this->_python_script_calc]['pdf']   = '--pdf';
    $convert_type[$this->_python_script_calc]['xls']   = '--excel';
    $convert_type[$this->_python_script_calc]['slk']   = '--sylk';
    $convert_type[$this->_python_script_writer]['pdf'] = '--pdf';
    $convert_type[$this->_python_script_writer]['htm'] = '--html';
    $convert_type[$this->_python_script_writer]['txt'] = '--text';
    $convert_type[$this->_python_script_writer]['rtf'] = '--rtf';
    $convert_type[$this->_python_script_writer]['doc'] = '--word';

    // test if Python binary is set
    if ($this->_python_bin == '') {
      $this->error_message = 'Python binary not fixed';
      return false;
    }
    // test if Python binary exists
    if (!file_exists($this->_python_bin)) {
      $this->error_message = 'Python binary not exists';
      return false;
    }
    // test if source filename exists
    if (!file_exists($this->source_filename)) {
      $this->error_message = 'OpenOffice file not exists : \''.$this->source_filename.'\'';
      return false;
    }
    // get the document extension
    $source_extension = '';
    $a_pathinfo = pathinfo($this->source_filename);
    $source_extension = strtolower($a_pathinfo['extension']);
   
    // test if the OOo document can be convert
    if (!isset($script_name[$source_extension])) {
      $this->error_message = 'can convert only OpenOffice files with extension : sxc, stc, sdc, ods, ots, sxw, stw, sdw, odt, ott';
      return false;
    }

    // test if the Python script exists
    if (!file_exists($this->_python_script_dir.$script_name[$source_extension])) {
      $this->error_message = 'Python script file not exists';
      return false;
    }
    // test if the source document could be convert by the python script to the target type
    if (!isset($convert_type[$script_name[$source_extension]][$this->target_type])) {
      $this->error_message = 'target type : \''.$this->target_type.'\' not exists in Python script \''.$script_name[$source_extension].'\'';
      return false;
    }
    // test if target directory exist or is writable
    if ($this->target_filename != '') {
      if (!is_dir(dirname($this->target_filename))) {
        $this->error_message = 'target directory not found : \''.dirname($this->target_filename).'\'';
        return false;
      }
      if (!is_writable(dirname($this->target_filename))) {
        $this->error_message = 'target directory not writable : \''.dirname($this->target_filename).'\'';
        return false;
      }
      if (file_exists($this->target_filename)) {
        $this->error_message = 'target filename already exist : \''.$this->target_filename.'\'';
        return false;
      }
    }

    // generate shell command
    $command = $this->_python_bin;
    $command.= ' '.$this->_python_script_dir.$script_name[$source_extension];
    $command.= ' '.$convert_type[$script_name[$source_extension]][$this->target_type];
    $command.= ' '.$this->source_filename;
    $command.= ($this->target_filename != '' ? ' > '.$this->target_filename : '');
    return $command;
  }

  function _BinaryQuote($binary)
  {
    if (strpos($binary, ' ') !== false) {
      $binary = (strpos($binary, '"') === 0 ? '' : '"').$binary;
      $binary = $binary.((strrpos($binary, '"') == strlen($binary)-1) ? '' : '"');
    }
    return $binary;
  }
}
?>


7/---> Exemple d'utilisation en PHP pour la conversion du fichier OpenOffice en PDF ou autre

define('BIN_ZIP'      , 'zip');                           
define('BIN_UNZIP'    , 'unzip');                         
define('BIN_OOO'      , '/opt/soffice/program/soffice');  
define('BIN_PYTHON'   , '/usr/lib/ooo-1.1/program/python');
define('SCRIPT_PYTHON', 'python');                        

include_once('./inc/convooo_class.php');
$conv = new clsConvOOo();
$conv->SetPythonBin(BIN_PYTHON);
$conv->SetPythonScriptDir(SCRIPT_PYTHON);
$new_filename = $conv->ConvertToFile('fichier_openoffice.sxw', 'pdf');


Et voila le travail, pas si simple mais cela fonctionne sans probleme sur un serveur depuis 2 ans

Olivier
By: Bruno
Date: 2007-06-06
Time: 12:49

Re: Génération de document pdf

Olivier a été plus rapide que moi, mais cela peut être intéresant qu'en même

http://svn.nuxeo.org/trac/nuxeo/browser/tools/ooo-package/linux

Bruno
By: golivier
Date: 2007-06-06
Time: 14:20

Re: Génération de document pdf

Un grand merci.

J'essai de transposer ça sur une gentoo (OVH)

Olivier
By: Bruno
Date: 2007-06-06
Time: 14:39

Re: Génération de document pdf

golivier,

Je suis aussi chez OVH.
J'ai un mutualisé.

Si tu modifie des éléments, merci de me tenir au courant.

Bruno
By: Olivier
Date: 2007-06-06
Time: 15:03

Re: Génération de document pdf

Ca m'étonnerais que les deux méthodes proposées puissent être installés chez OVH. Il faut du dédié pour ça car il faut installer les packages xvfb, openoffice, le service qui va être à l'écoute, ... , et en plus cela va être consommateur de RAM et CPU, ce que n'aime pas les hébergeurs qui mutualisent

Olivier