问题描述
我有将一张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);
}
此代码可以正常工作。对于其他情况,例如“图形框架”,它看起来有点不同,因为每个图形框架可以包含多个图片对象。
// 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对象中的rId
处理外,所有代码均有效。我知道它是有效的,因为如果我只是将原始rId
从源对象传递到目标对象部分,就像这样:
var targetobjectPart = targetSlide.SlidePart
.AddNewPart<EmbeddedobjectPart>(contentType,rId);
只要目标幻灯片中没有rId
,它就会正常运行, 显然,它每次都无法像我需要的那样工作。
源幻灯片和目标幻灯片来自不同的PPTX文件。我们使用的是OpenXML,而不是Office Interop。
解决方法
由于您没有提供完整的代码,因此很难判断出什么问题。
我的猜测是您没有修改正确的对象。
在用于图片的代码示例中,您正在创建和修改elementClone
。
在ole对象的代码示例中,您正在使用和修改oleObject
(它是element
的后代),并且从上下文中还不清楚它是否是源文档的一部分。或目标文档。
您可以尝试以下最小示例:
- 为
c:\testdata\input.pptx
使用带有一个嵌入式ole对象的新pptx - 为
c:\testdata\output.pptx
使用新的pptx(空白)
运行代码后,我能够在输出文档中打开嵌入的ole对象。
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 Tools chrome扩展名很有帮助。
它可以比较两个文档的结构,因此更容易分析出了什么问题。
示例: