Remove Cover Letter from Mail Merged Word File using C# .NET | Append Documents & Preserve Font Formatting

Using Aspose.Words 13.2.0.0

Code from a sample console application:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Aspose.Words;
using System.IO;
using System.Text.RegularExpressions;
using AsposeCoverLetter;

namespace ConsoleAspose
{
    public class AsposeCoverLetter
    {
        public void DoWork()
        {
            Document template = new Document(this.GetType().Assembly.GetManifestResourceStream("ConsoleAspose.Template.docx"));
            TemplateDataSourceDTO ds = DefineDataSource();

            string hpCoverLetterName = "My Health Plan";
            string hpName = hpCoverLetterName;

            MailMerge(ds, template, hpCoverLetterName, hpName, true, false);
        }

        private Document MailMerge(TemplateDataSourceDTO dataSource, Document documentTemplate, string hpCoverLetterName, string hpName, bool isViewOnly, bool isFromTemplateMaintenancePreview)
        {
            if (dataSource != null && documentTemplate != null && string.IsNullOrEmpty(hpCoverLetterName) == false && string.IsNullOrEmpty(hpName) == false)
            {
                License asposeWordsLicense = new License();
                asposeWordsLicense.SetLicense("Aspose.Words.lic");

                // do the full merge with the template as is
                Document mergedFullDocument = documentTemplate.Clone();
                mergedFullDocument.MailMerge.Execute(dataSource);

                OutputDocs(" initial merge as is ", mergedFullDocument, dataSource, documentTemplate);

                if (isFromTemplateMaintenancePreview == true || dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.CoverLetterHealthPlan.ToString()) == true)
                {
                    // Determine if the template already contains a cover letter.
                    bool mergedFullDocumentContainsCoverLetter = false;
                    string address = dataSource[AuthLetterTemplateBusiness.FieldName.MemberStreetAddress.ToString()];
                    string cityStateZip = dataSource[AuthLetterTemplateBusiness.FieldName.MemberCityStateZip.ToString()];

                    if (string.IsNullOrEmpty(address) || string.IsNullOrEmpty(cityStateZip))
                    {
                        throw new Exception("Missing address and/or city state zip in Letter Gen mail merge");
                    }

                    Regex regAddress = new Regex(address, RegexOptions.None);
                    Regex regCityStateZip = new Regex(cityStateZip, RegexOptions.None);

                    DocumentPageSplitter splitterDocCopy = new DocumentPageSplitter(mergedFullDocument);
                    // mergedPartialDocument now contains the first 2 pages of the mergedFullDocument document
                    Document mergedPartialDocument = splitterDocCopy.GetDocumentOfPageRange(1, 2);    // startIndex and endIndex are 1-based! 

                    // looking for at least 2 instances: cover is 1, first page would be the 2nd
                    if (mergedPartialDocument.Range.Replace(regAddress, address) >= 2 && mergedPartialDocument.Range.Replace(regCityStateZip, cityStateZip) >= 2)
                    {
                        // yes, mergedPartialDocument has a cover page with member address embeded
                        mergedFullDocumentContainsCoverLetter = true;
                        // set mergedPartialDocument to page 2 and on.... essentially removes the cover page that's built into the template
                        //mergedPartialDocument = splitterDocCopy.GetDocumentOfPageRange(2, mergedFullDocument.PageCount).Clone();
                        mergedPartialDocument = splitterDocCopy.GetDocumentOfPageRange(2, mergedFullDocument.PageCount);

                        OutputDocs(" cover page removed", mergedPartialDocument, dataSource, documentTemplate);
                    }

                    Document coverLetter;
                    string coverLetterOverrideSalutation = null;

                    if (isFromTemplateMaintenancePreview == true
                        ||
                        (string.IsNullOrEmpty(dataSource[AuthLetterTemplateBusiness.FieldName.CoverLetterHealthPlan.ToString()]) == true
                        || dataSource[AuthLetterTemplateBusiness.FieldName.CoverLetterHealthPlan.ToString()].ToLower() == "unknown"))
                    {
                        // this has NOT been merged with a fixed cover letter
                        coverLetterOverrideSalutation = null;
                    }
                    else
                    {
                        // this has been merged with a fixed cover letter before
                        coverLetterOverrideSalutation = (isViewOnly == true ? dataSource[AuthLetterTemplateBusiness.FieldName.CoverLetterMemberTo.ToString()] : null);
                    }

                    coverLetter = GetCoverLetter(dataSource, hpCoverLetterName, hpName, coverLetterOverrideSalutation);
                    // adding .Clone() at the end does't fix everything
                    //coverLetter = GetCoverLetter(dataSource, hpCoverLetterName, hpName, coverLetterOverrideSalutation).Clone();
                    // try adding .Clone() with AppendDocument to coverLetter directly - no effect, font is still decreased
                    //coverLetter = GetCoverLetter(dataSource, hpCoverLetterName, hpName, coverLetterOverrideSalutation).Clone();

                    // Create a new combined document and start with adding the cover letter
                    //Document combinedDocument = new Document();
                    //combinedDocument.RemoveAllChildren();
                    //coverLetter.FirstSection.PageSetup.SectionStart = SectionStart.NewPage;
                    //combinedDocument.AppendDocument(coverLetter, ImportFormatMode.KeepSourceFormatting);

                    // now append to the combined document whatever is appropriate.
                    if (mergedFullDocumentContainsCoverLetter == true)
                    {
                        // combine with mergedPartialDocument that no longer contains the cover page (it was removed above)
                        mergedPartialDocument.FirstSection.PageSetup.SectionStart = SectionStart.NewPage;
                        // as is 
                        //combinedDocument.AppendDocument(mergedPartialDocument, ImportFormatMode.KeepSourceFormatting);
                        // try AppendDocument to coverLetter directly
                        coverLetter.AppendDocument(mergedPartialDocument, ImportFormatMode.KeepSourceFormatting);
                        // try AppendDocument to coverLetter directly - with ImportFormatMode.UseDestinationStyles
                        //coverLetter.AppendDocument(mergedPartialDocument, ImportFormatMode.UseDestinationStyles);
                        // *** Adding .Clone() doesn't fix it
                        //combinedDocument.AppendDocument(mergedPartialDocument.Clone(), ImportFormatMode.KeepSourceFormatting);
                    }
                    else
                    {
                        // combine with entire merged full document that doesn't contain a cover letter
                        mergedFullDocument.FirstSection.PageSetup.SectionStart = SectionStart.NewPage;
                        // as is
                        //combinedDocument.AppendDocument(mergedFullDocument, ImportFormatMode.KeepSourceFormatting);
                        // try AppendDocument to coverLetter directly
                        coverLetter.AppendDocument(mergedFullDocument, ImportFormatMode.KeepSourceFormatting);
                        // try AppendDocument to coverLetter directly - with ImportFormatMode.UseDestinationStyles
                        //coverLetter.AppendDocument(mergedFullDocument, ImportFormatMode.UseDestinationStyles);
                        // *** Adding .Clone() doesn't fix it
                        //combinedDocument.AppendDocument(mergedFullDocument.Clone(), ImportFormatMode.KeepSourceFormatting);
                    }

                    // as is
                    //OutputDocs(null, combinedDocument, dataSource, documentTemplate);
                    //return combinedDocument;

                    // try AppendDocument to coverLetter directly
                    OutputDocs(" with replaced cover letter", coverLetter, dataSource, documentTemplate);
                    return coverLetter;
                }
                else
                {
                    // generated/merged previously, before fixed cover page enhancement, return merged doc as as.
                    return mergedFullDocument;
                }
            }

