Creating an ImagePart isn't saving the Relatio

2019-02-25 12:42发布

问题:

--Updated to provide full working class example, with 2 sample documents--

www.sklinar.co.uk/wp-content/uploads/mydoc.docx - Original Document with a INCLUDETEXT instruction to merge in Footer.Docx

www.sklinar.co.uk/wp-content/uploads/footer.docx

I have added a ImagePart to my document as well as using FeedData() to supply it with streamed content.

But all I can get to appear in my document is a red box.

My initial code Creates a Run with a Drawing as it's child:

--For this example, I've used a hard-coded FileStream to weed out dodgy images--

Just to get this working, I am using the same footer each time, and this is currently hard-coded with the correct footer:

runToAmend.InsertAfterSelf(CreateImageRun(sourceDocument,run,target.MainDocumentPart.FooterParts.ElementAt(2)));

The runToAmend is taken from a run in the current document - which holds a field which is then removed and the picture (and other text taken from another document) placed in it's place.

    public Run CreateImageRun(WordprocessingDocument sourceDoc,  Run sourceRunFromOriginalDocument, FooterPart footerPart)
    {
        ImageData shape = sourceRun.Descendants<ImageData>().FirstOrDefault();

        ImagePart p = sourceDoc.MainDocumentPart.GetPartById(shape.RelationshipId) as ImagePart;

        ImagePart newPart = footerPart.AddImagePart(ImagePartType.Jpeg);

        using (Stream stream = new FileStream(@"C:\Users\SAS\Desktop\IMG_20130803_104521.jpg",FileMode.Open,FileAccess.Read))
        {
            stream.Position = 0;
            newPart.FeedData(stream);
        }

        string partId = footerPart.GetIdOfPart(newPart);

        Drawing newImage = CreateImage(partId);
        return new Run(newImage);
    }

Code to Create Drawing

private Drawing CreateImage(string relationshipId)
    {
        // Define the reference of the image.
        return new Drawing(
                 new DW.Inline(
                     new DW.Extent() { Cx = 990000L, Cy = 792000L },
                     new DW.EffectExtent()
                     {
                         LeftEdge = 0L,
                         TopEdge = 0L,
                         RightEdge = 0L,
                         BottomEdge = 0L
                     },
                     new DW.DocProperties()
                     {
                         Id = (UInt32Value)1U,
                         Name = "Picture 1"
                     },
                     new DW.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 = "New Bitmap Image.jpg"
                                     },
                                     new PIC.NonVisualPictureDrawingProperties()),
                                 new PIC.BlipFill(
                                     new A.Blip(
                                         new A.BlipExtensionList(
                                             new A.BlipExtension()
                                             {
                                                 Uri =
                                                   "{28A0092B-C50C-407E-A947-70E740481C1C}"
                                             })
                                     )
                                     {
                                         Embed = relationshipId,
                                         CompressionState =
                                         A.BlipCompressionValues.Print
                                     },
                                     new A.Stretch(
                                         new A.FillRectangle())),
                                 new PIC.ShapeProperties(
                                     new A.Transform2D(
                                         new A.Offset() { X = 0L, Y = 0L },
                                         new A.Extents() { Cx = 990000L, Cy = 792000L }),
                                     new A.PresetGeometry(
                                         new A.AdjustValueList()
                                     ) { Preset = A.ShapeTypeValues.Rectangle }))
                         ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
                 )
                 {
                     DistanceFromTop = (UInt32Value)0U,
                     DistanceFromBottom = (UInt32Value)0U,
                     DistanceFromLeft = (UInt32Value)0U,
                     DistanceFromRight = (UInt32Value)0U,
                     EditId = "50D07946"
                 });
    }

The XML produced appears correct, and the image gets added to the /Media/ folder.

