From 65b4e58aadc80ee56594e4f2634a8a0219448791 Mon Sep 17 00:00:00 2001 From: Aldo Mihasi Date: Thu, 17 Mar 2022 10:47:50 +0200 Subject: [PATCH] attach images in word export --- .../managers/DataManagementPlanManager.java | 2 +- .../eudat/logic/managers/DatasetManager.java | 4 +- .../documents/types/ParagraphStyle.java | 4 +- .../utilities/documents/word/WordBuilder.java | 104 ++++++++++++++---- 4 files changed, 89 insertions(+), 25 deletions(-) diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java index 997766bec..7c41f2f65 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java @@ -1173,7 +1173,7 @@ public class DataManagementPlanManager { } public FileEnvelope getWordDocument(String id, Principal principal, ConfigLoader configLoader, Boolean versioned) throws IOException { - WordBuilder wordBuilder = new WordBuilder(); + WordBuilder wordBuilder = new WordBuilder(this.environment); VisibilityRuleService visibilityRuleService = new VisibilityRuleServiceImpl(); DatasetWizardModel dataset = new DatasetWizardModel(); XWPFDocument document = configLoader.getDocument(); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java index ab664f997..a4d5d8bff 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java @@ -397,7 +397,7 @@ public class DatasetManager { } private XWPFDocument getWordDocument(ConfigLoader configLoader, eu.eudat.data.entities.Dataset datasetEntity, VisibilityRuleService visibilityRuleService) throws IOException { - WordBuilder wordBuilder = new WordBuilder(); + WordBuilder wordBuilder = new WordBuilder(this.environment); DatasetWizardModel dataset = new DatasetWizardModel(); XWPFDocument document = configLoader.getDocument(); @@ -481,7 +481,7 @@ public class DatasetManager { } private XWPFDocument getLightWordDocument(ConfigLoader configLoader, DatasetWizardModel dataset, VisibilityRuleService visibilityRuleService) throws IOException { - WordBuilder wordBuilder = new WordBuilder(); + WordBuilder wordBuilder = new WordBuilder(this.environment); XWPFDocument document = configLoader.getDocument(); // Space below Dataset title. diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/types/ParagraphStyle.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/types/ParagraphStyle.java index db6e0e207..dc88ef736 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/types/ParagraphStyle.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/types/ParagraphStyle.java @@ -4,7 +4,7 @@ package eu.eudat.logic.utilities.documents.types; * Created by ikalyvas on 2/26/2018. */ public enum ParagraphStyle { - TEXT(0), HEADER1(1), HEADER2(2), HEADER3(3), HEADER4(4), TITLE(5), FOOTER(6), COMMENT(7), HEADER5(8), HEADER6(9), HTML(10); + TEXT(0), HEADER1(1), HEADER2(2), HEADER3(3), HEADER4(4), TITLE(5), FOOTER(6), COMMENT(7), HEADER5(8), HEADER6(9), HTML(10), IMAGE(11); private Integer value; @@ -40,6 +40,8 @@ public enum ParagraphStyle { return HEADER6; case 10: return HTML; + case 11: + return IMAGE; default: throw new RuntimeException("Unsupported ParagraphStyle Code"); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java index 1a299e244..ca365b8cb 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java @@ -7,51 +7,57 @@ import eu.eudat.logic.utilities.documents.types.ParagraphStyle; import eu.eudat.logic.utilities.interfaces.ApplierWithValue; import eu.eudat.models.data.components.commons.datafield.CheckBoxData; import eu.eudat.models.data.components.commons.datafield.ComboBoxData; +import eu.eudat.models.data.components.commons.datafield.UploadData; import eu.eudat.models.data.components.commons.datafield.WordListData; import eu.eudat.models.data.user.components.datasetprofile.Field; import eu.eudat.models.data.user.components.datasetprofile.FieldSet; import eu.eudat.models.data.user.components.datasetprofile.Section; import eu.eudat.models.data.user.composite.DatasetProfilePage; import eu.eudat.models.data.user.composite.PagedDatasetProfile; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.*; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTLvl; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.STNumberFormat; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.http.MediaType; +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.math.BigInteger; -import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDate; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; -import java.time.temporal.TemporalAccessor; import java.util.*; import java.util.stream.Collectors; public class WordBuilder { private static final Logger logger = LoggerFactory.getLogger(WordBuilder.class); - private Map> options = new HashMap<>(); + private Map> options = new HashMap<>(); private CTAbstractNum cTAbstractNum; private BigInteger numId; private Integer indent; + private static final ObjectMapper mapper = new ObjectMapper(); - public WordBuilder() { + public WordBuilder(Environment environment) { this.cTAbstractNum = CTAbstractNum.Factory.newInstance(); this.cTAbstractNum.setAbstractNumId(BigInteger.valueOf(1)); this.indent = 0; - this.buildOptions(); + this.buildOptions(environment); } - private void buildOptions() { + private void buildOptions(Environment environment) { this.options.put(ParagraphStyle.TEXT, (mainDocumentPart, item) -> { XWPFParagraph paragraph = mainDocumentPart.createParagraph(); XWPFRun run = paragraph.createRun(); @@ -61,7 +67,7 @@ public class WordBuilder { return paragraph; }); this.options.put(ParagraphStyle.HTML, (mainDocumentPart, item) -> { - Document htmlDoc = Jsoup.parse(item.replaceAll("\n", "
")); + Document htmlDoc = Jsoup.parse(((String)item).replaceAll("\n", "
")); HtmlToWorldBuilder htmlToWorldBuilder = HtmlToWorldBuilder.convert(mainDocumentPart, htmlDoc, indent > 0 ? (indent/2.0F) * 0.8F : 0.8F); return htmlToWorldBuilder.getParagraph(); }); @@ -70,7 +76,7 @@ public class WordBuilder { paragraph.setStyle("Title"); paragraph.setAlignment(ParagraphAlignment.CENTER); XWPFRun run = paragraph.createRun(); - run.setText(item); + run.setText((String)item); run.setBold(true); run.setFontSize(14); return paragraph; @@ -79,7 +85,7 @@ public class WordBuilder { XWPFParagraph paragraph = mainDocumentPart.createParagraph(); paragraph.setStyle("Heading1"); XWPFRun run = paragraph.createRun(); - run.setText(item); + run.setText((String)item); // run.setBold(true); // run.setFontSize(12); // run.setStyle("0"); @@ -107,7 +113,7 @@ public class WordBuilder { XWPFParagraph paragraph = mainDocumentPart.createParagraph(); paragraph.setStyle("Heading4"); XWPFRun run = paragraph.createRun(); - run.setText(item); + run.setText((String)item); return paragraph; }); this.options.put(ParagraphStyle.HEADER5, (mainDocumentPart, item) -> { @@ -127,7 +133,7 @@ public class WordBuilder { this.options.put(ParagraphStyle.FOOTER, (mainDocumentPart, item) -> { XWPFParagraph paragraph = mainDocumentPart.createParagraph(); XWPFRun run = paragraph.createRun(); - run.setText(item); + run.setText((String)item); return paragraph; }); this.options.put(ParagraphStyle.COMMENT, (mainDocumentPart, item) -> { @@ -137,6 +143,41 @@ public class WordBuilder { run.setItalic(true); return paragraph; }); + this.options.put(ParagraphStyle.IMAGE, (mainDocumentPart, item) -> { + XWPFParagraph paragraph = mainDocumentPart.createParagraph(); + XWPFRun run = paragraph.createRun(); + String imageId = ((Map)item).get("id"); + String fileName = ((Map)item).get("name"); + int format = 0; + if (fileName.endsWith(".jpeg") || fileName.endsWith(".jpg")) { + format = org.apache.poi.xwpf.usermodel.Document.PICTURE_TYPE_JPEG; + } else if (fileName.endsWith(".png")) { + format = org.apache.poi.xwpf.usermodel.Document.PICTURE_TYPE_PNG; + } else if (fileName.endsWith(".gif")) { + format = org.apache.poi.xwpf.usermodel.Document.PICTURE_TYPE_GIF; + } else if (fileName.endsWith(".tiff")) { + format = org.apache.poi.xwpf.usermodel.Document.PICTURE_TYPE_TIFF; + } + try { + FileInputStream image = new FileInputStream(environment.getProperty("file.storage") + imageId); + ImageInputStream iis = ImageIO.createImageInputStream(new File(environment.getProperty("file.storage") + imageId)); + Iterator readers = ImageIO.getImageReaders(iis); + if (readers.hasNext()) { + ImageReader reader = readers.next(); + reader.setInput(iis); + int pageWidth = Math.round(mainDocumentPart.getDocument().getBody().getSectPr().getPgSz().getW().intValue() / (float)20); + int imageWidth = reader.getWidth(0); + int width = Math.min(imageWidth, pageWidth); + int pageHeight = Math.round(mainDocumentPart.getDocument().getBody().getSectPr().getPgSz().getH().intValue() / (float)20); + int imageHeight = reader.getHeight(0); + int height = Math.min(imageHeight, pageHeight); + run.addPicture(image, format, fileName, Units.toEMU(width), Units.toEMU(height)); + } + } catch (IOException | InvalidFormatException e){ + logger.error(e.getMessage(), e); + } + return paragraph; + }); } public XWPFDocument build(XWPFDocument document, PagedDatasetProfile pagedDatasetProfile, VisibilityRuleService visibilityRuleService) throws IOException { @@ -254,7 +295,27 @@ public class WordBuilder { if (visibilityRuleService.isElementVisible(field.getId())) { if (!createListing) { try { - if (field.getValue() != null && !field.getValue().toString().isEmpty()) { + if(field.getViewStyle().getRenderStyle().equals("upload")){ + boolean isImage = false; + for(UploadData.Option type: ((UploadData)field.getData()).getTypes()){ + String fileFormat = type.getValue(); + if(fileFormat.equals(MediaType.IMAGE_JPEG_VALUE) || fileFormat.equals(MediaType.IMAGE_PNG_VALUE) || fileFormat.equals(MediaType.IMAGE_GIF_VALUE)){ + isImage = true; + break; + } + } + if(isImage){ + if (!field.getValue().toString().isEmpty()) { + XWPFParagraph paragraph = addParagraphContent(mapper.convertValue(field.getValue(), Map.class), mainDocumentPart, ParagraphStyle.IMAGE, numId); + if (paragraph != null) { + CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl(); + number.setVal(BigInteger.valueOf(indent)); + hasValue = true; + } + } + } + } + else if (field.getValue() != null && !field.getValue().toString().isEmpty()) { this.indent = indent; String format = this.formatter(field); if(format != null && !format.isEmpty()){ @@ -281,9 +342,12 @@ public class WordBuilder { return hasValue; } - public XWPFParagraph addParagraphContent(String text, XWPFDocument mainDocumentPart, ParagraphStyle style, BigInteger numId) { - if (text != null && !text.isEmpty()) { - XWPFParagraph paragraph = this.options.get(style).apply(mainDocumentPart, text); + public XWPFParagraph addParagraphContent(Object content, XWPFDocument mainDocumentPart, ParagraphStyle style, BigInteger numId) { + if (content != null) { + if (content instanceof String && ((String)content).isEmpty()) { + return null; + } + XWPFParagraph paragraph = this.options.get(style).apply(mainDocumentPart, content); if (paragraph != null) { if (numId != null) { paragraph.setNumID(numId); @@ -343,7 +407,6 @@ public class WordBuilder { comboboxType = ((ComboBoxData) field.getData()).getType(); } if (comboboxType.equals("autocomplete")) { - ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); if (field.getValue() == null) return null; List> mapList = new ArrayList<>(); @@ -418,7 +481,6 @@ public class WordBuilder { case "validation": if (field.getValue() != null && !field.getValue().toString().isEmpty()) { Map identifierData; - ObjectMapper mapper = new ObjectMapper(); try { identifierData = mapper.readValue(field.getValue().toString(), HashMap.class); } catch (Exception ex) {