mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 14:07:55 +01:00
Closes #77: Metadata in PDF export
This commit is contained in:
parent
c2a2e9f585
commit
00ee2d3bf6
@ -7,6 +7,8 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
@ -17,6 +19,7 @@ import org.apache.pdfbox.pdmodel.PDDocument;
|
|||||||
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDType1Font;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
|
import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
||||||
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
||||||
@ -29,8 +32,11 @@ import org.odftoolkit.odfdom.doc.OdfTextDocument;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.io.Closer;
|
import com.google.common.io.Closer;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
|
import com.sismics.docs.core.util.pdf.PdfPage;
|
||||||
import com.sismics.util.ImageUtil;
|
import com.sismics.util.ImageUtil;
|
||||||
import com.sismics.util.mime.MimeType;
|
import com.sismics.util.mime.MimeType;
|
||||||
|
|
||||||
@ -141,21 +147,16 @@ public class PdfUtil {
|
|||||||
/**
|
/**
|
||||||
* Convert a document and its files to a merged PDF file.
|
* Convert a document and its files to a merged PDF file.
|
||||||
*
|
*
|
||||||
|
* @param documentDto Document DTO
|
||||||
* @param fileList List of files
|
* @param fileList List of files
|
||||||
* @param fitImageToPage Fit images to the page
|
* @param fitImageToPage Fit images to the page
|
||||||
* @param metadata Add a page with metadata
|
* @param metadata Add a page with metadata
|
||||||
* @param comment Add a page with comments
|
|
||||||
* @param margin Margins in millimeters
|
* @param margin Margins in millimeters
|
||||||
* @return PDF input stream
|
* @return PDF input stream
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public static InputStream convertToPdf(List<File> fileList, boolean fitImageToPage, boolean metadata, boolean comments, int margin) throws Exception {
|
public static InputStream convertToPdf(DocumentDto documentDto, List<File> fileList,
|
||||||
// TODO PDF Export: Option to add a front page with:
|
boolean fitImageToPage, boolean metadata, int margin) throws Exception {
|
||||||
// document title, document description, creator, date created, language,
|
|
||||||
// additional dublincore metadata (except relations)
|
|
||||||
// list of all files (and information if it is in this document or not)
|
|
||||||
// TODO PDF Export: Option to add the comments
|
|
||||||
|
|
||||||
// Setup PDFBox
|
// Setup PDFBox
|
||||||
Closer closer = Closer.create();
|
Closer closer = Closer.create();
|
||||||
MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage
|
MemoryUsageSetting memUsageSettings = MemoryUsageSetting.setupMixed(1000000); // 1MB max memory usage
|
||||||
@ -166,12 +167,45 @@ public class PdfUtil {
|
|||||||
try (PDDocument doc = new PDDocument(memUsageSettings)) {
|
try (PDDocument doc = new PDDocument(memUsageSettings)) {
|
||||||
// Add metadata
|
// Add metadata
|
||||||
if (metadata) {
|
if (metadata) {
|
||||||
|
PDPage page = new PDPage();
|
||||||
}
|
doc.addPage(page);
|
||||||
|
try (PdfPage pdfPage = new PdfPage(doc, page, margin * mmPerInch, PDType1Font.HELVETICA, 12)) {
|
||||||
// Add comments
|
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||||
if (comments) {
|
pdfPage.addText(documentDto.getTitle(), true, PDType1Font.HELVETICA_BOLD, 16)
|
||||||
|
.newLine()
|
||||||
|
.addText("Created by " + documentDto.getCreator()
|
||||||
|
+ " on " + dateFormat.format(new Date(documentDto.getCreateTimestamp())), true)
|
||||||
|
.newLine()
|
||||||
|
.addText(documentDto.getDescription())
|
||||||
|
.newLine();
|
||||||
|
if (!Strings.isNullOrEmpty(documentDto.getSubject())) {
|
||||||
|
pdfPage.addText("Subject: " + documentDto.getSubject());
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(documentDto.getIdentifier())) {
|
||||||
|
pdfPage.addText("Identifier: " + documentDto.getIdentifier());
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(documentDto.getPublisher())) {
|
||||||
|
pdfPage.addText("Publisher: " + documentDto.getPublisher());
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(documentDto.getFormat())) {
|
||||||
|
pdfPage.addText("Format: " + documentDto.getFormat());
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(documentDto.getSource())) {
|
||||||
|
pdfPage.addText("Source: " + documentDto.getSource());
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(documentDto.getType())) {
|
||||||
|
pdfPage.addText("Type: " + documentDto.getType());
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(documentDto.getCoverage())) {
|
||||||
|
pdfPage.addText("Coverage: " + documentDto.getCoverage());
|
||||||
|
}
|
||||||
|
if (!Strings.isNullOrEmpty(documentDto.getRights())) {
|
||||||
|
pdfPage.addText("Rights: " + documentDto.getRights());
|
||||||
|
}
|
||||||
|
pdfPage.addText("Language: " + documentDto.getLanguage())
|
||||||
|
.newLine()
|
||||||
|
.addText("Files in this document : " + fileList.size(), false, PDType1Font.HELVETICA_BOLD, 12);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add files
|
// Add files
|
||||||
|
@ -0,0 +1,153 @@
|
|||||||
|
package com.sismics.docs.core.util.pdf;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPage;
|
||||||
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
||||||
|
import org.apache.pdfbox.pdmodel.font.PDFont;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around PDFBox for high level abstraction of PDF writing.
|
||||||
|
*
|
||||||
|
* @author bgamard
|
||||||
|
*/
|
||||||
|
public class PdfPage implements Closeable {
|
||||||
|
private PDPage pdPage;
|
||||||
|
private PDPageContentStream pdContent;
|
||||||
|
private float margin;
|
||||||
|
private PDFont defaultFont;
|
||||||
|
private int defaultFontSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a wrapper around a PDF page.
|
||||||
|
*
|
||||||
|
* @param pdDoc Document
|
||||||
|
* @param pdPage Page
|
||||||
|
* @param margin Margin
|
||||||
|
* @param defaultFont Default font
|
||||||
|
* @param defaultFontSize Default fond size
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public PdfPage(PDDocument pdDoc, PDPage pdPage, float margin, PDFont defaultFont, int defaultFontSize) throws IOException {
|
||||||
|
this.pdPage = pdPage;
|
||||||
|
this.pdContent = new PDPageContentStream(pdDoc, pdPage);
|
||||||
|
this.margin = margin;
|
||||||
|
this.defaultFont = defaultFont;
|
||||||
|
this.defaultFontSize = defaultFontSize;
|
||||||
|
|
||||||
|
pdContent.beginText();
|
||||||
|
pdContent.newLineAtOffset(margin, pdPage.getMediaBox().getHeight() - margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a text with default font.
|
||||||
|
*
|
||||||
|
* @param text Text
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public PdfPage addText(String text) throws IOException {
|
||||||
|
drawText(pdPage.getMediaBox().getWidth() - 2 * margin, defaultFont, defaultFontSize, text, false);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a text with default font.
|
||||||
|
*
|
||||||
|
* @param text Text
|
||||||
|
* @param centered If true, the text will be centered in the page
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public PdfPage addText(String text, boolean centered) throws IOException {
|
||||||
|
drawText(pdPage.getMediaBox().getWidth() - 2 * margin, defaultFont, defaultFontSize, text, centered);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a text in the page.
|
||||||
|
*
|
||||||
|
* @param text Text
|
||||||
|
* @param centered If true, the text will be centered in the page
|
||||||
|
* @param font Font
|
||||||
|
* @param fontSize Font size
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public PdfPage addText(String text, boolean centered, PDFont font, int fontSize) throws IOException {
|
||||||
|
drawText(pdPage.getMediaBox().getWidth() - 2 * margin, font, fontSize, text, centered);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new line.
|
||||||
|
*
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public PdfPage newLine() throws IOException {
|
||||||
|
pdContent.newLineAtOffset(0, - defaultFont.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * defaultFontSize);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw a text with low level PDFBox API.
|
||||||
|
*
|
||||||
|
* @param paragraphWidth Paragraph width
|
||||||
|
* @param font Font
|
||||||
|
* @param fontSize Font size
|
||||||
|
* @param text Text
|
||||||
|
* @param centered If true, the text will be centered in the paragraph
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
private void drawText(float paragraphWidth, PDFont font, int fontSize, String text, boolean centered) throws IOException {
|
||||||
|
pdContent.setFont(font, fontSize);
|
||||||
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
float height = font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;
|
||||||
|
for (int i : possibleWrapPoints(text)) {
|
||||||
|
float width = font.getStringWidth(text.substring(start, i)) / 1000 * fontSize;
|
||||||
|
if (start < end && width > paragraphWidth) {
|
||||||
|
// Draw partial text and increase height
|
||||||
|
pdContent.newLineAtOffset(0, - height);
|
||||||
|
String line = text.substring(start, end);
|
||||||
|
float lineWidth = font.getStringWidth(line) / 1000 * fontSize;
|
||||||
|
float offset = (paragraphWidth - lineWidth) / 2;
|
||||||
|
if (centered) pdContent.newLineAtOffset(offset, 0);
|
||||||
|
pdContent.showText(line);
|
||||||
|
if (centered) pdContent.newLineAtOffset(- offset, 0);
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
end = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last piece of text
|
||||||
|
String line = text.substring(start);
|
||||||
|
float lineWidth = font.getStringWidth(line) / 1000 * fontSize;
|
||||||
|
float offset = (paragraphWidth - lineWidth) / 2;
|
||||||
|
pdContent.newLineAtOffset(0, - height);
|
||||||
|
if (centered) pdContent.newLineAtOffset(offset, 0);
|
||||||
|
pdContent.showText(line);
|
||||||
|
if (centered) pdContent.newLineAtOffset(- offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns wrap points for a given piece of text.
|
||||||
|
*
|
||||||
|
* @param text Text
|
||||||
|
* @return Wrap points
|
||||||
|
*/
|
||||||
|
private int[] possibleWrapPoints(String text) {
|
||||||
|
String[] split = text.split("(?<=\\W)");
|
||||||
|
int[] ret = new int[split.length];
|
||||||
|
ret[0] = split[0].length();
|
||||||
|
for (int i = 1 ; i < split.length ; i++) {
|
||||||
|
ret[i] = ret[i-1] + split[i].length();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
pdContent.endText();
|
||||||
|
pdContent.close();
|
||||||
|
}
|
||||||
|
}
|
@ -1,18 +1,22 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
import com.google.common.io.Resources;
|
import com.google.common.io.Resources;
|
||||||
|
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.util.mime.MimeType;
|
import com.sismics.util.mime.MimeType;
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test of the file entity utilities.
|
* Test of the file entity utilities.
|
||||||
*
|
*
|
||||||
@ -50,6 +54,21 @@ public class TestFileUtil {
|
|||||||
InputStream inputStream2 = Resources.getResource("file/udhr_encrypted.pdf").openStream();
|
InputStream inputStream2 = Resources.getResource("file/udhr_encrypted.pdf").openStream();
|
||||||
InputStream inputStream3 = Resources.getResource("file/document.docx").openStream();
|
InputStream inputStream3 = Resources.getResource("file/document.docx").openStream();
|
||||||
InputStream inputStream4 = Resources.getResource("file/document.odt").openStream()) {
|
InputStream inputStream4 = Resources.getResource("file/document.odt").openStream()) {
|
||||||
|
// Document
|
||||||
|
DocumentDto documentDto = new DocumentDto();
|
||||||
|
documentDto.setTitle("My super document 1");
|
||||||
|
documentDto.setDescription("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis id turpis iaculis, commodo est ac, efficitur quam. Nam accumsan magna in orci vulputate ultricies. Sed vulputate neque magna, at laoreet leo ultricies vel. Proin eu hendrerit felis. Quisque sit amet arcu efficitur, pulvinar orci sed, imperdiet elit. Nunc posuere ex sed fermentum congue. Aliquam ultrices convallis finibus. Praesent iaculis justo vitae dictum auctor. Praesent suscipit imperdiet erat ac maximus. Aenean pharetra quam sed fermentum commodo. Donec sagittis ipsum nibh, id congue dolor venenatis quis. In tincidunt nisl non ex sollicitudin, a imperdiet neque scelerisque. Nullam lacinia ac orci sed faucibus. Donec tincidunt venenatis justo, nec fermentum justo rutrum a.");
|
||||||
|
documentDto.setSubject("A set of random picture");
|
||||||
|
documentDto.setIdentifier("ID-2016-08-00001");
|
||||||
|
documentDto.setPublisher("My Publisher, Inc.");
|
||||||
|
documentDto.setFormat("A4 standard ISO format");
|
||||||
|
documentDto.setType("Image");
|
||||||
|
documentDto.setCoverage("France");
|
||||||
|
documentDto.setRights("Public Domain");
|
||||||
|
documentDto.setLanguage("en");
|
||||||
|
documentDto.setCreator("user1");
|
||||||
|
documentDto.setCreateTimestamp(new Date().getTime());
|
||||||
|
|
||||||
// First file
|
// First file
|
||||||
Files.copy(inputStream0, DirectoryUtil.getStorageDirectory().resolve("apollo_landscape"), StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(inputStream0, DirectoryUtil.getStorageDirectory().resolve("apollo_landscape"), StandardCopyOption.REPLACE_EXISTING);
|
||||||
File file0 = new File();
|
File file0 = new File();
|
||||||
@ -81,7 +100,10 @@ public class TestFileUtil {
|
|||||||
file4.setId("document_odt");
|
file4.setId("document_odt");
|
||||||
file4.setMimeType(MimeType.OPEN_DOCUMENT_TEXT);
|
file4.setMimeType(MimeType.OPEN_DOCUMENT_TEXT);
|
||||||
|
|
||||||
PdfUtil.convertToPdf(Lists.newArrayList(file0, file1, file2, file3, file4), true, true, true, 10).close();
|
try (InputStream pdfInputStream = PdfUtil.convertToPdf(documentDto, Lists.newArrayList(file0, file1, file2, file3, file4), true, true, 10);
|
||||||
|
OutputStream fileOutputStream = Files.newOutputStream(Paths.get("c:/temp.pdf"))) {
|
||||||
|
ByteStreams.copy(pdfInputStream, fileOutputStream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ public class DocumentResource extends BaseResource {
|
|||||||
|
|
||||||
// Get document and check read permission
|
// Get document and check read permission
|
||||||
DocumentDao documentDao = new DocumentDao();
|
DocumentDao documentDao = new DocumentDao();
|
||||||
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId);
|
final DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, shareId == null ? principal.getId() : shareId);
|
||||||
if (documentDto == null) {
|
if (documentDto == null) {
|
||||||
return Response.status(Status.NOT_FOUND).build();
|
return Response.status(Status.NOT_FOUND).build();
|
||||||
}
|
}
|
||||||
@ -226,7 +226,7 @@ public class DocumentResource extends BaseResource {
|
|||||||
StreamingOutput stream = new StreamingOutput() {
|
StreamingOutput stream = new StreamingOutput() {
|
||||||
@Override
|
@Override
|
||||||
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
|
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
|
||||||
try (InputStream inputStream = PdfUtil.convertToPdf(fileList, fitImageToPage, metadata, comments, margin)) {
|
try (InputStream inputStream = PdfUtil.convertToPdf(documentDto, fileList, fitImageToPage, metadata, margin)) {
|
||||||
ByteStreams.copy(inputStream, outputStream);
|
ByteStreams.copy(inputStream, outputStream);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
|
Loading…
Reference in New Issue
Block a user