话不多说,直接上代码。

https://docs.microsoft.com/zh-cn/previous-versions/office/cc850828(v=office.14)

openxml 官方使用

https://docs.microsoft.com/zh-cn/dotnet/api/documentformat.openxml.wordprocessing.tablecellproperties?view=openxml-2.8.1

首先创建帮助类分别,Pptx,PptxParagraph,PptxSlide,PptxTable。引用

 

 

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;

namespace Pt.Slm.WebApi.Common
{
    public sealed class Pptx : IDisposable
    {
        private readonly PresentationDocument presentationDocument;
        /// <summary>
        /// 正则表达式模式从模板中提取标记
        /// </summary>
        public static readonly Regex TagPattern = new Regex(@"{{[A-Za-z0-9_+\-\.]*}}");
        /// <summary>
        /// PowerPoint pptx文件的MIME类型
        /// </summary>
        public const string MimeType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
        /// <summary>
        /// 初始化类的新实例。
        /// </summary>
        /// <param name="file"></param>
        /// <param name="access"></param>
        public Pptx(string file, FileAccess access)
        {
            bool isEditable = false;
            switch (access)
            {
                case FileAccess.Read:
                    isEditable = false;
                    break;
                case FileAccess.Write:
                case FileAccess.ReadWrite:
                    isEditable = true;
                    break;
            }

            this.presentationDocument = PresentationDocument.Open(file, isEditable);
        }
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="stream">PowerPoint流.</param>
        /// <param name="access">用于打开PowerPoint文件的访问模式</param>
        /// <remarks>以读写(默认)或只读模式打开PowerPoint流.</remarks>
        public Pptx(Stream stream, FileAccess access)
        {
            bool isEditable = false;
            switch (access)
            {
                case FileAccess.Read:
                    isEditable = false;
                    break;
                case FileAccess.Write:
                case FileAccess.ReadWrite:
                    isEditable = true;
                    break;
            }

            this.presentationDocument = PresentationDocument.Open(stream, isEditable);
        }



        /// <summary>
        /// 关闭PowerPoint文件
        /// </summary>
        public void Dispose()
        {
            this.Close();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Close()
        {
            this.presentationDocument.Close();
        }
        /// <summary>
        /// 计算幻灯片数。
        /// </summary>
        /// <returns></returns>
        public int SlidesCount()
        {
            PresentationPart presentationPart = this.presentationDocument.PresentationPart;
            return presentationPart.SlideParts.Count();
        }


        /// <summary>
        /// Finds the slides matching a given note.
        /// </summary>
        /// <param name="note">Note to match the slide with.</param>
        /// <returns>The matching slides.</returns>
        public IEnumerable<PptxSlide> FindSlides(string note)
        {
            List<PptxSlide> slides = new List<PptxSlide>();

            for (int i = 0; i < this.SlidesCount(); i++)
            {
                PptxSlide slide = this.GetSlide(i);
                IEnumerable<string> notes = slide.GetNotes();
                foreach (string tmp in notes)
                {
                    if (tmp.Contains(note))
                    {
                        slides.Add(slide);
                        break;
                    }
                }
            }

            return slides;
        }

        /// <summary>
        /// Gets the thumbnail (PNG format) associated with the PowerPoint file.
        /// </summary>
        /// <param name="size">The size of the thumbnail to generate, default is 256x192 pixels in 4:3 (160x256 in 16:10 portrait).</param>
        /// <returns>The thumbnail as a byte array (PNG format).</returns>
        /// <remarks>
        /// Even if the PowerPoint file does not contain any slide, still a thumbnail is generated.
        /// If the given size is superior to the default size then the thumbnail is upscaled and looks blurry so don't do it.
        /// </remarks>
        public byte[] GetThumbnail(Size size = default(Size))
        {
            byte[] thumbnail;

            var thumbnailPart = this.presentationDocument.ThumbnailPart;
            using (var stream = thumbnailPart.GetStream(FileMode.Open, FileAccess.Read))
            {
                var image = Image.FromStream(stream);
                if (size != default(Size))
                {
                    image = image.GetThumbnailImage(size.Width, size.Height, null, IntPtr.Zero);
                }

                using (var memoryStream = new MemoryStream())
                {
                    image.Save(memoryStream, ImageFormat.Png);
                    thumbnail = memoryStream.ToArray();
                }
            }

            return thumbnail;
        }

        /// <summary>
        /// Gets all the slides inside PowerPoint file.
        /// </summary>
        /// <returns>All the slides.</returns>
        public IEnumerable<PptxSlide> GetSlides()
        {
            List<PptxSlide> slides = new List<PptxSlide>();
            int nbSlides = this.SlidesCount();
            for (int i = 0; i < nbSlides; i++)
            {
                slides.Add(this.GetSlide(i));
            }
            return slides;
        }

        /// <summary>
        /// 获取给定幻灯片索引的PptxSlide
        /// </summary>
        /// <param name="slideIndex">Index of the slide.</param>
        /// <returns>A PptxSlide.</returns>
        public PptxSlide GetSlide(int slideIndex)
        {
            PresentationPart presentationPart = this.presentationDocument.PresentationPart;

            // Get the collection of slide IDs
            OpenXmlElementList slideIds = presentationPart.Presentation.SlideIdList.ChildElements;

            // Get the relationship ID of the slide
            string relId = ((SlideId)slideIds[slideIndex]).RelationshipId;

            // Get the specified slide part from the relationship ID
            SlidePart slidePart = (SlidePart)presentationPart.GetPartById(relId);

            return new PptxSlide(presentationPart, slidePart);
        }

        /// <summary>
        /// Replaces the cells from a table (tbl).
        /// Algorithm for a slide template containing one table.
        /// </summary>
        public static IEnumerable<PptxSlide> ReplaceTable_One(PptxSlide slideTemplate, PptxTable tableTemplate, IList<PptxTable.Cell[]> rows)
        {
            return ReplaceTable_Multiple(slideTemplate, tableTemplate, rows, new List<PptxSlide>());
        }

        /// <summary>
        /// Replaces the cells from a table (tbl).
        /// Algorithm for a slide template containing multiple tables.
        /// </summary>
        /// <param name="slideTemplate">The slide template that contains the table(s).</param>
        /// <param name="tableTemplate">The table (tbl) to use, should be inside the slide template.</param>
        /// <param name="rows">The rows to replace the table's cells.</param>
        /// <param name="existingSlides">Existing slides created for the other tables inside the slide template.</param>
        /// <returns>The newly created slides if any.</returns>
        public static IEnumerable<PptxSlide> ReplaceTable_Multiple(PptxSlide slideTemplate, PptxTable tableTemplate, IList<PptxTable.Cell[]> rows, List<PptxSlide> existingSlides)
        {
            List<PptxSlide> slidesCreated = new List<PptxSlide>();

            string tag = tableTemplate.Title;

            PptxSlide lastSlide = slideTemplate;
            if (existingSlides.Count > 0)
            {
                lastSlide = existingSlides.Last();
            }

            PptxSlide lastSlideTemplate = lastSlide.Clone();

            foreach (PptxSlide slide in existingSlides)
            {
                PptxTable table = slide.FindTables(tag).First();
                List<PptxTable.Cell[]> remainingRows = table.SetRows(rows);
                rows = remainingRows;
            }

            // Force SetRows() at least once if there is no existingSlides
            // this means we are being called by ReplaceTable_One()
            bool loopOnce = existingSlides.Count == 0;

            while (loopOnce || rows.Count > 0)
            {
                PptxSlide newSlide = lastSlideTemplate.Clone();
                PptxTable table = newSlide.FindTables(tag).First();
                List<PptxTable.Cell[]> remainingRows = table.SetRows(rows);
                rows = remainingRows;

                PptxSlide.InsertAfter(newSlide, lastSlide);
                lastSlide = newSlide;
                slidesCreated.Add(newSlide);

                if (loopOnce) loopOnce = false;
            }

            lastSlideTemplate.Remove();

            return slidesCreated;
        }

    }
}
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace Pt.Slm.WebApi.Common
{
    using A = DocumentFormat.OpenXml.Drawing;
    /// <summary>
    /// Represents a paragraph inside a PowerPoint file.
    /// </summary>
    /// <remarks>
    /// Could not simply be named Paragraph, conflicts with DocumentFormat.OpenXml.Drawing.Paragraph.
    ///
    /// Structure of a paragraph:
    /// a:p (Paragraph)
    ///  a:r (Run)
    ///   a:t (Text)
    ///
    /// <![CDATA[
    /// <a:p>
    ///  <a:r>
    ///   <a:rPr lang="en-US" dirty="0" smtClean="0"/>
    ///   <a:t>
    ///    Hello this is a tag: {{hello}}
    ///   </a:t>
    ///  </a:r>
    ///  <a:endParaRPr lang="fr-FR" dirty="0"/>
    /// </a:p>
    ///
    /// <a:p>
    ///  <a:r>
    ///   <a:rPr lang="en-US" dirty="0" smtClean="0"/>
    ///   <a:t>
    ///    Another tag: {{bonjour
    ///   </a:t>
    ///  </a:r>
    ///  <a:r>
    ///   <a:rPr lang="en-US" dirty="0" smtClean="0"/>
    ///   <a:t>
    ///    }} le monde !
    ///   </a:t>
    ///  </a:r>
    ///  <a:endParaRPr lang="en-US" dirty="0"/>
    /// </a:p>
    /// ]]>
    /// </remarks>
    internal static class PptxParagraph
    {
        /// <summary>
        /// Replaces a tag inside a paragraph (a:p).
        /// </summary>
        /// <param name="p">The paragraph (a:p).</param>
        /// <param name="tag">The tag to replace by newText, if null or empty do nothing; tag is a regex string.</param>
        /// <param name="newText">The new text to replace the tag with, if null replaced by empty string.</param>
        /// <returns><c>true</c> if a tag has been found and replaced, <c>false</c> otherwise.</returns>
        internal static bool ReplaceTag(A.Paragraph p, string tag, string newText)
        {
            bool replaced = false;

            if (string.IsNullOrEmpty(tag))
            {
                return replaced;
            }

            if (newText == null)
            {
                newText = string.Empty;
            }
            newText = RemoveInvalidXMLChars(newText);

            while (true)
            {
                // Search for the tag
                Match match = Regex.Match(GetTexts(p), tag);
                if (!match.Success)
                {
                    break;
                }

                replaced = true;

                List<TextIndex> texts = GetTextIndexList(p);

                for (int i = 0; i < texts.Count; i++)
                {
                    TextIndex text = texts[i];
                    if (match.Index >= text.StartIndex && match.Index <= text.EndIndex)
                    {
                        // Got the right A.Text

                        int index = match.Index - text.StartIndex;
                        int done = 0;

                        for (; i < texts.Count; i++)
                        {
                            TextIndex currentText = texts[i];
                            List<char> currentTextChars = new List<char>(currentText.Text.Text.ToCharArray());

                            for (int k = index; k < currentTextChars.Count; k++, done++)
                            {
                                if (done < newText.Length)
                                {
                                    if (done >= tag.Length - 1)
                                    {
                                        // Case if newText is longer than the tag
                                        // Insert characters
                                        int remains = newText.Length - done;
                                        currentTextChars.RemoveAt(k);
                                        currentTextChars.InsertRange(k, newText.Substring(done, remains));
                                        done += remains;
                                        break;
                                    }
                                    else
                                    {
                                        currentTextChars[k] = newText[done];
                                    }
                                }
                                else
                                {
                                    if (done < tag.Length)
                                    {
                                        // Case if newText is shorter than the tag
                                        // Erase characters
                                        int remains = tag.Length - done;
                                        if (remains > currentTextChars.Count - k)
                                        {
                                            remains = currentTextChars.Count - k;
                                        }
                                        currentTextChars.RemoveRange(k, remains);
                                        done += remains;
                                        break;
                                    }
                                    else
                                    {
                                        // Regular case, nothing to do
                                        //currentTextChars[k] = currentTextChars[k];
                                    }
                                }
                            }

                            currentText.Text.Text = new string(currentTextChars.ToArray());
                            index = 0;
                        }
                    }
                }
            }

            return replaced;
        }

        /// <summary>
        /// Removes characters that are invalid for XML encoding.
        /// </summary>
        /// <param name="input">Text to be encoded.</param>
        /// <returns>Text with invalid XML characters removed.</returns>
        /// <remarks>
        /// <see href="http://stackoverflow.com/questions/20762/how-do-you-remove-invalid-hexadecimal-characters-from-an-xml-based-data-source-p">How do you remove invalid hexadecimal characters from an XML-based data source</see>
        /// </remarks>
        private static string RemoveInvalidXMLChars(string input)
        {
            return new string(input.Where(value =>
                                (value >= 0x0020 && value <= 0xD7FF) ||
                                (value >= 0xE000 && value <= 0xFFFD) ||
                                value == 0x0009 ||
                                value == 0x000A ||
                                value == 0x000D).ToArray());
        }

        /// <summary>
        /// Returns all the texts found inside a given paragraph.
        /// </summary>
        /// <remarks>
        /// If all A.Text in the given paragraph are empty, returns an empty string.
        /// </remarks>
        internal static string GetTexts(A.Paragraph p)
        {
            StringBuilder concat = new StringBuilder();
            foreach (A.Text t in p.Descendants<A.Text>())
            {
                concat.Append(t.Text);
            }
            return concat.ToString();
        }

        /// <summary>
        /// Associates a A.Text with start and end index matching a paragraph full string (= the concatenation of all A.Text of a paragraph).
        /// </summary>
        private class TextIndex
        {
            public A.Text Text { get; private set; }
            public int StartIndex { get; private set; }
            public int EndIndex { get { return StartIndex + Text.Text.Length; } }

            public TextIndex(A.Text t, int startIndex)
            {
                this.Text = t;
                this.StartIndex = startIndex;
            }
        }

        /// <summary>
        /// Gets all the TextIndex for a given paragraph.
        /// </summary>
        private static List<TextIndex> GetTextIndexList(A.Paragraph p)
        {
            List<TextIndex> texts = new List<TextIndex>();

            StringBuilder concat = new StringBuilder();
            foreach (A.Text t in p.Descendants<A.Text>())
            {
                int startIndex = concat.Length;
                texts.Add(new TextIndex(t, startIndex));
                concat.Append(t.Text);
            }

            return texts;
        }
    }
}
View Code
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;

namespace Pt.Slm.WebApi.Common
{

