none
Mail Merge Image displays a red x RRS feed

  • Question

  • Usage: Mail Merge application

    Version: Office 2003 compatibility

    In part of the application, we have a correspondence section where the user may enter in data that will then become part of a Word document that is printed and sent to the user's client.  The document used for the mail merge is saved as Word 97 – 2003 compatible document.  Everything about this works just fine except for the signature image.  No matter what I do the signature image always comes up with as a red X and with the words “The linked image cannot be displayed.  The file may have been moved, renamed, or deleted.  Verify that the link points to the correct file and location.”  Of course it’s deleted, it’s a temp file and it should be embedded in the document.  But I’ll return to that in a moment.  Let me explain how I generate the document.

    Data for the merge is stored in a data table where the column names are the same as the mail merge names in the Word document.  This is then passed to a method that performs the necessary steps to do the merge.  (Note that all temp files are deleted once the merge is done except for the final results file which is deleted later.  Here is how the image is defined.

    {INCLUDEPICTURE { IF TRUE "{MERGEFIELD SignatureImage }"}\d}

    public string MergeAndSaveDoc(DataTable dataTable)
    {
        // Get a system generated temp file name
        var dataFilename = GetTempFilename();
        // Generates string with a comma delimited list of field names representing the columns of the data table
        var headerRecord = CreateHeaderRecord(dataTable);
        // Get the mail merge document.
        var mailmerge = _templateDocument.MailMerge;
        /*
         * The _templateDocument is a class level variable that is set at ctor time.
         * // Open the Word merge template document. The filename has to contain the full path;
         * // relative paths don't seems to work properly.
         * _templateFilename = Path.IsPathRooted(templateFilename) ? templateFilename : Path.GetFullPath(templateFilename);
         * _templateDocument = _application.Documents.Open(_templateFilename);
         */
        // Validate that the columns in the data table match the merge fields in the
        // template document.
        ValidateTemplateWithData(dataTable);
        // Converts any images to files so that Word can read them from the filesystem.
        ConvertImagesToFiles(dataTable);
        // Create a new Word document to act as the data source.
        mailmerge.CreateDataSource(dataFilename, Missing.Value, Missing.Value, headerRecord);
        // Open the data document and populate it with the data in the data table.
        var dataDocument = _application.Documents.Open(dataFilename);
        PopulateData(dataDocument, dataTable);
        dataDocument.Save();
        dataDocument.Close();
        // Perform the merge and output to the printer.
        mailmerge.Destination = WdMailMergeDestination.wdSendToNewDocument;
        mailmerge.Execute();
        // Save the newest (mailmerged) document
        Document d = _application.Documents[_application.Documents.Count - 1];
        string mergedFileName = Path.Combine(Path.GetTempPath(), "tmpPreviewCorrespondence.docx");
        d.SaveAs(Path.Combine(mergedFileName));
        return mergedFileName;
    }

    There are two methods of particular interest that I need to elaborate on.  The first is ConvertImagesToFiles and the second is PopulateData.

    In the data table, the initial image is stored as a bit map.  I take that bitmap and export it to a file then replace the data in the data table as the full path to that file name. Like this.

    var image = row[column] as Image;
    if (image != null)
    {
        var filename = GetTempFilename();
        image.Save(filename);
        row[column] = filename;
    }

    Here’s the meat of it all.  This is where the replacement takes place.

            /// <summary>
            /// Populates a table in a Word document with the data in a data table object.
            /// </summary>
            private void PopulateData(Document dataDocument, DataTable dataTable)
            {
                var wordTable = dataDocument.Tables[1];
                for (var row = 0; row < dataTable.Rows.Count; row++)
                {
                    var wordRow = (row == 0) ? wordTable.Rows[2] : wordTable.Rows.Add();
                    var dataRow = dataTable.Rows[row];
                    for (var col = 0; col < dataTable.Columns.Count; col++)
                    {
                        var wordCell = wordRow.Cells[col + 1];
                        var dataCell = dataRow[col];
                        if (File.Exists(dataCell.ToString())) // if true then this is an image file
                        {
                            wordCell.Select();
                            wordCell.Range.InlineShapes.AddPicture(dataCell.ToString(), false, true, Missing.Value);
                        }
                        else
                            wordCell.Range.Text = dataCell.ToString();
                    }
                }
            }

    My first pass through this I just set the text value to datacell value no matter what.  Then I found the AddPicture and started using that.  Either way it doesn’t work.

     

    I’ve tried setting my Word document that I use as a template in other formats such as a DOT, DOCT, DOM, DOCM, DOTX.  None of these make a difference.  I’ve tried not deleting the temp file or using a file that isn’t stored in user temp with no luck.

    I’ve searched the net for a couple of days looking for the right way to do this so the image can be displayed.  If you have some suggestions, please share.




    Thursday, May 2, 2013 4:17 PM

Answers

  • A few questions/comments:

    1. If you insert { MERGEFIELD SignatureImage } on its own, what does the result look like? Is it a path like c:\xyz\something.xyz or like c:/xyz/something.xyz ? Are there any leading/trailing spaces or other characters in the string (I am looking at the fact that you are using dataCell.ToString()

    2. You may need either a simpler, or a more complex, set of field codes. The conversation at http://social.msdn.microsoft.com/Forums/en-US/worddev/thread/e74d46fe-7b5e-40bb-b33a-40301b19f3c7
    has some suggestions (and perhaps, if you are inserting these images into a data source that is formatted as a Word document , a REF field would be enough to include the image as I suggested there).

    3. Without diving too deeply, I get the impression that you are using ConvertImages to files to save your images into the file system, then using PopulateData to insert the images into your data source. But unless you are going to use REF (and it works) the data source should contain the fullname (pathname+filename) of the file, not the image. i.e. it's either "put the image in the data source, then use REF, or put the image in the file system, put the fullname in the data source, and then use one of the INCLUDEPICTURE constructs.


    Peter Jamieson

    • Marked as answer by wrwoodman Friday, May 3, 2013 1:23 PM
    Thursday, May 2, 2013 5:26 PM
  • After doing the merge, when you get the "The linked image cannot be displayed" message, select one of the fields displaying that message and check its contents. It should be displaying:
    {INCLUDEPICTURE "C:\\Users\\<<username>>\\AppData\\Local\\Temp\\tmpB63D.tmp" \d}
    where '<<username>>' is the actual user's name.

    Assuming it's correct (notwithstanding that the folder has been deleted), the fix may be as simple as inserting:
    dataDocument.Fields.Update();
    before:
    dataDocument.Save();

    Alternatively, try a field coded as:
    {IF {INCLUDEPICTURE {IF TRUE «SignatureImage»} \d} {INCLUDEPICTURE {IF TRUE «SignatureImage»} \d}}
    or
    {IF {INCLUDEPICTURE {IF TRUE {MERGEFIELD SignatureImage}} \d} {INCLUDEPICTURE {IF TRUE {MERGEFIELD SignatureImage}} \d}}

    Note: The field brace pairs (ie '{ }') for the above examples are created in the body of the document via Ctrl-F9 (Cmd-F9 on a Mac) - you can't simply type them or copy & paste them from this message. Likewise, you can't type or copy & paste the chevrons (ie '« »') - they're part of the actual mergefields, which you can insert from the mailmerge toolbar.


    Cheers
    Paul Edstein
    [MS MVP - Word]


    Friday, May 3, 2013 2:36 AM

All replies

  • A few questions/comments:

    1. If you insert { MERGEFIELD SignatureImage } on its own, what does the result look like? Is it a path like c:\xyz\something.xyz or like c:/xyz/something.xyz ? Are there any leading/trailing spaces or other characters in the string (I am looking at the fact that you are using dataCell.ToString()

    2. You may need either a simpler, or a more complex, set of field codes. The conversation at http://social.msdn.microsoft.com/Forums/en-US/worddev/thread/e74d46fe-7b5e-40bb-b33a-40301b19f3c7
    has some suggestions (and perhaps, if you are inserting these images into a data source that is formatted as a Word document , a REF field would be enough to include the image as I suggested there).

    3. Without diving too deeply, I get the impression that you are using ConvertImages to files to save your images into the file system, then using PopulateData to insert the images into your data source. But unless you are going to use REF (and it works) the data source should contain the fullname (pathname+filename) of the file, not the image. i.e. it's either "put the image in the data source, then use REF, or put the image in the file system, put the fullname in the data source, and then use one of the INCLUDEPICTURE constructs.


    Peter Jamieson

    • Marked as answer by wrwoodman Friday, May 3, 2013 1:23 PM
    Thursday, May 2, 2013 5:26 PM
  • 1) I tried using { MERGEFIELD SignatureImage } and it works for my simple test app but not in the main app.  The result of the dataCell.ToString() is the file path.  (e.g. "C:\\Users\\<<username>>\\AppData\\Local\\Temp\\tmpB63D.tmp")

    2) Looking that over.

    3) The ConvertImages does what you suspect it does.  It takes the bitmap image data and exports it to the file system in the user's temp directory.  It then replaces the bitmap data with the full filepath to the temp file.  I'm not sure how to use this REF you speak of.  I've never used Word merge before so this is all rather new to me.  I'm just the lucky one that gets to figure it out and make it work. :)

    I appreciate your help.

    Thursday, May 2, 2013 7:38 PM
  • After doing the merge, when you get the "The linked image cannot be displayed" message, select one of the fields displaying that message and check its contents. It should be displaying:
    {INCLUDEPICTURE "C:\\Users\\<<username>>\\AppData\\Local\\Temp\\tmpB63D.tmp" \d}
    where '<<username>>' is the actual user's name.

    Assuming it's correct (notwithstanding that the folder has been deleted), the fix may be as simple as inserting:
    dataDocument.Fields.Update();
    before:
    dataDocument.Save();

    Alternatively, try a field coded as:
    {IF {INCLUDEPICTURE {IF TRUE «SignatureImage»} \d} {INCLUDEPICTURE {IF TRUE «SignatureImage»} \d}}
    or
    {IF {INCLUDEPICTURE {IF TRUE {MERGEFIELD SignatureImage}} \d} {INCLUDEPICTURE {IF TRUE {MERGEFIELD SignatureImage}} \d}}

    Note: The field brace pairs (ie '{ }') for the above examples are created in the body of the document via Ctrl-F9 (Cmd-F9 on a Mac) - you can't simply type them or copy & paste them from this message. Likewise, you can't type or copy & paste the chevrons (ie '« »') - they're part of the actual mergefields, which you can insert from the mailmerge toolbar.


    Cheers
    Paul Edstein
    [MS MVP - Word]


    Friday, May 3, 2013 2:36 AM
  • Looking again, I have not been able to replicate a problem here as long as the path is correct and Word recognises the file format. 

    re. (3) AFAICS the version of the code you posted inserts an image into your data source file.

    If you have the actual image in your data source file, using an INCLUDEPICTURE field won't work. You have to use a { REF SignatureImage } field or a { SignatureImage } field. This relies on old code in Word (it works up to Word 2010 on Windows), so it cannot be recommended. It only works during the merge, not at the Preview stage. There might be some advantage in using it if it would allow you to avoid saving all the .bmp files to disk (perhaps using copy/paste to put the image sin the data source file. But then it's probably only a few more steps to insert the image directly into the Mail Merge Main Document, e.g. using Word's MailMerge events.

    If you put the file's fullname in the data source, you have to use INCLUDEPICTURE. The field codes you posted work OK here regardless of whether the file is .doc or .docx. 

    Re. (2), macropod has put the relevant field codes in his response in this conversation.



    Peter Jamieson


    Friday, May 3, 2013 9:06 AM
  • I did manage to get it to work.  It was a combination of changing the code and modifying the word document.

    For the word document, I simply used { MERGEFIELD SignatureImage }

    For the code, I inserted dataDocument.Fields.Update() before saving.

    Thanks to everyone for the advise and help.

    Friday, May 3, 2013 1:25 PM
  • If any of the fields pointing to your temp folder remain after you've deleted that folder, anything that later causes the fields to update (eg printing the document) is liable to result in the same errors you reported before. Accordingly, after:
    dataDocument.Fields.Update()
    insert:
    dataDocument.Fields.Unlink()


    Cheers
    Paul Edstein
    [MS MVP - Word]

    Friday, May 3, 2013 1:29 PM