简体   繁体   English

C# - OpenXML SDK 2.5 - 从包含图像的布局插入来自幻灯片母版的新幻灯片

[英]C# - OpenXML SDK 2.5 - Insert New Slide from Slide Masters with the layout that contains images

I used this to create my new slides with the help of the OpenXML 2.5 SDK.我用在 OpenXML 2.5 SDK 的帮助下创建了我的新幻灯片。 I designed and used my own Slide Master to create a new slide.我设计并使用我自己的幻灯片母版来创建一个新幻灯片。 My Slide Master includes some layouts with images and some layouts without images.我的幻灯片母版包括一些带图像的布局和一些不带图像的布局。

If I create a slide from my Master Layout without images, everything works fine.如果我从没有图像的主布局创建幻灯片,一切正常。 If I create a slide with the layout, that contains images, I get the right layout BUT on top of every fixed images there is another movable image overlapping the fixed one, so there are unnecessary duplicates of fixed images, that I don't need in my new created slide.如果我创建一个带有布局的幻灯片,其中包含图像,我会得到正确的布局,但是在每个固定图像之上,还有另一个可移动图像与固定图像重叠,因此固定图像有不必要的重复,我不需要在我新创建的幻灯片中。

How can I solve this problem?我怎么解决这个问题?

My code is below:我的代码如下:

       public static void InsertNewSlide(string presentationFile, int position, string layoutName)
      {
        using (PresentationDocument presentationDocument = PresentationDocument.Open(presentationFile, true))
        {

            InsertNewSlide(presentationDocument, position, layoutName);
        }
      }

    public static void InsertNewSlide(PresentationDocument presentationDocument, int position, string layoutName)
    {
        PresentationPart presentationPart = presentationDocument.PresentationPart;

        OpenXML.Slide slide = new OpenXML.Slide(new CommonSlideData(new ShapeTree()));

        SlidePart slidePart = presentationPart.AddNewPart<SlidePart>();

        slide.Save(slidePart);

        SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.First();

        SlideLayoutPart slideLayoutPart = slideMasterPart.SlideLayoutParts.SingleOrDefault(sl => sl.SlideLayout.CommonSlideData.Name.Value.Equals(layoutName, StringComparison.OrdinalIgnoreCase));

        slidePart.AddPart<SlideLayoutPart>(slideLayoutPart);

        slidePart.Slide.CommonSlideData = (CommonSlideData)slideMasterPart.SlideLayoutParts.SingleOrDefault(sl => sl.SlideLayout.CommonSlideData.Name.Value.Equals(layoutName)).SlideLayout.CommonSlideData.Clone();

        using (Stream stream = slideLayoutPart.GetStream())
        {
            slidePart.SlideLayoutPart.FeedData(stream);

        }

        foreach (ImagePart iPart in slideLayoutPart.ImageParts)
        {
             ImagePart newImagePart = slidePart.AddImagePart(iPart.ContentType, slideLayoutPart.GetIdOfPart(iPart));
                                                        newImagePart.FeedData(iPart.GetStream());
        }
        
        uint maxSlideId = 1;
        SlideId prevSlideId = null;
        var slideIdList = presentationPart.Presentation.SlideIdList;
        foreach (SlideId slideId in slideIdList.ChildElements)
        {
            if (slideId.Id > maxSlideId)
            {
                maxSlideId = slideId.Id;
            }

            position--;
            if (position == 0)
            {
                prevSlideId = slideId;
            }

        }
        maxSlideId++;
        SlideId newSlideId = slideIdList.InsertAfter(new SlideId(), prevSlideId);
        newSlideId.Id = maxSlideId;
        newSlideId.RelationshipId = presentationPart.GetIdOfPart(slidePart);

        presentationPart.Presentation.Save();
    }

}

Years later, but to help others - there is no need to copy nodes which are not serving a placeholder function to the new slide being based on the master slide template (including images).多年后,但为了帮助他人 - 无需将不提供占位符功能的节点复制到基于主幻灯片模板(包括图像)的新幻灯片。

The line线

