Replacing a Picture in a Picture Content Control in an Open XML WordprocessingML Document

You may have a picture content control where you want to replace the picture with a different picture.  This post shows the Open XML SDK V2 code that is necessary to find a picture content control with an alias of “MyPicture”.  It then finds the ImagePart, and then replaces the contents of the image part with a different image.


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml.Drawing;

class Program
{
    static void Main(string[] args)
    {
        using (WordprocessingDocument doc =
            WordprocessingDocument.Open("Test1.docx", true))
        {
            SdtBlock cc = doc.MainDocumentPart.Document.Body.Descendants<SdtBlock>()
                .FirstOrDefault(c =>
                    {
                        SdtProperties p = c.Elements<SdtProperties>().FirstOrDefault();
                        if (p != null)
                        {
                            // Is it a picture content control?
                            SdtContentPicture pict =
                                p.Elements<SdtContentPicture>().FirstOrDefault();
                            // Get the alias.
                            SdtAlias a = p.Elements<SdtAlias>().FirstOrDefault();
                            if (pict != null && a.Val == "MyPicture")
                                return true;
                        }
                        return false;
                    });
            string embed = null;
            if (cc != null)
            {
                Drawing dr = cc.Descendants<Drawing>().FirstOrDefault();
                if (dr != null)
                {
                    Blip blip = dr.Descendants<Blip>().FirstOrDefault();
                    if (blip != null)
                        embed = blip.Embed;
                }
            }
            if (embed != null)
            {
                IdPartPair idpp = doc.MainDocumentPart.Parts
                    .Where(pa => pa.RelationshipId == embed).FirstOrDefault();
                if (idpp != null)
                {
                    ImagePart ip = (ImagePart)idpp.OpenXmlPart;
                    using (FileStream fileStream =
                        File.Open("After.jpg", FileMode.Open))
                        ip.FeedData(fileStream);
                    Console.WriteLine("done");
                }
            }
        }
    }
}

!!!

