All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xlrit.gears.plugin.docx.XDocReportDocumentProcessor Maven / Gradle / Ivy

package com.xlrit.gears.plugin.docx;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import com.lowagie.text.FontFactory;
import com.xlrit.gears.base.content.ContentRef;
import com.xlrit.gears.base.content.ContentStore;
import com.xlrit.gears.base.model.Document;
import com.xlrit.gears.engine.document.DocumentProcessor;
import com.xlrit.gears.engine.meta.*;
import fr.opensagres.xdocreport.converter.ConverterTypeTo;
import fr.opensagres.xdocreport.converter.ConverterTypeVia;
import fr.opensagres.xdocreport.converter.Options;
import fr.opensagres.xdocreport.core.XDocReportException;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;

@Component
@Order(200)
@RequiredArgsConstructor
public class XDocReportDocumentProcessor implements DocumentProcessor {
	private static final Logger LOG = LoggerFactory.getLogger(XDocReportDocumentProcessor.class);

	private final ContentStore contentStore;
	private final MetaManager metaManager;
	private final XDocReportProperties properties;

	@PostConstruct
	public void initialize() throws IOException {
		for (Resource fontResource : findFontResources()) {
			String path = fontResourceToFile(fontResource).getAbsolutePath();
			FontFactory.register(path);
			LOG.info("Registered font from {} ({})", fontResource, path);
		}
	}

	@Override
	public boolean supports(Document doc) {
		return ("docx".equals(doc.getType()) || "pdf".equals(doc.getType()))
				&& doc.getTemplate() != null
				&& doc.getTemplate().endsWith(".docx");
	}

	@Override
	public ContentRef process(Document doc) {
		Resource template = new ClassPathResource(doc.getTemplate());
		IXDocReport report = loadTemplate(doc, template);

		IContext context = createContext(doc, report);

		byte[] data = exportDocument(report, doc, context);

		if (properties.isDebug()) {
			try {
				properties.getDir().mkdirs();
				File debugFile = new File(properties.getDir(), doc.getFilename() + "-debug." + doc.getType());
				FileUtils.writeByteArrayToFile(debugFile, data);
				LOG.info("Document written to {}", debugFile);
			}
			catch (IOException e) {
				throw new RuntimeException("Unable to write debug output to file");
			}
		}

		return contentStore.putContent(doc.getFilename(), getContentType(doc.getType()), data);
	}

	private IXDocReport loadTemplate(Document doc, Resource template) {
		try (InputStream is = template.getInputStream()) {
			return XDocReportRegistry.getRegistry().loadReport(is, TemplateEngineKind.Velocity);
		}
		catch (IOException | XDocReportException e) {
			throw new RuntimeException("Unable to load docx template: '" + doc.getTemplate() + "'", e);
		}
	}

	private IContext createContext(Document doc, IXDocReport report) {
		try {
			IContext context = report.createContext();
			Object params = doc.getParameters();
			context.putMap(makeMapFromObject(params));
			return context;
		}
		catch (XDocReportException e) {
			throw new RuntimeException("Unable to create context (this SHOULD NEVER happen)");
		}
	}

	private Map makeMapFromObject(Object object) {
		Map map = new HashMap<>();
		ObjectInfo objectInfo = metaManager.getObjectInfo(object);
		for (BaseField field : objectInfo.getFields()) {
			Object fieldValue = field.getValue(object);
			Object mapValue = convert(fieldValue);
			map.put(field.getName(), mapValue);
		}
		return map;
	}

	private Object convert(Object fieldValue) {
		TypeInfo typeInfo = metaManager.requireTypeInfo(fieldValue);
		if (typeInfo instanceof ObjectInfo) {
			return makeMapFromObject(fieldValue);
		}
		if (typeInfo instanceof MultipleInfo) {
			return ((Collection)fieldValue)
					.stream()
					.map(this::convert)
					.collect(Collectors.toList());
		}
		return fieldValue;
	}

	private byte[] exportDocument(IXDocReport report, Document doc, IContext context) {
		try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
			if ("docx".equals(doc.getType())) {
				report.process(context, os);
			}
			else if ("pdf".equals(doc.getType())) {
				Options options = Options.getTo(ConverterTypeTo.PDF).via(ConverterTypeVia.XWPF);
				report.convert(context, options, os);
			}
			return os.toByteArray();
		}
		catch (IOException | XDocReportException ex) {
			throw new RuntimeException("Unable to export to byte array output stream", ex);
		}
	}

	private String getContentType(String documentType) {
        return switch (documentType) {
            case "pdf"  -> "application/pdf";
            case "docx" -> "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            default     -> throw new RuntimeException("Unsupported document type: " + documentType);
        };
	}

	@SneakyThrows
	private static Resource[] findFontResources() {
			PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
			Resource[] resources = resolver.getResources("classpath*:/fonts/*.ttf");
			LOG.debug("Found font resources: {}", (Object) resources);
			return resources;
	}

	private static File fontResourceToFile(Resource resource) throws IOException {
		if (resource.isFile()) {
			File file = resource.getFile();
			LOG.debug("Font from {} representable as file {}", resource, file);
			return file;
		}

		try (InputStream is = resource.getInputStream()) {
			File file = File.createTempFile("font_", ".ttf");
			LOG.debug("Font from {} written to temp file {}", resource, file);
			FileUtils.copyInputStreamToFile(is, file);
			return file;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy