4D, XML et Word 2003 Windows

Christophe KEROMEN

Nous allons montrer dans ce document comment 4D 2004 peut exporter des données en XML qui pourront directement être utilisées par MS Word 2003 afin de construire des documents dynamiques. Nous verrons également des exemples de code VBA, de scripts VBScript et une application des possibilités de la nouvelle commande TRAITER BALISES HTML. ♪

- Titre : 4D, XML et Word 2003 Windows

- Auteur : Christophe KEROMEN

- Parution : 1 juillet 2005

- Licence : Ce document est issu de https://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- I. Introduction

- II. Le cahier des charges

- Organisation de l’exemple

- Liaison avec les données

- III. Export des données XML

- Génération du document XML

- IV. Liaison avec les données XML

- Test de notre solution

- Provoquer la mise à jour des champs

- Figer les références

- Déclencher la mise à jour

- V. La couche finale : scripting de Word depuis 4D

- La méthode complète d’export

- VI. Conclusion

- VII. Base exemple

I - Introduction

Les ouvertures apportées par MS Office 2003 dans la coopération avec 4D ont déjà fait l’objet de différentes notes techniques :

- Intégration de 4D et Office 2003, par Julien FEASSON, 4D Inc., Note technique 4D-200402-05-FR ;

- Interaction entre 4D 2003 et Microsoft Excel (Services Web), par Matthieu Chevrier, 4D Inc., Note technique 4D-200302-06-FR.

Nous allons montrer dans ce document comment 4D 2004 peut exporter des données en XML qui pourront directement être utilisées par MS Word 2003 afin de construire des documents dynamiques. Nous verrons également des exemples de code VBA, de scripts VBScript et une application des possibilités de la nouvelle commande TRAITER BALISES HTML.

Note : Cette technique ne fonctionne pas sur Macintosh et n’est disponible que dans la version stand-alone de MS Word 2003 Windows ou dans la version « MS Office Professionnal ».

II - Le cahier des charges

Soit un cabinet médical devant rédiger des lettres à destination du cabinet d’un confrère. Le modèle des lettres est défini et des champs variables relatifs aux informations du patient et du confrère destinataire de la lettre sont recensés.

Voici un exemple de lettre :

Figure 1 - exemple de compte-rendu

Voici la structure extrêmement simplifiée de la base de données correspondante :

Figure 2 - structure de la base de données des actes

Organisation de l’exemple

Nous ne traitons pas ici de la création des enregistrements côté 4D, supposons simplement qu’à un acte chirurgical corresponde un enregistrement dans la table [ACTE] qui nous permet de retrouver les données associées. Nous nous focalisons ici exclusivement sur la liaison avec Word.

Quand un utilisateur visualisant un enregistrement ACTE clique sur le bouton 'Compte_Rendu', le logiciel doit présenter une liste de modèles de documents Word. L’utilisateur choisit un modèle, clique sur un bouton 'Editer' et se retrouve dans Word qui affiche un document généré à partir du modèle sélectionné et présentant les données relatives à l’acte en cours de consultation.

Liaison avec les données

Il existe plusieurs moyens dans Word pour référencer les items d’un document en relation avec des données variables. Dans cet exemple, notre choix se porte sur l’insertion de champ de type INCLUDETEXT décrivant un chemin d’accès vers le contenu d’un élément XML dans un fichier externe.

Cette possibilité de liaison avec une donnée XML est apparue avec la version 2003 d’Office, même si les champs de type INCLUDETEXT existaient déjà dans les versions précédentes. Cependant, cette fonctionnalité :

- n’existe pas dans les versions Macintosh d’Office ;

- n’est pas activée dans les versions autres que stand-alone ou « Office Professional » de Word Windows 2003.

Nous verrons dans une prochaine note une autre manière de référencer des données externes qui sera utilisable sur Office 2004 Mac.

III - Export des données XML

Le modèle Word se réfère donc aux champs dynamiques présents dans un document externe.

Deux possibilités s’offrent à nous pour construire ce document :

- générer complètement le document XML à chaque demande ;

- utiliser un modèle (template) où nous modifions seulement les valeurs à chaque demande et pas la structure, c’est-à-dire les noms et organisations des balises XML.

Dans les deux cas, nous souhaitons obtenir un document semblable à celui-ci :

<root>
<Medecin>
<Prenom>Louis</Prenom>
<Nom>Knock</Nom>
</Medecin>
<Chirurgien>
<Prenom>Arthur</Prenom>
<Nom>Delascie</Nom>
</Chirurgien>
<Patient>
<Prenom>Honoré</Prenom>
<Nom>Duchemin</Nom>
<Titre>Monsieur</Titre>
<NoSecu>1234567890</NoSecu>
<Telephone>01 45 67 38 90</Telephone>
<Adresse>2 place des actes</Adresse>
<Adresse_suite/>
<CP>92000</CP>
<Ville>Nanterre</Ville>
</Patient>
</root>

