While generating using Aspose.Words mail merge functionality font colour and is different

Hello All,
We are using DocParts to generate content for Aspose.Words mail merge. Within the DocParts, we utilize Word’s Outline/Navigation feature, applying built-in heading styles (e.g., Heading 1, Heading 2) to enable collapsible sections for better document structure.

However, when we insert these DocParts into our template and generate the final document using our algorithm, we’re encountering formatting inconsistencies:

  • The font color changes from black (as defined in the DocPart) to blue.
  • The font style changes from Arial to Aptos (Body).

We want to preserve the original formatting defined in the DocPart (including font family, size, and color) and prevent any automatic style overrides during document generation.

Attaching screenshot for your reference

@Vaibhavi_Lad Could you please attach your sample template and data that will allow us to reproduce the problem? We will check the issue and provide you more information.
As I can see from the provided screenshot you are using LINQ Reporting Engine, not Mail Merge feature.

Yes, We are using LINQ Reporting Engine only. So
this is the docpart which I am using
docpart.docx (24.6 KB)
in below template
aspose.docx (14.3 KB)

in our system we are using docpart as above in template then using
data.docx (18.0 KB)

as data/json for generating document while doing that I am getting
output.docx (15.9 KB)

We are using the LINQ Reporting Engine provided by Aspose.Words.

  • We have a reusable content part file: docpart.docx
  • This docpart is included in our main template: aspose.docx
  • During document generation, we use data defined in: data.docx (converted to JSON or mapped accordingly)
  • However, the final generated output: output.docx does not produce the expected results.

We are inserting the docpart into the main template using the LINQ tag syntax

@Vaibhavi_Lad Thank you for additional information. The problem occurs because by default, while inserting content of an outer document, the engine applies corresponding styles of a template document. This makes content of a result document look more consistent. However, you can keep source formatting for content being inserted by using a sourceStyles switch. Please try modifying your template like this:

<<doc [DocPart_test_aspose] -build -sourceStyles>>

Hello ,
Above syntax works perfectly well but If I have two same docpart which I want to give for two docpart like

 <<if [false]>><<doc [DocPart_CoverLetter_Docpart06212025_2] -build>><</if>>
<<doc [DocPart_CoverLetter_Docpart06212025_2] -sourceStyles -build>>

<<if [false]>><<doc [DocPart_StateDisc_Docpart_06212025_2] -build>><</if>>
<<doc [DocPart_StateDisc_Docpart_06212025_2] -sourceStyles -build>>

above syntax give me error which I attach screen shot while DocPart_StateDisc_Docpart_06212025_2 this docpart exists .

can you provide me syntax which can retain style for both docpart in one template ?

@Vaibhavi_Lad Could you please provide simple code that will allow us to reproduce the problem? I tested with the following simple code and everything works as expected:

Document doc = new Document(@"C:\Temp\in.docx");
            
ReportingEngine engine = new ReportingEngine();
engine.BuildReport(doc,
    new object[] { new Document(@"C:\Temp\1.docx"), new Document(@"C:\Temp\2.docx") },
    new string[] { "DocPart_CoverLetter_Docpart06212025_2", "DocPart_StateDisc_Docpart_06212025_2" });

doc.Save(@"C:\Temp\out.docx");

1.docx (13.8 KB)
2.docx (13.8 KB)
in.docx (14.1 KB)
out.docx (11.2 KB)

Hello,
I am provide you sample example that you can regenerate above output
docpart_1 and docpart_2 are two doc part which I want to generate together
I uploaded using template name two_docpart then the resulted output has which has mismatch of styles which is attach screen shot and for same if I do with tag you suggested then it template used updated with latest syntax then output that I am getting. Can you please suggest how can I use both docpart in one template ?
output.docx (14.6 KB)

Updated_with_latest_syntax.docx (14.6 KB)

Two_docpart.docx (14.5 KB)

Generated_Document.docx (15.7 KB)

