Affichage d'un document Office sous Android

Post on 22-Jun-2015

3.602 views 0 download

description

Présentation réalisée à l'occasion de Devoxx France 2012. Auteurs : Johann Hilbold et Alain Boudard

Transcript of Affichage d'un document Office sous Android

Visualiser un document dans une appli Android

by Johann Hilbold & Alain Boudard

@OxianeIT

1

Abstract

• Ce que je veux faire: afficher un document Office ou PDF dans une application Android

• Comment pallier à l’absence de gestion native des documents Office et PDF?

• Présentation des différentes solutions• Un retour sur expérience d’une aventure folle

Portabilité de Java vers Android Utilisation du code natif “quand y’a pas le choix!”

2

Johann Hilbold & Alain Boudard

Développeur Android Développeur Swing à ses débutsOxiane

3

Développeur Android WebdesignerOxiane Studio

Plan

4

• Introduction• Contraintes• Solutions• Conclusion

Contraintes• Ne pas sortir de l’application pour ouvrir un document (!)• Ne pas sortir le document du SI (= pas de google docs)• Afficher des documents Office (Office 2003, 2007) et PDF

5

Solutions• Afficher un document Office « binaire » dans une app• Afficher un document Office « xml » dans une app• Afficher un PDF dans une app

6

Afficher un fichier Office « binaire » - 1

C’est l’exemple le plus simple:

Il suffit d’utiliser la librairie Apache POI.

3 jars à télécharger:• poi-scratchpad-3.8.jar, • poi-3.8.jar • et commons-codec.jar

Copier des fichiers de test dans le répertoire Assets

7

Afficher un fichier Office « binaire » - 2

public void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);  //copier les fichiers de test depuis mon répertoire assets vers la SDCard      copyAssetToSDCard("test.doc");      copyAssetToSDCard("testxls.xls");    //lancer la conversion avec les jars d'apache POI(source, dest)      WordToHtmlConverter.main(new String[]{"/mnt/sdcard/test.doc", "/mnt/sdcard/test.html"});      ExcelToHtmlConverter.main(new String[]{"/mnt/sdcard/testxls.xls", "/mnt/sdcard/testxls.html"});  }  

8

Afficher un fichier Office « binaire » - 3

private final int BUF_SIZE = 8192;  private File copyAssetToSDCard(String assetName) {         File copiedFileStoragePath = new File("/mnt/sdcard/"+assetName);         BufferedInputStream bis = null;         OutputStream dexWriter = null;         try {             bis = new BufferedInputStream(getAssets().open(assetName));             dexWriter = new BufferedOutputStream(new FileOutputStream(copiedFileStoragePath));             byte[] buf = new byte[BUF_SIZE];             int len;             while((len = bis.read(buf, 0, BUF_SIZE)) > 0) {                 dexWriter.write(buf, 0, len);             }             dexWriter.close();             bis.close();           } catch (Exception e) {e.printStackTrace();         return null;}         Log.d("OfficeCopy", "copied "+assetName+" to /sdcard");         return copiedFileStoragePath; }

9

Afficher un fichier Office « binaire » - 4

Et voila!

Maintenant, on peut afficher le résultat dans une webview

WebView v = (WebView) findViewById(R.id.ma_web_view);  v.loadUrl("file://mnt/sdcard/test.html"); 

10

DEMO

Afficher un document Office « xml » - 1

Apache POI offre des services de lecture/écriture de fichiers Open XML

Mais c’est plus compliqué!

Si on essaye la méthode précédente (documents binaires)…

org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)

11

Afficher un document Office « xml » - 2

Utilisation d’Apache POI – XSSF

Le plan:

Utiliser le même raisonnement que pour les documents « binaires »: • télécharger les jars pour les documents Open XML

http://poi.apache.org/overview.html#components OK!• Les ajouter au build path OK!• Créer une petite classe pour appeler les bonnes méthodes

org.apache.poi.ss.examples.html.ToHtml.main(String[] files) OK!

• Lancer le projet et prier! NOK!

12

Afficher un document Office « xml » - 3

Que s’est-il passé?Avant même de pouvoir installer l’APK sur le device,

[Dex Loader] Unable to execute dex: null[myProject] Conversion to Dalvik format failed: Unable to execute dex: null

Lors de la génération du fichier APK, tous les fichiers .class sont transformés en un unique fichier .dex (Dalvik EXecutable).

Cette transformation est limitée à 64k références de méthodes (!)Nos jars (de 24Mo) pèsent plus de 64k méthodes!

13

Afficher un document Office « xml » - 4

La solution du blog Android Developper

Il « suffit » de découper nos classes en plusieurs fichiers .jar

