HTML mail merge with before/after text

Hi,

in our application we have implemented mail merge such that, if the merge field name starts with “html_”, the merge is done using html format in this way:

public void fieldMerging(FieldMergingArgs fma) throws Exception {
    // Se il nome del campo inizia con "html_" assumo sia formattato HTML
    if (
        fma.getDocumentFieldName().startsWith("html_")
        && fma.getFieldValue() != null
        && Strings.isValid(fma.getFieldValue().toString().trim())
    ) {
        // effettuo inserimento tramite un DocumentBuilder
        com.aspose.words.DocumentBuilder builder = new com.aspose.words.DocumentBuilder(fma.getDocument());
        builder.moveToMergeField(fma.getDocumentFieldName());
        builder.insertHtml(fma.getFieldValue().toString(), true);

        // e annullo il testo effettivamente già inserito
        fma.setText("");
        return;
    }
}

However, if the field contains any \b or \f switches, to insert text before or after the field when not empty, those are lost.

Is there a way to do the html merge while applying also \b and/or \f switches, with their own format if set?

@mtassinari

Thanks for your inquiry. I am afraid there is no direct method to preserve the switches when moving to Mail Merge Field and inserting HTML using DocumentBuilder. However, as a workaround you can get the text from Mail Merge Field and use it accordingly. Please check sample code snippet as following, hopefully it will help you to accomplish the task.

if (e.getFieldName() == "Name1")
{
    String html = "<b>Jim</b>";
    if (e.getField().getFieldCode().contains("\\b"))
    {
        String textBefore = e.getField().getResult();
        textBefore = textBefore.replace(e.getFieldName(), "");
        textBefore = textBefore.replace("«»", "");

        DocumentBuilder builder = new DocumentBuilder(e.getDocument());
        builder.moveToMergeField(e.getDocumentFieldName(), true, false);
        builder.insertHtml(textBefore + html);

        e.setText("");
    }
}

Thanks for the suggestion.

However, such code would not preserve the specific \b switch text format, right?

Couldn’t the Field object returned by fieldMergingArgs.getField() also expose such switches programmatically? It would be easier this way.

@mtassinari

Yes, in case of inserting HTML using DocumentBuilder, it loses the switches. However, in case of normal Mail Merge data it works.

Thanks for your suggestion, we have logged a ticket WORDSNET-16088 for further investigation. We will keep you updated about the issue resolution progress within this forum thread.

@mtassinari,
The issues you have found earlier (filed as WORDSNET-16088) have been fixed in this Aspose.Words for .NET 17.12 update and this Aspose.Words for Java 17.12 update.
Please also check the following articles:

I really appreciate you have implemented the feature I suggested, however, I have tested the new version, and our previously working code now generates the following exception:

Exception in thread "main" java.lang.NoSuchMethodError: com.aspose.words.ImageFieldMergingArgs.getField()Lcom/aspose/words/Field;
        at com.agews.suite626.mailmerge.ImageMerge.imageFieldMerging(ImageMerge.java:46)
        at com.aspose.words.MailMerge.zzZ(Unknown Source)
        at com.aspose.words.zzZK7.zzZ(Unknown Source)
        at com.aspose.words.FieldMergeField.zzZ(Unknown Source)
        at com.aspose.words.FieldMergeField.zz0h(Unknown Source)
        at com.aspose.words.zz1W.zzx(Unknown Source)
        at com.aspose.words.zz1W.zzy(Unknown Source)
        at com.aspose.words.zz1W.zzR(Unknown Source)
        at com.aspose.words.zz1W.zzT(Unknown Source)
        at com.aspose.words.zz1W.zzW(Unknown Source)
        at com.aspose.words.zz1W.zzYV(Unknown Source)
        at com.aspose.words.zz1W.zzx(Unknown Source)
        at com.aspose.words.zzZK7.zzZ(Unknown Source)
        at com.aspose.words.MailMerge.zzZ(Unknown Source)
        at com.aspose.words.MailMerge.execute(Unknown Source)
        at com.agews.suite626.util.MailMerge.merge(MailMerge.java:204)
        at com.agews.suite626.util.MailMerge.merge(MailMerge.java:126)
        at com.agews.suite626.util.MailMerge.merge(MailMerge.java:101)
        at com.agews.suite626.util.MailMerge.merge(MailMerge.java:89)
        at com.agews.suite626.report.sgi.pannello.Scheda.compile(Scheda.java:523)
        at com.agews.suite626.report.Report.compile(Report.java:192)
        at com.agews.suite626.printer.impl.ReportClass.print(ReportClass.java:36)
        at com.agews.suite626.printer.Print.main(Print.java:133)

line 46 of ImageMerge.java, which is the main cause of this error, is simply:

ifma.getField().remove();

where ifma is an ImageFieldMergingArgs; that line gets called while performing an image merge in case the field is empty or the given path does not exist, so it should just remove the field.

I have also tried going back to previous version, and the code started working again.

