Как получить диаграмму (или изображение) в памяти в документе OpenXML в памяти? - PullRequest
4 голосов
/ 02 апреля 2012

Мне приснился кошмар, когда я пытался добавить Chart в MemoryStream в памяти.

Я создаю документ Word на лету, используя OpenXML, и у меня есть диаграмма, которая также динамически генерируется из данных в базе данных.

Я получаю шаблон из базы данных в виде байтового массива, передавая его в метод, который также принимает бизнес-объект, который содержит набор данных для заполнения закладок, содержащихся в этом шаблоне.

Вот метод:

public Stream Parse(byte[] array, AudiometryReport AudReport)
{

    using (MemoryStream Stream = new MemoryStream())
    {
        Stream.Write(array, 0, (int)array.Length);
        Stream.Position = 0;

        using (document = WordprocessingDocument.Open(Stream, true))
        {
            XDocument doc = document.MainDocumentPart.GetXDocument();

            List<XElement> bookmarks = doc.Descendants()
                .Where(n => n.NodeType == XmlNodeType.Element && n.Name.LocalName == "bookmarkStart")
                .ToList();

            PropertyInfo[] reportInfo = AudReport.GetType().GetProperties();

            foreach (XElement bm in bookmarks)
            {
                try
                {

                    if (bm.LastAttribute.Value == "AudiometryChart")
                    {
                        string partId = InsertImage(document.MainDocumentPart);

                        var element = AddImageToDocument(document.MainDocumentPart, partId);
                        //var element = InsertImageXElement(partId);

                        //bm.ReplaceWith(new XElement(w + "r", element));
                    }
                    else
                    {
                        string val = reportInfo.Single(x => x.Name == bm.LastAttribute.Value).GetValue(AudReport, null).ToString();

                        bm.ReplaceWith(new XElement(w + "r",
                                new XElement(w + "t", val)));
                    }
                }
                catch
                { }
            }

            document.MainDocumentPart.PutXDocument();
            //foreach (BookmarkStart bm in (IEnumerable<BookmarkStart>)document.MainDocumentPart.Document.Descendants<BookmarkStart>())
            //{
            //    if (bm.Name == "AudiometryChart")
            //    {
            //        // Insert the chart object here.
            //        //AddImage(document);
            //    }
            //    populateStaffDetails(AudReport.Report.Employee, bm);
            //    populateAudiometryDetails(AudReport, bm);
            //}

        }


        MemoryStream s = new MemoryStream();
        Stream.WriteTo(s);
        s.Position = 0;
        return s;
    }

}

Изображение InsertImage берет MainDocumentPart и присоединяет новый ImagePart из изображения, которое я передаю из базы данных. Я передаю идентификатор этой части обратно вызывающему методу.

private string InsertImage(MainDocumentPart docPart)
{
    //DrawingsPart dp = docPart.AddNewPart<DrawingsPart>();
    //ImagePart part = dp.AddImagePart(ImagePartType.Png, docPart.GetIdOfPart(dp));
    ImagePart part = docPart.AddImagePart(ImagePartType.Png);
    Chart cht = new ChartBuilder().DoChart(Data, new string[] { "Left", "Right", "Normal" });

    using (MemoryStream ms = new MemoryStream())
    {
        cht.SaveImage(ms, ChartImageFormat.Png);
        ms.Position = 0;

        part.FeedData(ms);
    }

    //int count = dp.ImageParts.Count<ImagePart>();
    int count = docPart.ImageParts.Count<ImagePart>();


    return docPart.GetIdOfPart(part);
}

Последняя часть - это серьезная неприятность, которая, как утверждается, требуется добавить одно изображение в одно слово документа, но какого черта - вот оно, в любом случае:

