Eric White's Blog RSS icon RSS icon

Open XML, SharePoint, and Office

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

13 Responses to “Replacing a Picture in a Picture Content Control in an Open XML WordprocessingML Document”

  • victor nilo valenzuela says:

    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.

    • Attila Danicska says:

      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.

  • [...] Read OpenXML Content Control and Replace Data [...]

  • arwa says:

    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,

  • Eric says:

    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.

  • Shailesh B Davara says:

    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?

  • Patrick says:

    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.

  • Jinesh says:

    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

  • Rajesh says:

    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

  • Paul says:

    Hi Eric,

    In your code –

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

    what does c reference in c => ??

    Thanks,
    Paul.

  • AJ says:

    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


Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>