diff --git a/gradle.properties b/gradle.properties index 8e36c34..035debe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib.graphics name = graphics -version = 4.4.1 +version = 4.5.0 org.gradle.warning.mode = ALL diff --git a/graphics-ghostscript/src/main/java/module-info.java b/graphics-ghostscript/src/main/java/module-info.java index ad1693b..ef73c3e 100644 --- a/graphics-ghostscript/src/main/java/module-info.java +++ b/graphics-ghostscript/src/main/java/module-info.java @@ -4,4 +4,5 @@ module org.xbib.graphics.ghostscript { requires java.logging; requires transitive java.desktop; requires transitive org.apache.pdfbox; + requires transitive org.apache.pdfbox.io; } diff --git a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFRasterizer.java b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFRasterizer.java index 173be12..305f5f6 100644 --- a/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFRasterizer.java +++ b/graphics-ghostscript/src/main/java/org/xbib/graphics/ghostscript/PDFRasterizer.java @@ -7,7 +7,8 @@ import javax.imageio.ImageIO; import javax.imageio.ImageReadParam; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; -import org.apache.pdfbox.io.MemoryUsageSetting; + +import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; @@ -201,7 +202,7 @@ public class PDFRasterizer { PathMatcher pathMatcher = sourceDir.getFileSystem().getPathMatcher("glob:" + globPattern); List coverPageDocs = new ArrayList<>(); try (Stream files = Files.list(sourceDir); - PDDocument pdDocument = new PDDocument(MemoryUsageSetting.setupTempFileOnly()); + PDDocument pdDocument = new PDDocument(); OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(targetFile))) { pdDocument.setResourceCache(null); List entries = files.sorted() @@ -223,7 +224,7 @@ public class PDFRasterizer { if (path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".pdf")) { logger.info("found pdf " + path); try (InputStream inputStream = Files.newInputStream(path)) { - PDDocument doc = PDDocument.load(inputStream); + PDDocument doc = Loader.loadPDF(inputStream.readAllBytes()); for (int i = 0; i < doc.getNumberOfPages(); i++) { PDPage page = doc.getPage(i); PDPage newPage = pdDocument.importPage(page); // shallow copy :( diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/analyze/DocumentAnalyzer.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/analyze/DocumentAnalyzer.groovy index 8459646..61309bf 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/analyze/DocumentAnalyzer.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/analyze/DocumentAnalyzer.groovy @@ -1,6 +1,7 @@ package org.xbib.graphics.pdfbox.groovy.analyze import groovy.util.logging.Log +import org.apache.pdfbox.Loader import org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine import org.apache.pdfbox.cos.COSName import org.apache.pdfbox.cos.COSStream @@ -23,7 +24,8 @@ class DocumentAnalyzer { DocumentAnalyzer(InputStream inputStream) { inputStream.withCloseable { - PDDocument document = PDDocument.load(inputStream) + byte[] bytes = inputStream.readAllBytes() + PDDocument document = Loader.loadPDF(bytes) result."author" = document.getDocumentInformation().author result."creator" = document.getDocumentInformation().creator result."producer" = document.getDocumentInformation().producer diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocument.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocument.groovy index 602e781..7af4f7d 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocument.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfDocument.groovy @@ -99,7 +99,7 @@ class PdfDocument implements Closeable { void setPageNumber(int value) { this.pageNumber = value contentStream?.close() - contentStream = new PDPageContentStream(pdDocument, currentPage, true, true) + contentStream = new PDPageContentStream(pdDocument, currentPage, PDPageContentStream.AppendMode.APPEND, true) toStartPosition() } diff --git a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfFont.groovy b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfFont.groovy index 8fcd4b3..efa33a6 100644 --- a/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfFont.groovy +++ b/graphics-pdfbox-groovy/src/main/groovy/org/xbib/graphics/pdfbox/groovy/builder/PdfFont.groovy @@ -4,27 +4,28 @@ import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.pdmodel.font.PDFont import org.apache.pdfbox.pdmodel.font.PDType0Font import org.apache.pdfbox.pdmodel.font.PDType1Font +import org.apache.pdfbox.pdmodel.font.Standard14Fonts import org.xbib.graphics.pdfbox.groovy.Font class PdfFont { - private static final DEFAULT_FONT = PDType1Font.HELVETICA + private static final DEFAULT_FONT = new PDType1Font(Standard14Fonts.FontName.HELVETICA) private static fonts = [ - 'Times-Roman': [regular: PDType1Font.TIMES_ROMAN, - bold: PDType1Font.TIMES_BOLD, - italic : PDType1Font.TIMES_ITALIC, - boldItalic: PDType1Font.TIMES_BOLD_ITALIC], - 'Helvetica' : [regular: PDType1Font.HELVETICA, - bold: PDType1Font.HELVETICA_BOLD, - italic : PDType1Font.HELVETICA_OBLIQUE, - boldItalic: PDType1Font.HELVETICA_BOLD_OBLIQUE], - 'Courier' : [regular: PDType1Font.COURIER, - bold: PDType1Font.COURIER_BOLD, - italic : PDType1Font.COURIER_OBLIQUE, - boldItalic: PDType1Font.COURIER_BOLD_OBLIQUE], - 'Symbol' : [regular: PDType1Font.SYMBOL], - 'Dingbat' : [regular: PDType1Font.ZAPF_DINGBATS] + 'Times-Roman': [regular: new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN), + bold: new PDType1Font(Standard14Fonts.FontName.TIMES_BOLD), + italic : new PDType1Font(Standard14Fonts.FontName.TIMES_ITALIC), + boldItalic: new PDType1Font(Standard14Fonts.FontName.TIMES_BOLD_ITALIC) ], + 'Helvetica' : [regular: new PDType1Font(Standard14Fonts.FontName.HELVETICA), + bold: new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD), + italic : new PDType1Font(Standard14Fonts.FontName.HELVETICA_OBLIQUE), + boldItalic: new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD_OBLIQUE) ], + 'Courier' : [regular: new PDType1Font(Standard14Fonts.FontName.COURIER), + bold: new PDType1Font(Standard14Fonts.FontName.COURIER_BOLD), + italic : new PDType1Font(Standard14Fonts.FontName.COURIER_OBLIQUE), + boldItalic: new PDType1Font(Standard14Fonts.FontName.COURIER_BOLD_OBLIQUE) ], + 'Symbol' : [regular: new PDType1Font(Standard14Fonts.FontName.SYMBOL) ], + 'Dingbat' : [regular: new PDType1Font(Standard14Fonts.FontName.ZAPF_DINGBATS) ] ] static boolean addFont(PDDocument document, String name, InputStream inputStream, boolean bold, boolean italic) { diff --git a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/LoadFontTest.groovy b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/LoadFontTest.groovy index bb7d9f4..16e5846 100644 --- a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/LoadFontTest.groovy +++ b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/LoadFontTest.groovy @@ -1,10 +1,13 @@ package org.xbib.graphics.pdfbox.groovy.test import groovy.util.logging.Log +import org.apache.fontbox.ttf.CmapLookup import org.apache.fontbox.ttf.CmapSubtable import org.apache.fontbox.ttf.NamingTable import org.apache.fontbox.ttf.TTFParser import org.apache.fontbox.ttf.TrueTypeFont +import org.apache.pdfbox.io.RandomAccessRead +import org.apache.pdfbox.io.RandomAccessReadBuffer import org.junit.Test import java.nio.file.Files @@ -28,8 +31,9 @@ class LoadFontTest { private final Map otf = new HashMap<>() private void addOpenTypeFont(String name, InputStream inputStream) { - TTFParser ttfParser = new TTFParser(false, true) - TrueTypeFont trueTypeFont = ttfParser.parse(inputStream) + TTFParser ttfParser = new TTFParser(true) + RandomAccessRead read = new RandomAccessReadBuffer(inputStream) + TrueTypeFont trueTypeFont = ttfParser.parse(read) try { NamingTable nameTable = trueTypeFont.getNaming() if (!nameTable) { @@ -51,7 +55,7 @@ class LoadFontTest { log.warning("Missing 'name' entry for PostScript name in font " + inputStream) } } - CmapSubtable cmapSubtable = trueTypeFont.getUnicodeCmap(true) + CmapLookup cmapSubtable = trueTypeFont.getUnicodeCmapLookup(true) if (!cmapSubtable) { log.warning('missing cmap table in ' + name) } else { diff --git a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfBoxBarcodeTest.groovy b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfBoxBarcodeTest.groovy index 8fb7fcf..b6144b6 100644 --- a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfBoxBarcodeTest.groovy +++ b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfBoxBarcodeTest.groovy @@ -5,6 +5,7 @@ import org.apache.pdfbox.pdmodel.PDPage import org.apache.pdfbox.pdmodel.PDPageContentStream import org.apache.pdfbox.pdmodel.common.PDRectangle import org.apache.pdfbox.pdmodel.font.PDType1Font +import org.apache.pdfbox.pdmodel.font.Standard14Fonts import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject @@ -67,7 +68,7 @@ class PdfBoxBarcodeTest { private static void createText(PDPage page, PDPageContentStream contentStream) { contentStream.moveTo(32.0f, (page.getBBox().height - 80f) as float) contentStream.beginText() - contentStream.setFont(PDType1Font.HELVETICA, 12f) + contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12f) contentStream.showText("Hello World") contentStream.endText() contentStream.close() diff --git a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfDocumentLoader.groovy b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfDocumentLoader.groovy index 6ec5ba7..488059b 100644 --- a/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfDocumentLoader.groovy +++ b/graphics-pdfbox-groovy/src/test/groovy/org/xbib/graphics/pdfbox/groovy/test/PdfDocumentLoader.groovy @@ -1,6 +1,7 @@ package org.xbib.graphics.pdfbox.groovy.test import groovy.xml.XmlParser +import org.apache.pdfbox.Loader import org.apache.pdfbox.pdmodel.PDDocument import org.xbib.graphics.pdfbox.groovy.Cell import org.xbib.graphics.pdfbox.groovy.Document @@ -12,7 +13,7 @@ import org.xbib.graphics.pdfbox.groovy.TextBlock class PdfDocumentLoader { static Document load(byte[] data) { - PDDocument pdfDoc = PDDocument.load(new ByteArrayInputStream(data)) + PDDocument pdfDoc = Loader.loadPDF(data) Document document = new Document(element: pdfDoc) def metaData = new XmlParser().parse(pdfDoc.documentCatalog.metadata.createInputStream()) document.margin.top = metaData.'@marginTop' as Integer diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/boxable/Cell.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/boxable/Cell.java index 8c1f3f9..36d27ae 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/boxable/Cell.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/boxable/Cell.java @@ -6,14 +6,17 @@ import org.apache.pdfbox.pdmodel.font.PDType1Font; import java.awt.Color; import java.io.IOException; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD; + public class Cell { private float width; private Float height; private String text; - private PDFont font = PDType1Font.HELVETICA; - private PDFont fontBold = PDType1Font.HELVETICA_BOLD; + private PDFont font = new PDType1Font(HELVETICA); + private PDFont fontBold = new PDType1Font(HELVETICA_BOLD); private float fontSize = 8; private Color fillColor; diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/boxable/Paragraph.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/boxable/Paragraph.java index 7416c8a..fe1aec1 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/boxable/Paragraph.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/boxable/Paragraph.java @@ -13,6 +13,10 @@ import java.util.List; import java.util.Map; import java.util.Stack; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_OBLIQUE; + public class Paragraph { private float width; @@ -74,9 +78,9 @@ public class Paragraph { this.font = font; // check if we have different default font for italic and bold text if (FontUtils.getDefaultfonts().isEmpty()) { - fontBold = PDType1Font.HELVETICA_BOLD; - fontItalic = PDType1Font.HELVETICA_OBLIQUE; - fontBoldItalic = PDType1Font.HELVETICA_BOLD_OBLIQUE; + fontBold = new PDType1Font(HELVETICA_BOLD); + fontItalic = new PDType1Font(HELVETICA_OBLIQUE); + fontBoldItalic = new PDType1Font(HELVETICA_BOLD_OBLIQUE); } else { fontBold = FontUtils.getDefaultfonts().get("fontBold"); fontBoldItalic = FontUtils.getDefaultfonts().get("fontBoldItalic"); diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/Document.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/Document.java index 304fd29..b985961 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/Document.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/Document.java @@ -1,6 +1,5 @@ package org.xbib.graphics.pdfbox.layout.element; -import org.apache.pdfbox.io.MemoryUsageSetting; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.xbib.graphics.pdfbox.layout.element.render.Layout; @@ -45,17 +44,7 @@ public class Document implements Element, Closeable, RenderListener { * Creates a Document. */ public Document() { - this(PageFormats.A4_PORTRAIT, true); - } - - /** - * Creates a Document based on the given page format. By default, a - * {@link VerticalLayout} is used. - * - * @param pageFormat the page format box to use. - */ - public Document(PageFormat pageFormat) { - this(pageFormat, true); + this(PageFormats.A4_PORTRAIT); } /** @@ -71,18 +60,17 @@ public class Document implements Element, Closeable, RenderListener { float marginRight, float marginTop, float marginBottom) { - this(marginLeft, marginRight, marginTop, marginBottom, true); + this(PageFormat.builder().margins(marginLeft, marginRight, marginTop, marginBottom).build()); } - public Document(float marginLeft, - float marginRight, - float marginTop, - float marginBottom, boolean memory) { - this(PageFormat.builder().margins(marginLeft, marginRight, marginTop, marginBottom).build(), memory); - } - - public Document(PageFormat pageFormat, boolean memory) { - this.pdDocument = new PDDocument(memory ? MemoryUsageSetting.setupMainMemoryOnly() : MemoryUsageSetting.setupTempFileOnly()); + /** + * Creates a Document based on the given page format. By default, a + * {@link VerticalLayout} is used. + * + * @param pageFormat the page format box to use. + */ + public Document(PageFormat pageFormat) { + this.pdDocument = new PDDocument(); this.pdDocumentInformation = new PDDocumentInformation(); setPageFormat(pageFormat); } diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/TextElement.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/TextElement.java index d66f560..5f2766e 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/TextElement.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/TextElement.java @@ -2,6 +2,8 @@ package org.xbib.graphics.pdfbox.layout.element; import org.xbib.graphics.pdfbox.layout.font.Font; +import java.util.Objects; + public class TextElement implements Element { private final boolean markup; @@ -17,6 +19,8 @@ public class TextElement implements Element { this.value = value; this.font = font; this.fontsize = fontsize; + Objects.requireNonNull(value, "you must specify a value"); + Objects.requireNonNull(font, "you must specify a font"); } public String getValue() { diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/Engine.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/Engine.java index d802981..9f8f454 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/Engine.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/Engine.java @@ -1,5 +1,6 @@ package org.xbib.graphics.pdfbox.layout.element.scripting; +import java.io.Closeable; import java.util.logging.Level; import org.xbib.graphics.pdfbox.layout.element.scripting.command.Command; import org.xbib.settings.Settings; @@ -7,7 +8,7 @@ import org.xbib.settings.Settings; import java.io.IOException; import java.util.logging.Logger; -public class Engine { +public class Engine implements Closeable { private static final Logger logger = Logger.getLogger(Engine.class.getName()); @@ -73,4 +74,8 @@ public class Engine { } } + @Override + public void close() throws IOException { + state.close(); + } } diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/State.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/State.java index 197e44f..0f377b5 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/State.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/State.java @@ -3,11 +3,12 @@ package org.xbib.graphics.pdfbox.layout.element.scripting; import org.xbib.graphics.pdfbox.layout.element.Document; import org.xbib.graphics.pdfbox.layout.element.Element; +import java.io.Closeable; +import java.io.IOException; import java.util.List; import java.util.Stack; -import java.util.stream.Collectors; -public class State { +public class State implements Closeable { private final Stack elements = new Stack<>(); @@ -26,8 +27,26 @@ public class State { List list = elements.stream() .filter(e -> e instanceof Document) .map(e -> (Document) e) - .collect(Collectors.toList()); + .toList(); int size = list.size(); return size == 0 ? null : list.get(size - 1); } + + /** + * Close all documents and free resources. + * @throws IOException if a document can not be closed. + */ + @Override + public void close() throws IOException { + elements.stream() + .filter(e -> e instanceof Document) + .map(e -> (Document) e) + .forEach(d -> { + try { + d.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } } diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/TextCommand.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/TextCommand.java index 8e59847..7fd3c61 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/TextCommand.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/element/scripting/command/TextCommand.java @@ -11,7 +11,6 @@ import org.xbib.graphics.pdfbox.layout.element.scripting.Engine; import org.xbib.graphics.pdfbox.layout.element.scripting.State; import org.xbib.graphics.pdfbox.layout.text.Alignment; import org.xbib.graphics.pdfbox.layout.position.Position; -import org.xbib.graphics.pdfbox.layout.text.TextSequence; import org.xbib.settings.Settings; import java.util.Locale; diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/font/BaseFont.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/font/BaseFont.java index abd606a..04c7c52 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/font/BaseFont.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/font/BaseFont.java @@ -2,25 +2,34 @@ package org.xbib.graphics.pdfbox.layout.font; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType1Font; +import org.apache.pdfbox.pdmodel.font.Standard14Fonts; -import java.io.IOException; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_BOLD_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_BOLD_ITALIC; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_ITALIC; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_ROMAN; /** * In order to easy handling with fonts, this enum bundles the * plain/italic/bold/bold-italic variants of the three standard font types - * {@link PDType1Font#TIMES_ROMAN Times},{@link PDType1Font#COURIER Courier} and - * {@link PDType1Font#HELVETICA Helveticy}. + * Times, Courier, Helveticy. */ public enum BaseFont implements Font { - TIMES(PDType1Font.TIMES_ROMAN, PDType1Font.TIMES_BOLD, - PDType1Font.TIMES_ITALIC, PDType1Font.TIMES_BOLD_ITALIC), + TIMES(new PDType1Font(TIMES_ROMAN), new PDType1Font(TIMES_BOLD), + new PDType1Font(TIMES_ITALIC), new PDType1Font(TIMES_BOLD_ITALIC)), - COURIER(PDType1Font.COURIER, PDType1Font.COURIER_BOLD, - PDType1Font.COURIER_OBLIQUE, PDType1Font.COURIER_BOLD_OBLIQUE), + COURIER(new PDType1Font(Standard14Fonts.FontName.COURIER), new PDType1Font(COURIER_BOLD), + new PDType1Font(COURIER_OBLIQUE), new PDType1Font(COURIER_BOLD_OBLIQUE)), - HELVETICA(PDType1Font.HELVETICA, PDType1Font.HELVETICA_BOLD, - PDType1Font.HELVETICA_OBLIQUE, PDType1Font.HELVETICA_BOLD_OBLIQUE); + HELVETICA(new PDType1Font(Standard14Fonts.FontName.HELVETICA), new PDType1Font(HELVETICA_BOLD), + new PDType1Font(HELVETICA_OBLIQUE), new PDType1Font(HELVETICA_BOLD_OBLIQUE)); private final PDFont plainFont; diff --git a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/font/Fonts.java b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/font/Fonts.java index 704a452..cc4ce1a 100644 --- a/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/font/Fonts.java +++ b/graphics-pdfbox-layout/src/main/java/org/xbib/graphics/pdfbox/layout/font/Fonts.java @@ -2,23 +2,16 @@ package org.xbib.graphics.pdfbox.layout.font; import org.xbib.graphics.pdfbox.layout.element.Document; -import java.util.HashMap; -import java.util.Map; - public enum Fonts { HELVETICA, TIMES, COURIER, NOTOSANS; - private final Map map = new HashMap<>(); - public Font getFont(Document document) { - return map.computeIfAbsent(name(), name -> { - if ("notosans".equalsIgnoreCase(name())) { - return new NotoSansFont(document); - } - return BaseFont.valueOf(name()); - }); + if ("notosans".equalsIgnoreCase(name())) { + return new NotoSansFont(document); + } + return BaseFont.valueOf(name()); } } diff --git a/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/CustomAnnotationTest.java b/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/CustomAnnotationTest.java index 544e39d..b7550ba 100644 --- a/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/CustomAnnotationTest.java +++ b/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/CustomAnnotationTest.java @@ -5,6 +5,7 @@ import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; +import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationHighlight; import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup; import org.junit.jupiter.api.Test; import org.xbib.graphics.pdfbox.layout.element.Document; @@ -66,8 +67,7 @@ public class CustomAnnotationTest { .getAnnotationsOfType(HighlightAnnotation.class); for (HighlightAnnotation highlightAnnotation : HighlightAnnotations) { // use PDF text markup to implement the highlight - PDAnnotationTextMarkup markup = new PDAnnotationTextMarkup( - PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT); + PDAnnotationTextMarkup markup = new PDAnnotationHighlight(); // use the bounding box of the drawn object to position the // highlight PDRectangle bounds = new PDRectangle(); diff --git a/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/HelloNotoFontTest.java b/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/HelloNotoFontTest.java index 0052c7c..f4cf755 100644 --- a/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/HelloNotoFontTest.java +++ b/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/HelloNotoFontTest.java @@ -13,11 +13,11 @@ import java.io.FileOutputStream; public class HelloNotoFontTest { @Test - public void test() throws Exception { + public void testDocumentOne() throws Exception { Document document = new Document(PageFormats.A4_PORTRAIT); + Font font = new NotoSansFont(document); Paragraph paragraph = new Paragraph(); paragraph.add(new Indent(32, SpaceUnit.pt)); - Font font = new NotoSansFont(document); paragraph.addMarkup("Hello Noto Regular\n", 12, font); paragraph.addMarkup("*Hello Noto Bold*\n", 12, font); paragraph.addMarkup("_Hello Noto Italic_\n", 12, font); @@ -25,6 +25,31 @@ public class HelloNotoFontTest { paragraph.addText("Hello Unicode Text: Zwrotki dla Dorotki : arcyksiążę fiołków\n", 12, font); paragraph.addMarkup("Hello Unicode Markup: _Zwrotki dla Dorotki : arcyksiążę fiołków_\n", 12, font); document.add(paragraph); + Paragraph anotherParagraph = new Paragraph(); + anotherParagraph.add(new Indent(32, SpaceUnit.pt)); + anotherParagraph.addMarkup("Hello Noto Regular\n", 12, font); + document.add(anotherParagraph); document.render().save(new FileOutputStream("build/hellonotofont.pdf")).close(); } + + @Test + public void testDocumentTwo() throws Exception { + Document document = new Document(PageFormats.A4_PORTRAIT); + Font font = new NotoSansFont(document); + Paragraph paragraph = new Paragraph(); + paragraph.add(new Indent(32, SpaceUnit.pt)); + paragraph.addMarkup("Hello Noto Regular 2\n", 12, font); + paragraph.addMarkup("*Hello Noto Bold*\n", 12, font); + paragraph.addMarkup("_Hello Noto Italic_\n", 12, font); + paragraph.addMarkup("*_Hello Noto Bold Italic_*\n", 12, font); + paragraph.addText("Hello Unicode Text: Zwrotki dla Dorotki : arcyksiążę fiołków\n", 12, font); + paragraph.addMarkup("Hello Unicode Markup: _Zwrotki dla Dorotki : arcyksiążę fiołków_\n", 12, font); + document.add(paragraph); + Paragraph anotherParagraph = new Paragraph(); + anotherParagraph.add(new Indent(32, SpaceUnit.pt)); + anotherParagraph.addMarkup("Hello Noto Regular 2\n", 12, font); + document.add(anotherParagraph); + document.render().save(new FileOutputStream("build/hellonotofont2.pdf")).close(); + } + } diff --git a/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/ScriptingTest.java b/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/ScriptingTest.java index 72da077..3a59ff9 100644 --- a/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/ScriptingTest.java +++ b/graphics-pdfbox-layout/src/test/java/org/xbib/graphics/pdfbox/layout/test/ScriptingTest.java @@ -10,13 +10,27 @@ import java.io.FileOutputStream; public class ScriptingTest { @Test - public void script() throws Exception { + public void scriptOne() throws Exception { Settings settings = Settings.settingsBuilder() .loadFromResource("json", getClass().getResourceAsStream("scripting.json")) .build(); - Engine engine = new Engine(); - engine.execute(settings); - Document document = engine.getState().getDocument(); - document.render().save(new FileOutputStream("build/scripting.pdf")).close(); + try (Engine engine = new Engine()) { + engine.execute(settings); + Document document = engine.getState().getDocument(); + document.render().save(new FileOutputStream("build/scripting1.pdf")).close(); + } } + + @Test + public void scriptTwo() throws Exception { + Settings settings = Settings.settingsBuilder() + .loadFromResource("json", getClass().getResourceAsStream("scripting.json")) + .build(); + try (Engine engine = new Engine()) { + engine.execute(settings); + Document document = engine.getState().getDocument(); + document.render().save(new FileOutputStream("build/scripting2.pdf")).close(); + } + } + } diff --git a/graphics-pdfbox-layout/src/test/resources/org/xbib/graphics/pdfbox/layout/test/scripting.json b/graphics-pdfbox-layout/src/test/resources/org/xbib/graphics/pdfbox/layout/test/scripting.json index 5b59878..cb01f9f 100644 --- a/graphics-pdfbox-layout/src/test/resources/org/xbib/graphics/pdfbox/layout/test/scripting.json +++ b/graphics-pdfbox-layout/src/test/resources/org/xbib/graphics/pdfbox/layout/test/scripting.json @@ -108,7 +108,7 @@ "elements": [ { "type": "text", - "value": "Hello World 5", + "value": "Hello World 5 Zwrotki dla Dorotki : arcyksiążę fiołków", "fontsize": 20, "font": "notosans" } diff --git a/graphics-pdfbox-print/src/main/java/org/xbib/graphics/pdfbox/print/PrintUtility.java b/graphics-pdfbox-print/src/main/java/org/xbib/graphics/pdfbox/print/PrintUtility.java index 73bfb15..0fc04a2 100644 --- a/graphics-pdfbox-print/src/main/java/org/xbib/graphics/pdfbox/print/PrintUtility.java +++ b/graphics-pdfbox-print/src/main/java/org/xbib/graphics/pdfbox/print/PrintUtility.java @@ -1,6 +1,6 @@ package org.xbib.graphics.pdfbox.print; -import org.apache.pdfbox.io.MemoryUsageSetting; +import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.printing.PDFPageable; @@ -86,7 +86,8 @@ public class PrintUtility { } public static void print(InputStream inputStream, Printer printer) throws Exception { - PDDocument document = PDDocument.load(inputStream, MemoryUsageSetting.setupTempFileOnly()); + byte[] bytes = inputStream.readAllBytes(); + PDDocument document = Loader.loadPDF(bytes); PrinterJob job = PrinterJob.getPrinterJob(); job.setPageable(new PDFPageable(document)); job.setPrintService(printer.getService()); diff --git a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/PdfBoxGraphics2D.java b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/PdfBoxGraphics2D.java index e2e83b8..b985089 100644 --- a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/PdfBoxGraphics2D.java +++ b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/PdfBoxGraphics2D.java @@ -251,15 +251,14 @@ public class PdfBoxGraphics2D extends Graphics2D { PDRectangle bbox, PdfBoxGraphics2D parentGfx) throws IOException { - this(document, createXObject(document, bbox), parentGfx); + this(document, createAppearanceStream(document, bbox), parentGfx); } public PdfBoxGraphics2D(PDDocument document, - PDFormXObject xFormObject, + PDAppearanceStream pdAppearanceStream, PdfBoxGraphics2D parentGfx) throws IOException { - this(document, xFormObject, new PDPageContentStream(document, xFormObject, - xFormObject.getStream().createOutputStream(COSName.FLATE_DECODE)), parentGfx); + this(document, pdAppearanceStream, new PDPageContentStream(document, pdAppearanceStream, pdAppearanceStream.getStream().createOutputStream(COSName.FLATE_DECODE)), parentGfx); } public PdfBoxGraphics2D(PDDocument document, @@ -1152,17 +1151,17 @@ public class PdfBoxGraphics2D extends Graphics2D { } private void checkNoCopyActive() { - if (copyList.size() > 0) { + if (!copyList.isEmpty()) { throw new IllegalStateException("Don't use the main context as long as a copy is active! Child context is missing a .dispose() call\n" + gatherDebugCopyInfo(this)); } } - private static PDFormXObject createXObject(PDDocument document, PDRectangle bbox) { - PDFormXObject xFormObject = new PDAppearanceStream(document); - xFormObject.setResources(new PDResources()); - xFormObject.setBBox(bbox); - return xFormObject; + private static PDAppearanceStream createAppearanceStream(PDDocument document, PDRectangle bbox) { + PDAppearanceStream appearanceStream = new PDAppearanceStream(document); + appearanceStream.setResources(new PDResources()); + appearanceStream.setBBox(bbox); + return appearanceStream; } private static String gatherDebugCopyInfo(PdfBoxGraphics2D gfx) { diff --git a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/font/DefaultFontDrawer.java b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/font/DefaultFontDrawer.java index dffa36f..f840204 100644 --- a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/font/DefaultFontDrawer.java +++ b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/font/DefaultFontDrawer.java @@ -1,7 +1,6 @@ package org.xbib.graphics.pdfbox.font; import org.apache.fontbox.ttf.TrueTypeCollection; -import org.apache.pdfbox.io.IOUtils; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType0Font; @@ -28,6 +27,21 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_BOLD_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.SYMBOL; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_BOLD_ITALIC; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_ITALIC; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_ROMAN; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.ZAPF_DINGBATS; + /** * Default implementation to draw fonts. You can reuse instances of this class * within a PDDocument for more then one {@link PdfBoxGraphics2D}. @@ -72,7 +86,7 @@ public class DefaultFontDrawer implements FontDrawer, Closeable { public void registerFont(String fontName, InputStream fontStream) throws IOException { File fontFile = File.createTempFile("pdfboxgfx2dfont", ".ttf"); try (FileOutputStream out = new FileOutputStream(fontFile)) { - IOUtils.copy(fontStream, out); + fontStream.transferTo(out); } fontFile.deleteOnExit(); tempFiles.add(fontFile); @@ -412,10 +426,10 @@ public class DefaultFontDrawer implements FontDrawer, Closeable { return chooseMatchingTimes(font); } if (fontNameEqualsAnyOf(font, "Symbol")) { - return PDType1Font.SYMBOL; + return new PDType1Font(SYMBOL); } if (fontNameEqualsAnyOf(font, "ZapfDingbats", "Dingbats")) { - return PDType1Font.ZAPF_DINGBATS; + return new PDType1Font(ZAPF_DINGBATS); } return null; } @@ -429,18 +443,17 @@ public class DefaultFontDrawer implements FontDrawer, Closeable { */ public static PDFont chooseMatchingHelvetica(Font font) { if ((font.getStyle() & (Font.ITALIC | Font.BOLD)) == (Font.ITALIC | Font.BOLD)) { - return PDType1Font.HELVETICA_BOLD_OBLIQUE; + return new PDType1Font(HELVETICA_BOLD_OBLIQUE); } if ((font.getStyle() & Font.ITALIC) == Font.ITALIC) { - return PDType1Font.HELVETICA_OBLIQUE; + return new PDType1Font(HELVETICA_OBLIQUE); } if ((font.getStyle() & Font.BOLD) == Font.BOLD) { - return PDType1Font.HELVETICA_BOLD; + return new PDType1Font(HELVETICA_BOLD); } - return PDType1Font.HELVETICA; + return new PDType1Font(HELVETICA); } - /** * Get a PDType1Font.COURIER-variant, which matches the given font * @@ -450,15 +463,15 @@ public class DefaultFontDrawer implements FontDrawer, Closeable { */ public static PDFont chooseMatchingCourier(Font font) { if ((font.getStyle() & (Font.ITALIC | Font.BOLD)) == (Font.ITALIC | Font.BOLD)) { - return PDType1Font.COURIER_BOLD_OBLIQUE; + return new PDType1Font(COURIER_BOLD_OBLIQUE); } if ((font.getStyle() & Font.ITALIC) == Font.ITALIC) { - return PDType1Font.COURIER_OBLIQUE; + return new PDType1Font(COURIER_OBLIQUE); } if ((font.getStyle() & Font.BOLD) == Font.BOLD) { - return PDType1Font.COURIER_BOLD; + return new PDType1Font(COURIER_BOLD); } - return PDType1Font.COURIER; + return new PDType1Font(COURIER); } /** @@ -470,15 +483,15 @@ public class DefaultFontDrawer implements FontDrawer, Closeable { */ public static PDFont chooseMatchingTimes(Font font) { if ((font.getStyle() & (Font.ITALIC | Font.BOLD)) == (Font.ITALIC | Font.BOLD)) { - return PDType1Font.TIMES_BOLD_ITALIC; + return new PDType1Font(TIMES_BOLD_ITALIC); } if ((font.getStyle() & Font.ITALIC) == Font.ITALIC) { - return PDType1Font.TIMES_ITALIC; + return new PDType1Font(TIMES_ITALIC); } if ((font.getStyle() & Font.BOLD) == Font.BOLD) { - return PDType1Font.TIMES_BOLD; + return new PDType1Font(TIMES_BOLD); } - return PDType1Font.TIMES_ROMAN; + return new PDType1Font(TIMES_ROMAN); } public static boolean fontNameEqualsAnyOf(Font font, String... names) { diff --git a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/paint/DefaultPaintApplier.java b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/paint/DefaultPaintApplier.java index 39809d2..95fcf2f 100644 --- a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/paint/DefaultPaintApplier.java +++ b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/paint/DefaultPaintApplier.java @@ -7,7 +7,6 @@ import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.cos.COSFloat; import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSStream; -import org.apache.pdfbox.multipdf.PDFCloneUtility; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDResources; diff --git a/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/paint/PDFCloneUtility.java b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/paint/PDFCloneUtility.java new file mode 100644 index 0000000..8c47f5f --- /dev/null +++ b/graphics-pdfbox/src/main/java/org/xbib/graphics/pdfbox/paint/PDFCloneUtility.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.xbib.graphics.pdfbox.paint; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSObject; +import org.apache.pdfbox.cos.COSStream; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.common.COSObjectable; + +/** + * Copied from org.apache.pdfbox.multipdf.util + */ +public class PDFCloneUtility { + + private final PDDocument destination; + private final Map clonedVersion = new HashMap<>(); + private final Set clonedValues = new HashSet<>(); + + /** + * Creates a new instance for the given target document. + * + * @param dest the destination PDF document that will receive the clones + */ + PDFCloneUtility(PDDocument dest) { + this.destination = dest; + } + + /** + * Returns the destination PDF document this cloner instance is set up for. + * + * @return the destination PDF document + */ + PDDocument getDestination() { + return this.destination; + } + + /** + * Deep-clones the given object for inclusion into a different PDF document identified by the destination parameter. + *

+ * Expert use only, don’t use it if you don’t know exactly what you are doing. + * + * @param base the initial object as the root of the deep-clone operation + * @return the cloned instance of the base object + * @throws IOException if an I/O error occurs + */ + @SuppressWarnings("unchecked") + public TCOSBase cloneForNewDocument(TCOSBase base) throws IOException { + if (base == null) { + return null; + } + COSBase retval = clonedVersion.get(base); + if (retval != null) { + // we are done, it has already been converted. + return (TCOSBase) retval; + } + if (clonedValues.contains(base)) { + // Don't clone a clone + return base; + } + retval = cloneCOSBaseForNewDocument(base); + clonedVersion.put(base, retval); + clonedValues.add(retval); + return (TCOSBase) retval; + } + + COSBase cloneCOSBaseForNewDocument(COSBase base) throws IOException { + if (base instanceof COSObject) { + return cloneForNewDocument(((COSObject) base).getObject()); + } + if (base instanceof COSArray) { + return cloneCOSArray((COSArray) base); + } + if (base instanceof COSStream) { + return cloneCOSStream((COSStream) base); + } + if (base instanceof COSDictionary) { + return cloneCOSDictionary((COSDictionary) base); + } + return base; + } + + private COSArray cloneCOSArray(COSArray array) throws IOException { + COSArray newArray = new COSArray(); + for (int i = 0; i < array.size(); i++) { + COSBase value = array.get(i); + if (hasSelfReference(array, value)) { + newArray.add(newArray); + } else { + newArray.add(cloneForNewDocument(value)); + } + } + return newArray; + } + + private COSStream cloneCOSStream(COSStream stream) throws IOException { + COSStream newStream = destination.getDocument().createCOSStream(); + try (OutputStream output = newStream.createRawOutputStream(); + InputStream input = stream.createRawInputStream()) { + input.transferTo(output); + } + clonedVersion.put(stream, newStream); + for (Map.Entry entry : stream.entrySet()) { + COSBase value = entry.getValue(); + if (hasSelfReference(stream, value)) { + newStream.setItem(entry.getKey(), newStream); + } else { + newStream.setItem(entry.getKey(), cloneForNewDocument(value)); + } + } + return newStream; + } + + private COSDictionary cloneCOSDictionary(COSDictionary dictionary) throws IOException { + COSDictionary newDictionary = new COSDictionary(); + clonedVersion.put(dictionary, newDictionary); + for (Map.Entry entry : dictionary.entrySet()) { + COSBase value = entry.getValue(); + if (hasSelfReference(dictionary, value)) { + newDictionary.setItem(entry.getKey(), newDictionary); + } else { + newDictionary.setItem(entry.getKey(), cloneForNewDocument(value)); + } + } + return newDictionary; + } + + /** + * Merges two objects of the same type by deep-cloning its members.
+ * Base and target must be instances of the same class. + * + * @param base the base object to be cloned + * @param target the merge target + * @throws IOException if an I/O error occurs + */ + void cloneMerge(final COSObjectable base, COSObjectable target) throws IOException { + if (base == null || base == target) { + return; + } + cloneMergeCOSBase(base.getCOSObject(), target.getCOSObject()); + } + + private void cloneMergeCOSBase(final COSBase source, final COSBase target) throws IOException { + COSBase sourceBase = source instanceof COSObject ? ((COSObject) source).getObject() : source; + COSBase targetBase = target instanceof COSObject ? ((COSObject) target).getObject() : target; + if (sourceBase instanceof COSArray array && targetBase instanceof COSArray) { + for (int i = 0; i < array.size(); i++) { + ((COSArray) targetBase).add(cloneForNewDocument(array.get(i))); + } + } else if (sourceBase instanceof COSDictionary sourceDict && targetBase instanceof COSDictionary targetDict) { + for (Map.Entry entry : sourceDict.entrySet()) { + COSName key = entry.getKey(); + COSBase value = entry.getValue(); + if (targetDict.getItem(key) != null) { + cloneMerge(value, targetDict.getItem(key)); + } else { + targetDict.setItem(key, cloneForNewDocument(value)); + } + } + } + } + + /** + * Check whether an element (of an array or a dictionary) points to its parent. + * + * @param parent COSArray or COSDictionary + * @param value an element + */ + private boolean hasSelfReference(COSBase parent, COSBase value) { + if (value instanceof COSObject) { + COSBase actual = ((COSObject) value).getObject(); + return actual == parent; + } + return false; + } +} diff --git a/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/FontDrawerTest.java b/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/FontDrawerTest.java index 13af1c7..05fdb81 100644 --- a/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/FontDrawerTest.java +++ b/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/FontDrawerTest.java @@ -1,12 +1,25 @@ package org.xbib.graphics.pdfbox.test; import org.apache.pdfbox.pdmodel.font.PDType1Font; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.xbib.graphics.pdfbox.font.CoreFontDrawer; import java.awt.Font; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_BOLD_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.COURIER_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_OBLIQUE; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.SYMBOL; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_BOLD; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_ITALIC; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_BOLD_ITALIC; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.TIMES_ROMAN; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.ZAPF_DINGBATS; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -19,51 +32,29 @@ public class FontDrawerTest { Font anyFontItalic = anyFont.deriveFont(Font.ITALIC); Font anyFontBoldItalic = anyFont.deriveFont(Font.BOLD | Font.ITALIC); - Assertions.assertEquals(PDType1Font.COURIER, - CoreFontDrawer.chooseMatchingCourier(anyFont)); - assertEquals(PDType1Font.COURIER_BOLD, - CoreFontDrawer.chooseMatchingCourier(anyFontBold)); - assertEquals(PDType1Font.COURIER_OBLIQUE, - CoreFontDrawer.chooseMatchingCourier(anyFontItalic)); - assertEquals(PDType1Font.COURIER_BOLD_OBLIQUE, - CoreFontDrawer.chooseMatchingCourier(anyFontBoldItalic)); - - assertEquals(PDType1Font.HELVETICA, - CoreFontDrawer.chooseMatchingHelvetica(anyFont)); - assertEquals(PDType1Font.HELVETICA_BOLD, - CoreFontDrawer.chooseMatchingHelvetica(anyFontBold)); - assertEquals(PDType1Font.HELVETICA_OBLIQUE, - CoreFontDrawer.chooseMatchingHelvetica(anyFontItalic)); - assertEquals(PDType1Font.HELVETICA_BOLD_OBLIQUE, - CoreFontDrawer.chooseMatchingHelvetica(anyFontBoldItalic)); - - assertEquals(PDType1Font.TIMES_ROMAN, - CoreFontDrawer.chooseMatchingTimes(anyFont)); - assertEquals(PDType1Font.TIMES_BOLD, - CoreFontDrawer.chooseMatchingTimes(anyFontBold)); - assertEquals(PDType1Font.TIMES_ITALIC, - CoreFontDrawer.chooseMatchingTimes(anyFontItalic)); - assertEquals(PDType1Font.TIMES_BOLD_ITALIC, - CoreFontDrawer.chooseMatchingTimes(anyFontBoldItalic)); + assertEquals(new PDType1Font(COURIER).getName(), CoreFontDrawer.chooseMatchingCourier(anyFont).getName()); + assertEquals(new PDType1Font(COURIER_BOLD).getName(), CoreFontDrawer.chooseMatchingCourier(anyFontBold).getName()); + assertEquals(new PDType1Font(COURIER_OBLIQUE).getName(), CoreFontDrawer.chooseMatchingCourier(anyFontItalic).getName()); + assertEquals(new PDType1Font(COURIER_BOLD_OBLIQUE).getName(), CoreFontDrawer.chooseMatchingCourier(anyFontBoldItalic).getName()); + assertEquals(new PDType1Font(HELVETICA).getName(), CoreFontDrawer.chooseMatchingHelvetica(anyFont).getName()); + assertEquals(new PDType1Font(HELVETICA_BOLD).getName(), CoreFontDrawer.chooseMatchingHelvetica(anyFontBold).getName()); + assertEquals(new PDType1Font(HELVETICA_OBLIQUE).getName(), CoreFontDrawer.chooseMatchingHelvetica(anyFontItalic).getName()); + assertEquals(new PDType1Font(HELVETICA_BOLD_OBLIQUE).getName(), CoreFontDrawer.chooseMatchingHelvetica(anyFontBoldItalic).getName()); + assertEquals(new PDType1Font(TIMES_ROMAN).getName(), CoreFontDrawer.chooseMatchingTimes(anyFont).getName()); + assertEquals(new PDType1Font(TIMES_BOLD).getName(), CoreFontDrawer.chooseMatchingTimes(anyFontBold).getName()); + assertEquals(new PDType1Font(TIMES_ITALIC).getName(), CoreFontDrawer.chooseMatchingTimes(anyFontItalic).getName()); + assertEquals(new PDType1Font(TIMES_BOLD_ITALIC).getName(), CoreFontDrawer.chooseMatchingTimes(anyFontBoldItalic).getName()); } @Test public void testDefaultFontMapping() { - assertEquals(PDType1Font.HELVETICA, - CoreFontDrawer.mapToCoreFonts(Font.decode(Font.DIALOG))); - assertEquals(PDType1Font.HELVETICA, - CoreFontDrawer.mapToCoreFonts(Font.decode(Font.DIALOG_INPUT))); - assertEquals(PDType1Font.HELVETICA, - CoreFontDrawer.mapToCoreFonts(Font.decode("Arial"))); - assertEquals(PDType1Font.COURIER, - CoreFontDrawer.mapToCoreFonts(Font.decode(Font.MONOSPACED))); - assertEquals(PDType1Font.TIMES_ROMAN, - CoreFontDrawer.mapToCoreFonts(Font.decode(Font.SERIF))); - assertEquals(PDType1Font.ZAPF_DINGBATS, - CoreFontDrawer.mapToCoreFonts(Font.decode("Dingbats"))); - assertEquals(PDType1Font.SYMBOL, - CoreFontDrawer.mapToCoreFonts(Font.decode("Symbol"))); + assertEquals(new PDType1Font(HELVETICA).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode(Font.DIALOG)).getName()); + assertEquals(new PDType1Font(HELVETICA).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode(Font.DIALOG_INPUT)).getName()); + assertEquals(new PDType1Font(HELVETICA).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode("Arial")).getName()); + assertEquals(new PDType1Font(COURIER).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode(Font.MONOSPACED)).getName()); + assertEquals(new PDType1Font(TIMES_ROMAN).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode(Font.SERIF)).getName()); + assertEquals(new PDType1Font(ZAPF_DINGBATS).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode("Dingbats")).getName()); + assertEquals(new PDType1Font(SYMBOL).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode("Symbol")).getName()); assertNull(CoreFontDrawer.mapToCoreFonts(Font.decode("Georgia"))); } - } diff --git a/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/PdfBoxGraphics2DTestBase.java b/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/PdfBoxGraphics2DTestBase.java index 1cf4543..932f340 100644 --- a/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/PdfBoxGraphics2DTestBase.java +++ b/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/PdfBoxGraphics2DTestBase.java @@ -20,6 +20,9 @@ import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA; +import static org.apache.pdfbox.pdmodel.font.Standard14Fonts.FontName.HELVETICA_BOLD; + class PdfBoxGraphics2DTestBase { enum Mode { @@ -29,7 +32,7 @@ class PdfBoxGraphics2DTestBase { void exportGraphic(String dir, String name, GraphicsExporter exporter) { try { PDDocument document = new PDDocument(); - PDFont helvetica = PDType1Font.HELVETICA; + PDFont helvetica = new PDType1Font(HELVETICA); File parentDir = new File("build/test/" + dir); parentDir.mkdirs(); BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_4BYTE_ABGR); @@ -45,7 +48,7 @@ class PdfBoxGraphics2DTestBase { contentStream.beginText(); contentStream.setStrokingColor(0f, 0f, 0f); contentStream.setNonStrokingColor(0f, 0f, 0f); - contentStream.setFont(PDType1Font.HELVETICA_BOLD, 15); + contentStream.setFont(new PDType1Font(HELVETICA_BOLD), 15); contentStream.setTextMatrix(Matrix.getTranslateInstance(10, 800)); contentStream.showText("Mode " + m); contentStream.endText(); diff --git a/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/PdfRerenderTest.java b/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/PdfRerenderTest.java index 8c80e0d..dde14bb 100644 --- a/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/PdfRerenderTest.java +++ b/graphics-pdfbox/src/test/java/org/xbib/graphics/pdfbox/test/PdfRerenderTest.java @@ -1,5 +1,6 @@ package org.xbib.graphics.pdfbox.test; +import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; @@ -29,6 +30,7 @@ import java.awt.Stroke; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; +import java.io.InputStream; public class PdfRerenderTest { @@ -55,7 +57,9 @@ public class PdfRerenderTest { File parentDir = new File("build/test"); parentDir.mkdirs(); PDDocument document = new PDDocument(); - PDDocument sourceDoc = PDDocument.load(PdfRerenderTest.class.getResourceAsStream(name)); + InputStream inputStream = PdfRerenderTest.class.getResourceAsStream(name); + byte[] bytes = inputStream.readAllBytes(); + PDDocument sourceDoc = Loader.loadPDF(bytes); for (PDPage sourcePage : sourceDoc.getPages()) { PDRectangle mediaBox = sourcePage.getMediaBox(); @@ -87,7 +91,9 @@ public class PdfRerenderTest { parentDir.mkdirs(); PDDocument document = new PDDocument(); - PDDocument sourceDoc = PDDocument.load(PdfRerenderTest.class.getResourceAsStream(name)); + InputStream inputStream = PdfRerenderTest.class.getResourceAsStream(name); + byte[] bytes = inputStream.readAllBytes(); + PDDocument sourceDoc = Loader.loadPDF(bytes); for (PDPage sourcePage : sourceDoc.getPages()) { PDPage rerenderedPage = new PDPage(sourcePage.getMediaBox()); @@ -113,7 +119,9 @@ public class PdfRerenderTest { parentDir.mkdirs(); PDDocument document = new PDDocument(); - PDDocument sourceDoc = PDDocument.load(PdfRerenderTest.class.getResourceAsStream(name)); + InputStream inputStream = PdfRerenderTest.class.getResourceAsStream(name); + byte[] bytes = inputStream.readAllBytes(); + PDDocument sourceDoc = Loader.loadPDF(bytes); for (PDPage sourcePage : sourceDoc.getPages()) { PDPage rerenderedPage = new PDPage(sourcePage.getMediaBox()); diff --git a/settings.gradle b/settings.gradle index 241ece5..a0f0056 100644 --- a/settings.gradle +++ b/settings.gradle @@ -39,7 +39,7 @@ dependencyResolutionManagement { library('bytebuddy', 'net.bytebuddy', 'byte-buddy').version('1.14.4') library('objenesis', 'org.objenesis', 'objenesis').version('2.6') library('jna', 'net.java.dev.jna', 'jna').version('5.13.0') - library('pdfbox', 'org.apache.pdfbox', 'pdfbox').version('2.0.28') + library('pdfbox', 'org.apache.pdfbox', 'pdfbox').version('3.0.0-beta1') library('zxing', 'com.google.zxing', 'javase').version('3.4.1') library('reflections', 'org.reflections', 'reflections').version('0.9.11') library('jfreechart', 'org.jfree', 'jfreechart').version('1.5.2')