Paru le 12 mars 2006
Par Pierre Lagarde, Microsoft France
Sur cette page
Introduction
Création du Template dans Word
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
Insertion d’une image
Ajout de l’image dans le package
Création de la relation
Chargement du XML d’insertion d’image depuis les ressources
Remplacement du troisième paragraphe par l’image
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 :
Le fichier DocX est un ZIP contenant :
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.
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.
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.
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.
On retrouve bien sûr les 2 fichiers dans le répertoire « Resources » du Projet.
Le XML d’insertion d’image avec reflet en WordProcessingML ressemble à ça :
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