Génération du document XML

Nous choisissons de générer un document « acte4D.xml » en utilisant les commandes DOM de 4D 2004. Celles-ci nous permettent de construire un arbre DOM en mémoire puis d’enregistrer (séraliser) cet arbre dans une variable ou un fichier disque.

Voici le code de génération du document :

C_BOOLEEN($0)

`--- Racine et espace de nom
C_TEXTE($_vt_root)
$_vt_root:="root"
C_TEXTE($_vt_ns)
$_vt_ns:="schema.xsd"

C_TEXTE($_vt_Ref_XML)
$_vt_Ref_XML:=DOM Creer ref XML($_vt_root;$_vt_ns)

DOM_SetEncoding ($_vt_Ref_XML;"UTF-8")  `encodage en utf-8

demo_get_Medecin_XML ($_vt_Ref_XML)
demo_get_Chirurgien_XML ($_vt_Ref_XML)
demo_get_Patient_XML ($_vt_Ref_XML)

`--- Le doc est-il bien formé ?
`pour le vérifier, on l’exporte vers une variable
`que l’on réanalyse sous forme d’arbre DOM
`(on pourrait aussi le valider/DTD)
C_BLOB($_vx_XML)
DOM EXPORTER VERS VARIABLE($_vt_Ref_XML;$_vx_XML)

C_TEXTE($_vt_Ref_XML_Analyse)
$_vt_Ref_XML_Analyse:=DOM Analyser variable XML($_vx_XML)

Si (ok=1) & (DOM_ifNoError ($_vt_Ref_XML_Analyse))
$0:=Vrai
`aucun contrôle n’est fait par 4D au moment de l’écriture
DOM EXPORTER VERS FICHIER($_vt_Ref_XML;<>DEMO_vt_docXML_path)
Sinon
$0:=Faux
`une erreur est survenue
`on recopie l’arbre DOM ds le presse-papiers pour débogage
EFFACER PRESSE PAPIERS
AJOUTER A PRESSE PAPIERS("TEXT";$_vx_XML)
ALERTE("L’arbre XML DOM a été copié dans les presse-papiers")
Fin de si

DOM FERMER XML($_vt_Ref_XML_Analyse)   `l’arbre analysé pour vérification

DOM FERMER XML($_vt_Ref_XML)   `l’arbre DOM généré pour export

Notez que nous utilisons DOM EXPORTER VERS VARIABLE, puis DOM Analyser variable XML pour réanalyser le document construit afin de vérifier qu’il est bien formé.

Les méthodes d’exports spécifiques à une table sont très simples, par exemple demo_get_Patient_XML :

C_TEXTE($1;$_vt_Ref_XML)

$_vt_Ref_XML:=$1

C_TEXTE($_vt_RefMedecin)
$_vt_RefMedecin:=DOM Creer element XML($_vt_Ref_XML;"Patient")

DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"Prenom";[PATIENT]Prenom)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"Nom";[PATIENT]Nom)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"Titre";[PATIENT]Titre)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"NoSecu";[PATIENT]NoSecu)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"Telephone";[PATIENT]Telephone)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"Adresse";[PATIENT]adresse)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"Adresse_suite";[PATIENT]adresse_suite)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"CP";[PATIENT]code_postal)
DOM_AJOUTER_ELEMENT_SIMPLE ($_vt_RefMedecin;"Ville";[PATIENT]ville)

Cette méthode utilise la routine DOM_AJOUTER_ELEMENT_SIMPLE afin de créer et d’écrire la valeur d’un élément XML en un seul appel :

C_TEXTE($1)

C_TEXTE($_vt_RefParen)
$_vt_RefParen:=$1

C_TEXTE($2)
C_TEXTE($_vt_NomElement)
$_vt_NomElement:=$2

C_TEXTE($3)
C_TEXTE($_vt_ValeurElement)
$_vt_ValeurElement:=$3

C_TEXTE($_vt_RefElement)

`Ajout d’un élément et de sa valeur
$_vt_RefElement:=DOM Creer element XML($_vt_RefParen;$_vt_NomElement)

DOM ECRIRE VALEUR ELEMENT XML($_vt_RefElement;$_vt_ValeurElement)

IV - Liaison avec les données XML

Ouvrons notre document Word et sélectionnons le menu 'Insertion/Champ…' puis le type de champ 'InclureTexte', le dialogue suivant s’affiche :