    using A = DocumentFormat.OpenXml.Drawing;
    using Picture = DocumentFormat.OpenXml.Presentation.Picture;
    public class PptxSlide
    {
        /// <summary>
        /// Holds the presentation part.
        /// </summary>
        private readonly PresentationPart presentationPart;

        /// <summary>
        /// Holds the slide part.
        /// </summary>
        private readonly SlidePart slidePart;

        /// <summary>
        /// Initializes a new instance of the <see cref="PptxSlide"/> class.
        /// </summary>
        /// <param name="presentationPart">The presentation part.</param>
        /// <param name="slidePart">The slide part.</param>
        internal PptxSlide(PresentationPart presentationPart, SlidePart slidePart)
        {
            this.presentationPart = presentationPart;
            this.slidePart = slidePart;
        }

        /// <summary>
        /// Gets all the texts found inside the slide.
        /// </summary>
        /// <returns>The list of texts detected into the slide.</returns>
        /// <remarks>
        /// Some strings inside the array can be empty, this happens when all A.Text from a paragraph are empty
        /// <see href="http://msdn.microsoft.com/en-us/library/office/cc850836">How to: Get All the Text in a Slide in a Presentation</see>
        /// </remarks>
        public IEnumerable<string> GetTexts()
        {
            return this.slidePart.Slide.Descendants<A.Paragraph>().Select(p => PptxParagraph.GetTexts(p));
        }

        /// <summary>
        /// Gets the slide title if any.
        /// </summary>
        /// <returns>The title or an empty string.</returns>
        public string GetTitle()
        {
            string title = string.Empty;

            // Find the title if any
            Shape titleShape = this.slidePart.Slide.Descendants<Shape>().FirstOrDefault(sp => IsShapeATitle(sp));
            if (titleShape != null)
            {
                title = string.Join(" ", titleShape.Descendants<A.Paragraph>().Select(p => PptxParagraph.GetTexts(p)));
            }

            return title;
        }

        /// <summary>
        /// Gets all the notes associated with the slide.
        /// </summary>
        /// <returns>All the notes.</returns>
        /// <remarks>
        /// <see href="http://msdn.microsoft.com/en-us/library/office/gg278319.aspx">Working with Notes Slides</see>
        /// </remarks>
        public IEnumerable<string> GetNotes()
        {
            var notes = new List<string>();
            if (this.slidePart.NotesSlidePart != null)
            {
                notes.AddRange(this.slidePart.NotesSlidePart.NotesSlide.Descendants<A.Paragraph>().Select(p => PptxParagraph.GetTexts(p)));
            }
            return notes;
        }

        /// <summary>
        /// 获取与幻灯片关联的所有表
        /// </summary>
        /// <returns>All the tables.</returns>
        /// <remarks>Assigns an "artificial" id (tblId) to the tables that match the tag.</remarks>
        public IEnumerable<PptxTable> GetTables()
        {
            var tables = new List<PptxTable>();

            int tblId = 0;
            foreach (GraphicFrame graphicFrame in this.slidePart.Slide.Descendants<GraphicFrame>())
            {
                var cNvPr = graphicFrame.NonVisualGraphicFrameProperties.NonVisualDrawingProperties;
                if (cNvPr.Description!=null)
                {
                    cNvPr.Title = cNvPr.Description;
                }
                if (cNvPr.Title != null)
                {
                    string title = cNvPr.Title.Value;
                    tables.Add(new PptxTable(this, tblId, title));
                    tblId++;
                }
            }

            return tables;
        }

        /// <summary>
        /// 在幻灯片中查找给定其标记的表
        /// </summary>
        /// <param name="tag">The tag associated with the table so it can be found.</param>
        /// <returns>The table or null.</returns>
        public IEnumerable<PptxTable> FindTables(string tag)
        {
            return this.GetTables().Where(table => table.Title.Contains(tag));
        }

        /// <summary>
        /// Type of replacement to perform inside ReplaceTag().
        /// </summary>
        public enum ReplacementType
        {
            /// <summary>
            /// Replaces the tags everywhere.
            /// </summary>
            Global,

            /// <summary>
            /// Does not replace tags that are inside a table.
            /// </summary>
            NoTable
        }

        /// <summary>
        /// 将幻灯片内的文本(标记)替换为另一个文本(标记)
        /// </summary>
        /// <param name="tag">The tag to replace by newText, if null or empty do nothing; tag is a regex string.</param>
        /// <param name="newText">The new text to replace the tag with, if null replaced by empty string.</param>
        /// <param name="replacementType">The type of replacement to perform.</param>
        public void ReplaceTag(string tag, string newText, ReplacementType replacementType)
        {
            foreach (A.Paragraph p in this.slidePart.Slide.Descendants<A.Paragraph>())
            {
                switch (replacementType)
                {
                    case ReplacementType.Global:
                        PptxParagraph.ReplaceTag(p, tag, newText);
                        break;

                    case ReplacementType.NoTable:
                        var tables = p.Ancestors<A.Table>();
                        if (!tables.Any())
                        {
                            // If the paragraph has no table ancestor
                            PptxParagraph.ReplaceTag(p, tag, newText);
                        }
                        break;
                }
            }

            this.Save();
        }

        /// <summary>
        /// 将幻灯片中的文本(标记)替换为给定PptxTable.Cell的另一个文本(标记)。
        /// This is a convenient method that overloads the original ReplaceTag() method.
        /// </summary>
        /// <param name="tagPair">The tag/new text, BackgroundPicture is ignored.</param>
        /// <param name="replacementType">The type of replacement to perform.</param>
        public void ReplaceTag(PptxTable.Cell tagPair, ReplacementType replacementType)
        {
            this.ReplaceTag(tagPair.Tag, tagPair.NewText, replacementType);
        }

        /// <summary>
        /// Replaces a picture by another inside the slide.
        /// </summary>
        /// <param name="tag">The tag associated with the original picture so it can be found, if null or empty do nothing.</param>
        /// <param name="newPicture">The new picture (as a byte array) to replace the original picture with, if null do nothing.</param>
        /// <param name="contentType">The picture content type: image/png, image/jpeg...</param>
        /// <remarks>
        /// <see href="http://stackoverflow.com/questions/7070074/how-can-i-retrieve-images-from-a-pptx-file-using-ms-open-xml-sdk">How can I retrieve images from a .pptx file using MS Open XML SDK?</see>
        /// <see href="http://stackoverflow.com/questions/7137144/how-can-i-retrieve-some-image-data-and-format-using-ms-open-xml-sdk">How can I retrieve some image data and format using MS Open XML SDK?</see>
        /// <see href="http://msdn.microsoft.com/en-us/library/office/bb497430.aspx">How to: Insert a Picture into a Word Processing Document</see>
        /// </remarks>
        public void ReplacePicture(string tag, byte[] newPicture, string contentType)
        {
            if (string.IsNullOrEmpty(tag))
            {
                return;
            }

            if (newPicture == null)
            {
                return;
            }

            ImagePart imagePart = this.AddPicture(newPicture, contentType);

            foreach (Picture pic in this.slidePart.Slide.Descendants<Picture>())
            {
                var cNvPr = pic.NonVisualPictureProperties.NonVisualDrawingProperties;
                if (cNvPr.Description != null)
                {
                    cNvPr.Title = cNvPr.Description.Value;
                }
                if (cNvPr.Title != null)
                {
                    string title = cNvPr.Title.Value;
                    if (title.Contains(tag))
                    {
                        // Gets the relationship ID of the part
                        string rId = this.slidePart.GetIdOfPart(imagePart);

                        pic.BlipFill.Blip.Embed.Value = rId;
                    }
                }
            }

            // Need to save the slide otherwise the relashionship is not saved.
            // Example: <a:blip r:embed="rId2">
            // r:embed is not updated with the right rId
            this.Save();
        }