<w:r>
  <w:drawing>
    <wp:inline distT="0" distB="0" distL="0" distR="0" wp14:editId="50D07946">
      <wp:extent cx="990000" cy="792000" />
      <wp:effectExtent l="0" t="0" r="0" b="0" />
      <wp:docPr id="1" name="Picture 1" />
      <wp:cNvGraphicFramePr>
        <a:graphicFrameLocks noChangeAspect="1" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" />
      </wp:cNvGraphicFramePr>
      <a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">
        <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
          <pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture">
            <pic:nvPicPr>
              <pic:cNvPr id="0" name="New Bitmap Image.jpg" />
              <pic:cNvPicPr />
            </pic:nvPicPr>
            <pic:blipFill>
              <a:blip r:embed="Raae77c5adb2e48f3" cstate="print">
                <a:extLst>
                  <a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}" />
                </a:extLst>
              </a:blip>
              <a:stretch>
                <a:fillRect />
              </a:stretch>
            </pic:blipFill>
            <pic:spPr>
              <a:xfrm>
                <a:off x="0" y="0" />
                <a:ext cx="990000" cy="792000" />
              </a:xfrm>
              <a:prstGeom prst="rect">
                <a:avLst />
              </a:prstGeom>
            </pic:spPr>
          </pic:pic>
        </a:graphicData>
      </a:graphic>
    </wp:inline>
  </w:drawing>
</w:r>

I've spent about 2 days now searching SO, Google MS's poor docs.