Dans la zone 'Propriétés du champ/Nom de fichier ou URL' nous saisissons l’emplacement du fichier XML exporté par 4D sur disque ou l’URL d’où recevoir le document. Dans notre exemple nous renseignons : « C:\\ acte4D.xml ».

La zone 'Options du champ' nous propose des paramétrages supplémentaires, mais la saisie y est malaisée en raison de la petite taille des champs de saisie, une maladresse d’interface récurrente chez Microsoft. C’est pourquoi nous basculons en mode de saisie directe par un clic sur le bouton en bas à gauche 'Codes de champ'. Nous obtenons le dialogue suivant :

Le bouton 'Masquer les codes' joue le rôle inverse pour revenir à la vue précédente.

Voici la chaîne de paramétrage complète à saisir sous 'Codes de champ :' pour lier le champ avec l’information nom du patient présente dans le document XML :

INCLUDETEXT « C:\\acte4D.xml » \* FirstCap \c xml \n xmlns:ns1='schema.xsd' \x /ns1:root/ns1:Patient/ns1:Nom

Le rôle de chaque paramètre de la chaîne (appelé commutateur dans Word) est décrit dans le dialogue qui s’obtient en cliquant sur le bouton 'Options…'. Le premier onglet fixe des options de mise en forme, comme la première lettre en majuscule, représentée par FirstCap dans notre chaîne. Le second onglet donne accès aux commutateurs spéciaux, ici ceux de la liaison avec une source XML :

Voici le rôle de chacun des commutateurs :

- \! Empêche la mise à jour des champs faisant partie du fichier inclus ;

- \c Nom du convertisseur de texte requis pour le format du fichier inclus, ici « \c xml » ;

- \n Mappages des espaces de noms pour les requêtes XPath, ici « \n ; xmlns:ns1=’schema.xsd' » ;

- \t Emplacement de la transformation XML à utiliser, ici nous n’en utilisons pas (il s’agit d’une transformation XSL-T) ;

- \x XPath pour la section souhaitée du fichier XML, ici « \x /ns1:root/ns1:Patient/ns1:Nom » Une expression XPath permet de désigner très précisément un élément au sein d’un document XML. Elle s’exprime dans sa forme la plus simple comme un chemin d’accès hiérarchique donnant les étapes pour atteindre l’élément souhaité : 'Nom' fils de 'Patient' fils de 'root' dans l’espace de nom 'ns1', soit ici 'schema.xsd'.

Une fois le champ paramétré, il s’affiche sur fond gris dans le document Word :

Un clic droit sur l’un des champs permet d’accéder aux différentes actions :

- mettre à jour les champs : rafraîchit la valeur du champ depuis le fichier lié ;

- modification du champ… : rouvre le dialogue de paramétrage, la chaîne est ré analysé :

- basculer les codes de champ : bascule entre l’affichage de la valeur du champ et l’affichage de la chaîne d’options. Ce basculement s’effectue champ par champ, comme le montre la copie d’écran un peu plus haut où seul le champ Nom est présenté de cette manière. La saisie directe des commutateurs est alors possible, ce qui se révèle la méthode la plus efficace à l’usage.

Pour plus d’informations, consulter l’aide en ligne 'à propos des champs'.

Test de notre solution

Une fois notre document Word complètement paramétré, nous pouvons tester la solution en déclenchant l’export des données et l’ouverture du document depuis 4D. L’ouverture du document Word est réalisée par la routine ZDOC_Ouvre_Word :

` ----------------------------------------------------

` Nom utilisateur (OS) : christophe Keromen
` Date et heure : 08/05/04, 07:48:12
` ----------------------------------------------------
` Methode : ZDOC_Ouvre_Word
` Description
` Ouverture d’un document $1 ds IE
`
` Parametres
`$1:TEXTE:Chemin d’accès vers le DOC
` ----------------------------------------------------

C_TEXTE($1)
C_TEXTE($_vt_PathDoc)
$_vt_PathDoc:=$1

Si (ENV_si_Windows )
CHANGER TYPE DOCUMENT($_vt_PathDoc;"DOC")
Sinon
CHANGER TYPE DOCUMENT($_vt_PathDoc;"DOC")
CHANGER CREATEUR DOCUMENT($_vt_PathDoc;"MSWD")
Fin de si

`Déclenche l’ouverture du doc suivant son type et créateur
C_ENTIER($_ve_Error)
$_ve_Error:=AP ShellExecute ($_vt_PathDoc)

