none
Constitution de document WordprocessingDocument à partir d'un modèle avec fusion de données RRS feed

  • Question

  • Bonjour,

    Je tente de générer un document .docx à partir d'un modèle .docx contenant des "tags" et d'un fichier csv de données.

    Je suis presque arrivé à mes fins. Il reste un point qui ne fonctionne pas et je ne comprends pas pourquoi (voir le source ci-après) :

    - La constitution du document final de fait bien pas ajout x fois du modèle.

    - Par contre, la technique de remplacement de texte ne donne pas satisfaction : Le teste est bien remplacé (j'ai utilisé une technique trouvée sur le web), mais l'objet document et notamment MainDocumentPart.Document.Body.OuterXml reste inchangé.

    - A noter que si je fais le remplacement en travaillant directement sur le document final, ça marche. Cela ne marche pas pour les pages suivantes pour lesquelles je recherche le modèle dans un document de travail sur lequel je tente d'appliquer les remplacements avant de rajouter son contenu au document final.

    Voici le source :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Xml;
    using System.Xml.Linq;
    
    using System.IO;
    
    using LumenWorks.Framework.IO.Csv;
    using DocumentFormat.OpenXml;
    using DocumentFormat.OpenXml.Packaging;
    using DocumentFormat.OpenXml.Wordprocessing;
    using System.Text.RegularExpressions;
    
    
    namespace GesDocxWordProcessing
    {
        class Program
        {
            /// <summary>
            /// Point d'entrée du programme
            /// </summary>
            /// <param name="modele">Chemin et nom du fichier modèle (avec extension .docx)</param>
            /// <param name="destination">Chemin et nom du fichier destinataire (avec extension .docx)</param>
            /// <param name="data">Chemin et nom du fichier de données à fusionner</param>
            static void Main(string[] args)
            {
                DateTime start = DateTime.Now;
    
                // Rappel des paramètres si plus ou moins de 3 paramètres
                if (args.Length < 3 || args.Length > 4)
                {
                    Console.WriteLine(@"GenDocx doit etre appelee avec les parametres suivants :");
                    Console.WriteLine(@"p1 : Chemin et nom complet du modèle de document");
                    Console.WriteLine(@"p2 : Chemin et nom complet du document à générer");
                    Console.WriteLine(@"p3 : Chemin et nom complet du fichier csv a fusionner");
                    Console.WriteLine(@"p4 (optionnel) : caractère séparateur de champ du fichier csv ("";"" par défaut)");
                    Console.WriteLine();
                    Console.WriteLine(@"Exemples :");
                    Console.WriteLine(@"  GenDocx.exe ""C:\Modeles\modele.docx"" ""C:\Documents\docgenere.docx"" ""C:\Data\donnees.csv"" ");
                    Console.WriteLine(@"  GenDocx.exe ""C:\Modeles\modele.docx"" ""C:\Documents\docgenere.docx"" ""C:\Data\donnees2.csv"" "","" ");
                    return;
                }
                // Chargement et contrôle des paramètres de la ligne de commande
    
                string pModeleFileName = args[0];
                if (!pModeleFileName.EndsWith(".docx", StringComparison.InvariantCultureIgnoreCase))
                {
                    Console.WriteLine("Le fichier modèle doit avoir l'extension .docx");
                    return;
                }
    
                string pDestFileName = args[1];
                if (!pDestFileName.EndsWith(".docx", StringComparison.InvariantCultureIgnoreCase))
                {
                    Console.WriteLine("Le fichier de destination doit avoir l'extension .docx");
                    return;
                }
    
                string pDataFileName = args[2];
    
                string pSeparateur = (args.Length == 4) ? args[3] : ";";
                if (pSeparateur.Length != 1)
                {
                    Console.WriteLine("Le séparateur de champ ne doit comporter qu'un caractère");
                    return;
                }
                char cSep = pSeparateur.ToCharArray()[0];
    
                try
                {
    
                    Directory.CreateDirectory(Path.GetDirectoryName(pDestFileName));
    
                    // Init FileStream du modèle de document
                    using (FileStream modeleStream = new FileStream(pModeleFileName, FileMode.Open, FileAccess.Read))
                    {
    
                        // Init stream du document de destination 
                        using (MemoryStream destStream = new MemoryStream())
                        {
                            // chargement du modéle dans le stream
                            modeleStream.CopyTo(destStream);
    
                            // Création du document de destination
                            WordprocessingDocument destination = WordprocessingDocument.Open(destStream, true);
                            try
                            {
                                // Extraction contenu du document de destination
                                XElement destBody = XElement.Parse(destination.MainDocumentPart.Document.Body.OuterXml);
    
                                // Chargement du fichier de données
                                using (CsvReader csv = new CsvReader(new StreamReader(pDataFileName), true, cSep))
                                {
    
                                    // chargement liste des noms de champs
                                    string[] headers = csv.GetFieldHeaders();
                                    bool isfirst = true;
    
                                    // boucle sur les lignes de données
                                    while (csv.ReadNextRecord())
                                    {
                                        // Constitution de la liste des remplacements à opérer
                                        Dictionary<string, string> replacements = new Dictionary<string, string>();
                                        for (int i = 0; i < headers.Count(); i++)
                                        {
                                            string newValue = csv[i].Trim();
    
                                            // Tags de type 1
                                            string oldValue = "@" + headers[i] + @"\|";
                                            replacements.Add(oldValue, newValue);
    
                                            // Tags de type 2
                                            oldValue = "@" + headers[i];
                                            replacements.Add(oldValue, newValue);
    
                                        }
    
                                        // Au 1er coup
                                        if (isfirst)
                                        {
                                            // Injection des données dans la copie d'initialisation
                                            ReplaceText(destination, replacements);
    
                                            // On baisse le top "isfirst"
                                            isfirst = false;
    
                                            continue;
                                        }
    
    
                                        // Init stream du document de travail 
                                        using (MemoryStream travStream = new MemoryStream())
                                        {
                                            // chargement du modéle dans le stream de travail
                                            modeleStream.Seek(0, SeekOrigin.Begin);
                                            modeleStream.CopyTo(travStream);
    
                                            // Init document de travail à partir du stream de travail et modifications
                                            using (WordprocessingDocument travail = WordprocessingDocument.Open(travStream, true))
                                            {
                                                // Injection des données dans la copie d'initialisation
                                                ReplaceText(travail, replacements);
                                                
                                                // Extraction contenu du document de travail
                                                XElement travBody = XElement.Parse(travail.MainDocumentPart.Document.Body.OuterXml);
    
    
                                                // Ajout contenu du document de travail au document de destination
                                                destBody.Add(travBody);
    
                                                // Mise à jour document de destination
                                                destination.MainDocumentPart.Document.Body = new Body(destBody.ToString());
                                                destination.MainDocumentPart.Document.Save();
                                                destination.Package.Flush();
    
                                            }
                                        }
                                    }
                                }
    
                                // Sauvegarde document de destination
                                using (FileStream fs = new FileStream(pDestFileName, FileMode.Create))
                                    destStream.WriteTo(fs);
                            }
                            finally
                            {
                                destination.Dispose();
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("Fin anormale du programme :");
                    Console.WriteLine(e.Message);
                }
    
                TimeSpan duree = DateTime.Now - start;
    
                Console.WriteLine("Traitement WordProcessing en " + String.Format("{0}h {1}m {2}s {3}ms", duree.Hours, duree.Minutes, duree.Seconds, duree.Milliseconds));
                Console.Read();
            }
    
            /// <summary>
            /// Remplacement de texte
            /// </summary>
            /// <param name="pPart">Référence du MainDocumentPart du document concerné</param>
            /// <param name="pReplacements">Liste des remplacements à effectuer sous forme d'un Dictonary avec
            ///   Key = tag à rechercher
            ///   Value = valeur de remplacement
            /// </param>
            static void ReplaceText(WordprocessingDocument pDocument, Dictionary<string, string> pReplacements)
            {
    
                // Chargement du texte du document
                string docText = null;
                using (StreamReader sr = new StreamReader(pDocument.MainDocumentPart.GetStream()))
                    docText = sr.ReadToEnd();
    
                // Boucle de remplacement
                foreach (KeyValuePair<string, string> pair in pReplacements)
                {
                    Regex regexText = new Regex(pair.Key);
                    docText = regexText.Replace(docText, pair.Value);
                }
    
                // Réécriture du texte dans le document
                using (StreamWriter sw = new StreamWriter(pDocument.MainDocumentPart.GetStream(FileMode.Create)))
                    sw.Write(docText);
    
            }
        }
    }

    Si quelqu'un pouvait jeter un oeil à ce code et me dire ce qui cloche !

    Merci



    mercredi 18 septembre 2013 15:34

Réponses

  • Le problème ne vient pas des regExp, mais je ne les utilise pus, elles ne m'étaient pas très utiles dans mon cas.

    Je n'ai pas reussi à faire fonctionner mon prog avec la technique décrite.

    a la place, j'ai travaillé directement avec un StringBuilder pour constituer le contenu du document de destination à partir du contenu du modèle après remplacement des tags par les valeurs des données injectées. 

    au final, j'écris le fichier de destination en une seule fois avec

    destination.MainDocumentPart.Document.Body = 
                                new Body(ch);

    Cette methode marche bien et s'avère beaucoup plus efficace que la méthode précédente (traitement de 100 itérations du modèle en 9 secondes contre plus de 4 minutes auparavant.

    • Marqué comme réponse Aurel Bera mardi 24 septembre 2013 06:47
    lundi 23 septembre 2013 16:15

Toutes les réponses