Пробел в Powerpoint OpenXML исчезает - PullRequest
       105

Пробел в Powerpoint OpenXML исчезает

4 голосов
/ 23 августа 2011

Я сталкиваюсь с проблемой, когда в документах 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 в текстовых элементах можно установить «preserve» для сохранения пробелов, но, похоже, что для Powerpoint нет эквивалента.

Это критическая проблема в ситуациях, когда пробел используется в качестве ключевого компонента дизайна слайдов. Кто-нибудь нашел решение этой проблемы?

Ответы [ 2 ]

6 голосов
/ 25 августа 2011

Да, вы нашли ошибку в 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

-Эрик

2 голосов
/ 20 мая 2013

Open XML SDK 2.5 исправил эту проблему

...