@mtassinari,

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

  • Your input Word document
  • Tell us Aspose.Words’ old version number for which there were no problems on your side previously?
  • The output document generated by old version of Aspose.Words.
  • Please create a standalone Java application (source code without compilation errors) that helps us reproduce your problem on our end and attach it here for testing.

As soon as you get these pieces of information ready, we’ll start further investigation into your issue and provide you more information. Thanks for your cooperation.

Hi,

  • the input document is irrelevant, any with an image merge field will do
  • no issue for example with 17.10
  • obviously no output document was generated, since an error was thrown …
  • I am sorry but I cannot provide such application, since that should be a task for the tech support, and not for other companies’ developers

However, for reference, this is the full code of the class where the error is generated:

package com.agews.suite626.mailmerge;

import com.agews.suite626.util.Files;
import com.agews.suite626.util.Strings;
import com.agews.suite626.util.Util;
import com.aspose.words.*;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.charset.StandardCharsets;

/**
 * Classe che si occupa di effettuare il merge delle immagini.
 */
public class ImageMerge implements IFieldMergingCallback {
	/**
	 * Trigger per image field merging, gestisce immagini come path sul disco.
	 *
	 * @param ifma
	 * @throws Exception
	 */
	public void imageFieldMerging(ImageFieldMergingArgs ifma) throws Exception {
		try {
			// immagine richiesta
			Object field = ifma.getFieldValue();
			String path = "";

			// array di byte come path
			if (field instanceof byte[]) {
				path = new String((byte[]) field, StandardCharsets.UTF_8);
			}
			// path come stringa semplice
			else if (field != null) {
				path = field.toString();
			}

			// se
			if (
				// non è valido
				!Strings.isValid(path)
				// o non esite
				|| !Files.exists(path)
			) {
				// rimuovo il campo per evitare errori di inserimento
				ifma.getField().remove();
				return;
			}

			// dimensioni specificate nel documento (eventualmente non presenti)
			double width = ifma.getImageWidth().getValue();
			double height = ifma.getImageHeight().getValue();

			// se le dimensioni non sono già specificate
			if (width < 0 && height < 0) {
				DocumentBuilder builder = new DocumentBuilder(ifma.getDocument());
				builder.moveToMergeField(ifma.getDocumentFieldName(), false, false);

				// se si trova in una cella di una tabella
				Cell cell = (Cell) builder.getCurrentParagraph().getAncestor(NodeType.CELL);
				if (cell != null) {
					CellFormat format = cell.getCellFormat();

					format.setLeftPadding(0.0);
					format.setRightPadding(0.0);
					format.setTopPadding(0.0);
					format.setBottomPadding(0.0);
					format.setWrapText(false);
					format.setFitText(true);

					// uso dimensioni cella
					width = format.getWidth();
					height = cell.getParentRow().getRowFormat().getHeight();
				}

				// se si trova in un textbox
				Shape shape = (Shape) builder.getCurrentParagraph().getAncestor(NodeType.SHAPE);
				if (shape != null && shape.getShapeType() == ShapeType.TEXT_BOX) {
					TextBox tb = shape.getTextBox();

					tb.setInternalMarginBottom(0.0);
					tb.setInternalMarginTop(0.0);
					tb.setInternalMarginLeft(0.0);
					tb.setInternalMarginRight(0.0);

					// uso dimensioni textbox
					width = shape.getWidth();
					height = shape.getHeight();
				}
			}

			// carico immagine
			BufferedImage img = ImageIO.read(new File(path));

			// se sono specificate misure massime
			if (width > 0 || height > 0) {
				// ricavo fattore di scala
				int imgw = img.getWidth();
				int imgh = img.getHeight();

				// calcolo fattore di scalatura
				double scale = Math.max(imgw / width, imgh / height);

				// inserisco l'immagine specificando le dimensioni
				width = imgw / scale;
				height = imgh / scale;
			}

			// immagine come path sul disco
			ifma.setImage(img);

			// imposto le dimensioni
			ifma.getImageWidth().setValue(width);
			ifma.getImageHeight().setValue(height);
		}
		// se qualcosa va male
		catch (Exception e) {
			// in modalità debug
			if (Util.DEBUG) {
				// riporta errore
				throw Util.error("Errore durante il caricamento di un'immagine", e);
			}

			// rimuovo il campo per evitare errori di inserimento
			ifma.getField().remove();
		}
	}

