stickfigure4
stickfigure4

Reputation: 185

OpenXML replacing image placeholder with actual image replaces all placeholders

I have some weird problem trying to replace image placeholders in a Word document. If I try to replace the placeholder with the code below it replaces all of them. Not only the one who matches the name in docproperties.

With some further inspection I've discovered that viewing the word document with OpenXML SDK Productivity Tool that my placeholder pictures share the same embed id.. Why is that? That seems to be why all my placeholders get replaced by the new image.

Part of Document using OpenXML Productivity Tool:

 <pic:blipFill>
                <a:blip r:embed="rId6">
                  <a:extLst>
                    <a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}">
                      <a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0" />
                    </a:ext>
                  </a:extLst>
                </a:blip>
                <a:stretch>
                  <a:fillRect />
                </a:stretch>
              </pic:blipFill>

My code in C#

public static void ReplacePlaceHolderWithPicture(this WordprocessingDocument doc, string nameOfImage, string imgFilePath)
{
    var drawings = doc.MainDocumentPart.Document.Descendants<Drawing>().ToList();
    var newImageBytes = File.ReadAllBytes(imgFilePath);

    foreach (var drawing in drawings)
    {
        var dpr = drawing.Descendants<DocProperties>().FirstOrDefault();
        if (dpr != null && dpr.Name == nameOfImage)
        {
            foreach (var b in drawing.Descendants<A.Blip>())
            {
                OpenXmlPart imagePart = doc.MainDocumentPart.GetPartById(b.Embed);

                using (var writer = new BinaryWriter(imagePart.GetStream()))
                {
                    writer.Write(newImageBytes);
                }
            }
        }
    }
}

Upvotes: 1

Views: 672

Answers (1)

Vincenzo Costantini
Vincenzo Costantini

Reputation: 11

The point is that the EmbedID links the Drawing to an image part, wich links to a image file. You are getting the imagepart ID from the Drawing you want to replace, and using it to fill the imagepart with a new image. This way, all the shapes linked to that imagepart will get the new image. To replace only the shape with the specified name, you will have to create a new imagepart with the new file and place the id into the Drawing element you are updating.

This way you will only update a single element.

public static void ReplacePlaceHolderWithPicture(this WordprocessingDocument doc, string nameOfImage, string imgFilePath)
{
    var drawings = doc.MainDocumentPart.Document.Descendants<Drawing>().ToList();
    var newImageBytes = File.ReadAllBytes(imgFilePath);

    foreach (var drawing in drawings)
    {
        var dpr = drawing.Descendants<DocProperties>().FirstOrDefault();
        if (dpr != null && dpr.Name == nameOfImage)
        {

            //Instead of replacing the imagePart Data (and replacing all the images with the same embed id) add a new imagepart
            var imagePart = doc.MainDocumentPart.AddImagePart(ImagePartType.Jpeg); //Check if the image extensions specified is correct

            using (var stream = new MemoryStream(newImageBytes))
            {
                imagePart.FeedData(stream);
            }

            //Get the embed id of the image just added
            var relationshipId = doc.MainDocumentPart.GetIdOfPart(imagePart);

            foreach (var b in drawing.Descendants<Blip>())
            {
                //Set the ID of the new image into the blip of the aimed Drawing element
                b.Embed = relationshipId;
            }
        }
    }
}

Upvotes: 0

Related Questions