using (Stream stream = slideLayoutPart.GetStream())
{
    slidePart.SlideLayoutPart.FeedData(stream);

obviously assumes you are not adding a slide within 1 working stream (that is you have separate streams for reading and writing), and the equivalent line显然假设您没有在 1 个工作流中添加幻灯片(即您有单独的读取和写入流),以及等效的行

slidePart.Slide.CommonSlideData = (CommonSlideData)layoutPart.SlideLayout.CommonSlideData.Clone();

Both of these lines of code copy all the master data as an overlay onto the new slide .这两行代码将所有主数据复制到新幻灯片上。 As an example, run one of the blocks of code from the answers above, open the resulting .pptx file in a presentation manager and delete whatever shapes you see - you will note each item is sitting on top of an identical copy (the master slide version) which you cannot delete.例如,运行上面答案中的一个代码块,在演示管理器中打开生成的 .pptx 文件并删除您看到的任何形状 - 您会注意到每个项目都位于相同副本(母版幻灯片版本),您无法删除。 This method therefore bloats the file unnecessarily and makes working with the .pptx messy and not as predicted for the end user.因此,这种方法会不必要地使文件膨胀,并使 .pptx 的处理变得混乱,并且不像最终用户所预测的那样。

the code below is working including with images下面的代码正在工作,包括图像

public static SlidePart AppendNewSlide(PresentationPart presentationPart, SlideLayoutPart masterLayoutPart, out IEnumerable<Shape> placeholderShapes)
{
    Slide clonedSlide = new Slide() {
        ColorMapOverride = new ColorMapOverride {
            MasterColorMapping = new Draw.MasterColorMapping()
        }
    };

    SlidePart clonedSlidePart = presentationPart.AddNewPart<SlidePart>();
    clonedSlidePart.Slide = clonedSlide;
    clonedSlidePart.AddPart(masterLayoutPart);
    clonedSlide.Save(clonedSlidePart);

    var masterShapeTree = masterLayoutPart.SlideLayout.CommonSlideData.ShapeTree;

    placeholderShapes = (from s in masterShapeTree.ChildElements<Shape>()
                           where s.NonVisualShapeProperties.OfType<ApplicationNonVisualDrawingProperties>().Any(anvdp=>anvdp.PlaceholderShape != null)
                           select new Shape()
                           {
                               NonVisualShapeProperties = (NonVisualShapeProperties)s.NonVisualShapeProperties.CloneNode(true),
                               TextBody = new TextBody(s.TextBody.ChildElements<Draw.Paragraph>().Select(p => p.CloneNode(true))) {
                                   BodyProperties = new Draw.BodyProperties(),
                                   ListStyle = new Draw.ListStyle()
                               },
                               ShapeProperties = new ShapeProperties()
                           }).ToList();

    clonedSlide.CommonSlideData = new CommonSlideData
    {
        ShapeTree = new ShapeTree(placeholderShapes) {
            GroupShapeProperties = (GroupShapeProperties)masterShapeTree.GroupShapeProperties.CloneNode(true),
            NonVisualGroupShapeProperties = (NonVisualGroupShapeProperties)masterShapeTree.NonVisualGroupShapeProperties.CloneNode(true)
        }
    };

    SlideIdList slideIdList = presentationPart.Presentation.SlideIdList;

    // Find the highest slide ID in the current list.
    uint maxSlideId = slideIdList.Max(c=>(uint?)((SlideId)c).Id) ?? 256;

    // Insert the new slide into the slide list after the previous slide.
    slideIdList.Append(new SlideId() {
        Id = ++maxSlideId,
        RelationshipId = presentationPart.GetIdOfPart(clonedSlidePart)
    });
    //presentationPart.Presentation.Save();

    return clonedSlidePart;
}

//helper method used above in separate static class
public static IEnumerable<T> ChildElements<T>(this OpenXmlElement el) where T: OpenXmlElement
{
    if (el.HasChildren)
    {
        var child = el.GetFirstChild<T>();
        while (child != null)
        {
            yield return child;
            child = child.NextSibling<T>();
        }
    }
}

the out parameter placeholderShapes is assuming after creating a new slide based on a master template that the developer will wish to alter some of the placeholder content out 参数 placeholderShapes 假设在基于主模板创建新幻灯片后,开发人员希望更改某些占位符内容

I think you should leave out the foreach loop that copies the image parts.我认为您应该省略复制图像部分的foreach循环。 When I normally copy slides from a slidemaster I use a similar code setup that you are using, but without the foreach .当我通常从幻灯片母版复制幻灯片时,我使用您正在使用的类似代码设置,但没有foreach It then copies the given slide from the slidemaster part including all images, layout, etc.然后它从幻灯片母版部分复制给定的幻灯片,包括所有图像、布局等。

The code I use in one of my projects is listed below (the SetTitle(string) call is to an external method, and I'm using a hard-coded position in the SlideMasterPart as opposed to your string based layout name.下面列出了我在我的一个项目中使用的代码( SetTitle(string)调用是对外部方法的调用,我在 SlideMasterPart 中使用硬编码位置,而不是基于string的布局名称。

public static void InsertNewSlideB(PresentationDocument presentationDocument, int position, string slideTitle)
{
    if (presentationDocument == null)
    {
        throw new ArgumentNullException("presentationDocument");
    }

    if (slideTitle == null)
    {
       throw new ArgumentNullException("slideTitle");
    }

    PresentationPart presentationPart = presentationDocument.PresentationPart;

    // Verify that the presentation is not empty.
    if (presentationPart == null)
    {
        throw new InvalidOperationException("The presentation document is empty.");
    }

    // Declare and instantiate a new slide.
    Slide slide = new Slide(new CommonSlideData(new ShapeTree()));

    SlidePart slidePart = presentationPart.AddNewPart<SlidePart>();
    slide.Save(slidePart);

    SlideLayoutPart layoutPart = presentationPart.SlideMasterParts.ElementAt(0).SlideLayoutParts.ElementAt(1);
    slidePart.AddPart<SlideLayoutPart>(layoutPart);

    slidePart.Slide.CommonSlideData = (CommonSlideData)layoutPart.SlideLayout.CommonSlideData.Clone();

    SetTitle(slidePart, slideTitle);

    SlideIdList slideIdList = presentationPart.Presentation.SlideIdList;

    // Find the highest slide ID in the current list.
    uint maxSlideId = 1;
    SlideId prevSlideId = null;

    foreach (SlideId slideId in slideIdList.ChildElements)
     {
        if (slideId.Id > maxSlideId)
        {
            maxSlideId = slideId.Id;
        }

        position--;
        if (position == 0)
        {
            prevSlideId = slideId;
        }
    }

    maxSlideId++;

    // Insert the new slide into the slide list after the previous slide.
    SlideId newSlideId = slideIdList.InsertAfter(new SlideId(), prevSlideId);
    newSlideId.Id = maxSlideId;
    newSlideId.RelationshipId = presentationPart.GetIdOfPart(slidePart);

    // Save the modified presentation.
    presentationPart.Presentation.Save();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用 OpenXML SDK 从 Powerpoint 演示文稿的主幻灯片中删除所有未使用的幻灯片布局 - Remove all unused Slide Layout from my Master Slide from Powerpoint Presentation using OpenXML SDK C#如何使用openxml sdk v 2.5将框架插入文字处理文档 - C# How to insert a frame into a wordprocessingdocument using the openxml sdk v 2.5 C# 复制 powerpoint 母版幻灯片布局 - C# Copy powerpoint Master Slide layout 如何使用OpenXML在powerpoint幻灯片中插入形状 - How to insert a shape in a powerpoint slide using OpenXML 使用 OpenXML 将包含注释的幻灯片从一个 PowerPoint 演示文稿复制到另一个 SDK - Copy slide containing notes from one PowerPoint presentaition to another with OpenXML SDK 如何使用 OpenXML SDK 在 Powerpoint 幻灯片中隐藏文本? - How to hide a text inside a Powerpoint slide using OpenXML SDK? C#如何使用OpenXmlWriter(OpenXML SDK 2.5)将单元格附加到Excel工作表中的每一行 - C# How do I append a Cell to every Row in a Excel Sheet using OpenXmlWriter (OpenXML SDK 2.5) 在幻灯片扩展程序Asp.net C#上显示数据库中的图像 - Show images from database on slide show extender Asp.net C# 在C#中将图像集转换为视频幻灯片放映 - Convert set of images to video slide show in C# 如何使用OpenXml在.pptx文件的末尾添加新的空白幻灯片? - How to add new - blank slide at the end of .pptx file using OpenXml?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM