ExecuteWithRegions fails when trying to merge image files to a document

Hello,

We are loading data.xml into a dataset and performing ExecuteWithRegions. The excecution fails with the following error message:

“Cannot load image from field ‘Picture’. The field contains data in unsupported format. The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.”

The image fields are handled by a implementation of IFieldMergingCallback as suggested in your documentation. The image data is correct, and is working if also “Group1” contains image data in the “Picture” element.

Please have a look at the attached template and XML-document demonstrating our issue with a simplified example.

Regards,

Hi
Thanks for your request. As I can see there is strange string inside your XML instead of path to image. Please try using the following code example:

Document doc = new Document("in.doc");
// Perform several mail merge operations populating only part of the document each time.
// Use DataTable as a data source.
DataTable resultsTable = GetInfo();
// Add FieldMergingCallback. It is needed to insert images upon mail merge.
doc.MailMerge.FieldMergingCallback = new MergeImageField();
doc.MailMerge.ExecuteWithRegions(resultsTable);
doc.Save("out.doc");
private class MergeImageField: IFieldMergingCallback
{
    void IFieldMergingCallback.FieldMerging(FieldMergingArgs args)
    {
        // Do nothing.
    }
    void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
    {
        string fileName = e.FieldValue.ToString();
        e.ImageFileName = fileName;
    }
}
private DataTable GetInfo()
{
    // Create data table
    DataTable table = new DataTable("Results");
    table.Columns.Add("Description");
    table.Columns.Add("Img");
    // Add some dummy data.
    table.Rows.Add(new object[]
    {
        "1",
        "C:\\Temp\\img1.jpg"
    });
    table.Rows.Add(new object[]
    {
        "2",
        "C:\\Temp\\img2.jpg"
    });
    table.Rows.Add(new object[]
    {
        "3",
        "C:\\Temp\\img3.jpg"
    });
    return table;
}

The input and the output documents are attached.
Best regards,

Also please see the following link to learn how to insert an image form a database during MailMerge:
https://docs.aspose.com/words/net/types-of-mail-merge-operations/
Hope this helps.
Best regards,

Hi,

Thank you for the quick response.

I will try to explain our issue a little more detailed.

The string inside the <Picture> element is a byte string, and is handled as follows:

void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs args)
{
    if (!string.IsNullOrEmpty(args.FieldValue.ToString()))
    {
        byte[] l_bytes;
        try
        {
            l_bytes = Convert.FromBase64String(args.FieldValue.ToString());
        }
        catch (Exception l_e)
        {
            l_e.Data.Add("FielName", args.FieldName);
            throw;
        }
 
        var l_builder = new DocumentBuilder(args.Document);
        InsertImage(args.FieldName, l_bytes, l_builder);
    }
}

And the byte array is handled in InsertImage:

// Create stream with image
using (Stream l_stream = new MemoryStream(l_bytes))
{
    var l_image = Image.FromStream(l_stream);
    // Calculate height or width to keep aspect ratio when resizing image
    CalculateSizeWithRatio(l_image, l_width, l_height, out l_width, out l_height);
}

This is working properly, and is not the problem.

We experience the problem when some of our xml elements does not contain data.

This XML does work:

<?xml version="1.0" encoding="utf-8"?>
<Report xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<Group1>
		<Tables>
			<Table>
				<Picture>iVBORw0KGgoAAAANSUhEUgAAABYAAAAZCAIAAAC6gEm5AAAAK3RFWHRDcmVhdGlvbiBUaW1lAG1hIDI0IG9rdCAyMDExIDE5OjAyOjMxICswMTAwhqSkGgAAAAd0SU1FB9sKGBEDD81GpogAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAAAH0lEQVR42mM8fus9A2WAcdSIUSNGjRg1YtSIUSMoAADauUAqsktYDQAAAABJRU5ErkJggg==</Picture>
			</Table>
		</Tables>
	</Group1>
	<Group2>
		<Tables>
			<Table>
				<Picture>iVBORw0KGgoAAAANSUhEUgAAABYAAAAZCAIAAAC6gEm5AAAAK3RFWHRDcmVhdGlvbiBUaW1lAG1hIDI0IG9rdCAyMDExIDE5OjAyOjMxICswMTAwhqSkGgAAAAd0SU1FB9sKGBEDD81GpogAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAAAH0lEQVR42mM8fus9A2WAcdSIUSNGjRg1YtSIUSMoAADauUAqsktYDQAAAABJRU5ErkJggg==</Picture>
			</Table>
		</Tables>
	</Group2>
</Report>

This does not work:

<?xml version="1.0" encoding="utf-8"?><Report xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<Group2>
		<Tables>
			<Table>
				<Picture>iVBORw0KGgoAAAANSUhEUgAAABYAAAAZCAIAAAC6gEm5AAAAK3RFWHRDcmVhdGlvbiBUaW1lAG1hIDI0IG9rdCAyMDExIDE5OjAyOjMxICswMTAwhqSkGgAAAAd0SU1FB9sKGBEDD81GpogAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAEZ0FNQQAAsY8L/GEFAAAAH0lEQVR42mM8fus9A2WAcdSIUSNGjRg1YtSIUSMoAADauUAqsktYDQAAAABJRU5ErkJggg==</Picture>
			</Table>
		</Tables>
	</Group2>
</Report> 

