Génération de document Word Open XML à partir d’un « Model »
Paru le 12 mars 2006
Par Pierre Lagarde, Microsoft France

Sur cette page

Introduction Introduction
Création du Template dans Word Création du Template dans Word
Ajout du document Word dans le projet Visual Studio en tant que ressource Ajout du document Word dans le projet Visual Studio en tant que ressource
Extraction du model et manipulation pour générer le document cible Word Extraction du model et manipulation pour générer le document cible Word
Insertion d’une image Insertion d’une image
Ajout de l’image dans le package Ajout de l’image dans le package
Création de la relation Création de la relation
Chargement du XML d’insertion d’image depuis les ressources Chargement du XML d’insertion d’image depuis les ressources
Remplacement du troisième paragraphe par l’image Remplacement du troisième paragraphe par l’image
Conclusion Conclusion

Introduction

Cet article fait suite à l’article sur la création de fichier OpenXML minimum. Ce dernier présentait comment, dans une application console, on pouvait créer un document Word à partir de rien. Pour rappel, les documents OpenXML, que ce soit Word, Excel ou Powerpoint, sont des packages ZIP qui contiennent des fichiers XML de description du document, et des médias que constitue le document.

Exemple d’un document Word qui contient une image :

Arborescence d'un fichier Excel

Le fichier DocX est un ZIP contenant :

Arborescence d'un fichier Excel

On remarque ici que tout n’est que XML pour la description du document ou binaire pour les médias, mais dans leur état natif (l’image est au format original dans le document).

Cet article va détailler comment on peut créer un document Word, non pas à partir de rien mais d’un model préalablement créé dans l’application .NET. Ce model va être embarqué dans l’application et sera manipulé en tant que ressource de l’application, pour ne pas avoir à tout générer à chaque fois et donc simplifier la génération de document OpenXML.

Pour simplifier la manipulation de document OpenXML, on utilisera l’API du Framework 3.0 « System.IO.Package ».

Pour pouvoir importer ce package dans une solution Visual Studio, il faut installer le Framework 3.0, son SDK et l’Add-on à Visual Studio.

Création du Template dans Word

La première étape consiste à créer un document Word qui nous servira de model dans notre application et qu’on ajoutera comme ressource dans notre projet Visual Studio.

Ouvrir Word et créer un titre, deux textes. Le premier contiendra le texte brut et le second représentera l’emplacement de l’image que l’on va insérer.

Arborescence d'un fichier Excel

PS : Le texte saisi en « [] » est juste là pour situer les éléments sur la page.

Ajout du document Word dans le projet Visual Studio en tant que ressource

Pour ajouter une ressource à un projet, il faut aller dans les propriétés du projet et ajouter la ressource.

Arborescence d'un fichier Excel

Ici, on ajoutera une ressource existante qui sera le document Word qu’on vient de créer.

Un répertoire « Resources » est créé dans la solution Visual Studio contenant le fichier Word.

Arborescence d'un fichier Excel

Extraction du model et manipulation pour générer le document cible Word

Ce qui reste à faire :

  • Extraire le document des ressources.

  • Ouvrir le package pour en extraire le WordProcessingML (/word/document.xml).

  • Manipuler le document XML pour remplacer les zones identifiées en vue du document cible.

  • Générer le document final sous forme de fichier physique.

Extraire le document des ressources

On va charger le document depuis les ressources dans un MemoryStream et créer une classe « System.IO.Package » à partir de ce MemoryStream.

MemoryStream m_packageData;
// Chargement dans le MemoryStream
m_packageData = new MemoryStream();
m_packageData.Write(Properties.Resources.WordSample, 0, data.Length);

// Ouvre le package
Package m_package =
	Package.Open(m_packageData,
		FileMode.Open,
		FileAccess.ReadWrite); // accé en lecture et ecriture
		

Ouvrir le Package pour en extraire le fichier WordProcessingML

Récupération de la « Part » en lecture / écriture.

Uri documentUri = new Uri(@"/word/document.xml", UriKind.Relative);
XmlDocument documentXml = m_package.GetWritablePart(documentUri);
		

Manipulation du XML

Recherche dans le XML du premier paragraphe qui représente le titre.

// Recherche de tous les paragraphes
XmlNodeList paragraph = 
documentXml.SelectNodes("w:document/w:body/w:p",
Namespaces.NamespaceManager);
// Navigue sur le texte du premier paragraphe
XPathNavigator documentNav =
paragraph[0].CreateNavigator().SelectSingleNode("w:r/w:t",
Namespaces.NamespaceManager);
// Remplace le texte
using (XmlWriter writer = documentNav.ReplaceRange(documentNav))
{
writer.WriteElementString(Prefixes.WordprocessingML,"t",
Namespaces.WordprocessingML, "Titre 1");
}

Générer le document physique

