|
@@ -0,0 +1,195 @@
|
|
|
+package com.yaoyicloud.render;
|
|
|
+
|
|
|
+import java.net.URL;
|
|
|
+
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.file.Paths;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.UUID;
|
|
|
+
|
|
|
+import javax.imageio.ImageIO;
|
|
|
+
|
|
|
+import org.apache.pdfbox.Loader;
|
|
|
+import org.apache.pdfbox.pdmodel.PDDocument;
|
|
|
+import org.apache.pdfbox.rendering.PDFRenderer;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFRun;
|
|
|
+
|
|
|
+import com.deepoove.poi.XWPFTemplate;
|
|
|
+import com.deepoove.poi.config.Configure;
|
|
|
+import com.deepoove.poi.config.ConfigureBuilder;
|
|
|
+import com.deepoove.poi.data.ByteArrayPictureRenderData;
|
|
|
+import com.deepoove.poi.data.HyperlinkTextRenderData;
|
|
|
+import com.deepoove.poi.data.PictureType;
|
|
|
+import com.deepoove.poi.data.style.PictureStyle;
|
|
|
+import com.deepoove.poi.exception.RenderException;
|
|
|
+import com.deepoove.poi.policy.PictureRenderPolicy;
|
|
|
+import com.deepoove.poi.policy.TextRenderPolicy;
|
|
|
+import com.deepoove.poi.template.ElementTemplate;
|
|
|
+import com.deepoove.poi.template.run.RunTemplate;
|
|
|
+import com.deepoove.poi.xwpf.BodyContainer;
|
|
|
+import com.deepoove.poi.xwpf.BodyContainerFactory;
|
|
|
+import com.google.protobuf.Descriptors;
|
|
|
+import com.google.protobuf.util.JsonFormat;
|
|
|
+import com.yaoyicloud.message.FxyProtos.AttachmentSection;
|
|
|
+import cn.hutool.core.lang.Pair;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+
|
|
|
+/**
|
|
|
+ * AttachmentSection渲染器
|
|
|
+ *
|
|
|
+ */
|
|
|
+public final class AttachmentSectionRender extends AbstractRender {
|
|
|
+
|
|
|
+ public AttachmentSectionRender(String cwd) {
|
|
|
+ super(cwd);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Docx 渲染
|
|
|
+ *
|
|
|
+ * @param info 数据
|
|
|
+ * @param templateFileContent 模板内容
|
|
|
+ * @return 本地文件目录
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
+ public String renderDocx(AttachmentSection info, Map<String, Object> addtionalMap, byte[] templateFileContent)
|
|
|
+ throws IOException {
|
|
|
+
|
|
|
+ // 不需要定制展示逻辑的时候,使用protobuf的转json方法
|
|
|
+ String jsonStr = JsonFormat.printer().print(info);
|
|
|
+
|
|
|
+ // 定制缺省文字,POI-TL模板变量只支持if,不支持else
|
|
|
+ Map<String, Object> newAddtionMap = new HashMap<>();
|
|
|
+ Descriptors.Descriptor descriptor = info.getDescriptorForType();
|
|
|
+ for (Descriptors.FieldDescriptor field : descriptor.getFields()) {
|
|
|
+ if (field.isRepeated()) {
|
|
|
+ List<?> li = (List<?>) info.getField(field);
|
|
|
+ if (li.size() == 0) {
|
|
|
+ newAddtionMap.put(field.getName() + "AltText", "未上传");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ newAddtionMap.putAll(addtionalMap);
|
|
|
+
|
|
|
+ // 注: 报告模板的模板变量按照json序列化的结果命名
|
|
|
+ // 注: 目前的实现假设:一个session对应一个cwd目录
|
|
|
+ ConfigureBuilder builder = Configure.builder();
|
|
|
+
|
|
|
+ // 新增特殊处理变量
|
|
|
+ builder.addPlugin('^', new AttachmentRenderPolicy());
|
|
|
+
|
|
|
+ this.docxResultPath = this.renderDocx(jsonStr, newAddtionMap, templateFileContent, builder,
|
|
|
+ Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
|
|
|
+ return this.docxResultPath;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
|
|
|
+ */
|
|
|
+ public class AttachmentRenderPolicy extends PictureRenderPolicy {
|
|
|
+
|
|
|
+ public AttachmentRenderPolicy() {}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Map<String, Object> mpData = (Map<String, Object>) data;
|
|
|
+ String filename = mpData.getOrDefault("fileName", "").toString().trim();
|
|
|
+ String url = mpData.getOrDefault("fileUri", "").toString();
|
|
|
+ float targetWidth = 456.5f;
|
|
|
+
|
|
|
+ if (StrUtil.isBlank(filename)) {
|
|
|
+ // uri render when no filename
|
|
|
+ TextRenderPolicy.Helper.renderTextRun(((RunTemplate) eleTemplate).getRun(),
|
|
|
+ new HyperlinkTextRenderData(url, url));
|
|
|
+ } else if (filename.endsWith(".pdf")) {
|
|
|
+ // pdf render, replace data with bytestream
|
|
|
+ PDDocument document = null;
|
|
|
+ try {
|
|
|
+ document = Loader.loadPDF(new URL(url).openStream().readAllBytes());
|
|
|
+ PDFRenderer renderer = new PDFRenderer(document);
|
|
|
+
|
|
|
+ XWPFRun run = ((RunTemplate) eleTemplate).getRun();
|
|
|
+ BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(run);
|
|
|
+ // 每页一张图片
|
|
|
+ for (int i = 0; i < document.getNumberOfPages(); i++) {
|
|
|
+ // pdf 转 jpeg
|
|
|
+ BufferedImage image = renderer.renderImageWithDPI(i, 150);
|
|
|
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
|
+ ImageIO.write(image, "jpg", stream);
|
|
|
+
|
|
|
+ // 准备docx元素
|
|
|
+ if (i == 0) {
|
|
|
+ run.setText("", 0);
|
|
|
+ } else {
|
|
|
+ run = bodyContainer.insertNewParagraph(run).createRun();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 准备POI-TL数据
|
|
|
+ ByteArrayPictureRenderData picData =
|
|
|
+ new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.JPEG);
|
|
|
+ Pair<Integer, Integer> targetSize =
|
|
|
+ getTargetSize(image.getWidth(), image.getHeight(), targetWidth);
|
|
|
+ PictureStyle style = new PictureStyle();
|
|
|
+ style.setWidth(targetSize.getKey());
|
|
|
+ style.setHeight(targetSize.getValue());
|
|
|
+ picData.setPictureStyle(style);
|
|
|
+ PictureRenderPolicy.Helper.renderPicture(run, picData);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RenderException(
|
|
|
+ "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
|
|
|
+ } finally {
|
|
|
+ if (document != null) {
|
|
|
+ try {
|
|
|
+ document.close();
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ BufferedImage image = null;
|
|
|
+ ByteArrayOutputStream stream = null;
|
|
|
+ try {
|
|
|
+ image = ImageIO.read(new URL(url));
|
|
|
+ stream = new ByteArrayOutputStream();
|
|
|
+ ImageIO.write(image, filename.substring(filename.lastIndexOf('.') + 1, filename.length()), stream);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RenderException(
|
|
|
+ "AttachmentRenderPolicy for " + eleTemplate + " error: " + e.getMessage(), e);
|
|
|
+ }
|
|
|
+
|
|
|
+ ByteArrayPictureRenderData picData =
|
|
|
+ new ByteArrayPictureRenderData(stream.toByteArray(), PictureType.suggestFileType(filename));
|
|
|
+ Pair<Integer, Integer> targetSize = getTargetSize(image.getWidth(), image.getHeight(), targetWidth);
|
|
|
+ PictureStyle style = new PictureStyle();
|
|
|
+ style.setWidth(targetSize.getKey());
|
|
|
+ style.setHeight(targetSize.getValue());
|
|
|
+ picData.setPictureStyle(style);
|
|
|
+ super.render(eleTemplate, picData, template);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 这个方法也应该独立出去共享
|
|
|
+ *
|
|
|
+ * @param originWidth
|
|
|
+ * @param originHeight
|
|
|
+ * @param targetWidth
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private static Pair<Integer, Integer> getTargetSize(int originWidth, int originHeight, float targetWidth) {
|
|
|
+ float targetHeight = (originHeight * targetWidth) / originWidth;
|
|
|
+ if (targetHeight > 645) {
|
|
|
+ targetHeight = 645;
|
|
|
+ targetWidth = (originWidth * targetHeight) / originHeight;
|
|
|
+ }
|
|
|
+ return Pair.of(Math.round(targetWidth), Math.round(targetHeight));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|