        /// <summary>
        /// Replaces a picture by another inside the slide.
        /// </summary>
        /// <param name="tag">The tag associated with the original picture so it can be found, if null or empty do nothing.</param>
        /// <param name="newPictureFile">The new picture (as a file path) to replace the original picture with, if null do nothing.</param>
        /// <param name="contentType">The picture content type: image/png, image/jpeg...</param>
        public void ReplacePicture(string tag, string newPictureFile, string contentType)
        {
            byte[] bytes = File.ReadAllBytes(newPictureFile);
            this.ReplacePicture(tag, bytes, contentType);
        }

        /// <summary>
        /// Clones this slide.
        /// </summary>
        /// <returns>The clone.</returns>
        /// <remarks>
        /// <see href="http://blogs.msdn.com/b/brian_jones/archive/2009/08/13/adding-repeating-data-to-powerpoint.aspx">Adding Repeating Data to PowerPoint</see>
        /// <see href="http://startbigthinksmall.wordpress.com/2011/05/17/cloning-a-slide-using-open-xml-sdk-2-0/">Cloning a Slide using Open Xml SDK 2.0</see>
        /// <see href="http://www.exsilio.com/blog/post/2011/03/21/Cloning-Slides-including-Images-and-Charts-in-PowerPoint-presentations-Using-Open-XML-SDK-20-Productivity-Tool.aspx">See Cloning Slides including Images and Charts in PowerPoint presentations and Using Open XML SDK 2.0 Productivity Tool</see>
        /// </remarks>
        public PptxSlide Clone()
        {
            SlidePart slideTemplate = this.slidePart;

            // Clone slide contents
            SlidePart slidePartClone = this.presentationPart.AddNewPart<SlidePart>();
            using (var templateStream = slideTemplate.GetStream(FileMode.Open))
            {
                slidePartClone.FeedData(templateStream);
            }

            // Copy layout part
            slidePartClone.AddPart(slideTemplate.SlideLayoutPart);

            // Copy the image parts
            foreach (ImagePart image in slideTemplate.ImageParts)
            {
                ImagePart imageClone = slidePartClone.AddImagePart(image.ContentType, slideTemplate.GetIdOfPart(image));
                using (var imageStream = image.GetStream())
                {
                    imageClone.FeedData(imageStream);
                }
            }

            return new PptxSlide(this.presentationPart, slidePartClone);
        }

        /// <summary>
        /// Inserts this slide after a given target slide.
        /// </summary>
        /// <param name="newSlide">The new slide to insert.</param>
        /// <param name="prevSlide">The previous slide.</param>
        /// <remarks>
        /// This slide will be inserted after the slide specified as a parameter.
        /// <see href="http://startbigthinksmall.wordpress.com/2011/05/17/cloning-a-slide-using-open-xml-sdk-2-0/">Cloning a Slide using Open Xml SDK 2.0</see>
        /// </remarks>
        public static void InsertAfter(PptxSlide newSlide, PptxSlide prevSlide)
        {
            // Find the presentationPart
            var presentationPart = prevSlide.presentationPart;

            SlideIdList slideIdList = presentationPart.Presentation.SlideIdList;

            // Find the slide id where to insert our slide
            SlideId prevSlideId = null;
            foreach (SlideId slideId in slideIdList.ChildElements)
            {
                // See http://openxmldeveloper.org/discussions/development_tools/f/17/p/5302/158602.aspx
                if (slideId.RelationshipId == presentationPart.GetIdOfPart(prevSlide.slidePart))
                {
                    prevSlideId = slideId;
                    break;
                }
            }

            // Find the highest id
            uint maxSlideId = slideIdList.ChildElements.Cast<SlideId>().Max(x => x.Id.Value);

            // public override T InsertAfter<T>(T newChild, DocumentFormat.OpenXml.OpenXmlElement refChild)
            // Inserts the specified element immediately after the specified reference element.
            SlideId newSlideId = slideIdList.InsertAfter(new SlideId(), prevSlideId);
            newSlideId.Id = maxSlideId + 1;
            newSlideId.RelationshipId = presentationPart.GetIdOfPart(newSlide.slidePart);
        }

        /// <summary>
        /// Removes the slide from the PowerPoint file.
        /// </summary>
        /// <remarks>
        /// <see href="http://msdn.microsoft.com/en-us/library/office/cc850840.aspx">How to: Delete a Slide from a Presentation</see>
        /// </remarks>
        public void Remove()
        {
            SlideIdList slideIdList = this.presentationPart.Presentation.SlideIdList;

            foreach (SlideId slideId in slideIdList.ChildElements)
            {
                if (slideId.RelationshipId == this.presentationPart.GetIdOfPart(this.slidePart))
                {
                    slideIdList.RemoveChild(slideId);
                    break;
                }
            }

            this.presentationPart.DeletePart(this.slidePart);
        }

        /// <summary>
        /// Determines whether the given shape is a title.
        /// </summary>
        private static bool IsShapeATitle(Shape sp)
        {
            bool isTitle = false;

            var ph = sp.NonVisualShapeProperties.ApplicationNonVisualDrawingProperties.GetFirstChild<PlaceholderShape>();
            if (ph != null && ph.Type != null && ph.Type.HasValue)
            {
                switch ((PlaceholderValues)ph.Type)
                {
                    case PlaceholderValues.Title:
                    case PlaceholderValues.CenteredTitle:
                        isTitle = true;
                        break;
                }
            }

            return isTitle;
        }

        /// <summary>
        /// 将新图片添加到以后的幻灯片中
        /// </summary>
        /// <param name="picture">The picture as a byte array.</param>
        /// <param name="contentType">The picture content type: image/png, image/jpeg...</param>
        /// <returns>The image part</returns>
        internal ImagePart AddPicture(byte[] picture, string contentType)
        {
            ImagePartType type = 0;
            switch (contentType)
            {
                case "image/bmp":
                    type = ImagePartType.Bmp;
                    break;
                case "image/emf": // TODO
                    type = ImagePartType.Emf;
                    break;
                case "image/gif": // TODO
                    type = ImagePartType.Gif;
                    break;
                case "image/ico": // TODO
                    type = ImagePartType.Icon;
                    break;
                case "image/jpeg":
                    type = ImagePartType.Jpeg;
                    break;
                case "image/pcx": // TODO
                    type = ImagePartType.Pcx;
                    break;
                case "image/png":
                    type = ImagePartType.Png;
                    break;
                case "image/tiff": // TODO
                    type = ImagePartType.Tiff;
                    break;
                case "image/wmf": // TODO
                    type = ImagePartType.Wmf;
                    break;
            }

            ImagePart imagePart = this.slidePart.AddImagePart(type);

            // FeedData() closes the stream and we cannot reuse it (ObjectDisposedException)
            // solution: copy the original stream to a MemoryStream
            using (MemoryStream stream = new MemoryStream(picture))
            {
                imagePart.FeedData(stream);
            }

            // No need to detect duplicated images
            // PowerPoint do it for us on the next manual save

            return imagePart;
        }

        /// <summary>
        /// Gets the relationship ID of a given image part.
        /// </summary>
        /// <param name="imagePart">The image part.</param>
        /// <returns>The relationship ID of the image part.</returns>
        internal string GetIdOfImagePart(ImagePart imagePart)
        {
            return this.slidePart.GetIdOfPart(imagePart);
        }

        /// <summary>
        /// Finds a table (a:tbl) given its "artificial" id (tblId).
        /// </summary>
        /// <param name="tblId">The table id.</param>
        /// <returns>The table or null if not found.</returns>
        /// <remarks>The "artificial" id (tblId) is created inside FindTables().</remarks>
        internal A.Table FindTable(int tblId)
        {
            A.Table tbl = null;

            IEnumerable<GraphicFrame> graphicFrames = this.slidePart.Slide.Descendants<GraphicFrame>();
            GraphicFrame graphicFrame = graphicFrames.ElementAt(tblId);
            if (graphicFrame != null)
            {
                tbl = graphicFrame.Descendants<A.Table>().First();
            }

            return tbl;
        }

        /// <summary>
        /// Removes a table (a:tbl) given its "artificial" id (tblId).
        /// </summary>
        /// <param name="tblId">The table id.</param>
        /// <remarks>
        /// <![CDATA[
        /// p:graphicFrame
        ///  a:graphic
        ///   a:graphicData
        ///    a:tbl (Table)
        /// ]]>
        /// </remarks>
        internal void RemoveTable(int tblId)
        {
            IEnumerable<GraphicFrame> graphicFrames = this.slidePart.Slide.Descendants<GraphicFrame>();
            GraphicFrame graphicFrame = graphicFrames.ElementAt(tblId);
            graphicFrame.Remove();
        }

