Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Julien Chable

He blogs, you blog, I blog ...

Archives

[Open XML] Utiliser le SDK Open XML pour fusionner des présentations Power Point 2007

Il y a des exercices plus rapides à réaliser avec les APIs de la couche de Packaging OPC (avec le fameux namespace System.IO.Packaging) et d'autres avec les parties fortement typées du SDK d'Open XML (namespace using Microsoft.Office.DocumentFormat.OpenXml.Packaging).

Voici un exemple (en réalité un proof-of-concept pour un ami qui ne croyait que cela était aussi facile et rapide) réalisé en une vingtaine minutes mettant en oeuvre le SDK Open XML et permettant de fusionner autant de présentation PowerPoint 2007 que vous le souhaitez (attention : cette version simplifiée vous oblige à ce que les présentations possédent la même définition de layout, cf en bas du post pour plus d'infos).

Pour rappel le SDK Open XML offre des parties fortement typées - contrairement au namespace System.IO.Packaging qui fournissait une classe 'abstraite' PackagePart - vous permettant d'aller un plus vite dans vos développements Open XML.

Voici le bout de code rapide :

 

using System;
using System.IO;
using System.IO.Packaging;
using Microsoft.Office.DocumentFormat.OpenXml.Packaging;
using System.Xml;

namespace DemoOpenXMLSDK
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] input = new string[] { @"samples/sample1.pptx", @"samples/sample2.pptx", @"samples/sample3.pptx" };
            PPTXMerger.Merge(input, "output.pptx");
        }
    }

    class PPTXMerger
    {
        const string typeRelationDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
        const string typeRelationDiapositive = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
        const string typeRelationDiapositiveLayout = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
        const string typeRelationImage = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";

        const string namespacePresentationML = "http://schemas.openxmlformats.org/presentationml/2006/main";
        const string namespaceDrawingML = "http://schemas.openxmlformats.org/drawingml/2006/main";
        const string namespaceRelationships = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";

        const string contentTypeSlide = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml";

        public static void Merge(string[] docSource, string docDest)
        {
            FileStream outputStream = new FileStream("output.pptx", FileMode.Create);
            CopyStream(new MemoryStream(Resource.Template1), outputStream);
            using (outputStream)
            {
                using (PresentationDocument presFinal =
                    PresentationDocument.Open(outputStream, true))
                {
                    //Propriétés

                    PackageProperties props = presFinal.PackageProperties;
                    props.Created = DateTime.Now;

                    // Présentation
                    PresentationPart presPart = presFinal.PresentationPart;

                    NameTable nt = new NameTable();
                    XmlNamespaceManager gestionnaireNS = new XmlNamespaceManager(nt);
                    gestionnaireNS.AddNamespace("p", namespacePresentationML);
                    gestionnaireNS.AddNamespace("a", namespaceDrawingML);
                    gestionnaireNS.AddNamespace("r", namespaceRelationships);

                    // Charge le XML de la présentation
                    XmlDocument docPresentation = new XmlDocument(nt);
                    docPresentation.Load(presPart.GetStream());

                    // Insertion de l'élément 'p:sldIdLst' après 'p:sldMasterIdLst' dans le template
                    XmlNode noeudPresentation = docPresentation.SelectSingleNode("//p:presentation", gestionnaireNS);
                    XmlNode noeudSldMasterIdLst = docPresentation.SelectSingleNode("//p:sldMasterIdLst", gestionnaireNS);
                    XmlNode noeudSldIdLst = docPresentation.CreateElement("p:sldIdLst", namespacePresentationML);
                    noeudPresentation.InsertAfter(noeudSldIdLst, noeudSldMasterIdLst);

                    int diapoId = 1; // Id de la slide que l'on rajoute dans la présentation finale.
                    // On traite chaque document
                    foreach (string doc in docSource)
                    {
                        using (PresentationDocument currentPres = PresentationDocument.Open(
                            doc, false))
                        {
                            PresentationPart currentPresPart = currentPres.PresentationPart;

                            // On traite chaque diapositive
                            foreach (SlidePart slide in currentPresPart.SlideParts)
                            {
                                // Crée la diapositive dans la présentation finale et copie les données de la slide de la source vers la destination
                                SlidePart sp = presPart.AddNewPart<SlidePart>();
                                sp.FeedData(slide.GetStream()); // On copie les données de la présentation courante dans la présentation finale

                                // Lien vers le layout (on cherche la correspondance puisque les présentations sont censées contenir les mêmes layout)
                                foreach (SlideLayoutPart lp in slide.GetPartsOfType<SlideLayoutPart>())
                                {
                                    SlideMasterPart masterPart;
                                    foreach (SlideMasterPart mp in presPart.SlideMasterParts)
                                    {
                                        masterPart = mp;
                                        foreach (SlideLayoutPart slp in mp.SlideLayoutParts)
                                        {
                                            if (slp.Uri == lp.Uri)
                                            {
                                                sp.AddPart<SlideLayoutPart>(slp);
                                                break;
                                            }
                                        }
                                        break; // Une seule correspondance
                                    }
                                }

                                // Ajout de la diapositive dans la liste des diapositives de la présentation
                                XmlNode noeudSldId = docPresentation.CreateElement("p:sldId", namespacePresentationML);
                                XmlAttribute idSldAtt = docPresentation.CreateAttribute("id");
                                idSldAtt.Value = diapoId + 255 + "";    // Les id de PowerPoint 2007 doivent commencer à 256
                                XmlAttribute idAtt = docPresentation.CreateAttribute("r:id", namespaceRelationships);
                                idAtt.Value = presPart.GetIdOfPart(sp);
                                noeudSldId.Attributes.Append(idSldAtt);
                                noeudSldId.Attributes.Append(idAtt);
                                noeudSldIdLst.AppendChild(noeudSldId);

                                ++diapoId;
                            }
                        }
                    }

                    docPresentation.Save(presPart.GetStream());
                }
            }
        }

        private static void CopyStream(Stream source, Stream target)
        {
            const int bufSize = 0x1000;
            byte[] buf = new byte[bufSize];
            int bytesRead = 0;
            while ((bytesRead = source.Read(buf, 0, bufSize)) > 0)
                target.Write(buf, 0, (int)bytesRead);
            source.Close();
        }
    }
}

Allez plus loin

Si vous souhaitez prendre en compte les différentes ressources (images, charts, notes de diapositives, etc) et layout/master différents, il vous faudra copier quelques parties supplémentaires en assurant les relations dans le nouveau package. Rien de bien compliqué en quelque sorte.

A quand une RTM du SDK Open XML ?

Le SDK Open XML permet de développer plus rapidement, néanmoins certaine fois vous aurez envie de toucher un peu plus aux relations de parties que vous le permettent les classes, ou de trouver des méthodes (ajouter une diapositive ?) dans les classes qui pourraient vous faire gagner encore plus de temps. Mais n'oublions pas c'est une CTP ... depuis début Juin !

Une mise à jour ou un indispensable modèle objet - pour manipuler le contenu des documents sous forme d'objet -  se font donc attendre. Néanmoins on comprendra bien Microsoft sur ce point : il serait stupide de proposer des APIs qui seraient obsolètes dans quelques mois suite à la résolution des commentaires du BRM et aux potentiels changements, même minimals, de certaines parties de la spécification.

Bizarrement c'est aussi la raison pour laquelle le projet OpenXML4J (API Java pour Open XML) attend également avant de passer vraiment à l'étape 2 du projet ...

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: mardi 11 décembre 2007 08:54 par neodante
Classé sous : ,

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01