docpart_2.docx (980.2 KB)

docpart_1.docx (980.7 KB)

@Vaibhavi_Lad With the following code:

Document doc = new Document(@"C:\Temp\Updated_with_latest_syntax.docx");

ReportingEngine engine = new ReportingEngine();
engine.BuildReport(doc,
    new object[] { new Document(@"C:\Temp\docpart_1.docx"), new Document(@"C:\Temp\docpart_2.docx") },
    new string[] { "DocPart_docpart_1", "DocPart_docpart_2" });

doc.Save(@"C:\Temp\out.docx");

I get the same output as yours: out.docx (13.1 KB), that is expected as formatting of the doc parts is preserved.

{
    if (mergeReq is null)
    {
        throw new ArgumentNullException(nameof(mergeReq));
    }

    using var template = new MemoryStream(Convert.FromBase64String(mergeReq.Settings.Template));
    var doc = new Document(template);

    //Append Docs
    if (mergeReq.Data.TryGetProperty("__AppendDocs", out var appendDocs))
    {
        doc.AppendDocs(appendDocs);
    }


    // Replace snippets
    if (mergeReq.Data.TryGetProperty("__Templates", out var templates))
    {
        doc.ExecuteReplaceBySnippet(templates);
    }

    // Report engine
    var reportingEngine = new ReportingEngine();
    reportingEngine.KnownTypes.Add(typeof(x));
    reportingEngine.KnownTypes.Add(typeof(m));
    reportingEngine.KnownTypes.Add(typeof(Math));
    reportingEngine.KnownTypes.Add(typeof(DateTime));
    // This is Code where we enable the error message in our generated document
    var reportBuildOptions = ReportBuildOptions.InlineErrorMessages;
    if (!mergeReq.Settings.ReportEngineOptions.IsNullOrWhiteSpace())
    {
        var engineOptions = mergeReq.Settings.ReportEngineOptions.Split(new[] { '|', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (var option in engineOptions)
        {
            reportBuildOptions = reportBuildOptions | option.ParseEnum<ReportBuildOptions>(true);
        }
    }
    reportingEngine.Options = reportBuildOptions;
    using MemoryStream ms = new(Encoding.UTF8.GetBytes(mergeReq.Data.ToJsonString()));
    JsonDataLoadOptions jOpt = new ();
    jOpt.SimpleValueParseMode = JsonSimpleValueParseMode.Strict;
    jOpt.ExactDateTimeParseFormats = [string.Empty];
    reportingEngine.BuildReport(doc, new JsonDataSource(ms, jOpt), "ds");


    // MailMerge
    Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
    var data = mergeReq.Data
        .EnumerateObject()
        .ToDictionary(p => p.Name, p =>
        {
            switch (p.Value.ValueKind)
            {
                case JsonValueKind.String:
                case JsonValueKind.True:
                case JsonValueKind.False:
                    return p.Value.GetString() as object;
                case JsonValueKind.Number:
                    return p.Value.GetDouble() as object;
                case JsonValueKind.Array:
                case JsonValueKind.Object:
                case JsonValueKind.Null:
                case JsonValueKind.Undefined:
                default:
                    return "" as object;
            }
        });

    var mailMergeCleanupOptions = MailMergeCleanupOptions.None;
    if (!mergeReq.Settings.MailMergeOptions.IsNullOrWhiteSpace())
    {
        var mailMergeOptions = mergeReq.Settings.MailMergeOptions.Split(new[] { '|', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
        foreach (var option in mailMergeOptions)
        {
            mailMergeCleanupOptions = mailMergeCleanupOptions | option.ParseEnum<MailMergeCleanupOptions>(true);
        }
    }

    doc.MailMerge.CleanupOptions = mailMergeCleanupOptions;
    doc.MailMerge.Execute(data.Keys.ToArray(), data.Values.ToArray());

    using var outputStream = new MemoryStream();
    var saveFormat = mergeReq.Settings.OutputDocType.ParseEnum<SaveFormat>(true);
    if (saveFormat == SaveFormat.Pdf)
    {
        PdfSaveOptions pdfOpts = new PdfSaveOptions();
        if (!mergeReq.Settings.OutputDocOptions.IsNullOrWhiteSpace())
            pdfOpts.Compliance = mergeReq.Settings.OutputDocOptions.ParseEnum<PdfCompliance>(true);
        pdfOpts.SaveFormat = saveFormat;
        doc.Save(outputStream, pdfOpts);
    }
    else if (saveFormat == SaveFormat.Text)
    {
        TxtSaveOptions textOpts = new();
        textOpts.SaveFormat = saveFormat;
        doc.Save(outputStream, textOpts);
    }
    else
    {
        OoxmlSaveOptions docOpts = new OoxmlSaveOptions();
        docOpts.SaveFormat = saveFormat;
        doc.Save(outputStream, docOpts);
    }

    return outputStream.ToArray();
}

above code I am using for generation can you please suggest me here why I am getting that error ??

@Vaibhavi_Lad Could you please create a simple console application that will demonstrate the problem? Unfortunately, I cannot reproduce the problem on my side.

Hello ,
Can you please try with below code snippet ?

using Aspose.Words;
using Aspose.Words.Reporting;
using System.Text;
using System.Text.Json;

var doc = new Document("two_docpart.docx");

// Reporting engine setup
var engine = new ReportingEngine();
engine.Options = ReportBuildOptions.InlineErrorMessages;

var json = "{ \"Title\": \"Test Document\" }";
using var jsonStream = new MemoryStream(Encoding.UTF8.GetBytes(json));

// Build document with both docparts
engine.BuildReport(doc, new JsonDataSource(jsonStream), "ds");

// Save output
doc.Save("Output.docx");

However, I would like to clarify that the code sample you’ve provided:

csharp

CopyEdit

engine.BuildReport(doc,
    new object[] { new Document("docpart_1.docx"), new Document("docpart_2.docx") },
    new string[] { "DocPart_docpart_1", "DocPart_docpart_2" });

is not the same scenario I’m reporting.


:firecracker: Why It’s Different:

  • Your code example builds external Document instances and injects them as data.
  • In my case, the DocParts are defined inside the GlossaryDocument of the template (two_docpart.docx) — and I’m referencing them with this syntax in the main body:
<<doc [docpart_1] -sourceStyles -build>>
<<doc [docpart_2] -sourceStyles -build>>

This is using Aspose’s DocPart rendering feature, not external file injection.

1 Like

Could you please:

  • Confirm whether <<doc [...] -build>> should apply the main template’s styles when rendering multiple DocParts?
  • Let us know if there’s a way to normalize styles across multiple DocParts inside a template?
  • Or whether we need to manually merge styles from DocParts into the main document before build?

@Vaibhavi_Lad Thank you for additional information.

  1. Yes, with your code I can reproduce the same output as yours and the output is expect. Your data source does not contain value for DocPart_docpart_1 field. Since ReportBuildOptions.InlineErrorMessages option is set, Aspose.Words shows the error in the output document. The error is shown for the first encountered problem, in your case, this is missed DocPart_docpart_1 field. So the output is expected and correct.

Quote from the documentation:

A doc tag denotes a placeholder within a template for a document to be inserted during runtime. > An expression declared within a doc tag is used by the engine to load a document to be inserted during runtime. The expression must return a value of one of the following types:

  • A byte array containing document data
  • A Stream instance > able to read document data
  • An instance of the Document class
  • A string containing a document URI, path, or Base64-encoded document data

As you can see there is no option to insert doc part defined inside the GlossaryDocument of the template.

By default, while inserting content of an outer document, the engine applies corresponding styles of a template document. You can keep source formatting for content being inserted by using a sourceStyles switch.