        /// <summary>
        /// Saves the slide.
        /// </summary>
        /// <remarks>
        /// This is mandatory to save the slides after modifying them otherwise
        /// the next manipulation that will be performed on the pptx won't
        /// include the modifications done before.
        /// </remarks>
        internal void Save()
        {
            this.slidePart.Slide.Save();
        }
    }
}
View Code
using DocumentFormat.OpenXml.Packaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Pt.Slm.WebApi.Common
{
    using A = DocumentFormat.OpenXml.Drawing;
    public class PptxTable
    {
        private PptxSlide slideTemplate;

        private readonly int tblId;

        public string Title { get; private set; }

        internal PptxTable(PptxSlide slideTemplate, int tblId, string title)
        {
            this.slideTemplate = slideTemplate;
            this.tblId = tblId;
            this.Title = title;
        }

        /// <summary>
        /// Represents a cell inside a table (a:tbl).
        /// </summary>
        public class Cell
        {
            internal string Tag { get; private set; }

            internal string NewText { get; private set; }

            public class BackgroundPicture
            {
                public byte[] Content { get; set; }
                public string ContentType { get; set; }
                public int Top { get; set; }
                public int Right { get; set; }
                public int Bottom { get; set; }
                public int Left { get; set; }
            }

            internal BackgroundPicture Picture { get; private set; }

            public Cell(string tag, string newText)
            {
                this.Tag = tag;
                this.NewText = newText;
            }

            public Cell(string tag, string newText, BackgroundPicture backgroundPicture)
            {
                this.Tag = tag;
                this.NewText = newText;
                this.Picture = backgroundPicture;
            }
        }

        /// <summary>
        /// Removes the table from the slide.
        /// </summary>
        public void Remove()
        {
            this.slideTemplate.RemoveTable(this.tblId);
        }

        /// <summary>
        /// Removes the given columns.
        /// </summary>
        /// <param name="columns">Indexes of the columns to remove.</param>
        public void RemoveColumns(IEnumerable<int> columns)
        {
            A.Table tbl = this.slideTemplate.FindTable(this.tblId);
            A.TableGrid tblGrid = tbl.TableGrid;

            // Remove the latest columns first
            IEnumerable<int> columnsSorted = from column in columns
                                             orderby column descending
                                             select column;

            int tblRowsCount = RowsCount(tbl);

            foreach (int column in columnsSorted)
            {
                for (int row = 0; row < tblRowsCount; row++)
                {
                    A.TableRow tr = GetRow(tbl, row);

                    // Remove the column from the row
                    A.TableCell tc = GetCell(tr, column);
                    tc.Remove();
                }

                // Remove the column from TableGrid
                A.GridColumn gridCol = tblGrid.Descendants<A.GridColumn>().ElementAt(column);
                gridCol.Remove();
            }

            this.slideTemplate.Save();
        }

        /// <summary>
        /// Sets a background picture for a table cell (a:tc).
        /// </summary>
        /// <remarks>
        /// <![CDATA[
        /// <a:tc>
        ///  <a:txBody>
        ///   <a:bodyPr/>
        ///   <a:lstStyle/>
        ///   <a:p>
        ///    <a:endParaRPr lang="fr-FR" dirty="0"/>
        ///   </a:p>
        ///  </a:txBody>
        ///  <a:tcPr> (TableCellProperties)
        ///   <a:blipFill dpi="0" rotWithShape="1">
        ///    <a:blip r:embed="rId2"/>
        ///    <a:srcRect/>
        ///    <a:stretch>
        ///     <a:fillRect b="12000" r="90000" t="14000"/>
        ///    </a:stretch>
        ///   </a:blipFill>
        ///  </a:tcPr>
        /// </a:tc>
        /// ]]>
        /// </remarks>
        private static void SetTableCellPropertiesWithBackgroundPicture(PptxSlide slide, A.TableCellProperties tcPr, Cell.BackgroundPicture backgroundPicture)
        {
            if (backgroundPicture.Content == null)
            {
                return;
            }

            ImagePart imagePart = slide.AddPicture(backgroundPicture.Content, backgroundPicture.ContentType);

            A.BlipFill blipFill = new A.BlipFill();
            A.Blip blip = new A.Blip() { Embed = slide.GetIdOfImagePart(imagePart) };
            A.SourceRectangle srcRect = new A.SourceRectangle();
            A.Stretch stretch = new A.Stretch();
            A.FillRectangle fillRect = new A.FillRectangle()
            {
                Top = backgroundPicture.Top,
                Right = backgroundPicture.Right,
                Bottom = backgroundPicture.Bottom,
                Left = backgroundPicture.Left
            };
            stretch.AppendChild(fillRect);
            blipFill.AppendChild(blip);
            blipFill.AppendChild(srcRect);
            blipFill.AppendChild(stretch);
            tcPr.AppendChild(blipFill);
        }

        /// <summary>
        /// Replaces a tag inside the table (a:tbl).
        /// </summary>
        /// <param name="cell">Contains the tag, the new text and a pciture.</param>
        /// <returns><c>true</c> if a tag has been found and replaced, <c>false</c> otherwise.</returns>
        public bool ReplaceTag(Cell cell)
        {
            bool replacedAtLeastOnce = false;

            PptxSlide slide = this.slideTemplate;
            A.Table tbl = slide.FindTable(this.tblId);

            // a:tr
            foreach (A.TableRow tr in tbl.Descendants<A.TableRow>())
            {
                // a:tc
                foreach (A.TableCell tc in tr.Descendants<A.TableCell>())
                {
                    bool replaced = ReplaceTag(slide, tc, cell);
                    if (replaced)
                    {
                        replacedAtLeastOnce = true;
                    }
                }
            }

            return replacedAtLeastOnce;
        }

        /// <summary>
        /// Replaces a tag inside a given table cell (a:tc).
        /// </summary>
        /// <param name="slide">The PptxSlide.</param>
        /// <param name="tc">The table cell (a:tc).</param>
        /// <param name="cell">Contains the tag, the new text and a picture.</param>
        /// <returns><c>true</c> if a tag has been found and replaced, <c>false</c> otherwise.</returns>
        private static bool ReplaceTag(PptxSlide slide, A.TableCell tc, Cell cell)
        {
            bool replacedAtLeastOnce = false;

            // a:p
            foreach (A.Paragraph p in tc.Descendants<A.Paragraph>())
            {
                bool replaced = PptxParagraph.ReplaceTag(p, cell.Tag, cell.NewText);
                if (replaced)
                {
                    replacedAtLeastOnce = true;

                    // a:tcPr
                    if (cell.Picture != null)
                    {
                        A.TableCellProperties tcPr = tc.GetFirstChild<A.TableCellProperties>();
                        SetTableCellPropertiesWithBackgroundPicture(slide, tcPr, cell.Picture);
                    }
                }
            }

            return replacedAtLeastOnce;
        }

        /// <summary>
        /// Replaces the cells from the table (tbl).
        /// </summary>
        /// <returns>The list of remaining rows that could not be inserted, you will have to create a new slide.</returns>
        public List<Cell[]> SetRows(IList<Cell[]> rows)
        {
            PptxSlide slide = this.slideTemplate;
            A.Table tbl = slide.FindTable(this.tblId);

            int tblRowsCount = RowsCount(tbl);

            // done starts at 1 instead of 0 because we don't care about the first row
            // The first row contains the titles for the columns
            int done = 1;
            for (int i = 0; i < rows.Count(); i++)
            {
                Cell[] row = rows[i];

                if (done < tblRowsCount)
                {
                    // a:tr
                    A.TableRow tr = GetRow(tbl, done);

                    // a:tc
                    foreach (A.TableCell tc in tr.Descendants<A.TableCell>())
                    {
                        foreach (Cell cell in row)
                        {
                            ReplaceTag(slide, tc, cell);
                        }
                    }

                    done++;
                }
                else
                {
                    break;
                }
            }

            // Remove the last remaining rows if any
            for (int row = tblRowsCount - 1; row >= done; row--)
            {
                A.TableRow tr = GetRow(tbl, row);
                tr.Remove();
            }

            // Save the latest slide
            // Mandatory otherwise the next time SetRows() is run (on a different table)
            // the rows from the previous tables will not contained the right data (from PptxParagraph.ReplaceTag())
            slide.Save();

            // Computes the remaining rows if any
            List<Cell[]> remainingRows = new List<Cell[]>();
            for (int row = done - 1; row < rows.Count; row++)
            {
                remainingRows.Add(rows[row]);
            }

            return remainingRows;
        }

        /// <summary>
        /// Gets the columns titles as an array of strings.
        /// </summary>
        public IEnumerable<string> ColumnTitles()
        {
            List<string> titles = new List<string>();

            A.Table tbl = this.slideTemplate.FindTable(this.tblId);
            A.TableRow tr = GetRow(tbl, 0); // The first table row == the columns

            int columnsCount = this.ColumnsCount();
            for (int i = 0; i < columnsCount; i++)
            {
                A.TableCell tc = GetCell(tr, i);
                var text = string.Join(" ", tc.Descendants<A.Paragraph>().Select(PptxParagraph.GetTexts).ToArray());
                titles.Add(text);
            }

            return titles;
        }

        /// <summary>
        /// Gets the number of columns inside the table (tbl).
        /// </summary>
        /// <returns>The number of columns.</returns>
        public int ColumnsCount()
        {
            A.Table tbl = this.slideTemplate.FindTable(this.tblId);
            return CellsCount(tbl) / RowsCount(tbl);
        }

        /// <summary>
        /// Gets the number of cells inside the table (tbl).
        /// </summary>
        /// <returns>The number of cells.</returns>
        public int CellsCount()
        {
            A.Table tbl = this.slideTemplate.FindTable(this.tblId);
            return CellsCount(tbl);
        }

        /// <summary>
        /// Helper method.
        /// </summary>
        private static int CellsCount(A.Table tbl)
        {
            return tbl.Descendants<A.TableCell>().Count();
        }

        /// <summary>
        /// Helper method.
        /// </summary>
        private static int RowsCount(A.Table tbl)
        {
            return tbl.Descendants<A.TableRow>().Count();
        }

        /// <summary>
        /// Helper method.
        /// </summary>
        private static A.TableRow GetRow(A.Table tbl, int row)
        {
            A.TableRow tr = tbl.Descendants<A.TableRow>().ElementAt(row);
            return tr;
        }

        /// <summary>
        /// Helper method.
        /// </summary>
        private static A.TableCell GetCell(A.TableRow tr, int column)
        {
            A.TableCell tc = tr.Descendants<A.TableCell>().ElementAt(column);
            return tc;
        }
    }
}
View Code

单元测试

namespace PptxTemplater.Tests
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Text;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.Web;
    [TestClass]
    public class PptxTest
    {
        private void AssertPptxEquals(string file, int nbSlides, string expected)
        {
            Pptx pptx = new Pptx(file, FileAccess.Read);
            Assert.AreEqual(nbSlides, pptx.SlidesCount());
            StringBuilder result = new StringBuilder();
            for (int i = 0; i < nbSlides; i++)
            {
                PptxSlide slide = pptx.GetSlide(i);
                IEnumerable<string> texts = slide.GetTexts();
                result.Append(string.Join(" ", texts));
                result.Append(" ");
            }
            pptx.Close();
            Assert.AreEqual(expected, result.ToString());
        }

        [TestMethod]
        [ExpectedException(typeof(FileFormatException))]
        public void FileFormatException()
        {
            Pptx pptx = new Pptx("../../files/picture1.png", FileAccess.Read);
            pptx.Close();
        }

        [TestMethod]
        public void EmptyPowerPoint()
        {
            const string file = "../../files/EmptyPowerPoint.pptx";
            const string thumbnail_empty_png = "../../files/thumbnail_empty.png";
            const string thumbnail_empty_output_png = "../../files/thumbnail_empty_output.png";

            Pptx pptx = new Pptx(file, FileAccess.Read);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(0, nbSlides);

            byte[] thumbnail_empty_output = pptx.GetThumbnail();
            File.WriteAllBytes(thumbnail_empty_output_png, thumbnail_empty_output);
            byte[] thumbnail_empty = File.ReadAllBytes(thumbnail_empty_png);
            CollectionAssert.AreEqual(thumbnail_empty, thumbnail_empty_output);

            pptx.Close();
        }

        [TestMethod]
        public void GetTexts()
        {
            const string file = "../../files/GetTexts.pptx";

            Pptx pptx = new Pptx(file, FileAccess.Read);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(3, nbSlides);

            Assert.AreEqual("test1", pptx.GetSlide(0).GetTitle());
            Assert.AreEqual("Title 1", pptx.GetSlide(1).GetTitle());
            Assert.AreEqual("Title 2", pptx.GetSlide(2).GetTitle());

            var slidesTexts = new Dictionary<int, string[]>();
            for (int i = 0; i < nbSlides; i++)
            {
                PptxSlide slide = pptx.GetSlide(i);
                IEnumerable<string> texts = slide.GetTexts();
                slidesTexts.Add(i, texts.ToArray());
            }

            string[] expected = { "test1", "Hello, world!" };
            CollectionAssert.AreEqual(expected, slidesTexts[0]);

            expected = new string[]
                           {
                               "Title 1", "Bullet 1", "Bullet 2",
                               "Column 1", "Column 2", "Column 3", "Column 4", "Column 5",
                               "Line 1", string.Empty, string.Empty, string.Empty, string.Empty,
                               "Line 2", string.Empty, string.Empty, string.Empty, string.Empty,
                               "Line 3", string.Empty, string.Empty, string.Empty, string.Empty,
                               "Line 4", string.Empty, string.Empty, string.Empty, string.Empty
                           };
            CollectionAssert.AreEqual(expected, slidesTexts[1]);

            expected = new string[] { "Title 2", "Bullet 1", "Bullet 2", "Comment ça va ?" };
            CollectionAssert.AreEqual(expected, slidesTexts[2]);

            pptx.Close();
        }

