Да, вы нашли ошибку в SDK.
@ Крис, прежде всего, этот код, в соответствии с семантикой 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 = " ";
}
Если вы запустите приведенный выше код в презентации, вы увидите, что к тому времени, когда вы получите доступ к этому текстовому элементу, текст текстового элемента уже будет неправильным.
Если вы раскомментируете строку, которая устанавливает текст, что интересно, слайд содержит пробел.
Это явно ошибка. Я сообщил об этом руководителю программы в Microsoft, который отвечает за Open XML SDK.
Поскольку этот сценарий важен для вас, я рекомендую вам использовать 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 и установить текст этих элементов. Это немного путаница, но когда вы это сделаете, вы можете использовать строго типизированный OM Open XML SDK 2.0. Значения таких элементов были бы тогда правильными.
Одним из методов, упрощающих использование LINQ to XML с Open 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
-Эрик