RegexBase64BackgroundImageInliner.java

package fr.sii.ogham.html.inliner.impl.regexp;

import static fr.sii.ogham.html.inliner.impl.regexp.CssImageInlinerConstants.InlineModes.BASE64;
import static java.text.MessageFormat.format;

import java.util.ArrayList;
import java.util.List;

import fr.sii.ogham.core.util.Base64Utils;
import fr.sii.ogham.html.inliner.ContentWithImages;
import fr.sii.ogham.html.inliner.ImageInliner;
import fr.sii.ogham.html.inliner.ImageResource;
import fr.sii.ogham.html.inliner.impl.regexp.CssImageInlinerConstants.InlineModes;

/**
 * Image inliner that reads the image and converts it into a base64 string. The
 * string is then included directly in the HTML content in place of the previous URL/path.
 * 
 * <p>
 * The inlining using base64 is only applied if the attribute
 * {@link CssImageInlinerConstants#INLINE_MODE_PROPERTY} is set to
 * {@link InlineModes#BASE64}.
 * </p>
 * 
 * @author Aurélien Baudet
 *
 */
public class RegexBase64BackgroundImageInliner implements ImageInliner {
	private static final String BASE64_URI = "data:{0};base64,{1}";

	@Override
	public ContentWithImages inline(String htmlContent, List<ImageResource> images) {
		List<Encoded> encoded = new ArrayList<>(images.size());
		String inlined = CssImageInlineUtils.inline(htmlContent, images, BASE64, decl -> encodeImage(decl.getUrl().getUrl(), decl.getImage(), encoded));
		return new ContentWithImages(inlined, new ArrayList<>(0));
	}

	private String encodeImage(String imageUrl, ImageResource image, List<Encoded> encoded) {
		Encoded alreadyEncoded = getEncodedForUrl(imageUrl, encoded);
		if (alreadyEncoded != null) {
			return alreadyEncoded.getEncodedUrl();
		}
		String encodedUrl = format(BASE64_URI, image.getMimetype(), Base64Utils.encodeToString(image.getContent()));
		encoded.add(new Encoded(imageUrl, encodedUrl));
		return encodedUrl;
	}

	private Encoded getEncodedForUrl(String imageUrl, List<Encoded> encoded) {
		// @formatter:off
		return encoded.stream()
				.filter(e -> imageUrl.equals(e.getMatchedUrl()))
				.findFirst()
				.orElse(null);
		// @formatter:on
	}

	private static class Encoded {
		private final String matchedUrl;
		private final String encodedUrl;

		public Encoded(String matchedUrl, String encodedUrl) {
			super();
			this.matchedUrl = matchedUrl;
			this.encodedUrl = encodedUrl;
		}

		public String getMatchedUrl() {
			return matchedUrl;
		}

		public String getEncodedUrl() {
			return encodedUrl;
		}
	}
}