Blog de JP Gouigoux

02/08/2009

Mise à jour des librairies pour FOP en .NET

Filed under: .NET,FOP — jpgouigoux @ 2:13
Tags: ,

Suite à une demande dans les commentaires d’un précédent article, voici les librairies compilées en .NET pour faire du XSL/FO par interop avec IKVM. J’en profite pour passer aux nouvelles versions de FOP (0.95) et d’IKVM (0.40.0.1).

Le tout a été buildé sur la base d’une VM 1.6.0_13-b03, et sans clé de nom fort.

Voici le lien. Si vous utilisez ces librairies, merci de m’envoyer un petit message sur votre retour d’expérience, satisfaisant ou pas.

Tant que j’y suis… Cette méthode est manuelle, et ne sélectionne pas les classes utilisées pour la compilation par IKVM. Si vous voulez seulement faire du XSLFO, vous pouvez utiliser également le projet NFOP. D’après ce que j’ai compris, ce n’est pas un portage de FOP en .NET, mais plutôt la même chose que ce que j’ai détaillé dans le post sur XSL/FO en .NET, à savoir une recompilation. Par contre, ils font bien sûr beaucoup mieux les choses, et en particulier, ils doivent filtrer les classes nécessaires lorsqu’ils compilent les JAR de dépendances, ce qui fait que le résultat est bien plus léger. Je vais tester si il n’y a pas autre chose, et je posterai mes remarques dans quelque temps.

21/08/2008

IKVM et FOP

Filed under: .NET,FOP — jpgouigoux @ 8:15

SI vous avez lu mon premier post sur l’utilisation d’Apache FOP en .NET par interop IKVM, vous serez peut-être intéressés de savoir que FOP 0.95 est sorti en version finale, et que les tests d’interop sont concluants.

En tout cas, pas de problème de mise à niveau sur les quelques XSL-FO testé…

05/05/2008

XSL/FO et .NET

Filed under: .NET,FOP — jpgouigoux @ 9:30
Tags: , , ,

Vous êtes-vous déjà demandé comment faire pour générer un PDF en XSL-FO depuis ASP.NET, ou pour le coup n’importe quelle application .NET ? J’ai eu beau chercher, je n’ai pas trouvé de moteur XSL-FO écrit en code managé et open-source.

La solution glanée sur différents sites web est de faire de l’interop avec ApacheFOP, qui est écrit en Java. Mais là, deux voies sont possibles :

– Ou bien on recompile le code Java en J#, et on obtient des assemblages .NET nécessitant les redistribuables J# pour fonctionner. Avantage : le tout ne pèse que quelques Mo. Inconvénient : ça n’a aucune chance de fonctionner sous Mono.

– Ou bien on exécute en mémoire les JAR en passant par IKVM, qui est une machine virtuelle embarquée dans un processus .NET. Avantage : c’est propre et tout tourne en mémoire, de plus pas de problème théoriquement avec Mono, vu qu’IKVM est carrément livré avec. Inconvénient : ça pèse une trentaine de Mo, car il faut livrer avec la totalité des classes Java. Il doit y avoir moyen de faire autrement en générant une dll IKVM.ClassPath.dll avec seulement ce qui est nécessaire, mais je n’ai pas essayé.

On va expliquer sur ce post la deuxième méthode, qui de mon point de vue est la meilleure. Oui, je sais, ça a déjà été fait sur d’autres pages web, mais je n’ai rien trouvé qui était à jour avec la dernière version de FOP et avec la liste des références nécessaires.

Récupération des librairies

On commence par récupérer FOP sur le site Apache, http://xmlgraphics.apache.org/fop/download.html (la toute dernière version stable est la 0.94). On va également télécharger la dernière version en date de IKVM, à savoir la 0.36.0.11 sur http://sourceforge.net/project/showfiles.php?group_id=69637.

Petite remarque au passage : je suis toujours autant émerveillé de la modestie des équipes capables de réaliser un développement aussi remarquable, et de laisser un numéro de version inférieur à 1. Quand on voit des logiciels soi-disant professionnels toujours aussi buggés en version 10, on se dit que beaucoup d’entre eux devraient prendre exemple sur les développements OpenSource.

On décompresse ensuite tout ça dans deux répertoires /FOP et /IKVM.

Recompilation des librairies FOP par IKVM

IKVM fournit un environnement d’exécution .NET d’une VM Java sous forme d’un exécutable, mais également un compilateur permettant de générer des assemblages .NET à partir des JAR, et qui se nomme IKVMC.

Dans notre cas, on se place dans /IKVM/bin, et on lance successivement les commandes suivantes :

ikvmc -target:library
    -reference:IKVM.OpenJDK.ClassLibrary.dll
    /fop-0.94/lib/xml-apis-1.3.02.jar

ikvmc -target:library
    -reference:IKVM.OpenJDK.ClassLibrary.dll
    -reference:xml-apis-1.3.02.dll
    /fop-0.94/lib/xercesImpl-2.7.1.jar

ikvmc -target:library
    -reference:IKVM.OpenJDK.ClassLibrary.dll
    -reference:xml-apis-1.3.02.dll
    /fop-0.94/lib/avalon-framework-4.2.0.jar

