java code

Intégration de Word dans une application Java

Ces derniers temps j’ai travaillé sur l’intégration de Word dans une application Java. En effet, grâce à plusieurs apis créées par Microsoft, il est possible non seulement possible d’intégrer les applications de la suite Office (mais aussi Internet Explorer) dans d’autres applications mais également de les piloter. En Java, la librairie graphique SWT et plus précisément son package org.eclipse.swt.ole.win32 permet une intégration rapide. Seulement, je me suis rapidement rendu compte qu’il y a assez peu de documentation disponible sur Internet sur ce sujet. Une fois passés les premiers tutoriaux qui expliquent comment ouvrir Excel ou Internet Explorer, c’est le désert… J’ai donc décidé de partager quelques petits exemples de code simple et quelques conseils pour aller plus loin avec l’automation Microsoft!

Histoire de passer rapidement sur la base, voici un exemple de code qui montre comment lancer Word dans une application Java :

 

public static void main(String[] args) {
	Display display = new Display();
	final Shell shell = new Shell(display);
	shell.setText("Word Example");
	shell.setLayout(new FillLayout());
	try {
		frame = new OleFrame(shell, SWT.NONE);
		clientSite = new OleClientSite(frame, SWT.NONE, "Word.Document");
		clientSite.doVerb(OLE.OLEIVERB_INPLACEACTIVATE);
		addFileMenu(frame);
	} catch (SWTError e) {
		System.out.println("Unable to open activeX control");
		display.dispose();
		return;
	}

	shell.setSize(800, 600);
	shell.open();

	while (!shell.isDisposed()) {
		if (!display.readAndDispatch())
			display.sleep();
	}
	display.dispose();
}

 

On a besoin d’une OleFrame qui représente le conteneur de l’application et d’un OleClientSite qui va permettre de gérer l’application et son contenu. Autre chose à savoir, l’application embarque son propre menu. Pour ajouter des éléments de menu, il faut les créer puis les intégrer au menu de l’application avec les méthodes setFileMenus(MenuItem[] items), setContainerMenus(MenuItem[]) et  setWindowMenus(MenuItem[] items) de l’objet OleFrame.

 

static void addFileMenu(OleFrame frame) {
	final Shell shell = frame.getShell();
	Menu menuBar = shell.getMenuBar();
	if (menuBar == null) {
		menuBar = new Menu(shell, SWT.BAR);
		shell.setMenuBar(menuBar);
	}
	MenuItem fileMenu = new MenuItem(menuBar, SWT.CASCADE);
	fileMenu.setText("&File");
	Menu menuFile = new Menu(fileMenu);
	fileMenu.setMenu(menuFile);
	frame.setFileMenus(new MenuItem[] { fileMenu });

	MenuItem menuFileOpen = new MenuItem(menuFile, SWT.CASCADE);
	menuFileOpen.setText("Open...");
	menuFileOpen.addSelectionListener(new SelectionAdapter() {
		public void widgetSelected(SelectionEvent e) {
			fileOpen();
		}
	});
	MenuItem menuFileExit = new MenuItem(menuFile, SWT.CASCADE);
	menuFileExit.setText("Exit");
	menuFileExit.addSelectionListener(new SelectionAdapter() {
		public void widgetSelected(SelectionEvent e) {
			shell.dispose();
		}
	});
}

static void fileOpen() {
	FileDialog dialog = new FileDialog(clientSite.getShell(), SWT.OPEN);
	dialog.setFilterExtensions(new String[] { "*.docx" });
	String fileName = dialog.open();
	if (fileName != null) {
		clientSite.dispose();
		clientSite = new OleClientSite(frame, SWT.NONE, "Word.Document", new File(fileName));
		clientSite.doVerb(OLE.OLEIVERB_INPLACEACTIVATE);
	}
}

 

Bon, maintenant qu’on a notre application fonctionnelle, on va s’attaquer à l’automation, à savoir communiquer avec Word pour récupérer des informations sur le document ouvert et exécuter des actions comme insérer du texte à un endroit précis, rechercher du texte ou lister les paragraphes.

