簡體   English   中英

將 OLE 對象從一張幻燈片復制到另一張幻燈片會損壞生成的 PowerPoint

[英]Copying OLE Objects from one slide to another corrupts the resulting PowerPoint

我有將一張 PowerPoint 幻燈片的內容復制到另一張幻燈片的代碼。 以下是如何處理圖像的示例。

foreach (OpenXmlElement element in sourceSlide.CommonSlideData.ShapeTree.ChildElements.ToList())
{
    string elementType = element.GetType().ToString();

    if (elementType.EndsWith(".Picture"))
    {
        // Deep clone the element.
        elementClone = element.CloneNode(true);
        var picture = (Picture)elementClone;

        // Get the picture's original rId
        var blip = picture.BlipFill.Blip;
        string rId = blip.Embed.Value;
        
        // Retrieve the ImagePart from the original slide by rId
        ImagePart sourceImagePart = (ImagePart)sourceSlide.SlidePart.GetPartById(rId);

        // Add the image part to the new slide, letting OpenXml generate the new rId
        ImagePart targetImagePart = targetSlidePart.AddImagePart(sourceImagePart.ContentType);

        // And copy the image data.
        targetImagePart.FeedData(sourceImagePart.GetStream());

        // Retrieve the new ID from the target image part,
        string id = targetSlidePart.GetIdOfPart(targetImagePart);

        // and assign it to the picture.
        blip.Embed.Value = id;

        // Get the shape tree that we're adding the clone to and append to it.
        ShapeTree shapeTree = targetSlide.CommonSlideData.ShapeTree;
        shapeTree.Append(elementClone);
    }

此代碼工作正常。 對於Graphic Frames這樣的其他場景,它看起來有點不同,因為每個graphic frame可以包含多個圖片對象。

// Go thru all the Picture objects in this GraphicFrame.
foreach (var sourcePicture in element.Descendants<Picture>())
{
    string rId = sourcePicture.BlipFill.Blip.Embed.Value;
    ImagePart sourceImagePart = (ImagePart)sourceSlide.SlidePart.GetPartById(rId);
    var contentType = sourceImagePart.ContentType;

    var targetPicture = elementClone.Descendants<Picture>().First(x => x.BlipFill.Blip.Embed.Value == rId);
    var targetBlip = targetPicture.BlipFill.Blip;

    ImagePart targetImagePart = targetSlidePart.AddImagePart(contentType);
    targetImagePart.FeedData(sourceImagePart.GetStream());
    string id = targetSlidePart.GetIdOfPart(targetImagePart);
    targetBlip.Embed.Value = id;
}

現在我需要對 OLE 對象做同樣的事情。

// Go thru all the embedded objects in this GraphicFrame.
foreach (var oleObject in element.Descendants<OleObject>())
{
    // Get the rId of the embedded OLE object.
    string rId = oleObject.Id;

    // Get the EmbeddedPart from the source slide.
    var embeddedOleObj = sourceSlide.SlidePart.GetPartById(rId);

    // Get the content type.
    var contentType = embeddedOleObj.ContentType;

    // Create the Target Part.  Let OpenXML assign an rId.
    var targetObjectPart = targetSlide.SlidePart.AddNewPart<EmbeddedObjectPart>(contentType, null);

    // Get the embedded OLE object data from the original object.
    var objectStream = embeddedOleObj.GetStream();

    // And give it to the ObjectPart.
    targetObjectPart.FeedData(objectStream);

    // Get the new rId and assign it to the OLE Object.
    string id = targetSlidePart.GetIdOfPart(targetObjectPart);
    oleObject.Id = id;
}

但它沒有用。 生成的 PowerPoint 已損壞。

我究竟做錯了什么?


注意:除了 OLE Object 中的rId處理之外,所有代碼都有效。我知道它有效,因為如果我只是將原始rId從源 object 傳遞到目標 Object 部分,如下所示:

var targetObjectPart = targetSlide.SlidePart
   .AddNewPart<EmbeddedObjectPart>(contentType, rId);

它會正確地 function,只要該rId不存在於目標幻燈片中,這顯然不會像我需要的那樣每次都有效。

源幻燈片和目標幻燈片來自不同的 PPTX 文件。 我們使用的是 OpenXML,而不是 Office Interop。

由於您沒有提供完整的代碼,因此很難判斷哪里出了問題。
我的猜測是您沒有修改正確的 object。

在圖片的代碼示例中,您正在創建和修改elementClone
在您的 ole 對象的代碼示例中,您正在使用和修改oleObject (它是element的后代)並且從上下文中並不清楚它是源文檔還是目標文檔的一部分。


你可以試試這個最小的例子:

  • 使用帶有一個嵌入 ole object 的新 pptx 用於c:\testdata\input.pptx
  • c:\testdata\output.pptx

運行代碼后,我能夠打開 output 文檔中嵌入的 ole object。

using DocumentFormat.OpenXml.Presentation;
using DocumentFormat.OpenXml.Packaging;
using System.Linq;

namespace ooxml
{
    class Program
    {
        static void Main(string[] args)
        {            
            CopyOle("c:\\testdata\\input.pptx", "c:\\testdata\\output.pptx");
        }

        private static void CopyOle(string inputFile, string outputFile)
        {
            using (PresentationDocument sourceDocument = PresentationDocument.Open(inputFile, true))
            {
                using (PresentationDocument targetDocument = PresentationDocument.Open(outputFile, true))
                {
                    var sourceSlidePart = sourceDocument.PresentationPart.SlideParts.First();
                    var targetSlidePart = targetDocument.PresentationPart.SlideParts.First();

                    
                    foreach (var element in sourceSlidePart.Slide.CommonSlideData.ShapeTree.ChildElements)
                    {
                        //clones an element, does not copy the actual relationship target (e.g. ppt\embeddings\oleObject1.bin)
                        var elementClone = element.CloneNode(true);                      
                        
                        //for each cloned OleObject, fix its relationship
                        foreach(var clonedOleObject in elementClone.Descendants<OleObject>())
                        {
                            //find the original EmbeddedObjectPart in the source document
                            //(we can use the id from the clonedOleObject to do that, since it contained the same id
                            // as the source ole object)
                            var sourceObjectPart = sourceSlidePart.GetPartById(clonedOleObject.Id);

                            //create a new EmbeddedObjectPart in the target document and copy the data from the original EmbeddedObjectPart
                            var targetObjectPart = targetSlidePart.AddEmbeddedObjectPart(sourceObjectPart.ContentType);
                            targetObjectPart.FeedData(sourceObjectPart.GetStream());

                            //update the relationship target on the clonedOleObject to point to the newly created EmbeddedObjectPath
                            clonedOleObject.Id = targetSlidePart.GetIdOfPart(targetObjectPart);
                        }

                        //add cloned element to the document
                        targetSlidePart.Slide.CommonSlideData.ShapeTree.Append(elementClone);
                    }
                    targetDocument.PresentationPart.Presentation.Save();
                }
            }
        }
    }
}

至於故障排除, OOXML 工具chrome 擴展很有幫助。
它允許比較兩個文檔的結構,因此更容易分析哪里出了問題。

例子:

  • 如果您只克隆所有元素,您會看到 /ppt/embeddings/* 和 /ppt/media/* 會丟失在此處輸入圖像描述
  • 或者您可以檢查關系是否正確(例如,輸入文檔使用“rId1”來引用嵌入數據,而 output 文檔使用“R3a2fa0c37eaa42b5”) 在此處輸入圖像描述

    在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM