Search code examples
c#mailmergeasposeaspose.wordsinserthtml

MailMerge InsertHtml follow Word-doc alignment


I'm currently working on a MailMerge Word-doc. It is partially filled with strings, and partially with html (which can contain tables, pictures, bold/italic/underlined text, etc.). The word doc also has a custom alignment compared to the default word alignment (so the ruler for left, top, right, bottom is something like 1.5, 0.3, 0.3, 0.3 in my MailMerge word-doc, while the word-default it's something like 2.5, 2.5, 2.5, 2.5.)

Now the issue is that the InsertHtml jumps in (and seem to follow the default word-alignement, instead of our custom one), instead of being aligned by the static text and tables present in the word-doc.

I know the MailMerging uses the default MS Word functions for inserting html, so perhaps the issue lies within that default function instead of Apose's MailMerging. I was just wondering if someone knows a solution.

Here is a visual example of what the result looks like. I've included the ruler where X is the position set on the ruler for alignement. Let's say the text we insert at the HTML part is something very basic, like plain text: "this is a test HTML text". The Word-doc template looks like this:

+ . 2 . | X 1 ............................................... 17 . | . X . |
X         *Start of a table*
.         ...
.         Value:       |     {{Value}}
.         *End of the table*
.
.         *A bold title*
.
.         {{html_Text}}
.
.         *Another bold title*
.
X
29
+

But the result after MailMerging looks like this:

+ . 2 . | X 1 ............................................... 17 . | . X . |
X         *Start of a table*
.         ...
.         Value:       |     2500
.         *End of the table*
.
.         *A bold title*
.
.                  this is a test HTML text
.
.         *Another bold title*
.
X
29
+

The html text is incorrectly aligned following the default Word-ruler, instead of the custom one of that document.

(PS: I know the MailMerging with brackets {{Something}} is less used than <<Something>>, but both work the same. I've had people question the MailMerge-syntax we use in the past, therefore this heads-up.)


Here is the relevant code in our .NET project:

Print DTO object:

public class OurObjectPrintDto
{
    public OurObjectPrintDto(OurObject ob)
    {
        ...
        Value = ob.Value;

        ...

        html_Text = ob.Text;
    }

    public string Value { get; private set; }
    public string html_Text { get; private set; }
}

Method when we click on the Generate Document button:

[HttpGet]
public ActionResult Print(Guid id)
{
    var ourObject = NhSession.GetByGuid<OurObject>(id);
    var printData = new OutObjectPrintDto(ourObject);

    var documentAsByteArray = _documentService.CreateMyObjectPrintAsBytes(printData);

    return File(documentAsByteArray, "application/pdf");
}

CreateMyObjectPrintAsBytes-method:

public byte[] CreateMyObjectPrintAsBytes(MyObject printData)
{
    return GenerateDocument("myobject.docx", printData);
}

private byte[] GenerateDocument(string fileName, object printData)
{
    if (printData == null) throw new ArgumentNullException("printData");

    var path = Path.Combine(_templatePath, fileName);

    using (var fileStream = new File.OpenRead(path))
    {
        var dataSource = new DocumentDataSource(printData);
        return DocumentConverter.GenerateDocument(fileStream, dataSource);
    }
}

DocumentConvert.GenerateDocument-method:

public byte[] GenerateDocument(Stream template, DocumentDataSource dataSource)
{
    var doc = new Document(template);

    doc.MailMerge.UseNonMergeFields = true;
    doc.MailMerge.CleanupOptions = MailMergeCleanupOption.RemoveContainingFields |
                                   MailMergeCleanupOptions.RemoveUnusedFields |
                                   MailMergeCleanupOptions.RemoveUnusedRegions |
                                   MailMergeCleanupOptions.RemoveEmptyParagraphs;
    doc.ResourceLoadingCallback = new ImageLoadingHandler();

    // Support html MailMerge-fields
    doc.MailMerge.FieldMergingCallback = new HandleMergeFieldInsertHtml();

    doc.MailMerge.Execute(dataSource);
    doc.MailMerge.ExecuteWithRegions((IMailMergeDataSourceRoot) dataSource);

    doc.UpdateFields();

    using (var memoryStream = new MemoryStream())
    {
        doc.Save(memoryStream, SaveFormat.Pdf);
        return memoryStream.ToArray();
    }
}

HandleMailMergeFieldInsertHtml-class:

using Aspose.Words;
using Aspose.Words.Reporting;

namespace Sogyo.Util.Pdf
{
    public class HandleMergeFieldInsertHtml : IFieldMergingCallback
    {
        // This is called when merge field is atually merged with data in the document
        void IFieldMergingCallback.FieldMerging(FieldMergingArgs e)
        {
            // All merge field that expect HTML data should be marked with the prefix 'html_'
            if (e.DocumentFieldName.StartsWith("html_") && e.FieldValue != null)
            {
                // Insert the text for this merge field as HTML data
                var documentBuilder = new DocumentBuilder(e.Document);
                documentBuilder.MoveToMergeField(e.DocumentFieldName);
                documentBuilder.InsertHtml((string) e.FieldValue);

                // The HTML text itself should not be inserted.
                // We have already inserted it as an HTML.
                e.Text = "";
            }
        }

        void IFieldMergingCallback.ImageFieldMerging(ImageFieldMergingArgs e)
        {
        }
    }
}

I did try to set the documentBuilder's .PageSetup.LeftMargin in code to change the ruler of the doc. This does change the ruler-alignment of the document, but the inserted html text still jumps in by the same amount, as if a tab is before it or something..


Solution

  • Ok, the problem is fixed.. As a test I tried temporary changing the out-put to .docx instead of .pdf, so I could better look at the tabs and such in the converted document:

    doc.Save(memoryStream, SaveFormat.Pdf);
    // changed to
    doc.Save(memoryStream, SaveFormat.Docx);
    

    and

    return File(documentAsByteArray, "application/pdf");
    // changed to
    return File(documentAsByteArray, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
    

    When I opened the converted .docx I quickly spotted the issue.. It didn't had anything to do with the MailMerging, but with the ruler of the doc itself. The Word-doc ruler has both a grey part and a hourglass-kinda-thingy (the X in my question).

    It was set to this: enter image description here

    instead of this: enter image description here

    After aligning both the hourglass-thingy AND the grey part in my Word doc on top of each other, the html text is also correctly aligned with the rest after MailMerging.