ikvmc -target:library
    -reference:IKVM.OpenJDK.ClassLibrary.dll
    -reference:xml-apis-1.3.02.dll
    /fop-0.94/lib/batik-all-1.6.jar

ikvmc -target:library
    -reference:IKVM.OpenJDK.ClassLibrary.dll
    /fop-0.94/lib/commons-logging-1.0.4.jar

ikvmc -target:library
    -reference:IKVM.OpenJDK.ClassLibrary.dll
    /fop-0.94/lib/commons-io-1.3.1.jar

ikvmc -target:library
    -reference:IKVM.OpenJDK.ClassLibrary.dll
    /fop-0.94/lib/xmlgraphics-commons-1.2.jar

ikvmc -target:library
    -reference:IKVM.OpenJDK.ClassLibrary.dll
    -reference:xml-apis-1.3.02.dll
    -reference:batik-all-1.6.dll
    -reference:commons-logging-1.0.4.dll
    -reference:xmlgraphics-commons-1.2.dll
    -reference:commons-io-1.3.1.dll
    -reference:avalon-framework-4.2.0.dll
    /fop-0.94/build/fop.jar

Si vous avez besoin d’assemblages signés, il suffit de spécifier la directive -keyfile:[votre fichier .snk] ou bien -key:[le nom de votre container de clé]. IKVMC contient de nombreuses autres options qui peuvent se réveler très utiles. La prise en compte de la signature par nom fort, dans mon cas, était très importante, et ce fut un vrai soulagement de voir que le paramètre était présent. Je me voyais déjà désassembler l’IL depuis la DLL puis recompiler en ajoutant la signature, et je ne suis même pas sûr que ça soit possible…

Utilisation des assemblages créés

Une fois ceci fait, vous pouvez créer un projet .NET dans lequel vous allez simplement mettre en référence les librairies fop.dll et IKVM.ClassPath.dll générées précédemment. Placer au même endroit toutes les autres références, qui ne seront pas nécessaires pour la compilation, mais indispensables lors de l’exécution. Vous pouvez ensuite taper le genre de code suivant pour vérifier que tout fonctionne. J’ai enlevé tout ce qui est gestion des exceptions et génération des noms de fichiers pour simplifier le code et ne faire voir que ce qui a vraiment du sens pour un tutoriel sur XSL-FO.

Note : je préfère écrire systématiquement les noms de classes complets dans ce genre d’exemple. Je trouve qu’on a toujours du mal à savoir d’où viennent les classes sinon, surtout qu’il est très facile d’oublier de donner les using lorsqu’on copie une simple portion de classe. Dans la pratique, il est bien sûr plus clair d’utiliser des noms de classe simples avec des using.
public void GenererPDF(System.Xml.XmlDocument Source, string FichierXSLFO, string FichierResultatPDF)
{
    // Récupération du fichier XSL
    System.Xml.Xsl.XslCompiledTransform Moteur = new System.Xml.Xslt.XslCompiledTransform();
    Moteur.Load(FichierXSLFO);

    // Application de la transformation
    string FichierResultatFO = System.IO.Path.GetTempFileName();
    System.Xml.XmlTextWriter Scribe = new System.Xml.XmlTextWriter(FichierResultatFO, System.Text.Encoding.Default);

    Moteur.Transform(Source.CreateNavigator(), Scribe);
    Scribe.Close();

    // Création du PDF par interop IKVM avec ApacheFOP
    java.io.File fo = new java.io.File(FichierResultatFO);
    java.io.File pdf = new java.io.File(FichierPDFGenere);
    org.apache.fop.apps.FopFactory fopFactory = org.apache.fop.apps.FopFactory.newInstance();
    org.apache.fop.apps.FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
    java.io.OutputStream sortie = new java.io.BufferedOutputStream(new java.io.FileOutputStream(pdf));
    org.apache.fop.apps.Fop fop = fopFactory.newFop("application/pdf", foUserAgent, sortie);
    javax.xml.transform.TransformerFactory factory = javax.xml.transform.TransformerFactory.newInstance();
    javax.xml.transform.Transformer transformer = factory.newTransformer();
    javax.xml.transform.Source src = new javax.xml.transform.stream.StreamSource(fo);
    javax.xml.transform.Result res = new javax.xml.transform.sax.SAXResult(fop.getDefaultHandler());
    transformer.transform(src, res);
    sortie.close();
}

Résultat

Une fois que les premiers résultats fonctionnels ont été atteint, la curiosité pousse à aller voir combien tout ça prend en mémoire. Il y a en effet finalement 13 librairies à mettre en place pour faire fonctionner le tout, pour un total de 36 Mo. Vu que toutes les classes de Java ont été reproduites en IKVM, c’est un peu logique, mais ce qui est intéressant, c’est que la CLR, comme pour des librairies .NET standards, ne charge que ce dont elle a besoin, à savoir en gros 5 Mo, et prend quelques dizaines de Mo pour un gros PDF, mais cette partie de mémoire est considérée comme ponctuelle, et sera libérée lors du prochain passage du garbage collector.

Bref, ça marche ! Honnêtement, je ne me suis pas penché sur IKVM pour vous indiquer clairement comment, mais je préfère avoir un produit qui fonctionne sans savoir pourquoi que l’inverse.

Propulsé par WordPress.com.