【问题标题】:C# - OpenXML SDK 2.5 - Insert New Slide from Slide Masters with the layout that contains imagesC# - OpenXML SDK 2.5 - 从幻灯片母版插入新幻灯片,其布局包含图像
【发布时间】:2015-08-18 15:06:34
【问题描述】:

我使用this 在 OpenXML 2.5 SDK 的帮助下创建了我的新幻灯片。 我设计并使用了自己的幻灯片母版来创建一张新幻灯片。我的幻灯片母版包括一些带图像的布局和一些不带图像的布局。

如果我从没有图像的主布局创建幻灯片,一切正常。如果我使用包含图像的布局创建幻灯片,我会得到正确的布局但是在每个固定图像之上还有另一个可移动图像与固定图像重叠,因此固定图像存在不必要的重复,我不需要在我新创建的幻灯片中。

我该如何解决这个问题?

我的代码如下:

       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();
    }

}

【问题讨论】:

    标签: c# openxml openxml-sdk


    【解决方案1】:

    多年后,但为了帮助其他人 - 无需将不提供占位符功能的节点复制到基于主幻灯片模板(包括图像)的新幻灯片。

    线

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

    显然假设您没有在 1 个工作流中添加幻灯片(即您有单独的读取和写入流),以及等效的行

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

    这两行代码都将所有主数据作为叠加层复制到新幻灯片上。例如,运行上面答案中的代码块之一,在演示管理器中打开生成的 .pptx 文件并删除您看到的任何形状 - 您会注意到每个项目都位于相同副本的顶部(母版幻灯片版本),您无法删除。因此,此方法不必要地使文件膨胀,并使 .pptx 的工作变得混乱,并且不像最终用户所预期的那样。

    下面的代码可以处理图片

    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>();
            }
        }
    }
    

    out 参数 placeholderShapes 在基于主模板创建新幻灯片后假设开发人员希望更改一些占位符内容

    【讨论】:

    • 形状复制也让我感到困惑。奇怪的是,当我运行您的代码时,图像占位符不会重复,但文本占位符是重复的。知道为什么吗?我还注意到,如果我删除您返回的集合中的占位符,则占位符和重复的形状都会被删除。
    • 有没有办法让这种方法适应现有的幻灯片?例如,我是否可以从 PowerPoint 演示文稿中获取现有幻灯片,直接将其幻灯片母版布局应用于幻灯片,然后将其复制到新演示文稿中没有相应的幻灯片大师?
    【解决方案2】:

    我认为您应该省略复制图像部分的foreach 循环。 当我通常从幻灯片母版复制幻灯片时,我使用您正在使用的类似代码设置,但没有foreach。 然后它从幻灯片母版部分复制给定的幻灯片,包括所有图像、布局等。

    下面列出了我在我的一个项目中使用的代码(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();
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2014-10-19
      • 1970-01-01
      • 2017-12-31
      • 1970-01-01
      • 1970-01-01
      • 2011-08-23
      相关资源
      最近更新 更多