	/**
	 * Trigger per field merging, gestisce HTML.
	 *
	 * @param fma
	 * @throws Exception
	 */
	public void fieldMerging(FieldMergingArgs fma) throws Exception {
		// Se il nome del campo inizia con "html_" assumo sia formattato HTML
		if (
			fma.getDocumentFieldName().startsWith("html_")
			&& fma.getFieldValue() != null
			&& Strings.isValid(fma.getFieldValue().toString().trim())
		) {
			// effettuo inserimento tramite un DocumentBuilder
			com.aspose.words.DocumentBuilder builder = new com.aspose.words.DocumentBuilder(fma.getDocument());
			builder.moveToMergeField(fma.getDocumentFieldName());
			builder.insertHtml(fma.getFieldValue().toString(), true);

			// e annullo il testo effettivamente già inserito
			fma.setText("");
			return;
		}

		// se il valore è una stringa
		if (fma.getFieldValue() instanceof String) {
			// la estraggo
			String value = (String) fma.getFieldValue();
			Boolean checked = false;

			// ne verifico il valore
			switch (value) {
				// se è "true" o false"
				case "true":
				case "TRUE":
					checked = true;
					// fallthrough
				case "false":
				case "FALSE":
					// inserisco un checkbox tramite DocumentBuilder
					DocumentBuilder builder = new DocumentBuilder(fma.getDocument());
					builder.moveToMergeField(fma.getFieldName());
					builder.insertCheckBox(
						fma.getFieldName() + fma.getRecordIndex(),
						checked,
						0
					);

					// e annullo il testo effettivamente già inserito
					fma.setText("");
					break;

				// altrimenti merge normale
				default: break;
			}
		}
	}
}

the error is triggered at line 46, where is the block:

if (
	// non è valido
	!Strings.isValid(path)
	// o non esite
	|| !Files.exists(path)
) {
	// rimuovo il campo per evitare errori di inserimento
	ifma.getField().remove();
	return;
}

@mtassinari,

The fix of WORDSNET-16088 is working correctly on our end. Please try running the following code:

static class MailMergeSwitches implements IFieldMergingCallback {
    public void fieldMerging(FieldMergingArgs e) throws Exception {

        if (e.getFieldName().startsWith("HTML")) {
            if (e.getField().getFieldCode().contains("\\b")) {
                FieldMergeField field = (FieldMergeField) e.getField();

                DocumentBuilder builder = new DocumentBuilder(e.getDocument());
                builder.moveToMergeField(e.getDocumentFieldName(), true, false);
                builder.write(field.getTextBefore());
                builder.insertHtml(e.getFieldValue().toString());
                builder.write(field.getTextAfter());

                e.setText("");
            }
        }
    }

    public void imageFieldMerging(ImageFieldMergingArgs args) throws Exception {
        // Do Nothing
    }
} 

Document doc = new Document("D:\\temp\\in.docx");

doc.getMailMerge().setFieldMergingCallback(new MailMergeSwitches());

doc.getMailMerge().execute(
        new String[]{"HTML_Name"},
        new Object[]{"James Bond"});
        
doc.save("D:\\temp\\awjava-17.12.docx");

Sample input and output Word documents: Docs.zip (17.4 KB)

Since this reply is not, in any way, related to what I reported, I think I might have not correctly explained my problem.

I am not saying that the new feature was implemented incorrectly, in fact I do appreciate that my feedback was listened to.

What I am saying is that, after trying to update to version 17.12, our code wasn’t working anymore, throwing an exception when calling the getField() method on an instance of ImageFieldMergingArgs.

See also my previous post: https://forum.aspose.com/t/html-mail-merge-with-before-after-text/165953/6?u=mtassinari

EDIT: if you’d like, I can open a new post, so that it does not look like that this issue is directly related to “WORDSNET-16088”

@mtassinari,

We are working over your query and will get back to you soon.

@mtassinari,

Please see these documents: image-merging-test.zip (20.9 KB)

And try running the following code with Aspose.Words 17.12:

Document doc = new Document("D:\\temp\\input.docx");

doc.getMailMerge().setFieldMergingCallback(new MailMergeSwitches());

doc.getMailMerge().execute(
        new String[]{"HTML_Name", "mf"},
        new Object[]{"James Bond", ImageIO.read(new File("D:\\temp\\aspose.words.jpg"))});

doc.save("D:\\temp\\awjava-17.12.docx");

static class MailMergeSwitches implements IFieldMergingCallback {
    public void fieldMerging(FieldMergingArgs e) throws Exception {

        if (e.getFieldName().startsWith("HTML")) {
            if (e.getField().getFieldCode().contains("\\b")) {
                FieldMergeField field = (FieldMergeField) e.getField();

                DocumentBuilder builder = new DocumentBuilder(e.getDocument());
                builder.moveToMergeField(e.getDocumentFieldName(), true, false);
                builder.write(field.getTextBefore());
                builder.insertHtml(e.getFieldValue().toString());
                builder.write(field.getTextAfter());

                e.setText("");
            }
        }
    }

    public void imageFieldMerging(ImageFieldMergingArgs e) throws Exception {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write((BufferedImage) e.getFieldValue(), "jpg", os);
        InputStream is = new ByteArrayInputStream(os.toByteArray());
        e.setImageStream(is);
    }
}

I am afraid, we need your input Word document and simple Java application (source code without compilation errors) that will help us to reproduce your problem on our end.

Also, please feel free to create new threads to keep discussions separate.

Hi,

I encountered the same issue today. And had it in the past before as well.
The simple solution is to recompile your code. The obfuscation that happens in the aspose libraries sometimes break links to methods.
This is extremely annoying but anyways now you know the solution.

Kind regards

Thanks for your suggestion, however I had recompiled many times without any effect.