private Run AddImageToDocument(MainDocumentPart docPart, string ImageRelId)
{
    string ImageFileName = "Audiometry Chart Example";
    string GraphicDataUri = "http://schemas.openxmlformats.org/drawingml/2006/picture";

    long imageLength = 990000L;
    long imageHeight = 792000L;

    var run = new Run(
        new Drawing(
            new wp.Inline(

                new wp.Extent() { Cx = imageLength, Cy = imageHeight },

                new wp.EffectExtent()
                {
                    LeftEdge = 19050L,
                    TopEdge = 0L,
                    RightEdge = 9525L,
                    BottomEdge = 0L
                },

                new wp.DocProperties()
                {
                    Id = (UInt32Value)1U,
                    Name = "Inline Text Wrapping Picture",
                    Description = ImageFileName
                },

                new wp.NonVisualGraphicFrameDrawingProperties(
                    new a.GraphicFrameLocks() { NoChangeAspect = true }),

                new a.Graphic(
                    new a.GraphicData(
                        new pic.Picture(

                            new pic.NonVisualPictureProperties(
                                new pic.NonVisualDrawingProperties() { Id = (UInt32Value)0U, Name = ImageFileName },
                                new pic.NonVisualPictureDrawingProperties()),

                            new pic.BlipFill(
                                new a.Blip() { Embed = ImageRelId },
                                new a.Stretch(
                                    new a.FillRectangle())),

                            new pic.ShapeProperties(
                                new a.Transform2D(
                                    new a.Offset() { X = 0L, Y = 0L },
                                    new a.Extents() { Cx = imageLength, Cy = imageHeight }),

                                new a.PresetGeometry(
                                    new a.AdjustValueList()) { Preset = a.ShapeTypeValues.Rectangle }))

                        ) { Uri = GraphicDataUri }))
                {
                    DistanceFromTop = (UInt32Value)0U,
                    DistanceFromBottom = (UInt32Value)0U,
                    DistanceFromLeft = (UInt32Value)0U,
                    DistanceFromRight = (UInt32Value)0U
                }
            ));

    return run;
}

Итак, я решил проблемы, из-за которых поток памяти вызывал проблемы, преждевременно закрыв и, возможно, дюжину других ненужных проблем любительских садовых дорожек, но это изображение просто не будет отображаться в моем документе. Разочарование. Предложения или божественное вдохновение очень приветствуются прямо сейчас.

(этот вопрос был сильно отредактирован, поэтому некоторые ответы могут не относиться к формулировке этого вопроса).

Ответы [ 2 ]

1 голос
/ 04 сентября 2012

Я только что проверил вашу AddImageToDocument функцию в небольшом тесте Сценарий с использованием следующего кода:

string partId = ...

Run element = AddImageToDocument(newdoc.MainDocumentPart, partId);

Paragraph p = new Paragraph() { RsidParagraphAddition = "00EA6221", RsidRunAdditionDefault = "008D25CC" };

p.AppendChild(element);

newdoc.MainDocumentPart.Document.Body.Append(p);

// Save the word document here...

Все работает, как ожидалось, и изображение отображается в текстовом документе.

Тогда я пришел к выводу, что проблема в вашем коде должна заключаться в замене тега bookmarkStart и преобразовании от Run (содержащего изображение) до XElement.

Итак, я изменил ваш код следующим образом (используя XElement.Parse для преобразования OpenXmlElement до XElement):

foreach (XElement bm in bookmarks)
{
  try
  {
    if (bm.LastAttribute.Value == "AudiometryChart")
    {
      string partId = InsertImage(document.MainDocumentPart);

      Run element = AddImageToDocument(document.MainDocumentPart, partId);

      bm.ReplaceWith(XElement.Parse(element.OuterXml)); // Use XElement.Parse to convert an OpenXmlElement to an XElement.
    }
    else
    {
      ...                    }
    }
  }
  catch
  {
  }
}

Изображение теперь отображается в документе word.

Затем я проанализировал слово документ, используя Инструмент производительности OpenXml SDK и обнаружил, что теги bookmarkEnd все еще существуют в документе.

Чтобы удалить эти теги, используйте следующий код:

List<XElement> bookmarksEnd = doc.Descendants()
            .Where(n => n.NodeType == XmlNodeType.Element && n.Name.LocalName == "bookmarkEnd")
            .ToList();

foreach (XElement x in bookmarksEnd)
{
  x.Remove();  
}     
1 голос
/ 24 апреля 2012

Edit 3:: o) Хорошо, я нашел проблему.Если вы инициализируете MemoryStream документа документа с содержимым документа, буфер будет иметь фиксированный размер и не будет редактироваться.Просто изменил init для записи содержимого документа после создания, и все, казалось, работало нормально.

//using (MemoryStream stream = new MemoryStream (docxFile))
using (MemoryStream stream = new MemoryStream ())
{
    stream.Write (docxFile, 0, docxFile.Length);
    stream.Position = 0;
    using (WordprocessingDocument docx = WordprocessingDocument.Open (stream, true))
    {
         [....]

Cheers

...