是的,您在 SDK 中发现了一个错误。
@Chris,首先,根据 Open XML SDK 的语义,该代码正在修改文件。当您访问部件的内容,然后超出 using 语句的范围时,部件的内容将被写回到包中。这是因为演示文稿是为读/写而打开的(调用 Open 方法的第二个参数)。
问题是当从包中读取部件的内容时,空间被剥夺了。
//Open the document.
using (PresentationDocument presentationDocument = PresentationDocument.Open("test.pptx", true))
{
//Just making this reference modifies the whitespace in the slide.
Slide slide = presentationDocument.PresentationPart.SlideParts.First().Slide;
var sh = slide.CommonSlideData.ShapeTree.Elements<DocumentFormat.OpenXml.Presentation.Shape>().First();
Run r = sh.TextBody.Elements<Paragraph>().First().Elements<Run>().Skip(1).FirstOrDefault();
Console.WriteLine(">{0}<", r.Text.Text);
//r.Text.Text = " ";
}
如果您在演示文稿上运行上述代码,您可以看到当您访问该文本元素时,该文本元素的文本已经不正确。
如果取消注释设置文本的行,有趣的是,幻灯片确实包含空格。
这显然是一个错误。我已将其报告给负责 Open XML SDK 的 Microsoft 项目经理。
由于这种情况对您很重要,因此我建议您将 LINQ to XML 用于您的代码。以下代码工作正常:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;
using DocumentFormat.OpenXml.Drawing;
public static class PtOpenXmlExtensions
{
public static XDocument GetXDocument(this OpenXmlPart part)
{
XDocument partXDocument = part.Annotation<XDocument>();
if (partXDocument != null)
return partXDocument;
using (Stream partStream = part.GetStream())
using (XmlReader partXmlReader = XmlReader.Create(partStream))
partXDocument = XDocument.Load(partXmlReader);
part.AddAnnotation(partXDocument);
return partXDocument;
}
public static void PutXDocument(this OpenXmlPart part)
{
XDocument partXDocument = part.GetXDocument();
if (partXDocument != null)
{
using (Stream partStream = part.GetStream(FileMode.Create, FileAccess.Write))
using (XmlWriter partXmlWriter = XmlWriter.Create(partStream))
partXDocument.Save(partXmlWriter);
}
}
}
class Program
{
static void Main(string[] args)
{
using (PresentationDocument presentationDocument = PresentationDocument.Open("test.pptx", true))
{
XDocument slideXDoc = presentationDocument.PresentationPart.SlideParts.First().GetXDocument();
XNamespace p = "http://schemas.openxmlformats.org/presentationml/2006/main";
XNamespace a = "http://schemas.openxmlformats.org/drawingml/2006/main";
XElement sh = slideXDoc.Root.Element(p + "cSld").Element(p + "spTree").Elements(p + "sp").First();
XElement r = sh.Element(p + "txBody").Elements(a + "p").Elements(a + "r").Skip(1).FirstOrDefault();
Console.WriteLine(">{0}<", r.Element(a + "t").Value);
}
}
}
理论上,您可以编写一些通用代码来挖掘 LINQ to XML 树,找到仅包含重要空白的所有元素,然后遍历 Open XML SDK 元素树,并设置这些元素的文本。这有点乱,但是一旦完成,您就可以使用 Open XML SDK 2.0 的强类型 OM。这些元素的值将是正确的。
一种使使用 Open XML 更容易使用 LINQ to XML 的技术是预原子化 XName 对象。请参阅http://blogs.msdn.com/b/ericwhite/archive/2008/12/15/a-more-robust-approach-for-handling-xname-objects-in-linq-to-xml.aspx
-埃里克