This may look like a bug in ExecuteWithRegions, as it does not handle the image insert when the same element name (“Picture”) is used both in “Group1” and “Group2” in the template, and one is missing the image data.

Hi
Thank you for additional information. But unfortunately, I also cannot reproduce the problem on my side. I used the following code and it works fine with both data sources you provided:

[Test]
public void Test001()
{
    DataSet ds = new DataSet();
    ds.ReadXml(@"Test001\data.xml");
    Document doc = new Document(@"Test001\template.docx");
    doc.MailMerge.FieldMergingCallback = new FieldMergingCallback();
    doc.MailMerge.ExecuteWithRegions(ds);
    doc.Save(@"Test001\out.docx");
}
private class FieldMergingCallback: IFieldMergingCallback
{
    public void FieldMerging(FieldMergingArgs args)
    {
        // do nothing.
    }
    void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs args)
    {
        if (!string.IsNullOrEmpty(args.FieldValue.ToString()))
        {
            byte[] l_bytes;
            try
            {
                l_bytes = Convert.FromBase64String(args.FieldValue.ToString());
            }
            catch (Exception l_e)
            {
                l_e.Data.Add("FielName", args.FieldName);
                throw;
            }
            args.ImageStream = new MemoryStream(l_bytes);
        }
    }
}

I used the latest version of Aspose.Words for testing. You can download it from here:
https://releases.aspose.com/words/net
Best regards,

Hi,
Thank you for your patience.

In void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs args) , using args.ImageStream = new MemoryStream(l_bytes); works fine. However, we need to rescale images inserted in e.g. a shape.
We do this with the InsertImage-method (https://blog.aspose.com/2007/05/14/how-to-insert-image-rescaling-it-to-fit-the-contaner-s-size):

var l_builder = new DocumentBuilder(args.Document);
InsertImage(args.FieldName, l_bytes, l_builder);

In InsertImage, we have also tried to replace builder.MoveToMergeField(mergeFieldName) with builder.MoveToField(mergeField, false) to handle multiple fields with identical names, but without a luck.
It seems like the problem somehow is related to the way InsertImage handles image insertion when document contains multiple fields with identical names in different regions. Any clue what might be wrong?
Regards

Hi
Thank you for additional information. Please try using the following code:

[Test]
public void Test001()
{
    DataSet ds = new DataSet();
    ds.ReadXml(@"Test001\data.xml");
    Document doc = new Document(@"Test001\template.docx");
    doc.MailMerge.FieldMergingCallback = new FieldMergingCallback();
    doc.MailMerge.ExecuteWithRegions(ds);
    doc.Save(@"Test001\out.docx");
}
private class FieldMergingCallback: IFieldMergingCallback
{
    public void FieldMerging(FieldMergingArgs args)
    {
        // do nothing.
    }
    void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs args)
    {
        if (!string.IsNullOrEmpty(args.FieldValue.ToString()))
        {
            byte[] l_bytes;
            try
            {
                l_bytes = Convert.FromBase64String(args.FieldValue.ToString());
            }
            catch (Exception l_e)
            {
                l_e.Data.Add("FielName", args.FieldName);
                throw;
            }
            var l_builder = new DocumentBuilder(args.Document);
            InsertImage(args.Field, l_bytes, l_builder);
            // we alredy inserted the image.
            args.Field.Remove();
        }
    }
}
///
/// Replace merge field of the specified name with image.
///
/// Merge field, where the image should be inserted.
/// The merge field itself is destroyed in the process.
/// Byte array of the image file.
/// DocumentBuilder.
/// Returns true, if the merge field with the specified name was found in the document,
/// false û if no such filed exists in the document.
private static void InsertImage(Field mergeField, byte[] imageBytes, DocumentBuilder builder)
{
    builder.MoveToField(mergeField, true);
    // No image resize by default.
    // (Setting size to negative values makes image to be inserted without resizing.)
    double width = -1;
    double height = -1;
    // Check if the image is inserted into table cell.
    Cell cell = (Cell) builder.CurrentParagraph.GetAncestor(typeof(Aspose.Words.Tables.Cell));
    if (cell != null)
    {
        // Set the cell properties so that the inserted image could occupy the cell space exactly.
        cell.CellFormat.LeftPadding = 0;
        cell.CellFormat.RightPadding = 0;
        cell.CellFormat.TopPadding = 0;
        cell.CellFormat.BottomPadding = 0;
        cell.CellFormat.WrapText = false;
        cell.CellFormat.FitText = true;
        // Get cell dimensions.
        width = cell.CellFormat.Width;
        height = cell.ParentRow.RowFormat.Height;
    }
    // Check if the image is inserted into a textbox.
    Shape shape = (Shape) builder.CurrentParagraph.GetAncestor(typeof(Aspose.Words.Drawing.Shape));
    if ((shape != null) && (shape.ShapeType == ShapeType.TextBox))
    {
        // Set the textbox properties so that the inserted image could occupy the textbox space exactly.
        shape.TextBox.InternalMarginTop = 0;
        shape.TextBox.InternalMarginLeft = 0;
        shape.TextBox.InternalMarginBottom = 0;
        shape.TextBox.InternalMarginRight = 0;
        // Get cell dimensions.
        width = shape.Width;
        height = shape.Height;
    }
    // Insert image with or without rescaling, based on the previously done analysis.
    builder.InsertImage(imageBytes, width, height);
}

Hope this helps.
Best regards,

It worked! Thank you very much for excellent support.