Si ($_ve_Error#0)
ALERTE(Chaine($_ve_Error))
TRACE
Fin de si

Nous utilisons la commande AP ShellExecute du plugin 4D Pack, car elle nous permet de nous affranchir de la connaissance de l’emplacement de l’application Word sur le disque hôte. L’ouverture de l’application est réalisée par le système sur la base de l’application associée à l’extension du fichier à ouvrir, ici « .doc ».

Tout se passe bien… sauf en ce qui concerne les informations affichées : ce ne sont pas celles de notre enregistrement courant ! Pourtant le document XML exporté est correct, que se passe-t-il ?

Effectuons un clic droit sur l’un de nos champs et sélectionnons ' Mettre à jour les champs' : la valeur correcte s’affiche. Conclusion, Word ne met pas à jour les champs à l’ouverture d’un document. Problème !

Provoquer la mise à jour des champs

Il faut donc que nous puissions provoquer la mise à jour des champs. Comment faire ? Il suffit d’écrire le code VBA (VisualBasic For Application) suivant :

Sub CK_MajChamps()
'
' CK_MajChamps
' Mise à jour des champs
'

' désactiver la mise à jour de l’écran
ScreenUpdating = False

' màj les champs
ActiveDocument.Fields.Update

' réactiver la mise à jour de l’écran
ScreenUpdating = True
Application.ScreenRefresh

End Sub

Mais comment provoquer l’exécution de ce code ?

Une première solution consiste à déclencher l’exécution de cette fonction à l’ouverture du document, de manière systématique. Pour cela, il suffit d’utiliser une macro AutoOpen :

Sub AutoOpen()
' à l’ouverture du document : màj les champs
' CK_MajChamps
End Sub

Les macros nommées AutoOpen s’exécutent systématiquement à l’ouverture d’un document.

Nous pouvons également recourir à une méthode événementielle, nommée « Document_ » suivi du nom de l’événement déclenchant la méthode :

Private Sub Document_Open()
'à l’ouverture du document : màj les champs
CK_MajChamps
End Sub

Cela fonctionne, mais la mise à jour sera effectuée à chaque ouverture du document par rapport aux valeurs du dernier fichier exporté depuis 4D! Autrement dit, les valeurs initiales du document seront écrasées à chaque mise à jour.

Deux approches permettent de résoudre ce problème :

1. Figer les références une fois la mise à jour effectuée ;

2. Déclencher la mise à jour par programmation de manière non systématique.

Figer les références

Encore une fois, nous exploitons la richesse de VBA, la petite fonction ci-dessous s’acquitte de cette tâche :

Sub CK_FigerValeur()
' pour chaque champ du document
For Each aField In ActiveDocument.Fields
' s’il s’agit d’un champ de type IncludeText
If (aField.Type = wdFieldIncludeText) Then
aField.Update ' mettre à jour le champ
aField.Unlink ' supprimer le lien en conservant la valeur
End If
Next aField
End Sub

Déclencher la mise à jour

Cette solution s’avère plus délicate, car elle demande à pouvoir programmer Word depuis l’extérieur, objectif pour lequel VBA trouve ses limites. La seule manière de programmer Word depuis 4D consiste à recourir à un plugin apportant à 4D le support d’OLE Automation. Cette technologie consiste justement à piloter une application compatible depuis une autre application. Mais nous souhaitons éviter le recours à un plugin.

Heureusement, Windows Host Scripting (WHS) va venir à notre secours… Cette technologie présente au cœur du système Windows ne permet pas seulement aux virus d’effacer le contenu des disques durs, elle autorise également l’instanciation et le pilotage des ActiveX par programmation, dont ceux des applications Office.

V - La couche finale : scripting de Word depuis 4D

En réalité ce n’est pas réellement 4D qui va piloter Word. 4D va traiter un script écrit en VBScript (la déclinaison de Visual Basic pour le scripting) et demander à WHS d’exécuter ce script.

Voici le script que nous utiliserons, notez que les parties variables du script sont implémentées sous la forme d’appels à des variables 4D par des balises propriétaires, celles utilisées par le serveur Web, par exemple <!--#4DVAR <>DEMO_vt_docWord_path--> :

'Creation d’un Objet de type Word
Dim WordObj
Set WordObj=CreateObject("Word.application")
WordObj.Visible = false ' Exécution en arrière-plan
' ouverture du document Word contenant les champs à mettre à jour
Dim objWordDoc
Set objWordDoc=WordObj.Documents.open("<!--#4DVAR <>DEMO_vt_docWord_path--> ")
' Mise à jour des champs
'
' désactiver la mise à jour de l’écran
ScreenUpdating = False

' màj les champs
objWordDoc.Fields.Update

' réactiver la mise à jour de l’écran
ScreenUpdating = True
WordObj.ScreenRefresh
' enregistrer le résultat
objWordDoc.SaveAs "<!--#4DVAR DEMO_vt_docWordResult_path--> "
' quitter Word
WordObj.Quit
' Nettoyer la mémoire
Set objWordDoc = nothing
Set WordObj = nothing

Il nous suffit maintenant de rajouter dans notre méthode 4D le traitement de ce script et son exécution depuis 4D :

Méthode demo_VBS_prepare

`chargement du script en BLOB
C_BLOB($_vx_script)
DOCUMENT VERS BLOB(<>DEMO_vt_script_path;$_vx_script)

`traitement des balises 4D contenues ds le script
TRAITER BALISES HTML($_vx_script;$_vx_script)
`ECRIRE TEXTE DANS PRESSE PAPIERS(BLOB vers texte($_vx_script;Texte sans longueur ))

`enregistrement du nouveau script
C_TEXTE($_vt_script_path)
$_vt_script_path:=Dossier temporaire+"updateFields.vbs"
BLOB VERS DOCUMENT($_vt_script_path;$_vx_script)

`exécution du script
C_ENTIER($_ve_Error)
$_ve_Error:=AP ShellExecute ($_vt_script_path)
Si ($_ve_Error#0)
ALERTE(Chaine($_ve_Error))
TRACE
Fin de si

`on attend que le document soit généré
Repeter
ENDORMIR PROCESS(Numero du process courant;10)
Jusque (Tester chemin acces(DEMO_vt_docWordResult_path)=Est un document )

Notez l’utilisation de la commande 4D 2004 TRAITER BALISES HTML qui permet de remplacer les balises 4D présentes dans le document par les valeurs des variables 4D.

Les possibilités de cette commande sont décrites dans la note « Quelques utilisations de la commande TRAITER BALISES HTML… » par Julien FEASSON, 4D Inc., note technique 4D-200504-12-FR.

Attention : la commande TRAITER BALISES HTML encode les caractères étendus en appels de caractères : « è » devient « &#232 ». La technique utilisée ici trouve ainsi ses limites. Si vous devez utiliser, par exemple, des chemins d’accès comportant des accents, il faudra passer par un traitement direct des chaînes dans un BLOB.

Nous exploitons au travers d’un appel à AP ShellExecute le fait que le système appelle automatiquement WHS lors d’une demande d’ouverture de tout document d’extension « .vbs ».

La méthode complète d’export

` ----------------------------------------------------

` Nom utilisateur (OS) : christophe Keromen
` Date et heure : 24/05/04, 16:27:54
` ----------------------------------------------------
` Methode : demo_Acte_Exporter_XML
` Description
` ex. de génération d’un document XML servant à une lettre Word 2003
`
` Parametres
` ----------------------------------------------------

XML_AppelerSurErreur_Start    `sert à détecter une erreur capturée par XML_AppelerSurErreur

`Chemin d’accès en dur au template XML
Si (Tester chemin acces(<>DEMO_vt_docXML_path)=Est un document )
SUPPRIMER DOCUMENT(<>DEMO_vt_docXML_path)
Fin de si

C_HEURE($_vh_refDoc)
$_vh_refDoc:=XML_Creer_Document (<>DEMO_vt_docXML_path)   `crée le doc XML sur disque
FERMER DOCUMENT($_vh_refDoc)

Si (ok=1)

Si (demo_XML_genere )   `génération du doc XML OK

`suppression du document Word pouvant déjà exister
C_TEXTE(DEMO_vt_docWordResult_path)
DEMO_vt_docWordResult_path:=<>DEMO_vt_docWordResult_path+[PATIENT]Nom+".doc"
Si (Tester chemin acces(DEMO_vt_docWordResult_path)=Est un document )
SUPPRIMER DOCUMENT(DEMO_vt_docWordResult_path)
Fin de si

APPELER SUR ERREUR("")

`--- exécution d’un VBScript pour rafraichir les champs de données avant présentation à l’utilisateur
demo_VBS_prepare

`--- ouverture du document dans Word
ZDOC_Ouvre_Word (DEMO_vt_docWordResult_path)

Fin de si

Fin de si

VI - Conclusion

Le support croissant de XML par les applications ouvre de nouvelles perspectives d’échanges. Couplé avec un peu de scripting rendu dynamique grâce la commande TRAITER BALISES HTML, cela nous permet de réaliser dans un document Word une fusion de données avec des informations issues de 4D. Le tout de manière pleinement automatisée et sans devoir recourir à un plug-in spécialisé.

VII - Base exemple

Téléchargez la base exemple : base exemple