Puis de lancer la dx.bat sur chacun des jars

Il convient d’utiliser le script Ant fourni dans le SDKandroid-sdk\tools\ant\build.xml

Et de le modifier de la sorte:<dex-helper-mod input-dir="${basedir}/libsToSpecialDEX/1"output-dex-file="${out.absolute.dir}/onary_dex_dir/classes.dex"/>

<jar destfile="${asset.absolute.dir}/onary_dex.jar" basedir="${out.absolute.dir}/onary_dex_dir" includes="classes.dex" />

14

Afficher un document Office « xml » - 5

Si tout s’est bien passé… On peut maintenant charger les classes en mémoire à l’exécution

private Class loadClass(String className) {      new File("/sdcard/office/opti").mkdirs();      final File optimizedDexOutputPath = new File("/sdcard/office/opti");        DexClassLoader cl = new DexClassLoader("/sdcard/office/one.jar:/sdcard/office/two.jar", optimizedDexOutputPath.getAbsolutePath(),  

null,  

TestOfficeAndroidActivity.this.getClassLoader());      Class libProviderClazz = null;      try {            libProviderClazz = cl.loadClass(className);      }      catch(Exception e)      {e.printStackTrace();}      return libProviderClazz;  

}   15

Afficher un document Office « xml » - 6

Une fois la classe chargée en mémoire…

Class cl = loadClass( "org.apache.poi.ss.examples.html.ToHtml");  Class[] paramTypes = new Class[] { String[].class };Method main = cl.getMethod("main", paramTypes);main.invoke(o, (Object) new String[] {"/sdcard/office/SampleSS.xlsx", 

"/sdcard/office/SampleSS.html"}); 

Et c’est seulement à partir d’ici que ça devient vraiment intéressant!

W/System.err(370): Caused by: java.lang.RuntimeException: Installation Problem??? Couldn’t load messages: Can’t find resource for bundle ‘org.apache.xmlbeans.impl.regex.message_en_US’, key  »

16

Afficher un document Office « xml » - 7

Que peut bien vouloir dire cette erreur?Le fichier message.properties est introuvable !

Il n’est plus présent dans mon fichier dex! Mais présent dans XmlBeans-1.0.jar

La compilation en fichier APK, c’est un zip avec un fichier .dex pour les .class (et uniquement les .class!) tout le reste (images, xml de layout, fichiers raw) dans des dossiers /res/drawable, /res/layout, /raw…

Le fichier .properties n’est donc pas resté dans le .dex!

17

Afficher un document Office « xml » - 8

On peut tricher… XmlBeans est OpenSource!

public void setLocale(Locale locale) {try {//this.resources = ResourceBundle.getBundle("org.apache.xmlbeans.impl.regex.message", locale); this.resources = new ResourceBundle() {

@Overrideprotected Object handleGetObject(String key) {return res.get(key);}@Overridepublic Enumeration<String> getKeys() {return Collections.enumeration(res.keySet());}

}; } catch (MissingResourceException mre) {

throw new RuntimeException("Installation Problem??? Couldn't load messages: "+mre.getMessage());

}}static HashMap<String, String> res = new HashMap<String, String>();static{res.put("parser.parse.1", "Wrong character.");} 1

8

Afficher un document Office « xml » - 9

Cette fois-ci, ça va passer…Toujours pas!org.apache.xmlbeans.SchemaTypeLoaderException: XML-BEANS compiled schema: Could not locate compiled schema resource {…..}/index.xsb Les fichiers .xsb ne sont pas dans les .dex, mais cette fois, on sait pourquoi!

