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");
                }
            }
        }
    }
}

!!!

14 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/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://ericwhite.com/blog/2011/03/27/replacing-a-picture-in-a-picture-content-control-in-an-open-xml… [...]

RSS feed for comments on this post · TrackBack URI

Leave a Comment