            return null;
        }

        private void OutputDocs(string addFileName, Document letter, TemplateDataSourceDTO dataSource, Document template)
        {
            string path = @"C:\Temp\Letter Gen INC\Output Letters\";
            if (Directory.Exists(path) == false)
            {
                Directory.CreateDirectory(path);
            }

            string authNo = dataSource[AuthLetterTemplateBusiness.FieldName.AuthorizationNumber.ToString()];
            string outputFile = null;

            // output template only if doesn't already exist
            outputFile = string.Format("{0}{1} Template.docx", path, authNo);
            if (File.Exists(outputFile) == false)
            {
                template.Save(outputFile);
            }

            // output docx, delete first if already exists
            outputFile = string.Format("{0}{1}{2}.docx", path, authNo, addFileName);
            if (File.Exists(outputFile) == true)
            {
                File.Delete(outputFile);
            }
            letter.Save(outputFile);

            // output pdf, delete first if already exists
            outputFile = string.Format("{0}{1}{2}.pdf", path, authNo, addFileName);
            if (File.Exists(outputFile) == true)
            {
                File.Delete(outputFile);
            }
            letter.Save(outputFile, SaveFormat.Pdf);
        }

        private Document GetCoverLetter(TemplateDataSourceDTO dataSource, string letterHPName, string hpName, string overrideSalutation)
        {
            Document coverLetter = null;
            Document coverLetterTemplate = null;

            if (dataSource != null)
            {
                coverLetterTemplate = new Document(this.GetType().Assembly.GetManifestResourceStream("ConsoleAspose.MHCAddress.docx"));

                string mbrSalutation = null;
                string mbrStreetAddress = null;
                string mbrCityStateZip = null;
                string mbrHealthPlan = null;

                if (string.IsNullOrEmpty(overrideSalutation) == true)
                {
                    if (dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.Salutation.ToString()))
                    {
                        mbrSalutation = dataSource[AuthLetterTemplateBusiness.FieldName.Salutation.ToString()];
                    }
                    else if (dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.BeneficiaryName.ToString()))
                    {
                        mbrSalutation = dataSource[AuthLetterTemplateBusiness.FieldName.BeneficiaryName.ToString()];
                    }
                    else if (dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.AddresseeName.ToString()))
                    {
                        mbrSalutation = dataSource[AuthLetterTemplateBusiness.FieldName.AddresseeName.ToString()];
                    }
                    else if (dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.MemberName.ToString()))
                    {
                        mbrSalutation = dataSource[AuthLetterTemplateBusiness.FieldName.MemberName.ToString()];
                    }
                }
                else
                {
                    mbrSalutation = overrideSalutation;
                }

                if (dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.MemberStreetAddress.ToString()))
                {
                    mbrStreetAddress = dataSource[AuthLetterTemplateBusiness.FieldName.MemberStreetAddress.ToString()];
                }

                if (dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.MemberCityStateZip.ToString()))
                {
                    mbrCityStateZip = dataSource[AuthLetterTemplateBusiness.FieldName.MemberCityStateZip.ToString()];
                }

                mbrHealthPlan = letterHPName;
                if (string.IsNullOrEmpty(mbrHealthPlan) || mbrHealthPlan.ToLower() == "unknown")
                {
                    mbrHealthPlan = hpName;
                }

                if (string.IsNullOrEmpty(mbrHealthPlan) == false && string.IsNullOrEmpty(mbrSalutation) == false && string.IsNullOrEmpty(mbrStreetAddress) == false
                    && string.IsNullOrEmpty(mbrCityStateZip) == false)
                {
                    TemplateDataSourceDTO dsCover = new TemplateDataSourceDTO();

                    dsCover[AuthLetterTemplateBusiness.FieldName.MemberHealthPlan.ToString()] = mbrHealthPlan;
                    dsCover[AuthLetterTemplateBusiness.FieldName.Salutation.ToString()] = mbrSalutation;
                    dsCover[AuthLetterTemplateBusiness.FieldName.MemberStreetAddress.ToString()] = mbrStreetAddress;
                    dsCover[AuthLetterTemplateBusiness.FieldName.MemberCityStateZip.ToString()] = mbrCityStateZip;

                    //coverLetterTemplate.MailMerge.Execute(dsCover);

                    coverLetter = coverLetterTemplate.Clone();
                    coverLetter.MailMerge.Execute(dsCover);

                    if (dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.CoverLetterHealthPlan.ToString()) == true)
                    {
                        dataSource[AuthLetterTemplateBusiness.FieldName.CoverLetterHealthPlan.ToString()] = mbrHealthPlan;
                    }
                    else
                    {
                        dataSource.Add(AuthLetterTemplateBusiness.FieldName.CoverLetterHealthPlan.ToString(), mbrHealthPlan);
                    }

                    if (dataSource.ContainsKey(AuthLetterTemplateBusiness.FieldName.CoverLetterMemberTo.ToString()) == true)
                    {
                        dataSource[AuthLetterTemplateBusiness.FieldName.CoverLetterMemberTo.ToString()] = mbrSalutation;
                    }
                    else
                    {
                        dataSource.Add(AuthLetterTemplateBusiness.FieldName.CoverLetterMemberTo.ToString(), mbrSalutation);
                    }
                }
                else
                {
                    throw new Exception("One more missing member fields for Letter Gen cover letter");
                }
            }

            return coverLetter;
        }

        private TemplateDataSourceDTO DefineDataSource()
        {
            TemplateDataSourceDTO ds = new TemplateDataSourceDTO();
            ds.Add("DateToday", DateTime.Today.ToShortDateString());
            ds.Add("BeneficiaryName", "TOE, HARRY");
            ds.Add("MemberStreetAddress", "1234 MOCKINGBIRD LANE");
            ds.Add("MemberCityStateZip", "GARDEN GROVE CA 92843");
            ds.Add("DateToday, June 01", "2020]");
            ds.Add("MemberID", "007123");
            ds.Add("AuthorizationNumber", "PIPER1234");
            ds.Add("RequestingProviderName", "DR.SAM GOTCHA");
            ds.Add("ServiceRequested", "A Dermatology(skin specialist) Office Visit");
            ds.Add("ClinicalReviewerSpecialty", "B.Smith, MD., Dermatology");
            ds.Add("ClinicalReviewerSpecialty_Text", "B.Smith, MD., Dermatology");
            ds.Add("ReasonsForDenial", "The information we received from your doctor does not support the need for this service.We cannot approve your request for a dermatology(skin specialist) office visit.              We have reviewed the information provided.Your information states that you have dermatitis.This is swelling of the skin.You also have furuncle.This is a boil or painful swelling of the skin.The requested office visit is not medically necessary for your condition.The information does not show that your doctor can not care for your condition; you have failed standard treatment; or there is a question about your condition.As a result, the requested service has been denied.We based this decision on Monarch Healthcare Clinical Review Criteria- Dermatology Referral Guidelines.");
            ds.Add("RecomendAltTreatment", "Instead of the service requested, we are recommending the following: please contact your primary care doctor for further evaluation, treatment and discussion of alternatives");
            ds.Add("ServiceRequestedProviderName", "DR.JONATHAN FEELGOOD");
            ds.Add("MemberPCP", "DR.SAM GOTCHA");
            ds.Add("CoverLetterHealthPlan", "CareShore");
            ds.Add("CoverLetterMemberTo", "TOE, HARRY");
            ds.Add("ClinicalNoteDenialType", "Medical Necessity(CALO)");
            ds.Add("ClinicalNoteDenialType_Text", "Medical Necessity(CALO)");
            ds.Add("ClinicalNoteMedicalDirector", "Deanna Wasabi, MD");
            ds.Add("ClinicalNoteMedicalDirector_Text", "Deanna Wasabi, MD");

            return ds;
        }

    }
}