Tout d’abord, il faut savoir qu’avec l’api SWT OLE, grosso modo, tout est soit Variant soit OleAutomation. Un Variant est un objet générique qui peut contenir tout type d’information, que ce soit une chaîne de caractère, un entier, un booléen ou autre. On va s’en servir pour récupérer les valeurs de retour des méthodes de nos objets ou contrôles COM ou bien pour définir leurs paramètres. Un OleAutomation est un objet générique qui va permettre d’accéder aux propriétés et fonctions d’un objet ou contrôle COM. Concrètement, on va partir d’un OleAutomation représentant l’application pour récupérer une propriété, disons le document actif. À partir du Variant représentant le document actif, on récupère un OleAutomation qui va à son tour permettre de récupérer la propriété ‘liste des paragraphes’, etc.

Voici un exemple concret :

 

OleAutomation ole = new OleAutomation(clientSite);
int[] appId = ole.getIDsOfNames(new String[]{ "Application" });
if(appId != null) {
	Variant vApp = ole.getProperty(appId[0]);
	OleAutomation app = vApp.getAutomation();
}

 

Comme je le disais, on commence par créer un OleAutomation à partir du OleClientSite. On récupère ensuite sa propriété Application pour ensuite en extraire un nouveau OleAutomation. Afin de récupérer une propriété, il faut connaître son id. La méthode getIDsOfNames(String[]  ids) est là pour ça! Retenez cette méthode, vous en aurez besoin en permanence ! Pour connaître la liste des objets disponibles ainsi que leurs méthodes et leurs propriétés, la seule source d’info, c’est MSDN ! Faites toujours attention à utiliser les noms exacts. Sinon la méthode getIDsOfName() vous retournera null.

Maintenant qu’on est capable de récupérer une propriété, voyons comment utiliser une méthode. L’objet OleAutomation possède une méthode générique qui permet d’exécuter n’importe quelle méthode de l’objet OLE ou du contrôle COM qu’on manipule. Il s’agit de la méthode invoke(). Par exemple, toutes les collections d’objets comme la liste des paragraphes ont une méthode Item(int index) qui retourne le paragraphe à l’index sélectionné. Pour parcourir l’ensemble des paragraphes, pas question de faire un foreach sur le Variant de la liste! Non, ce serait trop facile ^^. Voici un exemple de code qui parcourt l’ensemble des paragraphes du document actif et affiche leur contenu dans la console :

note: j’ai volontairement évité les contrôles sur les valeurs de retour null pour ne pas alourdir le code mais il est préférable de les utiliser pour se repérer plus facilement en cas de mauvais nom de propriété ou de méthode utilisé!

 

int[] activeDocumentId = app.getIDsOfNames(new String[]{ "ActiveDocument" });
Variant vActiveDoc = app.getProperty(activeDocumentId[0]);
OleAutomation activeDoc = vActiveDoc.getAutomation();
int[] paragraphsId = activeDoc.getIDsOfNames(new String[]{ "Paragraphs" });
Variant vParagraphs = activeDoc.getProperty(paragraphsId[0]);
OleAutomation paragraphs = vParagraphs.getAutomation();
int[] countMethodId = paragraphs.getIDsOfNames(new String[]{ "Count" });
int[] itemMethodId = paragraphs.getIDsOfNames(new String[]{ "Item" });

Variant vCount = paragraphs.getProperty(countMethodId[0]);
int count = vCount.getInt();
for(int i = 1 ; i <= count ; i++) {
	Variant vParagraph = paragraphs.invoke(itemMethodId[0], new Variant[]{ new Variant(i) });
	OleAutomation paragraph = vParagraph.getAutomation();
	int[] rangeId = paragraph.getIDsOfNames(new String[]{ "Range" });
	Variant vRange = paragraph.getProperty(rangeId[0]);
	OleAutomation range = vRange.getAutomation();
	int[] textId = range.getIDsOfNames(new String[]{ "Text" });
	Variant vText = range.getProperty(textId[0]);
	String content = vText.getString();
	System.out.println(content);
}

 

On remarque que la méthode invoke() accepte entre autres un paramètre qui représente l’identifiant de la méthode qu’on souhaite utiliser ainsi qu’un tableau de Variant représentant les paramètres de cette méthode.

Voilà pour les petits éclaircissements sur l’utilisation de l’api SWT OLE :) Un dernier conseil pour la route, si vous ne voulez pas trop chercher dans la documentation MSDN, vous pouvez aussi utiliser un snippet swt qui permet d’imprimer dans la console toutes les propriétés et les méthodes d’un objet COM ou ActiveX. Vous le trouverez sur la page des snippets SWT ou bien en suivant directement ce lien. Happy coding!

One Comment

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *