4

我遇到了一个问题,即当我引用幻灯片时,PowerPoint 文档中的空格就会被删除。以下代码示例说明了我的意思-

//Open the document.
using(PresentationDocument presentationDocument = PresentationDocument.Open(pptxFileName, true))
{
 //Just making this reference modifies the whitespace in the slide.
 Slide slide = presentationDocument.PresentationPart.SlideParts.First().Slide;
}

要重现此问题,请使用一张幻灯片创建一个演示文稿,其中包含一个文本框,其中包含文本“[ ]”(无引号)。现在,将方括号之间空格的字体设置为与文本其余部分不同的颜色。这将导致 Run 仅包含空白字符。一旦上面的代码针对这个演示文稿运行,引用幻灯片的行将导致运行中的空白消失,最终给我们留下一个比我们最初开始时视觉上改变的演示文稿,即使我们从未明确地改变任何东西——在 powerpoint 应用程序中打开时,文本现在将是“[]”。

在 Word 中,可以在文本元素上将 xml:space 属性设置为“保留”以保留空格,但 Powerpoint 似乎没有等效属性。

在空白被用作幻灯片设计的关键组成部分的情况下,这是一个关键问题。有没有人想出解决这个问题的方法?

4

2 回答 2

7

是的,您在 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

-埃里克

于 2011-08-25T06:13:10.080 回答
2

Open XML SDK 2.5 已更正此问题

于 2013-05-20T04:55:16.133 回答