[Symfony] fichier OpenDocument avec odtPHP fichier corrompu

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

17 mai 2009, 18:54

Bonjour,

je colle ce sujet dans la rubrique XML car OpenDocument me paraît le sujet central. Si ce n'est pas bon, un modérateur pourra le déplacer :D


PHP : 5.2.5
Symfony : 1.1
odtPHP : 0.3


dans mon outil de gestion de cours, je veux proposer la création automatique d'attestation de suivi des cours. J'ai donc un modèle d'attestation, avec des variables (pas beaucoup, genre nom/prénom etc) et je génère mon document grâce à la librairire des camarades Cyruss et Julien Pauli : http://www.phpteam.net/index.php/articl ... t-avec-php

Si je prends mon modèle, mon petit bout de code ci-dessous, cela marche très bien : document généré et ouvert sans problème par OO Writer.
        // $attestation est un bête objet avec des attributs, des getters et des setters
	public function genererOpenDocument($attestation) {
		require_once('odf/odf.php');
		
                // pour tester en dehors de symfony je remplace par un chemin statique
		$templatesPath = sfConfig::get('sf_app_template_dir').'/docs/';
		$templateODF = $templatesPath.'modele_attestation.odt';
		
		$odf = new odf($templateODF);
		
		$odf->setVars('NOM_PRESIDENT',$attestation->nomPresidentDelegation);
		$odf->setVars('TITRE_APPRENANT',$attestation->titre);
		$odf->setVars('NOM_APPRENANT',$attestation->nomApprenant);
		$odf->setVars('PRENOM_APPRENANT',$attestation->prenomApprenant);
		$odf->setVars('DATE_ATTESTATION',$attestation->dateCreationAttestation);
		$odf->setVars('DATE_DEBUT_SESSION',$attestation->dateDebutSession);
		
		$nomFichier = 'attestation-'.strtoupper($attestation->nomApprenant).'.odt';
		// On exporte le fichier
		$odf->exportAsAttachedFile($nomFichier);
	}
Mais si j'intègre ça dans mon projet symfony (le même modèle, le même code !), en ouvrant le document OO me dit "document corrompu" et me propose de le réparer. Une fois réparé, c'est ok, il y a tout ce que je voulais dedans.

J'ai tenté un diff sur quelques fichiers xml contenu dans l'archive OK et celle KO, je ne vois rien de spécial. J'ai aussi cherché sur internet tout comme il faut.

Quelqu'un a-t-il une idée ? Y'a-t-il un log, un rapport d'erreur OpenOffice permettant de savoir ce qui ne va pas dans le fichier ?

Je sais il n'y a pas beaucoup de code mais franchement quoi mettre ? J'ai fait le test avec un des tutoriaux livrés avec la lib (un document ODF qui contient une seule variable, et le petit bout de code pour la valoriser) et c'est la même chose... enfin si vous en voulez plus, je fournis.

Merci de votre aide :)
Modifié en dernier par ouckileou le 18 mai 2009, 14:32, modifié 1 fois.

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

17 mai 2009, 20:31

J'ai trouvé un validateur de fichier ODF : http://opendocumentfellowship.com/validator

Mais pour lui même un fichier créé avec OpenOffice Writer (avec juste "coucou" dedans) n'est pas valide :
# error

in manifest.xml
Invalid attribute version for element file-entry
# error

in styles.xml
Expecting an element outline-level-style, got nothing
Extra element outline-style in interleave
Element styles failed to validate content
Quant à mon document généré (avant réparation Open Office), il n'arrive même pas à l'ouvrir.

Alors bon du coup, ça m'aide pas trop :)

La seule piste que j'ai pour le moment, c'est un problème d'encodage (mon projet symfony manipulé dans Eclipse est en UTF8) et là ça me fait peur car avec les problèmes d'encodage, je ne m'en sors jamais :(

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

17 mai 2009, 23:24

C'est plus logique de prendre le validateur d'OpenOffice directement : http://tools.services.openoffice.org/odfvalidator/

Lui, il valide bien le fichier réparé. Par contre le fichier généré, il n'arrive pas à l'ouvrir non plus, mais sans autre explication.
ODF Validator Result Page
Result for attestation-XXX-XXXXXXX-052009.odt

This file is NOT valid

Result details:

upload:///attestation-XXX-XXXXXXX-052009.odt:Fatal:error in opening zip file
Si quelqu'un avait une idée pour débugguer ce truc, ça m'aiderait beaucoup...

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

18 mai 2009, 12:35

Je poursuis mon monologue : je constate que mon archive ODF générée fait à peu près deux fois la taille de celle réparée par OpenOffice. Bizarre, car les fichiers contenus eux sont quasi identiques.

Quelqu'un aurait-il une idée pour identifier le problème, sans même parler de sa cause ? je n'ai même pas d'idée pour débugguer et c'est très frustant

Merci :)

Modérateur PHPfrance
Modérateur PHPfrance | 6037 Messages

18 mai 2009, 16:22

La différence dans les en-têtes envoyés entre les deux versions ?
Règle n°2 du webmaster : Toujours commencer par le HTML qu'on veut obtenir....toujours ! :priere:
J'aime apprendre de nouvelles choses.

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

18 mai 2009, 16:42

Les en-têtes HTTP ?

A priori les mêmes, c'est géré dans la classe :
<?php
class Odf {
    /**
     * Exporte le fichier en fichier attaché via HTTP
     *
     * @param string $name (optionnal)
     * @throws OdfException
     * @return void
     */
    public function exportAsAttachedFile($name="") {
        $this->file->open($this->tmpfile, ZIPARCHIVE::CREATE);
        $this->_save();
        if (headers_sent($filename, $linenum)) {
            throw new OdfException("headers already sent ($filename at $linenum)");
        }
        
        if( $name == "" )
        {
        		$name = md5(uniqid()) . ".odt";
        }
        
        header('Content-type: application/vnd.oasis.opendocument.text');
        header('Content-Disposition: attachment; filename="'.$name.'"');
        readfile($this->tmpfile);
    }
}

?>
Mais je vais vérifier avec un coup de Firebug. Sinon, si tu parlais d'un truc dans le fichier ODf, tu parlais de quoi exactement ? l'en-tête de content.xml ?

Edit :

avec Firebug, je vois bien cet en-tête pour mon script de test (hors symfony) :
Date Mon, 18 May 2009 22:48:25 GMT
Server Apache/2.2.8 (Win32) PHP/5.2.5
X-Powered-By PHP/5.2.5
Set-Cookie ZDEDebuggerPresent=php,phtml,php3; path=/
Content-Disposition attachment; filename="attestation.odt"
Keep-Alive timeout=5, max=99
Connection Keep-Alive
Transfer-Encoding chunked
Content-Type application/vnd.oasis.opendocument.text
Par contre dans symfony, avec le panneau d'inspection de la réponse, rien de tout ça.

Mais je pense que tu as raison et que le soucis est à ce niveau car si j'utilise la méthode saveToDisk() et que j'ouvre le fichier, il s'ouvre correctement.

Je vais chercher sur la doc symfony comment utiliser son système de header avec cette classe.

Merci ;)
Modifié en dernier par ouckileou le 24 mai 2009, 23:13, modifié 1 fois.

Modérateur PHPfrance
Modérateur PHPfrance | 6373 Messages

19 mai 2009, 15:36

C'était bien ça, un problème de headers avec symfony.

J'ai résolu le truc en stoppant l'exécution après l'appel à la méthode de téléchargement de la classe odf :
$odf->exportAsAttachedFile($fileName);
exit;
Ce qui, reconnaissons-le, n'est pas très bien, et pas super intégré à symfony :D

Le problème c'est que je ne sais pas trop quel est le mieux à faire pour le coup, vu que mes classes s'appellent comme cela :

sfAction
-- GenerateurAttestation : récupère l'attestation (objet), rempli l'attestation (document)
-- Odf : manipule le document, propose le téléchargement

Je ne savais pas comment arrêter proprement le script après l'appel au générateur, j'ai essayé des return sfView::NONE ou HEADER_ONLY, vu notamment sur ce bon sujet http://forum.symfony-project.org/index.php/m/30071/ , mais ça ne marche pas...

Par contre, je me rappellais de ce sujet : http://prendreuncafe.com/blog/post/2008 ... -un-bateau
Et à la fin, il y a ça :
2. lever une exception pour arrêter un script, c'est super cracra et mériterait éventuellement d'être patché ;)

Edit: Je m'exprime mal, c'est pas super cracra, c'est juste que ça introduit une petite complexité supplémentaire. Mais l'utilité de la chose est bien entendu totalement avérée si on a besoin d'effectuer des opérations particulières avant la fin du script ( ce qui est rarement le cas, enfin chez moi).
Je considère pour l'instant que mon téléchargement de fichier via des classes externes s'apparente à une opération particulière, donc en mettant :
$generateur->genererOpenDocument();
throw new sfStopException();
ça fonctionne, et c'est déjà mieux que le exit :)

Voilà, un long sujet pour pas grand-chose mais bon, dans la vie des fois, on galère.