Let me explain the issue better.

I have a template with a cover letter as well as content. I do a mail merge on that template. I then programmatic ally remove the cover letter from that resultant document.

I have another template - all it has is a cover letter. I do a mail merge on that template.to generate the cover letter. I then try to use AppendDocument to put the two documents together. In the resultant document there are unwanted font changes.

@dwasabi,

To ensure a timely and accurate response, please ZIP and attach the following resources here for testing:

  • Your simplified template Word documents
  • Aspose.Words for .NET 20.6 generated mail-merged output files
  • Aspose.Words’ generated final document showing the undesired behavior
  • Please also create a standalone simple Console application (source code without compilation errors) that helps us to reproduce your current problem on our end and attach it here for testing. Please do not include Aspose.Words DLL files in it to reduce the file size
  • Any other resources required to successfully replicate this issue on our end

As soon as you get these pieces of information ready, we will start investigation into your scenario and provide you more information. Thanks for your cooperation.

Please see attached files: Code.zip and Docs.zip using Aspose.Words 20.6.0.0

Here’s a description of the documents in Docs.zip:
PIPER1234 Template.docx — documentTemplate: Template containing cover letter and content. This is embedded resource file Template.docx
PIPER1234 initial merge as is.docx — mergedFullDocument: Mail merged document using documentTemplate as is
PIPER1234 cover page removed.docx — mergedPartialDocument: mergedFullDocument with cover page programatically removed
PIPER1234 cover page only.docx — coverLetter: created through merge using embedded resource file MHCAddress.docx as template
PIPER1234 with replaced cover letter.docx — result document from call to coverLetter.AppendDocument(mergedPartialDocument, ImportFormatMode.KeepSourceFormatting)

Prior to updating to 20.6 there were changes in the font in the final document, PIPER1234 with replaced cover letter.docx. That doesn’t seem to be an issue any longer.

The issue is now in the final document, following the text, “You have the right to appeal our decision”, there’s a section break that did not exist in the template.

Thank you for your help on this.Docs.zip (888.5 KB)
Code.zip (248.5 KB)

@dwasabi

This problem occurs because you are using 20.6 version of Aspose.Words for .NET in evaluation mode (i.e. without applying a license). After an initial test with the licensed latest (20.6) version of Aspose.Words for .NET, we were unable to reproduce this issue on our end. Please see the output documents generated on our end by using the code you provided.

If you want to test 20.6 version of ‘Aspose.Words for .NET’ API without the evaluation/trial version limitations, then you can request a 30-day Temporary License. Please refer to How to get a Temporary License?.