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

时间:2015-08-18 15:06:34

标签: c# openxml openxml-sdk

我使用此[https://codingthedocument.wordpress.com/author/agschwantes/]在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();
    }

}

2 个答案:

答案 0 :(得分:2)

我认为你应该省略复制图像部分的[ValidateSet("v100", "v110", "v120", "v140")] 循环。 当我通常从幻灯片管理员复制幻灯片时,我使用的是您正在使用的类似代码设置,但没有foreach。 然后它从slidemaster部分复制给定的幻灯片,包括所有图像,布局等。

我在我的一个项目中使用的代码如下所示(foreach调用是外部方法,我使用SlideMasterPart中的硬编码位置而不是SetTitle(string)基于1}}的布局名称。

string

答案 1 :(得分:2)

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

该行

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在创建基于主模板的新幻灯片后假定开发人员希望更改某些占位符内容