        [TestMethod]
        public void GetSlides()
        {
            const string file = "../../files/GetTexts.pptx";

            Pptx pptx = new Pptx(file, FileAccess.Read);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(3, nbSlides);

            IEnumerable<PptxSlide> slides = pptx.GetSlides();
            Assert.AreEqual(3, slides.Count());

            pptx.Close();
        }

        [TestMethod]
        public void GetNotes()
        {
            const string file = "../../files/GetNotes.pptx";

            Pptx pptx = new Pptx(file, FileAccess.Read);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(4, nbSlides);

            Assert.AreEqual(string.Empty, pptx.GetSlide(0).GetTitle());
            Assert.AreEqual(string.Empty, pptx.GetSlide(1).GetTitle());
            Assert.AreEqual(string.Empty, pptx.GetSlide(2).GetTitle());
            Assert.AreEqual(string.Empty, pptx.GetSlide(3).GetTitle());

            var slidesNotes = new Dictionary<int, string[]>();
            for (int i = 0; i < nbSlides; i++)
            {
                PptxSlide slide = pptx.GetSlide(i);
                IEnumerable<string> notes = slide.GetNotes();
                slidesNotes.Add(i, notes.ToArray());
            }

            string[] expected = { "Bonjour", "{{comment1}}", "Hello", "1" };
            CollectionAssert.AreEqual(expected, slidesNotes[0]);

            expected = new string[] { "{{comment2}}", "2" };
            CollectionAssert.AreEqual(expected, slidesNotes[1]);

            expected = new string[] { };
            CollectionAssert.AreEqual(expected, slidesNotes[2]);

            // TODO Why "Comment çava ?" instead of "Comment ça va ?"
            expected = new string[] { "Bonjour {{comment3}} Hello", "Comment çava ?", string.Empty, string.Empty, "Hola!", string.Empty, "4" };
            CollectionAssert.AreEqual(expected, slidesNotes[3]);

            pptx.Close();
        }

        [TestMethod]
        public void FindSlides()
        {
            const string file = "../../files/GetNotes.pptx";

            Pptx pptx = new Pptx(file, FileAccess.Read);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(4, nbSlides);

            {
                IEnumerable<PptxSlide> slides = pptx.FindSlides("{{comment1}}");
                Assert.AreEqual(1, slides.Count());
            }

            {
                IEnumerable<PptxSlide> slides = pptx.FindSlides("{{comment2}}");
                Assert.AreEqual(1, slides.Count());
            }

            {
                IEnumerable<PptxSlide> slides = pptx.FindSlides("{{comment3}}");
                Assert.AreEqual(1, slides.Count());
            }

            pptx.Close();
        }

        [TestMethod]
        public void GetTables()
        {
            const string file = "../../files/ReplaceTables.pptx";

            Pptx pptx = new Pptx(file, FileAccess.Read);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(3, nbSlides);

            Assert.AreEqual(string.Empty, pptx.GetSlide(0).GetTitle());
            Assert.AreEqual(string.Empty, pptx.GetSlide(1).GetTitle());
            Assert.AreEqual(string.Empty, pptx.GetSlide(2).GetTitle());

            var slidesTables = new Dictionary<int, PptxTable[]>();
            for (int i = 0; i < nbSlides; i++)
            {
                PptxSlide slide = pptx.GetSlide(i);
                IEnumerable<PptxTable> tables = slide.GetTables();
                slidesTables.Add(i, tables.ToArray());
            }

            string[] expected = { "Table1", "Col2", "Col3", "Col4", "Col5", "Col6" };
            CollectionAssert.AreEqual(expected, slidesTables[1][0].ColumnTitles().ToArray());

            expected = new string[] { "Table2", "Col2", "Col3", "Col4", "Col5", "Col6" };
            CollectionAssert.AreEqual(expected, slidesTables[1][1].ColumnTitles().ToArray());

            expected = new string[] { "Table3", "Col2", "Col3", "Col4", "Col5", "Col6" };
            CollectionAssert.AreEqual(expected, slidesTables[1][2].ColumnTitles().ToArray());

            pptx.Close();
        }

        [TestMethod]
        public void TableColumnTitles()
        {
            const string file = "../../files/TableColumnTitles.pptx";

            Pptx pptx = new Pptx(file, FileAccess.Read);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(1, nbSlides);

            Assert.AreEqual(string.Empty, pptx.GetSlide(0).GetTitle());

            var slidesTables = new Dictionary<int, PptxTable[]>();
            for (int i = 0; i < nbSlides; i++)
            {
                PptxSlide slide = pptx.GetSlide(i);
                IEnumerable<PptxTable> tables = slide.GetTables();
                slidesTables.Add(i, tables.ToArray());
            }

            string[] expected = { "1 Multiple lines", "2", "3", "4", "5" };
            CollectionAssert.AreEqual(expected, slidesTables[0][0].ColumnTitles().ToArray());

            pptx.Close();
        }

        [TestMethod]
        public void ReplaceTags()
        {
            const string srcFileName = "../../files/ReplaceTags.pptx";
            const string dstFileName = "../../files/ReplaceTags_output.pptx";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(3, nbSlides);

            // First slide
            {
                PptxSlide slide = pptx.GetSlide(0);
                slide.ReplaceTag("{{hello}}", "HELLO HOW ARE YOU?", PptxSlide.ReplacementType.Global);
                slide.ReplaceTag("{{bonjour}}", "BONJOUR TOUT LE MONDE", PptxSlide.ReplacementType.Global);
                slide.ReplaceTag("{{hola}}", "HOLA MAMA QUE TAL?", PptxSlide.ReplacementType.Global);
            }

            // Second slide
            {
                PptxSlide slide = pptx.GetSlide(1);
                slide.ReplaceTag("{{hello}}", "H", PptxSlide.ReplacementType.Global);
                slide.ReplaceTag("{{bonjour}}", "B", PptxSlide.ReplacementType.Global);
                slide.ReplaceTag("{{hola}}", "H", PptxSlide.ReplacementType.Global);
            }

            // Third slide
            {
                PptxSlide slide = pptx.GetSlide(2);
                slide.ReplaceTag("{{hello}}", string.Empty, PptxSlide.ReplacementType.Global);
                slide.ReplaceTag("{{bonjour}}", string.Empty, PptxSlide.ReplacementType.Global);
                slide.ReplaceTag("{{hola}}", null, PptxSlide.ReplacementType.Global);
                slide.ReplaceTag(null, string.Empty, PptxSlide.ReplacementType.Global);
                slide.ReplaceTag(null, null, PptxSlide.ReplacementType.Global);
            }

            pptx.Close();

            this.AssertPptxEquals(dstFileName, 3, "words HELLO HOW ARE YOU?|HELLO HOW ARE YOU?|HOLA MAMA QUE TAL?, world! A tag {{hoHOLA MAMA QUE TAL?la}} inside a sentence BONJOUR TOUT LE MONDE A tag BONJOUR TOUT LE MONDEHOLA MAMA QUE TAL?BONJOUR TOUT LE MONDE inside a sentence HELLO HOW ARE YOU?, world! words H|H|H, world! A tag {{hoHla}} inside a sentence B A tag BHB inside a sentence H, world! words ||, world! A tag  inside a sentence  A tag inside a sentence , world! ");
        }

        [TestMethod]
        public void ReplacePictures()
        {
            const string srcFileName = "../../files/ReplacePictures.pptx";
            const string dstFileName = "../../files/ReplacePictures_output.pptx";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(4, nbSlides);

            Assert.AreEqual(string.Empty, pptx.GetSlide(0).GetTitle());
            Assert.AreEqual(string.Empty, pptx.GetSlide(1).GetTitle());

            {
                PptxSlide slide = pptx.GetSlide(0);
                slide.ReplaceTag("{{hello}}", "HELLO!", PptxSlide.ReplacementType.Global);
            }

            const string picture1_replace_png = "../../files/111.png";

            const string picture1_replace_png1 = "../../files/picture1_replace.png";
            //picture1_replace.png
            const string picture1_replace_png_contentType = "image/png";
            const string picture1_replace_bmp = "../../files/picture1_replace.bmp";
            const string picture1_replace_bmp_contentType = "image/bmp";
            const string picture1_replace_jpeg = "../../files/picture1_replace.jpeg";
            const string picture1_replace_jpeg_contentType = "image/jpeg";
            byte[] picture1_replace_empty = new byte[] { };

            {

                PptxSlide slide = pptx.GetSlide(1);

                slide.ReplacePicture("{{picture2}}", picture1_replace_png1, picture1_replace_png_contentType);
                slide.ReplacePicture("{{picture1png}}", picture1_replace_png, picture1_replace_png_contentType);
                slide.ReplacePicture("{{picture1}}", picture1_replace_png, picture1_replace_png_contentType);
            }

            for (int i = 0; i < nbSlides; i++)
            {
                PptxSlide slide = pptx.GetSlide(i);

                slide.ReplacePicture("{{picture2png}}", picture1_replace_png, picture1_replace_png_contentType);
                slide.ReplacePicture("{{picture1bmp}}", picture1_replace_bmp, picture1_replace_bmp_contentType);
                slide.ReplacePicture("{{picture1jpeg}}", picture1_replace_jpeg, picture1_replace_jpeg_contentType);

                slide.ReplacePicture(null, picture1_replace_png, picture1_replace_png_contentType);
                slide.ReplacePicture("{{picture1null}}", picture1_replace_empty, picture1_replace_png_contentType);
                slide.ReplacePicture("{{picture1null}}", picture1_replace_png, null);
                slide.ReplacePicture("{{picture1null}}", picture1_replace_empty, null);
            }

            pptx.Close();

            // Sorry, you will have to manually check that the pictures have been replaced
        }

        [TestMethod]
        public void RemoveColumns()
        {
            const string srcFileName = "../../files/RemoveColumns.pptx";
            const string dstFileName = "../../files/RemoveColumns_output.pptx";

            // Remove some columns
            {
                File.Delete(dstFileName);
                File.Copy(srcFileName, dstFileName);
                Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

                PptxSlide slide = pptx.GetSlide(0);
                PptxTable table = slide.FindTables("{{table1}}").First();

                Assert.AreEqual(5, table.ColumnsCount());
                Assert.AreEqual(30, table.CellsCount());
                int[] columns = new int[] { 1, 3 };
                table.RemoveColumns(columns);
                Assert.AreEqual(3, table.ColumnsCount());
                Assert.AreEqual(18, table.CellsCount());

                pptx.Close();

                this.AssertPptxEquals(dstFileName, 1, "Column 0 Column2 Column 4 Cell 1.0 Cell 1.2 Cell 1.4 Cell 2.0 Cell 2.2 Cell 2.4 Cell 3.0 Cell 3.2 Cell 3.4 Cell 4.0 Cell 4.2 Cell 4.4 Cell 5.0 Cell 5.2 Cell 5.4 ");
            }

            // Remove all the columns
            {
                File.Delete(dstFileName);
                File.Copy(srcFileName, dstFileName);
                Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

                PptxSlide slide = pptx.GetSlide(0);
                PptxTable table = slide.FindTables("{{table1}}").First();

                Assert.AreEqual(5, table.ColumnsCount());
                Assert.AreEqual(30, table.CellsCount());
                int[] columns = new int[] { 0, 1, 2, 3, 4 };
                table.RemoveColumns(columns);
                Assert.AreEqual(0, table.ColumnsCount());
                Assert.AreEqual(0, table.CellsCount());

                pptx.Close();

                this.AssertPptxEquals(dstFileName, 1, " ");
            }
        }