20 Comments »

  1. victor nilo valenzuela said,

    January 3, 2012 @ 7:09 pm

    Eric, the code work fine when you only have one picture content control, but if you have more all the pictures at the end have the last image processed.
    Could you help me with that?.

    Thanks.

  2. Attila Danicska said,

    January 6, 2012 @ 3:06 pm

    Yes, that’s the problem of me as well. Eric, could you give us a solution, please? I’m not familiar with OpenXml so it is hard to fix it for me. Thanks.

  3. OpenXML Research « gpluft said,

    January 12, 2012 @ 11:11 pm

    […] Read OpenXML Content Control and Replace Data […]

  4. arwa said,

    March 21, 2012 @ 12:05 pm

    I have the same problem that whatever image I use to replace the last content control is used for all of them.
    how can i solve this?

    Thank you,

  5. Eric said,

    June 1, 2012 @ 4:44 pm

    Eric, how do you assign an alias to Word document picture? I was testing your code with a Word document that have an image but the imaga hasn’t alias.

  6. Shailesh B Davara said,

    June 7, 2012 @ 2:40 pm

    Eric,
    when we use above sample code it seems work sometime and it does not work well when we try to integrate it somewhere.
    Also, we found that it just replace out all picture content with the image we supply, instead of that it should only replace tagged name picture control.
    would you give more info on this?

  7. Patrick said,

    June 9, 2012 @ 9:45 am

    Eric, when designing our word document, if we put in a few picture controls, it’s reference ID will be the same, pointing to the same image. That is why when the image is replaced, all the picture controls will have the same image, a problem faced by a few users here.

    What would be the best way to recode this? Anybody who can contribute here would be great.

  8. Jinesh said,

    June 13, 2012 @ 12:44 pm

    Hi Eric

    Just Try This

    Below Code Replace Particular Image

    Public string ApplySignature(string fileName, string imageName, string imageTagName, string SignatureName, string SignatureTag,
    string SignatureDesignation, string SignatureDesignationTag)
    {
    string folderPath = AppDomain.CurrentDomain.BaseDirectory + “shared-data\\”;
    string imagePath = AppDomain.CurrentDomain.BaseDirectory + “temp\\” + imageName;
    string filePath = folderPath + fileName ;
    ApplyContentControl(SignatureName, SignatureTag, SignatureDesignation, SignatureDesignationTag, fileName);
    //ApplyImage(imagePath, fileName, imageTagName, filePath);
    string[] results = new string[2];
    string strResult = “”;

    try
    {
    FileInfo fInfo = new FileInfo(imagePath);

    using (WordprocessingDocument doc = WordprocessingDocument.Open(filePath, true))
    {
    DocumentFormat.OpenXml.Drawing.Blip blip = this.GetBlip(doc, imageTagName);
    if (blip != null)
    {
    // Add image and change embeded id.

    blip.Embed = this.AddImagePart(doc, imagePath, imageTagName);
    doc.Package.Flush();

    results[0] = “OK”;

    results[1] = “0”;
    }
    }
    GC.Collect();
    }

    catch (Exception ex)
    {
    results[0] = ex.Message;

    results[1] = “-1”;
    }

    strResult = results[0] + “|” + results[1];
    GenerateValidationFile(filePath + “.log”, strResult);
    return strResult;
    }

    public DocumentFormat.OpenXml.Drawing.Blip GetBlip(WordprocessingDocument pDocument, string pPictureContentControlTagName)
    {
    foreach (SdtAlias alias in pDocument.MainDocumentPart.Document.Descendants().ToList())
    if (alias.Val == pPictureContentControlTagName)
    return alias.Parent.Parent.Descendants().FirstOrDefault();
    return null;
    }

    public string AddImagePart(WordprocessingDocument pDocument, string pPicturePath, string pImagePartId)
    {
    OpenXmlPart part = pDocument.MainDocumentPart.GetPartById(pImagePartId);
    if (part != null)
    {
    pDocument.MainDocumentPart.DeletePart(part);
    }
    ImagePart imagePart = pDocument.MainDocumentPart.AddImagePart(GetImagePartType(pPicturePath), pImagePartId);
    using (FileStream stream = new FileStream(pPicturePath, FileMode.Open, FileAccess.Read))
    {
    imagePart.FeedData(stream);
    }

    return pImagePartId;
    }

    public ImagePartType GetImagePartType(string imagePath)
    {
    ImagePartType imagePartType = ImagePartType.Bmp;

    switch (System.IO.Path.GetExtension(imagePath).ToUpper())
    {
    case “BMP”: imagePartType = ImagePartType.Bmp;
    break;
    case “JPEG”:
    case “JPG”:
    imagePartType = ImagePartType.Jpeg;
    break;
    case “PNG”: imagePartType = ImagePartType.Png;
    break;
    case “GIF”: imagePartType = ImagePartType.Gif;
    break;
    default: imagePartType = ImagePartType.Bmp;
    break;
    }
    return imagePartType;
    }

    private string GenerateValidationFile(string filePath, string Message)
    {
    string directoryPath = Path.GetDirectoryName(filePath);
    string finalFilename = Path.GetFileNameWithoutExtension(filePath) + “.log”;
    StreamWriter SW;
    SW = System.IO.File.CreateText(directoryPath + @”\” + finalFilename);
    SW.WriteLine(Message);
    SW.Close();
    GC.Collect();
    return finalFilename;
    }

    Cheers

    Jinesh

  9. Rajesh said,

    January 18, 2013 @ 4:12 pm

    I modified to use an image inside a footer and always i end up with Image cannot be currently displayed.

    If I place the control inside the body it works well.

    Any help?

    Thanks,
    Rajesh

  10. Eric White said,

    January 18, 2013 @ 7:44 pm

    Hi Rajesh,

    One way to debug these types of problems is to follow the procedure I show in this screen-cast. Give it a try and let me know how it works for you.

    http://openxmldeveloper.org/home2/bm8qcmjy/public_html/blog/b/openxmldeveloper/archive/2011/04/01/133710.aspx

    -Eric

  11. Rajesh said,

    January 22, 2013 @ 9:19 am

    Hi Eric,

    Thanks for the reply. Learned a lot of tools/technqs.
    So far badluck, I am unable fix the issue.

    Rajesh

  12. Paul said,

    February 1, 2013 @ 12:00 am

    Hi Eric,

    In your code –

    SdtBlock cc = doc.MainDocumentPart.Document.Body.Descendants().FirstOrDefault(c =>

    what does c reference in c => ??

    Thanks,
    Paul.

  13. AJ said,

    February 1, 2013 @ 12:08 am

    Hi Eric
    This code doesn’t work for me. I’m using SDK 2.5. I get the following error

    Error 4 Not all code paths return a value in lambda expression of type ‘System.Func’

    this is on line
    SdtBlock cc = doc.MainDocumentPart.Document.Body.Descendants().FirstOrDefault(c => {SdtProperties p = c.Elements().FirstOrDefault();

    what is ‘c’ here?

    thanks in advance

  14. WordprocessingDocument handling | SharePoint Q&A said,

    September 6, 2013 @ 9:06 am

    […] http://www.ericwhite.com/blog/2011/03/27/replacing-a-picture-in-a-picture-content-control-in-an-open-xml… […]

  15. Fariba said,

    October 14, 2015 @ 3:53 pm

    Hi Eric
    In this line:
    ImagePart ip = (ImagePart)idpp.OpenXmlPart;

    I get this error
    Unable to cast object of type ‘DocumentFormat.OpenXml.Packaging.CustomXmlPart’ to type ‘DocumentFormat.OpenXml.Packaging.ImagePart’.

    What is the problem? Could you please guide me?

  16. Fariba said,

    October 16, 2015 @ 8:18 am

    Hi Eric
    It doesn’t work when Picture Content Control is on the header of word document. This line:
    ImagePart ip = (ImagePart)idpp.OpenXmlPart;
    throws cast exception

  17. Krishna said,

    May 25, 2016 @ 6:37 am

    How to place the content from source document to template file. Source document might have the data combination of images and paragraphs. And i want to get this data and place it in Rich text content control in template.

    Please help me.

    Thanks & Regards,
    Krishna

  18. Brij said,

    June 25, 2018 @ 11:02 am

    Hey Eric, what is the solution for replace multi images to multi picture content control.
    As in this solution Blip embed is same for all Picture content.

    Please help..!!

    Thanks

  19. Ken L said,

    December 18, 2019 @ 4:11 am

    One way to solve the multi picture control replaced with the same image issue is to put a different dummy img for each of the picture content controls, this forces the document to create a different reference for each control under the media folder

    Thanks Eric for the great example, it helped me a lot.

    Cheers
    Ken L

  20. MayuriB said,

    July 3, 2020 @ 5:25 pm

    Hello Eric,

    I am using OpenXmlPowerTools for managing my document (docx). I am using OpenXmlRegex to replace placeholders with text or paragraphs. But I also want to replace the placeholder with images. How can I achieve that with OXPowerTool?

RSS feed for comments on this post · TrackBack URI

Leave a Comment