JsoupAttachImageInliner.java

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

import static fr.sii.ogham.email.attachment.ContentDisposition.INLINE;
import static fr.sii.ogham.html.inliner.ImageInlinerConstants.INLINED_ATTR;
import static fr.sii.ogham.html.inliner.ImageInlinerConstants.InlineModes.ATTACH;
import static fr.sii.ogham.html.inliner.impl.jsoup.ImageInlineUtils.isInlineModeAllowed;
import static java.text.MessageFormat.format;

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

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import fr.sii.ogham.core.id.generator.IdGenerator;
import fr.sii.ogham.core.resource.ByteResource;
import fr.sii.ogham.email.attachment.Attachment;
import fr.sii.ogham.html.inliner.ContentWithImages;
import fr.sii.ogham.html.inliner.ImageInliner;
import fr.sii.ogham.html.inliner.ImageInlinerConstants;
import fr.sii.ogham.html.inliner.ImageInlinerConstants.InlineModes;
import fr.sii.ogham.html.inliner.ImageResource;

/**
 * Image inliner that loads the image and attaches it to the mail. The image is
 * referenced using a content ID. The content ID is automatically generated.
 * 
 * <p>
 * The inlining using attach mode is only applied if the attribute
 * {@link ImageInlinerConstants#INLINE_MODE_ATTR} is set to
 * {@link InlineModes#ATTACH}.
 * </p>
 * 
 * @author Aurélien Baudet
 *
 */
public class JsoupAttachImageInliner implements ImageInliner {
	private static final String CONTENT_ID = "<{0}>";
	private static final String SRC_ATTR = "src";
	private static final String SRC_VALUE = "cid:{0}";
	private static final String IMG_SELECTOR = "img[src=\"{0}\"]";

	private IdGenerator idGenerator;

	public JsoupAttachImageInliner(IdGenerator idGenerator) {
		super();
		this.idGenerator = idGenerator;
	}

	@Override
	public ContentWithImages inline(String htmlContent, List<ImageResource> images) {
		Document doc = Jsoup.parse(htmlContent);
		List<Attachment> attachments = new ArrayList<>(images.size());
		for (ImageResource image : images) {
			// search all images in the HTML with the provided path or URL that
			// are not skipped
			Elements imgs = getImagesToAttach(doc, image);
			if (!imgs.isEmpty()) {
				String contentId = idGenerator.generate(image.getName());
				// generate attachment
				Attachment attachment = new Attachment(new ByteResource(image.getName(), image.getContent()), null, INLINE, format(CONTENT_ID, contentId));
				// update the HTML to use the generated content id instead of
				// the path or URL
				for (Element img : imgs) {
					img.attr(SRC_ATTR, format(SRC_VALUE, contentId));
					img.attr(INLINED_ATTR, true);
				}
				attachments.add(attachment);
			}
		}
		return new ContentWithImages(doc.outerHtml(), attachments);
	}

	private static Elements getImagesToAttach(Document doc, ImageResource image) {
		Elements imgs = doc.select(format(IMG_SELECTOR, image.getSrcUrl()));
		Elements found = new Elements();
		for (Element img : imgs) {
			// skip images that have skip-attach attribute
			if (isInlineModeAllowed(img, ATTACH)) {
				found.add(img);
			}
		}
		return found;
	}

}