3

We have conditional Footers that INCLUDETEXT based on the client:

IF $CLIENT = "CLIENT1" "{INCLUDETEXT "CLIENT1HEADER.DOCX"}" ""

Depending on our document, there could be a varying amount of IF/ELSE, and these all work correctly for merging the correct files in the correct place.

However, some of these documents may have client specific images/branding, which also need to be copied across from the INCLUDETEXT file.

Below is the method that is called to replace any Picture elements that exist in the IEnumerable<Run> that is copied from the Source document to the Target document.

The image is copied fine, however it doesn't appear to update the RID in my Picture or add a record into the .XML.Rels files. (I even tried adding a ForEach to add to all the headers and footers, to see if this made any difference.

    private void InsertImagesFromOldDocToNewDoc(WordprocessingDocument source, WordprocessingDocument target, IEnumerable<Picture> pics)
    {
        IEnumerable<Picture> imageElements = source.MainDocumentPart.Document.Descendants<Run>().Where(x => x.Descendants<Picture>().FirstOrDefault() != null).Select(x => x.Descendants<Picture>().FirstOrDefault());

        foreach (Picture pic in pics) //the new pics
        {
            Picture oldPic = imageElements.Where(x => x.Equals(pic)).FirstOrDefault();

            if (oldPic != null)
            {
                string imageId = "";

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

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

                ImagePart newPart = target.MainDocumentPart.AddPart<ImagePart>(p);

                newPart.FeedData(p.GetStream());

                shape.RelId = target.MainDocumentPart.GetIdOfPart(newPart);
                string relPart = target.MainDocumentPart.CreateRelationshipToPart(newPart);
            }
        }
    }

Has anyone come across this issue before?

It appears the OpenXML SDK documentation is a 'little' sparse...

Stuart.Sklinar
  • 3,683
  • 4
  • 35
  • 89

2 Answers2

8

Late reaction but this thread helped me a lot to got it working. Here my solution for copying a document with images

private static void CopyDocumentWithImages(string path)
{
    if (!Path.GetFileName(path).StartsWith("~$"))
    {
        using (var source = WordprocessingDocument.Open(path, false))
        {
            using (var newDoc = source.CreateNew(path.Replace(".docx", "-images.docx")))
            {
                foreach (var e in source.MainDocumentPart.Document.Body.Elements())
                {
                    var clonedElement = e.CloneNode(true);
                    clonedElement.Descendants<DocumentFormat.OpenXml.Drawing.Blip>()
                        .ToList().ForEach(blip =>
                        {
                            var newRelation = newDoc.CopyImage(blip.Embed, source);
                            blip.Embed = newRelation;
                        });
                    clonedElement.Descendants<DocumentFormat.OpenXml.Vml.ImageData>().ToList().ForEach(imageData =>
                    {
                        var newRelation = newDoc.CopyImage(imageData.RelationshipId, source);
                        imageData.RelationshipId = newRelation;
                    });
                    newDoc.MainDocumentPart.Document.Body.AppendChild(clonedElement);
                }
                newDoc.Save();
            }
        }
    }
}

CopyImage:

public static string CopyImage(this WordprocessingDocument newDoc, string relId, WordprocessingDocument org)
{
    var p = org.MainDocumentPart.GetPartById(relId) as ImagePart;
    var newPart = newDoc.MainDocumentPart.AddPart(p);
    newPart.FeedData(p.GetStream());
    return newDoc.MainDocumentPart.GetIdOfPart(newPart);
}

CreateNew:

public static WordprocessingDocument CreateNew(this WordprocessingDocument org, string name)
{
    var doc = WordprocessingDocument.Create(name, WordprocessingDocumentType.Document);
    doc.AddMainDocumentPart();
    doc.MainDocumentPart.Document = new Document(new Body());
    using (var streamReader = new StreamReader(org.MainDocumentPart.ThemePart.GetStream()))
    using (var streamWriter = new StreamWriter(doc.MainDocumentPart.AddNewPart<ThemePart>().GetStream(FileMode.Create)))
    {
        streamWriter.Write(streamReader.ReadToEnd());
    }
    using (var streamReader = new StreamReader(org.MainDocumentPart.StyleDefinitionsPart.GetStream()))
    using (var streamWriter = new StreamWriter(doc.MainDocumentPart.AddNewPart<StyleDefinitionsPart>().GetStream(FileMode.Create)))
    {
        streamWriter.Write(streamReader.ReadToEnd());
    }
    return doc;
}
Marcel Hoekstra
  • 1,334
  • 12
  • 19
  • I'll dig out the original source and see ;) Thanks – Stuart.Sklinar Feb 19 '18 at 16:58
  • This worked for me, thanks a lot. I wish there was a way to clone an image from an OpenXmlElement without the need to refer to the parent document. – grathad May 30 '18 at 03:21
  • Perfect! save me a lot of effort! – James Hao Jan 09 '20 at 08:18
  • @Marcel Hoekstra Is this the only problem when you copy fragment of word-document into another word-document? Themes, sreadsheets, styles... they must be copied the same complex way in order to be correctly viewed in the result document? – Timothy Feb 20 '20 at 14:16
  • After copying image that way I get an error from Word: Word found unreadable content in "document.docx". Do you want to recover the contents of this document? If you trust the source of the document, click yes. – Timothy Feb 21 '20 at 07:02
1

Stuart,

I had faced the same problem when I was trying to copy the numbering styles from one document to the other.

I think what Word does internally is, whenever an object is copied from one document to the other the ID for that object is not copied over to the new document and instead what happens is a new ID is assigned to it.

You'll have to get the ID after the image has been copied and then replace it everywhere your image has been used.

I hope this helps, this is what I to use copy numbering styles.

Cheers

Varun Rathore
  • 7,730
  • 3
  • 27
  • 30