Pour générer le fichier physique, il faut mettre à jour le WordProcessingML du MemorySteam et écrire le MemoryStream sur le système de fichier.

// Positionnement sur la Part "/word/document.xml"
PackagePart writablePart = m_package.GetPart(partUri);

// Mise à jour de la Part par le XML modifié [XmlDocument partXML]
using (Stream partStream = writablePart.GetStream(FileMode.Open, FileAccess.Write))
{
partStream.SetLength(0);
partXml.Save(partStream);
}

// Fush du Package
m_package.Flush();
m_package.Close();

// Ecriture sur disque [filename]
using (FileStream outputStream = File.Create(filename))
    m_packageData.WriteTo(outputStream);

// Fermeture du Steam
m_packageData.Close();
		

Insertion d’une image

Pour insérer une image dans un document Word, le WordProcessingML à générer étant plus volumineux, surtout si on veut y ajouter des effets visuels comme un reflet et des bords arrondis, l’astuce est de récupérer ce XML d’un document existant et de venir l’injecter dans le document cible.

Pour ce faire, nous allons stocker ce XML en ressource de l’application, tout comme l’image binaire que nous allons insérer.

Arborescence d'un fichier Excel

On retrouve bien sûr les 2 fichiers dans le répertoire « Resources » du Projet.

Arborescence d'un fichier Excel

Le XML d’insertion d’image avec reflet en WordProcessingML ressemble à ça :

Arborescence d'un fichier Excel

Il suffira de remplacer le « {0} » par l’ID de la relation qui correspondra à l’image insérée dans le package.

Ajout de l’image dans le package

Ajout dans le package de l’image qui se trouve en ressource.

//Insert Image dans le package
Uri imageUri = new Uri("/word/media/AutumnLeaves.jpeg", UriKind.Relative);
package.CreateNewPart(imageUri, "image/jpeg", Properties.Resources.AutumnLeaves);

Création de la relation

Création de la relation et récupération de l’ID de la relation ainsi créée.

// create the relationship between the document and the image
string HeaderImageRelId = package.CreateInternalRelationship(
	new Uri(@"/word/document.xml", UriKind.Relative),
    imageUri,
    "http://schemas.openxmlformats.org/officeDocument/" +
    "2006/relationships/image"
);
	

Chargement du XML d’insertion d’image depuis les ressources

XmlDocument drawingXml = new XmlDocument();
drawingXml.LoadXml(
string.Format(Properties.Resources.DrawingImage, HeaderImageRelId)
);

Remplacement du troisième paragraphe par l’image

documentNav = paragraph[2].CreateNavigator().
SelectSingleNode("w:r/w:t", Namespaces.NamespaceManager);
using (XmlWriter writer = documentNav.ReplaceRange(documentNav))
{
    // Chargement du XML depuis les ressources et insertion de l’ID de la relation
    XmlDocument drawingXml = new XmlDocument();
    drawingXml.LoadXml(
        string.Format(Properties.Resources.DrawingImage, HeaderImageRelId));

    // Insertion dans le paragraphe
    drawingXml.DocumentElement.WriteContentTo(writer);
}

Conclusion

Pour simplifier la manipulation des ressources et du package OpenXML dans la solution complète, il y a deux classes : une classe PackageHelper.cs qui simplifie la manipulation du Package, et une classe Namespaces.cs qui intègre les Namespaces WordProcessingML.

namespace ConsoleApplication1
{
    /// 
    /// Provides a standard way to access the namespace prefixes for the Open XML document namespaces.
    /// 
    public static class Prefixes
    {
        // Standard Open XML Namespace prefixes
        public const string WordprocessingML = "w";
    }

    /// 
    /// Provides a standard way to access the namespace URIs for the Open XML document namespaces.
    /// Also provides a preconstructed namespace manager for the namespaces and prefixes.
    /// 
    public static class Namespaces
    {   
    // Standard Open XML Namespace URIs
    public const string WordprocessingML = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";

    #region Namespace Manager Methods
    private static XmlNamespaceManager m_namespaceManager = new XmlNamespaceManager(new NameTable());

    /// 
    /// Initializes the namespace manager with all of the Open XML document namespaces.
    /// 
    static Namespaces()
    {
        // add each namespace to the namespace manager
        m_namespaceManager.AddNamespace(Prefixes.WordprocessingML, Namespaces.WordprocessingML);
    }

    /// 
    /// Returns the static namespace manager 
    /// 
    public static XmlNamespaceManager NamespaceManager
    {
        get { return m_namespaceManager; }
    }
    #endregion
  }
}

Ainsi, par la création d’un model, la manipulation de document OpenXML est plus simple. De plus, grâce à la création de « portions » de WordProcessingML, la composition de document Office devient simple et possible.

Téléchargez

OpenXML-Creation-document-Word-a-partir-Template-Sample.zip
832 Ko

Page view tracker