public class WordTest
    {
        using DocumentFormat.OpenXml;
        using DocumentFormat.OpenXml.Packaging;
        using DocumentFormat.OpenXml.Vml;
        using DocumentFormat.OpenXml.Wordprocessing;
        using System;
        using System.Collections.Generic;
        using System.Drawing;
        using System.IO;
        using System.Linq;
        using A = DocumentFormat.OpenXml.Drawing;
        using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
        using PIC = DocumentFormat.OpenXml.Drawing.Pictures;
        public void MethodName()
        {
            WordprocessingDocument mainDoc = WordprocessingDocument.Open("mydoc.docx", true);

            foreach (var item in mainDoc.MainDocumentPart.FooterParts)
            {
                ProcessParaIncludeTextMerge(item, item.Footer.Descendants<Run>(), mainDoc);
                item.Footer.Save();
            }

            mainDoc.MainDocumentPart.Document.Save();

        }

        private void ProcessParaIncludeTextMerge(OpenXmlPart part, IEnumerable<Run> runs, WordprocessingDocument originalDocument)
        {
            List<Run> paraRuns = runs.ToList();

            int runCount = paraRuns.Count();

            for (int i = 0; i < runCount; i++)
            {
                Run r = paraRuns.ElementAt(i);

                // check if this is a simple Merge Field
                if (r.HasChildren && r.Descendants<FieldCode>().Any())
                {
                    FieldCode code = r.Descendants<FieldCode>().First();

                    // we check the first fieldcode is a merge field - but we can't *just* use that one, as for
                    // some stupid reason, the fieldcodes can be split across runs.... :/
                    if (code.Text.Trim().IndexOf("INCLUDETEXT", StringComparison.InvariantCultureIgnoreCase) > -1) //this is actually piss-poor, but as a merge field can go across n runs, we simply check for the M and let the other function figure it out..
                    {
                        MergeIncludeText(i, paraRuns, originalDocument);
                    }
                }
            }
        }

        public Run CreateImageRun(WordprocessingDocument sourceDoc, Run sourceRun, WordprocessingDocument target, FooterPart footerPart)
        {
             ImagePart newPart = footerPart.AddImagePart(ImagePartType.Png);

            ImageData shape = sourceRun.Descendants<ImageData>().FirstOrDefault();

            ImagePart p = sourceDoc.MainDocumentPart.GetPartById(shape.RelationshipId) as ImagePart;

            Bitmap image = new Bitmap(p.GetStream()); 

            using (Stream s = p.GetStream())
            {
                s.Position = 0;
                newPart.FeedData(s);
            }

            string partId = footerPart.GetIdOfPart(newPart);

            Drawing newImage = CreateImage(partId);

            return new Run(newImage);
        }

        private Drawing CreateImage(string relationshipId)
        {
            // Define the reference of the image.
            return new Drawing(
                     new DW.Inline(
                         new DW.Extent() { Cx = 990000L, Cy = 792000L },
                         new DW.EffectExtent()
                         {
                             LeftEdge = 0L,
                             TopEdge = 0L,
                             RightEdge = 0L,
                             BottomEdge = 0L
                         },
                         new DW.DocProperties()
                         {
                             Id = (UInt32Value)1U,
                             Name = "Picture 1"
                         },
                         new DW.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 = "New Bitmap Image.jpg"
                                         },
                                         new PIC.NonVisualPictureDrawingProperties()),
                                     new PIC.BlipFill(
                                         new A.Blip(
                                             new A.BlipExtensionList(
                                                 new A.BlipExtension()
                                                 {
                                                     Uri =
                                                       "{28A0092B-C50C-407E-A947-70E740481C1C}"
                                                 })
                                         )
                                         {
                                             Embed = relationshipId,
                                             CompressionState =
                                             A.BlipCompressionValues.Print
                                         },
                                         new A.Stretch(
                                             new A.FillRectangle())),
                                     new PIC.ShapeProperties(
                                         new A.Transform2D(
                                             new A.Offset() { X = 0L, Y = 0L },
                                             new A.Extents() { Cx = 990000L, Cy = 792000L }),
                                         new A.PresetGeometry(
                                             new A.AdjustValueList()
                                         ) { Preset = A.ShapeTypeValues.Rectangle }))
                             ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
                     )
                     {
                         DistanceFromTop = (UInt32Value)0U,
                         DistanceFromBottom = (UInt32Value)0U,
                         DistanceFromLeft = (UInt32Value)0U,
                         DistanceFromRight = (UInt32Value)0U,
                         EditId = "50D07946"
                     });
        }

        private void ReplaceRunsWithRuns(IEnumerable<Run> fieldRuns, IEnumerable<Run> value, WordprocessingDocument source, WordprocessingDocument target)
        {
            FooterPart f = target.MainDocumentPart.FooterParts.ElementAt(1);

            f.Footer.RemoveAllChildren();

            Paragraph p = new Paragraph();
            //the run has no value to merge into, remove the field.
            if (value != null)
            {
                foreach (var item in value)
                {
                    if (!item.Descendants<Picture>().Any()) //pictures are processed differently - they're an absolute s**t storm to code...
                    {
                        p.Append(item.CloneNode(true));
                    }
                    else
                    {
                        p.Append(CreateImageRun(source, item, target, f));
                    }
                }
            }
            else
            {
            }
            f.Footer.Append(p);
        }

        private void MergeIncludeText(int curRunIdx, List<Run> runs, WordprocessingDocument originalDoc)
        {
            int startRun = GetBeginRun(runs, curRunIdx);

            if (startRun == -1)
            {
                return;
            }

            int endRun = GetEndRun(runs, startRun);

            if (endRun == -1)
            {
                return;
            }

            IEnumerable<Run> fieldRuns = WordMLHelpers.GetRunsBetweenTwoPoints(runs, startRun, endRun);

            IEnumerable<FieldCode> fieldCodes = fieldRuns.SelectMany(x => x.Descendants<FieldCode>());

            string mergeField = string.Concat(fieldCodes.Select(x => x.Text));

            string field = GetIncludeTextFilePath(mergeField);

            MemoryStream ms = LoadDocumentStream(field);

            WordprocessingDocument includeDoc = LoadDocumentFromStream(ms);

            IEnumerable<Run> includedRuns = includeDoc.MainDocumentPart.Document.Descendants<Run>();

            ReplaceRunsWithRuns(fieldRuns, includedRuns, includeDoc, originalDoc);
        }

        private string GetIncludeTextFilePath(string mergeFieldText)
        {
            int quoteStart = mergeFieldText.IndexOf('"') + 1;

            int quoteEnd = mergeFieldText.IndexOf('"', quoteStart);

            return mergeFieldText.Substring(quoteStart, quoteEnd - quoteStart);
        }

        private WordprocessingDocument LoadDocumentFromStream(Stream stream)
        {
            return WordprocessingDocument.Open(stream, true);
        }