        [TestMethod]
        public void SetTableCellBackgroundPicture()
        {
            const string srcFileName = "../../files/SetTableCellBackgroundPicture.pptx";
            const string dstFileName = "../../files/SetTableCellBackgroundPicture_output.pptx";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

            const string icon_png = "../../files/icon.png";
            const string icon_png_contentType = "image/png";
            byte[] icon = File.ReadAllBytes(icon_png);

            List<PptxTable.Cell[]> rows = new List<PptxTable.Cell[]>
                {
                    new[]
                        {
                            new PptxTable.Cell(
                                "{{cell0.0}}",
                                "Hello, world! 0.0",
                                new PptxTable.Cell.BackgroundPicture()
                                    {
                                        Content = icon,
                                        ContentType = icon_png_contentType,
                                        Top = 14000,
                                        Right = 90000,
                                        Bottom = 12000,
                                        Left = 0,
                                        Color="750000"

                                    }),
                            new PptxTable.Cell(
                                "{{cell3.0}}",
                                "Hello, world! 3.0",
                                new PptxTable.Cell.BackgroundPicture()
                                    {
                                        Content = icon,
                                        ContentType = icon_png_contentType,
                                        Top = 14000,
                                        Right = 90000,
                                        Bottom = 12000,
                                        Left = 0
                                    })
                        },
                    new[]
                        {
                            new PptxTable.Cell(
                                "{{cell0.1}}",
                                "Hello, world! 0.1",
                                new PptxTable.Cell.BackgroundPicture()
                                    {
                                        Content = icon,
                                        ContentType = icon_png_contentType,
                                        Top = 14000,
                                        Right = 90000,
                                        Bottom = 0,
                                        Left = 0
                                    })
                        },
                    new[]
                        {
                            new PptxTable.Cell(
                                "{{cell0.2}}",
                                "Hello, world! 0.2",
                                new PptxTable.Cell.BackgroundPicture()
                                    {
                                        Content = icon,
                                        ContentType = icon_png_contentType,
                                        Top = 14000,
                                        Right = 0,
                                        Bottom = 0,
                                        Left = 0
                                    })
                        },
                    new[]
                        {
                            new PptxTable.Cell(
                                "{{cell0.3}}",
                                "Hello, world! 0.3",
                                new PptxTable.Cell.BackgroundPicture()
                                    {
                                        Content = icon,
                                        ContentType = icon_png_contentType,
                                        Top = 0,
                                        Right = 0,
                                        Bottom = 0,
                                        Left = 0
                                    })
                        },
                    new[]
                        {
                            new PptxTable.Cell(
                                "{{cell0.4}}",
                                "Hello, world! 0.4",
                                new PptxTable.Cell.BackgroundPicture()
                                    {
                                        Content = icon,
                                        ContentType = icon_png_contentType
                                    })
                        },
                    new[]
                        {
                            new PptxTable.Cell("{{cell0.5}}", "Hello, world! 0.5"),
                            new PptxTable.Cell("{{cell3.5}}", "Hello, world! 3.5")
                        }
                };

            PptxSlide slideTemplate = pptx.GetSlide(0);

            var table = slideTemplate.FindTables("{{table1}}").First();
            var slidesCreated = Pptx.ReplaceTable_One(slideTemplate, table, rows);
            Assert.AreEqual(1, slidesCreated.Count());

            // Force a slide duplication
            // This is to test that PptxSlide.Clone() works with background images
            var slide = slidesCreated.First();
            var slideClone = slide.Clone();
            PptxSlide.InsertAfter(slideClone, slide);

            slideTemplate.Remove();

            pptx.Close();

            this.AssertPptxEquals(dstFileName, 2, "Col0 Col1 Col2 Col3 Col4 Hello, world! 0.0 Hello {{cell2.0}} Hello, world! 3.0 {{cell4.0}} Hello, world! 0.1 Hello {{cell2.1}} {{cell3.1}} {{cell4.1}} Hello, world! 0.2 Hello {{cell2.2}} {{cell3.2}} {{cell4.2}} Hello, world! 0.3 Hello {{cell2.3}} {{cell3.3}} {{cell4.3}} Hello, world! 0.4 Hello {{cell2.4}} {{cell3.4}} {{cell4.4}} Hello, world! 0.5 Hello {{cell2.5}} Hello, world! 3.5 {{cell4.5}} Col0 Col1 Col2 Col3 Col4 Hello, world! 0.0 Hello {{cell2.0}} Hello, world! 3.0 {{cell4.0}} Hello, world! 0.1 Hello {{cell2.1}} {{cell3.1}} {{cell4.1}} Hello, world! 0.2 Hello {{cell2.2}} {{cell3.2}} {{cell4.2}} Hello, world! 0.3 Hello {{cell2.3}} {{cell3.3}} {{cell4.3}} Hello, world! 0.4 Hello {{cell2.4}} {{cell3.4}} {{cell4.4}} Hello, world! 0.5 Hello {{cell2.5}} Hello, world! 3.5 {{cell4.5}} ");
            // Sorry, you will have to manually check the background pictures
        }

        [TestMethod]
        public void ReplaceTables()
        {
            string dstFileName = "../../files/ReplaceTables_output1.pptx";
            this.ReplaceTables(dstFileName, 0, 0, 0);
            this.AssertPptxEquals(dstFileName, 3, "HELLO! Table0      Table1 Col2 Col3 Col4 Col5 Col6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6  ");

            dstFileName = "../../files/ReplaceTables_output2.pptx";
            this.ReplaceTables(dstFileName, 10, 0, 0);
            this.AssertPptxEquals(dstFileName, 6, "HELLO! Table0      Table1 Col2 Col3 Col4 Col5 Col6 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6 Table1 Col2 Col3 Col4 Col5 Col6 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6 Table1 Col2 Col3 Col4 Col5 Col6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6 Table1 Col2 Col3 Col4 Col5 Col6 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6  ");

            dstFileName = "../../files/ReplaceTables_output3.pptx";
            this.ReplaceTables(dstFileName, 0, 10, 0);
            this.AssertPptxEquals(dstFileName, 5, "HELLO! Table0      Table1 Col2 Col3 Col4 Col5 Col6 Table2 Col2 Col3 Col4 Col5 Col6 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 Table3 Col2 Col3 Col4 Col5 Col6 Table1 Col2 Col3 Col4 Col5 Col6 Table2 Col2 Col3 Col4 Col5 Col6 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 Table3 Col2 Col3 Col4 Col5 Col6 Table1 Col2 Col3 Col4 Col5 Col6 Table2 Col2 Col3 Col4 Col5 Col6 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 Table3 Col2 Col3 Col4 Col5 Col6  ");

            dstFileName = "../../files/ReplaceTables_output4.pptx";
            this.ReplaceTables(dstFileName, 0, 0, 10);
            this.AssertPptxEquals(dstFileName, 5, "HELLO! Table0      Table1 Col2 Col3 Col4 Col5 Col6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 Table1 Col2 Col3 Col4 Col5 Col6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.7.1 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 Table1 Col2 Col3 Col4 Col5 Col6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.9.1 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6  ");

            dstFileName = "../../files/ReplaceTables_output5.pptx";
            this.ReplaceTables(dstFileName, 10, 10, 10);
            this.AssertPptxEquals(dstFileName, 6, "HELLO! Table0      Table1 Col2 Col3 Col4 Col5 Col6 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 Table2 Col2 Col3 Col4 Col5 Col6 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 Table3 Col2 Col3 Col4 Col5 Col6 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 Table1 Col2 Col3 Col4 Col5 Col6 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 Table2 Col2 Col3 Col4 Col5 Col6 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 Table3 Col2 Col3 Col4 Col5 Col6 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.7.1 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 Table1 Col2 Col3 Col4 Col5 Col6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 Table2 Col2 Col3 Col4 Col5 Col6 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 Table3 Col2 Col3 Col4 Col5 Col6 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.9.1 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 Table1 Col2 Col3 Col4 Col5 Col6 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 Table2 Col2 Col3 Col4 Col5 Col6 Table3 Col2 Col3 Col4 Col5 Col6  ");

            dstFileName = "../../files/ReplaceTables_output6.pptx";
            this.ReplaceTables(dstFileName, 11, 22, 33);
            this.AssertPptxEquals(dstFileName, 11, "HELLO! Table0      Table1 Col2 Col3 Col4 Col5 Col6 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 Table2 Col2 Col3 Col4 Col5 Col6 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 Table3 Col2 Col3 Col4 Col5 Col6 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 Table1 Col2 Col3 Col4 Col5 Col6 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 Table2 Col2 Col3 Col4 Col5 Col6 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 Table3 Col2 Col3 Col4 Col5 Col6 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.7.1 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 Table1 Col2 Col3 Col4 Col5 Col6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 Table2 Col2 Col3 Col4 Col5 Col6 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.10.1 2.10.2 2.10.3 2.10.4 2.10.5 2.10.6 2.11.1 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 Table3 Col2 Col3 Col4 Col5 Col6 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.9.1 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.10.1 3.10.2 3.10.3 3.10.4 3.10.5 3.10.6 3.11.1 3.11.2 3.11.3 3.11.4 3.11.5 3.11.6 Table1 Col2 Col3 Col4 Col5 Col6 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.10.6 Table2 Col2 Col3 Col4 Col5 Col6 2.12.1 2.12.2 2.12.3 2.12.4 2.12.5 2.12.6 2.13.1 2.13.2 2.13.3 2.13.4 2.13.5 2.13.6 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 2.15.1 2.15.2 2.15.3 2.15.4 2.15.5 2.15.6 Table3 Col2 Col3 Col4 Col5 Col6 3.12.1 3.12.2 3.12.3 3.12.4 3.12.5 3.12.6 3.13.1 3.13.2 3.13.3 3.13.4 3.13.5 3.13.6 3.14.1 3.14.2 3.14.3 3.14.4 3.14.5 3.14.6 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.15.6 Table1 Col2 Col3 Col4 Col5 Col6 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.10.6 Table2 Col2 Col3 Col4 Col5 Col6 2.16.1 2.16.2 2.16.3 2.16.4 2.16.5 2.16.6 2.17.1 2.17.2 2.17.3 2.17.4 2.17.5 2.17.6 2.18.1 2.18.2 2.18.3 2.18.4 2.18.5 2.18.6 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 Table3 Col2 Col3 Col4 Col5 Col6 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.1 3.18.2 3.18.3 3.18.4 3.18.5 3.18.6 3.19.1 3.19.2 3.19.3 3.19.4 3.19.5 3.19.6 Table1 Col2 Col3 Col4 Col5 Col6 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.10.6 Table2 Col2 Col3 Col4 Col5 Col6 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.1 2.21.2 2.21.3 2.21.4 2.21.5 2.21.6 Table3 Col2 Col3 Col4 Col5 Col6 3.20.1 3.20.2 3.20.3 3.20.4 3.20.5 3.20.6 3.21.1 3.21.2 3.21.3 3.21.4 3.21.5 3.21.6 3.22.1 3.22.2 3.22.3 3.22.4 3.22.5 3.22.6 3.23.1 3.23.2 3.23.3 3.23.4 3.23.5 3.23.6 Table1 Col2 Col3 Col4 Col5 Col6 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.10.6 Table2 Col2 Col3 Col4 Col5 Col6 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.1 2.21.2 2.21.3 2.21.4 2.21.5 2.21.6 Table3 Col2 Col3 Col4 Col5 Col6 3.24.1 3.24.2 3.24.3 3.24.4 3.24.5 3.24.6 3.25.1 3.25.2 3.25.3 3.25.4 3.25.5 3.25.6 3.26.1 3.26.2 3.26.3 3.26.4 3.26.5 3.26.6 3.27.1 3.27.2 3.27.3 3.27.4 3.27.5 3.27.6 Table1 Col2 Col3 Col4 Col5 Col6 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.10.6 Table2 Col2 Col3 Col4 Col5 Col6 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.1 2.21.2 2.21.3 2.21.4 2.21.5 2.21.6 Table3 Col2 Col3 Col4 Col5 Col6 3.28.1 3.28.2 3.28.3 3.28.4 3.28.5 3.28.6 3.29.1 3.29.2 3.29.3 3.29.4 3.29.5 3.29.6 3.30.1 3.30.2 3.30.3 3.30.4 3.30.5 3.30.6 3.31.1 3.31.2 3.31.3 3.31.4 3.31.5 3.31.6 Table1 Col2 Col3 Col4 Col5 Col6 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.10.6 Table2 Col2 Col3 Col4 Col5 Col6 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.1 2.21.2 2.21.3 2.21.4 2.21.5 2.21.6 Table3 Col2 Col3 Col4 Col5 Col6 3.32.1 3.32.2 3.32.3 3.32.4 3.32.5 3.32.6  ");
        }

        private void ReplaceTables(string dstFileName, int table1NbRows, int table2NbRows, int table3NbRows)
        {
            const string srcFileName = "../../files/ReplaceTables.pptx";

            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

            // Change the tags before to insert rows
            {
                PptxSlide slide = pptx.GetSlide(0);
                slide.ReplaceTag("{{hello}}", "HELLO!", PptxSlide.ReplacementType.NoTable);
            }

            // Change the pictures before to insert rows
            {
                const string picture1_replace_png = "../../files/picture1_replace.png";
                const string picture1_replace_png_contentType = "image/png";
                PptxSlide slide = pptx.GetSlide(2);
                slide.ReplacePicture("{{picture1png}}", picture1_replace_png, picture1_replace_png_contentType);
            }

            PptxSlide slideTemplate = pptx.GetSlide(1);
            List<PptxSlide> existingSlides = new List<PptxSlide>();

            {
                List<PptxTable.Cell[]> rows = new List<PptxTable.Cell[]>();
                for (int i = 0; i < table1NbRows; i++)
                {
                    PptxTable.Cell[] row = new[]
                            {
                                new PptxTable.Cell("{{cell1}}", "1." + i + ".1"),
                                new PptxTable.Cell("{{cell2}}", "1." + i + ".2"),
                                new PptxTable.Cell("{{cell3}}", "1." + i + ".3"),
                                new PptxTable.Cell("{{cell4}}", "1." + i + ".4"),
                                new PptxTable.Cell("{{cell5}}", "1." + i + ".5"),
                                new PptxTable.Cell("{{cell6}}", "1." + i + ".6")
                            };
                    rows.Add(row);
                }

                var table = slideTemplate.FindTables("{{table1}}").First();
                var slidesCreated = Pptx.ReplaceTable_Multiple(slideTemplate, table, rows, existingSlides);
                existingSlides.AddRange(slidesCreated);
            }

            {
                List<PptxTable.Cell[]> rows = new List<PptxTable.Cell[]>();
                for (int i = 0; i < table2NbRows; i++)
                {
                    PptxTable.Cell[] row = new[]
                        {
                            new PptxTable.Cell("{{cell1}}", "2." + i + ".1"),
                            new PptxTable.Cell("{{cell2}}", "2." + i + ".2"),
                            new PptxTable.Cell("{{cell3}}", "2." + i + ".3"),
                            new PptxTable.Cell("{{cell4}}", "2." + i + ".4"),
                            new PptxTable.Cell("{{cell5}}", "2." + i + ".5"),
                            new PptxTable.Cell("{{cell6}}", "2." + i + ".6")
                        };
                    rows.Add(row);
                }

                var table = slideTemplate.FindTables("{{table2}}").First();
                var slidesCreated = Pptx.ReplaceTable_Multiple(slideTemplate, table, rows, existingSlides);
                existingSlides.AddRange(slidesCreated);
            }

            {
                List<PptxTable.Cell[]> rows = new List<PptxTable.Cell[]>();
                for (int i = 0; i < table3NbRows; i++)
                {
                    PptxTable.Cell[] row = new[]
                        {
                            new PptxTable.Cell("{{cell1}}", "3." + i + ".1"),
                            new PptxTable.Cell("{{cell2}}", "3." + i + ".2"),
                            new PptxTable.Cell("{{cell3}}", "3." + i + ".3"),
                            new PptxTable.Cell("{{cell4}}", "3." + i + ".4"),
                            new PptxTable.Cell("{{cell5}}", "3." + i + ".5"),
                            new PptxTable.Cell("{{cell6}}", "3." + i + ".6")
                        };
                    rows.Add(row);
                }

                var table = slideTemplate.FindTables("{{table3}}").First();
                var slidesCreated = Pptx.ReplaceTable_Multiple(slideTemplate, table, rows, existingSlides);
                existingSlides.AddRange(slidesCreated);
            }

            slideTemplate.Remove();

            pptx.Close();
        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void FailToReplaceTable()
        {
            const string srcFileName = "../../files/ReplaceTables.pptx";
            const string dstFileName = "../../files/ReplaceTables_output.pptx";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

            PptxSlide slide0 = pptx.GetSlide(0);
            PptxSlide slide1 = pptx.GetSlide(1);

            // Make it fail
            List<PptxTable.Cell[]> rows = new List<PptxTable.Cell[]>();
            var table = slide0.FindTables("{{table0}}").First();
            Pptx.ReplaceTable_One(slide1, table, rows);

            pptx.Close();
        }

        [TestMethod]
        public void ReplaceTableMultipleTimes()
        {
            const string srcFileName = "../../files/ReplaceTableMultipleTimes.pptx";
            const string dstFileName = "../../files/ReplaceTableMultipleTimes_output.pptx";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

            // Après la bataille (Victor Hugo)
            // http://fr.wikisource.org/wiki/Apr%C3%A8s_la_bataille_(Hugo)
            const string apresLaBataille =
                @"Mon père, ce héros au sourire si doux,
Suivi d’un seul housard qu’il aimait entre tous
Pour sa grande bravoure et pour sa haute taille,
Parcourait à cheval, le soir d’une bataille,
Le champ couvert de morts sur qui tombait la nuit.
Il lui sembla dans l’ombre entendre un faible bruit.
C’était un Espagnol de l’armée en déroute
Qui se traînait sanglant sur le bord de la route,
Râlant, brisé, livide, et mort plus qu’à moitié,
Et qui disait : « À boire ! à boire par pitié ! »
Mon père, ému, tendit à son housard fidèle
Une gourde de rhum qui pendait à sa selle,
Et dit : « Tiens, donne à boire à ce pauvre blessé. »
Tout à coup, au moment où le housard baissé
Se penchait vers lui, l’homme, une espèce de Maure,
Saisit un pistolet qu’il étreignait encore,
Et vise au front mon père en criant : « Caramba ! »
Le coup passa si près, que le chapeau tomba
Et que le cheval fit un écart en arrière.
« Donne-lui tout de même à boire », dit mon père.";

            // Le Dormeur du val (Arthur Rimbaud)
            // http://fr.wikisource.org/wiki/Le_Dormeur_du_val
            const string dormeurDuVal =
                @"C’est un trou de verdure où chante une rivière
Accrochant follement aux herbes des haillons
D’argent ; où le soleil, de la montagne fière,
Luit : c’est un petit val qui mousse de rayons.

Un soldat jeune, bouche ouverte, tête nue,
Et la nuque baignant dans le frais cresson bleu,
Dort ; il est étendu dans l’herbe, sous la nue,
Pâle dans son lit vert où la lumière pleut.

Les pieds dans les glaïeuls, il dort. Souriant comme
Sourirait un enfant malade, il fait un somme :
Nature, berce-le chaudement : il a froid.

Les parfums ne font pas frissonner sa narine ;
Il dort dans le soleil, la main sur sa poitrine
Tranquille. Il a deux trous rouges au côté droit.";

            List<List<string[]>> poems = new List<List<string[]>>();

            {
                string[] apresLaBatailleLines = apresLaBataille.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                List<string[]> lines = new List<string[]>();
                foreach (string line in apresLaBatailleLines)
                {
                    lines.Add(line.Split(new string[] { " " }, StringSplitOptions.None));
                }
                poems.Add(lines);
            }

            {
                string[] dormeurDuValLines = dormeurDuVal.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                List<string[]> lines = new List<string[]>();
                foreach (string line in dormeurDuValLines)
                {
                    lines.Add(line.Split(new string[] { " " }, StringSplitOptions.None));
                }
                poems.Add(lines);
            }

            {
                PptxSlide slideTemplate = pptx.GetSlide(0);
                List<PptxSlide> existingSlides = new List<PptxSlide>();

                PptxTable tableTemplate = slideTemplate.FindTables("{{table1}}").First();
                int rowsCountTemplate = tableTemplate.ColumnTitles().Count();

                PptxSlide prevSlide = slideTemplate;
                for (int i = 0; i < poems.Count; i++)
                {
                    PptxSlide slideTemplate2 = slideTemplate.Clone();
                    PptxSlide.InsertAfter(slideTemplate2, prevSlide);
                    slideTemplate2.ReplaceTag("{{title}}", i.ToString(), PptxSlide.ReplacementType.NoTable);

                    List<PptxTable.Cell[]> rows = new List<PptxTable.Cell[]>();

                    List<string[]> poem = poems[i];
                    foreach (string[] line in poem)
                    {
                        List<PptxTable.Cell> row = new List<PptxTable.Cell>();
                        for (int j = 0; j < rowsCountTemplate; j++)
                        {
                            PptxTable.Cell cell = new PptxTable.Cell("{{cell" + j + "}}", j < line.Length ? line[j] : string.Empty);
                            row.Add(cell);
                        }
                        rows.Add(row.ToArray());
                    }

                    var table = slideTemplate2.FindTables("{{table1}}").First();
                    var slidesCreated = Pptx.ReplaceTable_One(slideTemplate2, table, rows);
                    existingSlides.AddRange(slidesCreated);

                    PptxSlide lastInsertedSlide = existingSlides.Last();
                    prevSlide = lastInsertedSlide;

                    slideTemplate2.Remove();
                }

                slideTemplate.Remove();
            }

            pptx.Close();

            this.AssertPptxEquals(dstFileName, 6, "Col0 Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10 Col11 Col12 Col13 Mon père, ce héros au sourire si doux,       Suivi d’un seul housard qu’il aimait entre tous       Pour sa grande bravoure et pour sa haute taille,      Parcourait à cheval, le soir d’une bataille,        Le champ couvert de morts sur qui tombait la nuit.     Il lui sembla dans l’ombre entendre un faible bruit.      C’était un Espagnol de l’armée en déroute        0 Col0 Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10 Col11 Col12 Col13 Qui se traînait sanglant sur le bord de la route,     Râlant, brisé, livide, et mort plus qu’à moitié,       Et qui disait : « À boire ! à boire par pitié ! » Mon père, ému, tendit à son housard fidèle       Une gourde de rhum qui pendait à sa selle,      Et dit : « Tiens, donne à boire à ce pauvre blessé. »  Tout à coup, au moment où le housard baissé      0 Col0 Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10 Col11 Col12 Col13 Se penchait vers lui, l’homme, une espèce de Maure,      Saisit un pistolet qu’il étreignait encore,         Et vise au front mon père en criant : « Caramba ! »  Le coup passa si près, que le chapeau tomba      Et que le cheval fit un écart en arrière.      « Donne-lui tout de même à boire », dit mon père.    0 Col0 Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10 Col11 Col12 Col13 C’est un trou de verdure où chante une rivière      Accrochant follement aux herbes des haillons         D’argent ; où le soleil, de la montagne fière,      Luit : c’est un petit val qui mousse de rayons.                   Un soldat jeune, bouche ouverte, tête nue,        Et la nuque baignant dans le frais cresson bleu,      1 Col0 Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10 Col11 Col12 Col13 Dort ; il est étendu dans l’herbe, sous la nue,     Pâle dans son lit vert où la lumière pleut.                    Les pieds dans les glaïeuls, il dort. Souriant comme      Sourirait un enfant malade, il fait un somme :      Nature, berce-le chaudement : il a froid.                      1 Col0 Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 Col10 Col11 Col12 Col13 Les parfums ne font pas frissonner sa narine ;      Il dort dans le soleil, la main sur sa poitrine     Tranquille. Il a deux trous rouges au côté droit.      1 ");
        }

        [TestMethod]
        public void GetThumbnail()
        {
            string file = "../../files/GetTexts.pptx";
            const string thumbnail_default_png = "../../files/thumbnail_default.png";
            const string thumbnail_default_output_png = "../../files/thumbnail_default_output.png";
            const string thumbnail_128x96_png = "../../files/thumbnail_128x96.png";
            const string thumbnail_128x96_output_png = "../../files/thumbnail_128x96_output.png";
            const string thumbnail_512x384_png = "../../files/thumbnail_512x384.png";
            const string thumbnail_512x384_output_png = "../../files/thumbnail_512x384_output.png";

            Pptx pptx = new Pptx(file, FileAccess.Read);
            byte[] thumbnail_default_output = pptx.GetThumbnail(); // Default size
            File.WriteAllBytes(thumbnail_default_output_png, thumbnail_default_output);
            byte[] thumbnail_128x96_output = pptx.GetThumbnail(new Size(128, 96));
            File.WriteAllBytes(thumbnail_128x96_output_png, thumbnail_128x96_output);
            byte[] thumbnail_512x384_output = pptx.GetThumbnail(new Size(512, 384));
            File.WriteAllBytes(thumbnail_512x384_output_png, thumbnail_512x384_output);

            // Check the generated thumbnail are ok
            byte[] thumbnail_default = File.ReadAllBytes(thumbnail_default_png);
            CollectionAssert.AreEqual(thumbnail_default, thumbnail_default_output);
            byte[] thumbnail_128x96 = File.ReadAllBytes(thumbnail_128x96_png);
            CollectionAssert.AreEqual(thumbnail_128x96, thumbnail_128x96_output);
            byte[] thumbnail_512x384 = File.ReadAllBytes(thumbnail_512x384_png); // Will look blurry
            CollectionAssert.AreEqual(thumbnail_512x384, thumbnail_512x384_output);

            pptx.Close();

            // Test a 16/10 portrait PowerPoint file
            file = "../../files/portrait_16_10.pptx";
            const string thumbnail_portrait_16_10_png = "../../files/thumbnail_portrait_16_10.png";
            const string thumbnail_portrait_16_10_output_png = "../../files/thumbnail_portrait_16_10_output.png";

            pptx = new Pptx(file, FileAccess.Read);
            byte[] thumbnail_portrait_16_10_output = pptx.GetThumbnail(); // Default size
            File.WriteAllBytes(thumbnail_portrait_16_10_output_png, thumbnail_portrait_16_10_output);

            byte[] thumbnail_portrait_16_10 = File.ReadAllBytes(thumbnail_portrait_16_10_png);
            CollectionAssert.AreEqual(thumbnail_portrait_16_10, thumbnail_portrait_16_10_output);

            pptx.Close();
        }

        [TestMethod]
        public void RemoveSlides()
        {
            const string srcFileName = "../../files/RemoveSlides.pptx";
            const string dstFileName = "../../files/RemoveSlides_output.pptx";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);
            Assert.AreEqual(5, pptx.SlidesCount());
            pptx.GetSlide(1).Remove();
            Assert.AreEqual(4, pptx.SlidesCount());
            pptx.Close();

            pptx = new Pptx(dstFileName, FileAccess.ReadWrite);
            Assert.AreEqual(4, pptx.SlidesCount());
            pptx.GetSlide(0).Remove();
            pptx.GetSlide(2).Remove(); // 2 = 3 - the first slide removed
            Assert.AreEqual(2, pptx.SlidesCount());
            pptx.Close();

            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);
            pptx = new Pptx(dstFileName, FileAccess.ReadWrite);
            int nbSlides = pptx.SlidesCount();
            Assert.AreEqual(5, nbSlides);
            for (int i = nbSlides - 1; i >= 0; i--)
            {
                if (i == 0 || i == 2)
                {
                    pptx.GetSlide(i).Remove();
                }
            }
            Assert.AreEqual(3, pptx.SlidesCount());
            pptx.Close();
        }

