How to implement a custom content inliner in YARG?

Hi

(In standalone YARG used with Spring and java configuration):

When the bands are in json, we cannot use the existing bitmap inliner (${bitmap:WIDTHxHEIGHT), because the binary data needs to be encoded in json (here - in base64).

So we can create a custom inliner, which will decode the base64 string with binary image data, and it is easy enough:

public class Base64BitmapContentInliner extends AbstractInliner {
    private final static String REGULAR_EXPRESSION = "\\$\\{base64bitmap:([0-9]+?)x([0-9]+?)\\}";

    public Base64BitmapContentInliner() {
        tagPattern = Pattern.compile(REGULAR_EXPRESSION, Pattern.CASE_INSENSITIVE);
    }

    public Pattern getTagPattern() {
        return tagPattern;
    }

    protected byte[] getContent(Object paramValue) {
        String base64string = (String) paramValue;
        return Base64.getDecoder().decode(base64string);
    }
}

But how can we use the custom inliner? How to pass it in code? The wiki says, that:

See the list com.haulmont.yarg.formatters.impl.AbstractFormatter#contentInliners where you can add your own inliners during formatter construction.

In the AbstractFormatter we have the getContentInliners() method, so maybe we can use it to modify the default list of inliners. But when can we use it?

I suppose it will be somewhere here:

Report report = reportBuilder.build();

Reporting reporting = new Reporting();
reporting.setFormatterFactory(new DefaultFormatterFactory());
reporting.setLoaderFactory(new DefaultLoaderFactory().setJsonDataLoader(new JsonDataLoader()));

reporting.runReport(new RunParams(report).params(reportParams), response.getOutputStream());

There is a “DefaultFormatterFactory”. But I have no idea how to pass the custom inliner?

Hi,

You can create own formatter factory that extends DefaultFormatterFactory and overrides method createFormatter:

 @Override
 public ReportFormatter createFormatter(FormatterFactoryInput factoryInput) {
        ReportFormatter formatter = super.createFormatter(factoryInput);
        if (formatter instanceof AbstractFormatter) {
            AbstractFormatter abstractFormatter = (AbstractFormatter) formatter;
            abstractFormatter.getContentInliners().add(new YourContentInliner());
        }
        return formatter;
 }

Thank you very much for the answer! I’ll check it later and mark it as solving the problem.

I have followed this approach to implement both a Base64BitmapContentInliner and a FormatterFactory that adds the formatter to the content inliners. Unfortunately, the produced Word Doc (and subsequently generated PDF) shows the encoded string as a string instead of an image. What am I doing wrong?

import com.haulmont.yarg.formatters.factory.FormatterFactoryInput;
import com.haulmont.yarg.formatters.impl.AbstractFormatter;

public class DMASFormatterFactory extends DefaultFormatterFactory {

	@Override
	public ReportFormatter createFormatter(FormatterFactoryInput factoryInput) {
		ReportFormatter formatter = super.createFormatter(factoryInput);
		if (formatter instanceof AbstractFormatter) {
			AbstractFormatter abstractFormatter = (AbstractFormatter) formatter;
			abstractFormatter.getContentInliners().add(new Base64BitmapContentInliner());
		}
		return formatter;
	}

}

public class Base64BitmapContentInliner extends AbstractInliner {
	private final static String REGULAR_EXPRESSION = "\\$\\{base64bitmap:([0-9]+?)x([0-9]+?)\\}";

	public Base64BitmapContentInliner() {
		tagPattern = Pattern.compile(REGULAR_EXPRESSION, Pattern.CASE_INSENSITIVE);
	}

	public Pattern getTagPattern() {
		return tagPattern;
	}

	protected byte[] getContent(Object paramValue) {
		String base64string = (String) paramValue;
		return Base64.getDecoder().decode(base64string);
	}
}

reportBuilder.format(new ReportFieldFormatImpl("Main.image", "${base64bitmap:100x40}"));

Never mind. Figured it out. Thanks anyway!