#region helpers
        public static int GetBeginRun(IEnumerable<Run> runs, int curIdx)
        {
            for (int i = curIdx; i < runs.Count(); i--)
            {
                if (i == -1)
                {
                    return -1;
                }
                Run run = runs.ElementAt(i);

                if (run.HasChildren && run.ChildElements.OfType<FieldChar>().Count() > 0
                      && (run.ChildElements.OfType<FieldChar>().First().FieldCharType == FieldCharValues.Begin))
                {
                    return i;
                }
            }

            throw new Exception("Begin not found");
        }

        /// <summary>
        /// Get the first End Run in a <see cref="List"/>(<see cref="Run"/>)
        /// </summary>
        /// <param name="runs">The runs.</param>
        /// <param name="curIdx">The cur idx.</param>
        /// <returns></returns>
        /// <exception cref="System.Exception">End not found</exception>
        public static int GetEndRun(IEnumerable<Run> runs, int curIdx)
        {
            //runs.FirstOrDefault(x => x.HasChildren && x.Descendants<FieldChar>().Count > 0 && (x.Descendants<FieldChar>().First().FieldCharType & FieldCharValues.End) == FieldCharValues.End);

            for (int i = curIdx; i < runs.Count(); i++)
            {
                if (i == -1)
                {
                    return -1;
                }
                Run run = runs.ElementAt(i);

                if (run.HasChildren && run.Descendants<FieldChar>().Any()
                       && (run.Descendants<FieldChar>().First().FieldCharType == FieldCharValues.End))
                {
                    return i;
                }
            }

            return -1;
        }

        public static MemoryStream LoadDocumentStream(string template)
        {
            using (FileStream fs = File.Open(template, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                // first read document in as stream
                MemoryStream ms = new MemoryStream();

                fs.CopyTo(ms);
                ms.Seek(0, SeekOrigin.Begin);
                return ms;
            }
        }
#endregion


    }

回答1:

I've analyzed your word document. There are a few issues with your word document:

  1. If I try to open your word document in the MS productivity toolkit I get the error message "Root element is missing".

  2. If I open your document as a zip file (renaming to mergedfooter.zip) then in the footer2.xml.rels file the relationship for your image is missing. I think that's the reason you get the "red box".

To further analyze your problem I need the complete code (how do you get the FooterPart)?

Below you will find an example of how to insert an image into the footer of a word document. Please note, that the example below assumes that your word document already contains a footer and your footer contains a paragraph element.

using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open("mydoc.docx", true))
{
  MainDocumentPart mainPart = wordprocessingDocument.MainDocumentPart;

  // Search for your footer part here.
  // Just for the sake of simplicity I take the second footer part.
  FooterPart fp = mainPart.FooterParts.ToList()[2];                

  // Create new image part.
  ImagePart ip = fp.AddImagePart(ImagePartType.Jpeg);

  using (FileStream fs = File.Open("mypicture.jpg", FileMode.Open))
  {         
    ip.FeedData(fs);         
  }

  string relationshipId = fp.GetIdOfPart(ip);

  // Create the image element using your function.
  Drawing img = CreateImage(relationshipId);

  Run r = new Run(img);

  Paragraph para = fp.RootElement.Descendants<Paragraph>().FirstOrDefault();

  if(para != null)
  {
    para.Append(r);   
  }      
  else
  {
    Console.WriteLine("paragraph is null...");
  }
}

EDIT:

After analyzing your newly provided documents:

The reason the releationship for your image is not saved is because you do not Dispose() or Close() your word document.

So, just add a using statement:

using (WordprocessingDocument mainDoc = WordprocessingDocument.Open("mydoc.docx", true))
{
  foreach (var item in mainDoc.MainDocumentPart.FooterParts)
  {
    ProcessParaIncludeTextMerge(item, item.Footer.Descendants<Run>(), mainDoc);
    item.Footer.Save();
  }

  mainDoc.MainDocumentPart.Document.Save();           
}

Furthermore in your ReplaceRunsWithRuns() method you must use PIC.Picture to reference the correct Picture class:

foreach (var item in value)
{
  if (!item.Descendants<PIC.Picture>().Any())
  {
    p.Append(item.CloneNode(true));
  }
  else
  {
    p.Append(CreateImageRun(source, item, target, f));
  }
}

By the same token in your CreateImageRun() method I've changed the first three code lines:

ImagePart newPart = footerPart.AddImagePart(ImagePartType.Jpeg);                   
A.Blip shape = sourceRun.Descendants<A.Blip>().FirstOrDefault();
ImagePart p = sourceDoc.MainDocumentPart.GetPartById(shape.Embed.Value) as ImagePart;

With theses changes the image appears in the mydoc.docx word document.