        [TestMethod]
        public void ReplaceTablesAndPictures()
        {
            const string srcFileName = "../../files/ReplaceTablesAndPictures.pptx";
            const string dstFileName = "../../files/ReplaceTablesAndPictures_output.pptx";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

            List<PptxTable.Cell[]> rows = new List<PptxTable.Cell[]>
                {
                    new[] { new PptxTable.Cell("{{cell}}", "1") },
                    new[] { new PptxTable.Cell("{{cell}}", "2") },
                    new[] { new PptxTable.Cell("{{cell}}", "3") },
                    new[] { new PptxTable.Cell("{{cell}}", "4") },
                    new[] { new PptxTable.Cell("{{cell}}", "5") },
                    new[] { new PptxTable.Cell("{{cell}}", "6") }
                };

            PptxSlide slideTemplate = pptx.GetSlide(0);

            slideTemplate.ReplaceTag("{{cell}}", "Bonjour", PptxSlide.ReplacementType.NoTable);

            const string picture1_replace_png = "../../files/picture1_replace.png";
            const string picture1_replace_png_contentType = "image/png";
            slideTemplate.ReplacePicture("{{picture1}}", picture1_replace_png, picture1_replace_png_contentType);

            List<PptxSlide> existingSlides = new List<PptxSlide>();

            {
                var table = slideTemplate.FindTables("{{table1}}").First();
                var slidesCreated = Pptx.ReplaceTable_Multiple(slideTemplate, table, rows, existingSlides);
                existingSlides.AddRange(slidesCreated);
            }

            {
                var table = slideTemplate.FindTables("{{table2}}").First();
                var slidesCreated = Pptx.ReplaceTable_Multiple(slideTemplate, table, rows, existingSlides);
                existingSlides.AddRange(slidesCreated);
            }

            slideTemplate.Remove();

            pptx.Close();

            this.AssertPptxEquals(dstFileName, 2, "Table1 1 2 3 4 Table2 1 2 3 4 Bonjour Table1 5 6 Table2 5 6 Bonjour ");
        }

        [TestMethod]
        public void ReplaceLotsOfPicturesAndSlides()
        {
            const string srcFileName = "../../files/ReplaceLotsOfPicturesAndSlides.pptx";
            const string dstFileName = "../../files/ReplaceLotsOfPicturesAndSlides_output.pptx";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

            const string picture1_replace_png = "../../files/picture1_replace.png";
            const string picture1_replace_png_contentType = "image/png";
            const string picture1_replace_bmp = "../../files/picture1_replace.bmp";
            const string picture1_replace_bmp_contentType = "image/bmp";
            const string picture1_replace_jpeg = "../../files/picture1_replace.jpeg";
            const string picture1_replace_jpeg_contentType = "image/jpeg";
            byte[] picture1_replace_empty = new byte[] { };

            PptxSlide slideTemplate = pptx.FindSlides("{{LotsOfPictures}}").FirstOrDefault();

            int nbSlidesToGenerate = 100;

            for (int i = 0; i < nbSlidesToGenerate; i++)
            {
                PptxSlide slide = slideTemplate.Clone();
                PptxSlide.InsertAfter(slide, slideTemplate);

                slide.ReplacePicture("{{picture1png}}", picture1_replace_png, picture1_replace_png_contentType);
                slide.ReplacePicture("{{picture1bmp}}", picture1_replace_bmp, picture1_replace_bmp_contentType);
                slide.ReplacePicture("{{picture1jpeg}}", picture1_replace_jpeg, picture1_replace_jpeg_contentType);

                slide.ReplacePicture(null, picture1_replace_png, picture1_replace_png_contentType);
                slide.ReplacePicture("{{picture1null}}", picture1_replace_empty, picture1_replace_png_contentType);
                slide.ReplacePicture("{{picture1null}}", picture1_replace_png, null);
                slide.ReplacePicture("{{picture1null}}", picture1_replace_empty, null);
            }

            slideTemplate.Remove();

            pptx.Close();

            // Sorry, you will have to manually check that the pictures have been replaced
        }

        [TestMethod]
        public void RemoveInvalidXMLChars()
        {
            const string srcFileName = "../../files/ReplaceTags.pptx";
            const string dstFileName = "../../files/ReplaceTags_removeinvalidxmlchars.pptx";
            const string thumbnail_default_png = "../../files/111.png";
            File.Delete(dstFileName);
            File.Copy(srcFileName, dstFileName);

            Pptx pptx = new Pptx(dstFileName, FileAccess.ReadWrite);

            // See UTF-8 encoding table and Unicode characters http://www.utf8-chartable.de/
            // See Table of ASCII Characters http://web.cs.mun.ca/~michael/c/ascii-table.html

            const string sub = "\u001A";
            pptx.GetSlide(0).ReplaceTag("{{hello}}","TTTTT" + sub, PptxSlide.ReplacementType.Global);
            pptx.GetSlide(0).Color("0070C0", "TTTTT");
            //const string esc = "\u001B";
            //pptx.GetSlide(0).ReplaceTag("{{hello}}", "hexadecimal value 0x1B (ESC), is an invalid character: " + esc, PptxSlide.ReplacementType.Global);


       
            
            pptx.Close();
        }
    }
}
View Code

 

posted on 2021-08-06 15:47  水。  阅读(177)  评论(0编辑  收藏  举报