package org.apache.xmlbeans.impl.schema;public class ClassLoaderResourceLoader{public InputStream getResourceAsStream(String name) { try {

return new FileInputStream("/sdcard/office/xsb/"+name.substring(name.lastIndexOf("/")+1));

} catch (FileNotFoundException e) {e.printStackTrace();} //_classLoader.getResourceAsStream(resourceName);return null;

} 19

Afficher un document Office « xml » - 10

On tient le bout…

Maintenant qu’on accède à toutes les ressources du jar original…

ClassNotFoundException: java.awt.Color

POI est censé tourner sous Java 1.5 Android ne connait pas les packages java.awt (et quelques autres)

Une ultime manipulation: remplacer tous les java.awt par des and.awt dans les sources de POI

http://code.google.com/p/awt-android-compat/Attention, le résultat n’est pas parfait

pas de gestion des images (pour l’instant)20

DEMO

21

Afficher un document Office « xml » - 11

Une approche plus sexy:- Intégrer les ressources manquantes aux jars

- Création d’un jar simple- « Dexisation » du jar en un fichier classes.dex- Réimportation des ressources dans un nouveau jar créé à la main NOK!

Le dexloader n’a pas l’air de gérer les jars et les APK de la même façon- Packaging du classes.dex dans un APK « maison » OK!

- En réutilisant cette technique, on peut donc, en théorie, se passer des étapes précédentes

21

DEMO

Afficher un document Office « xml » - 12

Conclusion de la méthode POI

Le code complet est disponible ici:https://code.google.com/p/display-msoffice-docs-android-with-apache-poi/

Avantages:• Permet une conversion très complète (celle que permet le client POI)• Est très rapide (adaptée aux processeurs « lents » des mobiles

d’aujourd’hui)• Donne accès à tous les formats (doc/docx, ppt/pptx, xls/xlsx, et même

Visio .vsd ou Outlook msg)• Est chargeable dynamiquement en mémoire• Laisse la porte ouverte à un futur « mode édition »

22

Afficher un document Office « xml » - 13

Inconvénients:• Utilise énormément de place sur la SDcard (8Mo d’APK + 15Mo de

ressources)• Compliqué à mettre en œuvre• Pour la partie AWT, repose sur le package and.awt

Pas de gestion des java.awt.BufferedImage. Il faudrait les convertir en android.graphics.Bitmap!

23

Afficher un document Office « xml » - 14

L’alternative

La transformation XSL basée sur la solution de Julien Chablehttp

://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2006/11/21/openxmlandjava.aspx

• Un procédé beaucoup plus simple, mais moins efficace.

<!-- Body and paragraphs --><xsl:template match="w:body">

<html><body>

<xsl:for-each select="w:p"><p>

<xsl:apply-templates select="w:pPr" /><xsl:apply-templates select="w:r" />

</p></xsl:for-each>

</body></html>

</xsl:template>24

Afficher un document Office « xml » - 15

Avantages:• D’une légèreté déconcertante: mon APK fait seulement 261k• D’une simplicité déconcertante: un seul jar à ajouter au Build Path et

c’est fini.• Facilement améliorable: il « suffit » d’ajouter les balises qu’on veut gérer

Inconvénients:• Ne gère que les OfficeOpen XML (donc pas les formats « binaires »)• Il faut trouver (ou écrire soi même?) un XSL vraiment complet pour un

résultat parfait. Les images ne sont pas du tout gérées Seuls quelques styles sont implémentés, pas de formule, tableaux,

wordart…

25

Afficher un document Office « xml » - 16

Structure d’un fichier docx

26

Afficher un document Office « xml » - 17

Utilisation des rID pour retrouver le style ou le nom de fichier pour une image avec le fichier document.xml.rels

w:drawing crée une balise <img>avec pour attribut id = rID

On extrait toutes les images de/word/media vers la SDCard

2ème parsing:scan de toutes les balises img et ajout de l’attribut grâce à la correspondance rID

27

Afficher un document Office « xml » - 18

Malgré cette technique pour afficher les images, cette solution reste imparfaite

• Les positions des éléments ne sont pas gérées• Les puces, formules, tableaux ne sont pas gérés

• Un exemple existant de traitement XSL de document duquel on pourrait s’inspirer: DocBook

Mon code est disponible icihttps

://code.google.com/p/display-msoffice-docs-android-without-apache-poi/

28

DEMO

Afficher un PDF -1Une bonne occasion de voir le NDK

• Sous windows, installer Cygwinhttp://mindtherobot.com/blog/452/android-beginners-ndk-setup-step-by-step/

(étapes 2 et 3)• Télécharger le projet APV

http://code.google.com/p/apv/

• Lancer la commande /script/build-native.sh du projet APV

• Si tout s’est bien passé, on peut lancer le projet APV sous Eclipse!

29

Afficher un PDF -2C’est très bien mais c’est pas ce qu’on veut!

On vient de faire l’équivalent d’installer l’appli « Acrobat Reader »

30

Fichier librairie qui permet de parser les PDFs

Afficher un PDF -2Customiser un peu…

Extraction de la View PagesView• Par défaut, apv offre une view qui fait de nombreux appels à une

activité.

Un composant personnalisé

<com.oxiane.DisplayPDF android:id="@+id/my_pdf_view" android:layout_width="fill_parent" android:layout_height="fill_parent" app:src="http://monpdf.com" />

31

Afficher un PDF -3Inconvénients de la méthode

• Pas de gestion des liens HTTP• Encore 1,7Mo à rajouter à l’APK!• Chargement se fait en rectangles

Avantages de la méthode

• Ça a le mérite de marcher!

32

Conclusion

• Une aventure pionnière!• Un aperçu des possibilités qu’offre l’open source Java dans Android• Des ouvertures pour arriver à des résultats plus professionnels

33