update to PDFbox 3.0.0 beta1, fix cmap ttf font bug in two consecutive script engine runs by removing cache from org.xbib.graphics.pdfbox.layout.font.Fonts

This commit is contained in:
Jörg Prante 2023-07-31 16:09:11 +02:00
parent ca317f41ec
commit 134343ba47
31 changed files with 455 additions and 166 deletions

View file

@ -1,5 +1,5 @@
group = org.xbib.graphics group = org.xbib.graphics
name = graphics name = graphics
version = 4.4.1 version = 4.5.0
org.gradle.warning.mode = ALL org.gradle.warning.mode = ALL

View file

@ -4,4 +4,5 @@ module org.xbib.graphics.ghostscript {
requires java.logging; requires java.logging;
requires transitive java.desktop; requires transitive java.desktop;
requires transitive org.apache.pdfbox; requires transitive org.apache.pdfbox;
requires transitive org.apache.pdfbox.io;
} }

View file

@ -7,7 +7,8 @@ import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam; import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream; 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.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;
@ -201,7 +202,7 @@ public class PDFRasterizer {
PathMatcher pathMatcher = sourceDir.getFileSystem().getPathMatcher("glob:" + globPattern); PathMatcher pathMatcher = sourceDir.getFileSystem().getPathMatcher("glob:" + globPattern);
List<PDDocument> coverPageDocs = new ArrayList<>(); List<PDDocument> coverPageDocs = new ArrayList<>();
try (Stream<Path> files = Files.list(sourceDir); try (Stream<Path> files = Files.list(sourceDir);
PDDocument pdDocument = new PDDocument(MemoryUsageSetting.setupTempFileOnly()); PDDocument pdDocument = new PDDocument();
OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(targetFile))) { OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(targetFile))) {
pdDocument.setResourceCache(null); pdDocument.setResourceCache(null);
List<Path> entries = files.sorted() List<Path> entries = files.sorted()
@ -223,7 +224,7 @@ public class PDFRasterizer {
if (path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".pdf")) { if (path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".pdf")) {
logger.info("found pdf " + path); logger.info("found pdf " + path);
try (InputStream inputStream = Files.newInputStream(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++) { for (int i = 0; i < doc.getNumberOfPages(); i++) {
PDPage page = doc.getPage(i); PDPage page = doc.getPage(i);
PDPage newPage = pdDocument.importPage(page); // shallow copy :( PDPage newPage = pdDocument.importPage(page); // shallow copy :(

View file

@ -1,6 +1,7 @@
package org.xbib.graphics.pdfbox.groovy.analyze package org.xbib.graphics.pdfbox.groovy.analyze
import groovy.util.logging.Log import groovy.util.logging.Log
import org.apache.pdfbox.Loader
import org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine import org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine
import org.apache.pdfbox.cos.COSName import org.apache.pdfbox.cos.COSName
import org.apache.pdfbox.cos.COSStream import org.apache.pdfbox.cos.COSStream
@ -23,7 +24,8 @@ class DocumentAnalyzer {
DocumentAnalyzer(InputStream inputStream) { DocumentAnalyzer(InputStream inputStream) {
inputStream.withCloseable { inputStream.withCloseable {
PDDocument document = PDDocument.load(inputStream) byte[] bytes = inputStream.readAllBytes()
PDDocument document = Loader.loadPDF(bytes)
result."author" = document.getDocumentInformation().author result."author" = document.getDocumentInformation().author
result."creator" = document.getDocumentInformation().creator result."creator" = document.getDocumentInformation().creator
result."producer" = document.getDocumentInformation().producer result."producer" = document.getDocumentInformation().producer

View file

@ -99,7 +99,7 @@ class PdfDocument implements Closeable {
void setPageNumber(int value) { void setPageNumber(int value) {
this.pageNumber = value this.pageNumber = value
contentStream?.close() contentStream?.close()
contentStream = new PDPageContentStream(pdDocument, currentPage, true, true) contentStream = new PDPageContentStream(pdDocument, currentPage, PDPageContentStream.AppendMode.APPEND, true)
toStartPosition() toStartPosition()
} }

View file

@ -4,27 +4,28 @@ import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.pdmodel.font.PDFont import org.apache.pdfbox.pdmodel.font.PDFont
import org.apache.pdfbox.pdmodel.font.PDType0Font import org.apache.pdfbox.pdmodel.font.PDType0Font
import org.apache.pdfbox.pdmodel.font.PDType1Font import org.apache.pdfbox.pdmodel.font.PDType1Font
import org.apache.pdfbox.pdmodel.font.Standard14Fonts
import org.xbib.graphics.pdfbox.groovy.Font import org.xbib.graphics.pdfbox.groovy.Font
class PdfFont { class PdfFont {
private static final DEFAULT_FONT = PDType1Font.HELVETICA private static final DEFAULT_FONT = new PDType1Font(Standard14Fonts.FontName.HELVETICA)
private static fonts = [ private static fonts = [
'Times-Roman': [regular: PDType1Font.TIMES_ROMAN, 'Times-Roman': [regular: new PDType1Font(Standard14Fonts.FontName.TIMES_ROMAN),
bold: PDType1Font.TIMES_BOLD, bold: new PDType1Font(Standard14Fonts.FontName.TIMES_BOLD),
italic : PDType1Font.TIMES_ITALIC, italic : new PDType1Font(Standard14Fonts.FontName.TIMES_ITALIC),
boldItalic: PDType1Font.TIMES_BOLD_ITALIC], boldItalic: new PDType1Font(Standard14Fonts.FontName.TIMES_BOLD_ITALIC) ],
'Helvetica' : [regular: PDType1Font.HELVETICA, 'Helvetica' : [regular: new PDType1Font(Standard14Fonts.FontName.HELVETICA),
bold: PDType1Font.HELVETICA_BOLD, bold: new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD),
italic : PDType1Font.HELVETICA_OBLIQUE, italic : new PDType1Font(Standard14Fonts.FontName.HELVETICA_OBLIQUE),
boldItalic: PDType1Font.HELVETICA_BOLD_OBLIQUE], boldItalic: new PDType1Font(Standard14Fonts.FontName.HELVETICA_BOLD_OBLIQUE) ],
'Courier' : [regular: PDType1Font.COURIER, 'Courier' : [regular: new PDType1Font(Standard14Fonts.FontName.COURIER),
bold: PDType1Font.COURIER_BOLD, bold: new PDType1Font(Standard14Fonts.FontName.COURIER_BOLD),
italic : PDType1Font.COURIER_OBLIQUE, italic : new PDType1Font(Standard14Fonts.FontName.COURIER_OBLIQUE),
boldItalic: PDType1Font.COURIER_BOLD_OBLIQUE], boldItalic: new PDType1Font(Standard14Fonts.FontName.COURIER_BOLD_OBLIQUE) ],
'Symbol' : [regular: PDType1Font.SYMBOL], 'Symbol' : [regular: new PDType1Font(Standard14Fonts.FontName.SYMBOL) ],
'Dingbat' : [regular: PDType1Font.ZAPF_DINGBATS] 'Dingbat' : [regular: new PDType1Font(Standard14Fonts.FontName.ZAPF_DINGBATS) ]
] ]
static boolean addFont(PDDocument document, String name, InputStream inputStream, boolean bold, boolean italic) { static boolean addFont(PDDocument document, String name, InputStream inputStream, boolean bold, boolean italic) {

View file

@ -1,10 +1,13 @@
package org.xbib.graphics.pdfbox.groovy.test package org.xbib.graphics.pdfbox.groovy.test
import groovy.util.logging.Log import groovy.util.logging.Log
import org.apache.fontbox.ttf.CmapLookup
import org.apache.fontbox.ttf.CmapSubtable import org.apache.fontbox.ttf.CmapSubtable
import org.apache.fontbox.ttf.NamingTable import org.apache.fontbox.ttf.NamingTable
import org.apache.fontbox.ttf.TTFParser import org.apache.fontbox.ttf.TTFParser
import org.apache.fontbox.ttf.TrueTypeFont import org.apache.fontbox.ttf.TrueTypeFont
import org.apache.pdfbox.io.RandomAccessRead
import org.apache.pdfbox.io.RandomAccessReadBuffer
import org.junit.Test import org.junit.Test
import java.nio.file.Files import java.nio.file.Files
@ -28,8 +31,9 @@ class LoadFontTest {
private final Map<String, TrueTypeFont> otf = new HashMap<>() private final Map<String, TrueTypeFont> otf = new HashMap<>()
private void addOpenTypeFont(String name, InputStream inputStream) { private void addOpenTypeFont(String name, InputStream inputStream) {
TTFParser ttfParser = new TTFParser(false, true) TTFParser ttfParser = new TTFParser(true)
TrueTypeFont trueTypeFont = ttfParser.parse(inputStream) RandomAccessRead read = new RandomAccessReadBuffer(inputStream)
TrueTypeFont trueTypeFont = ttfParser.parse(read)
try { try {
NamingTable nameTable = trueTypeFont.getNaming() NamingTable nameTable = trueTypeFont.getNaming()
if (!nameTable) { if (!nameTable) {
@ -51,7 +55,7 @@ class LoadFontTest {
log.warning("Missing 'name' entry for PostScript name in font " + inputStream) log.warning("Missing 'name' entry for PostScript name in font " + inputStream)
} }
} }
CmapSubtable cmapSubtable = trueTypeFont.getUnicodeCmap(true) CmapLookup cmapSubtable = trueTypeFont.getUnicodeCmapLookup(true)
if (!cmapSubtable) { if (!cmapSubtable) {
log.warning('missing cmap table in ' + name) log.warning('missing cmap table in ' + name)
} else { } else {

View file

@ -5,6 +5,7 @@ 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.font.PDType1Font
import org.apache.pdfbox.pdmodel.font.Standard14Fonts
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject
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
@ -67,7 +68,7 @@ class PdfBoxBarcodeTest {
private static void createText(PDPage page, PDPageContentStream contentStream) { private static void createText(PDPage page, PDPageContentStream contentStream) {
contentStream.moveTo(32.0f, (page.getBBox().height - 80f) as float) contentStream.moveTo(32.0f, (page.getBBox().height - 80f) as float)
contentStream.beginText() contentStream.beginText()
contentStream.setFont(PDType1Font.HELVETICA, 12f) contentStream.setFont(new PDType1Font(Standard14Fonts.FontName.HELVETICA), 12f)
contentStream.showText("Hello World") contentStream.showText("Hello World")
contentStream.endText() contentStream.endText()
contentStream.close() contentStream.close()

View file

@ -1,6 +1,7 @@
package org.xbib.graphics.pdfbox.groovy.test package org.xbib.graphics.pdfbox.groovy.test
import groovy.xml.XmlParser import groovy.xml.XmlParser
import org.apache.pdfbox.Loader
import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.pdmodel.PDDocument
import org.xbib.graphics.pdfbox.groovy.Cell import org.xbib.graphics.pdfbox.groovy.Cell
import org.xbib.graphics.pdfbox.groovy.Document import org.xbib.graphics.pdfbox.groovy.Document
@ -12,7 +13,7 @@ import org.xbib.graphics.pdfbox.groovy.TextBlock
class PdfDocumentLoader { class PdfDocumentLoader {
static Document load(byte[] data) { static Document load(byte[] data) {
PDDocument pdfDoc = PDDocument.load(new ByteArrayInputStream(data)) PDDocument pdfDoc = Loader.loadPDF(data)
Document document = new Document(element: pdfDoc) Document document = new Document(element: pdfDoc)
def metaData = new XmlParser().parse(pdfDoc.documentCatalog.metadata.createInputStream()) def metaData = new XmlParser().parse(pdfDoc.documentCatalog.metadata.createInputStream())
document.margin.top = metaData.'@marginTop' as Integer document.margin.top = metaData.'@marginTop' as Integer

View file

@ -6,14 +6,17 @@ import org.apache.pdfbox.pdmodel.font.PDType1Font;
import java.awt.Color; import java.awt.Color;
import java.io.IOException; 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<T extends PDPage> { public class Cell<T extends PDPage> {
private float width; private float width;
private Float height; private Float height;
private String text; private String text;
private PDFont font = PDType1Font.HELVETICA; private PDFont font = new PDType1Font(HELVETICA);
private PDFont fontBold = PDType1Font.HELVETICA_BOLD; private PDFont fontBold = new PDType1Font(HELVETICA_BOLD);
private float fontSize = 8; private float fontSize = 8;
private Color fillColor; private Color fillColor;

View file

@ -13,6 +13,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Stack; 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 { public class Paragraph {
private float width; private float width;
@ -74,9 +78,9 @@ public class Paragraph {
this.font = font; this.font = font;
// check if we have different default font for italic and bold text // check if we have different default font for italic and bold text
if (FontUtils.getDefaultfonts().isEmpty()) { if (FontUtils.getDefaultfonts().isEmpty()) {
fontBold = PDType1Font.HELVETICA_BOLD; fontBold = new PDType1Font(HELVETICA_BOLD);
fontItalic = PDType1Font.HELVETICA_OBLIQUE; fontItalic = new PDType1Font(HELVETICA_OBLIQUE);
fontBoldItalic = PDType1Font.HELVETICA_BOLD_OBLIQUE; fontBoldItalic = new PDType1Font(HELVETICA_BOLD_OBLIQUE);
} else { } else {
fontBold = FontUtils.getDefaultfonts().get("fontBold"); fontBold = FontUtils.getDefaultfonts().get("fontBold");
fontBoldItalic = FontUtils.getDefaultfonts().get("fontBoldItalic"); fontBoldItalic = FontUtils.getDefaultfonts().get("fontBoldItalic");

View file

@ -1,6 +1,5 @@
package org.xbib.graphics.pdfbox.layout.element; package org.xbib.graphics.pdfbox.layout.element;
import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDDocumentInformation; import org.apache.pdfbox.pdmodel.PDDocumentInformation;
import org.xbib.graphics.pdfbox.layout.element.render.Layout; import org.xbib.graphics.pdfbox.layout.element.render.Layout;
@ -45,17 +44,7 @@ public class Document implements Element, Closeable, RenderListener {
* Creates a Document. * Creates a Document.
*/ */
public Document() { public Document() {
this(PageFormats.A4_PORTRAIT, true); this(PageFormats.A4_PORTRAIT);
}
/**
* 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);
} }
/** /**
@ -71,18 +60,17 @@ public class Document implements Element, Closeable, RenderListener {
float marginRight, float marginRight,
float marginTop, float marginTop,
float marginBottom) { float marginBottom) {
this(marginLeft, marginRight, marginTop, marginBottom, true); this(PageFormat.builder().margins(marginLeft, marginRight, marginTop, marginBottom).build());
} }
public Document(float marginLeft, /**
float marginRight, * Creates a Document based on the given page format. By default, a
float marginTop, * {@link VerticalLayout} is used.
float marginBottom, boolean memory) { *
this(PageFormat.builder().margins(marginLeft, marginRight, marginTop, marginBottom).build(), memory); * @param pageFormat the page format box to use.
} */
public Document(PageFormat pageFormat) {
public Document(PageFormat pageFormat, boolean memory) { this.pdDocument = new PDDocument();
this.pdDocument = new PDDocument(memory ? MemoryUsageSetting.setupMainMemoryOnly() : MemoryUsageSetting.setupTempFileOnly());
this.pdDocumentInformation = new PDDocumentInformation(); this.pdDocumentInformation = new PDDocumentInformation();
setPageFormat(pageFormat); setPageFormat(pageFormat);
} }

View file

@ -2,6 +2,8 @@ package org.xbib.graphics.pdfbox.layout.element;
import org.xbib.graphics.pdfbox.layout.font.Font; import org.xbib.graphics.pdfbox.layout.font.Font;
import java.util.Objects;
public class TextElement implements Element { public class TextElement implements Element {
private final boolean markup; private final boolean markup;
@ -17,6 +19,8 @@ public class TextElement implements Element {
this.value = value; this.value = value;
this.font = font; this.font = font;
this.fontsize = fontsize; this.fontsize = fontsize;
Objects.requireNonNull(value, "you must specify a value");
Objects.requireNonNull(font, "you must specify a font");
} }
public String getValue() { public String getValue() {

View file

@ -1,5 +1,6 @@
package org.xbib.graphics.pdfbox.layout.element.scripting; package org.xbib.graphics.pdfbox.layout.element.scripting;
import java.io.Closeable;
import java.util.logging.Level; import java.util.logging.Level;
import org.xbib.graphics.pdfbox.layout.element.scripting.command.Command; import org.xbib.graphics.pdfbox.layout.element.scripting.command.Command;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
@ -7,7 +8,7 @@ import org.xbib.settings.Settings;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger; import java.util.logging.Logger;
public class Engine { public class Engine implements Closeable {
private static final Logger logger = Logger.getLogger(Engine.class.getName()); 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();
}
} }

View file

@ -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.Document;
import org.xbib.graphics.pdfbox.layout.element.Element; import org.xbib.graphics.pdfbox.layout.element.Element;
import java.io.Closeable;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
import java.util.stream.Collectors;
public class State { public class State implements Closeable {
private final Stack<Element> elements = new Stack<>(); private final Stack<Element> elements = new Stack<>();
@ -26,8 +27,26 @@ public class State {
List<Document> list = elements.stream() List<Document> list = elements.stream()
.filter(e -> e instanceof Document) .filter(e -> e instanceof Document)
.map(e -> (Document) e) .map(e -> (Document) e)
.collect(Collectors.toList()); .toList();
int size = list.size(); int size = list.size();
return size == 0 ? null : list.get(size - 1); 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);
}
});
}
} }

View file

@ -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.element.scripting.State;
import org.xbib.graphics.pdfbox.layout.text.Alignment; import org.xbib.graphics.pdfbox.layout.text.Alignment;
import org.xbib.graphics.pdfbox.layout.position.Position; import org.xbib.graphics.pdfbox.layout.position.Position;
import org.xbib.graphics.pdfbox.layout.text.TextSequence;
import org.xbib.settings.Settings; import org.xbib.settings.Settings;
import java.util.Locale; import java.util.Locale;

View file

@ -2,25 +2,34 @@ package org.xbib.graphics.pdfbox.layout.font;
import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType1Font; 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 * In order to easy handling with fonts, this enum bundles the
* plain/italic/bold/bold-italic variants of the three standard font types * plain/italic/bold/bold-italic variants of the three standard font types
* {@link PDType1Font#TIMES_ROMAN Times},{@link PDType1Font#COURIER Courier} and * Times, Courier, Helveticy.
* {@link PDType1Font#HELVETICA Helveticy}.
*/ */
public enum BaseFont implements Font { public enum BaseFont implements Font {
TIMES(PDType1Font.TIMES_ROMAN, PDType1Font.TIMES_BOLD, TIMES(new PDType1Font(TIMES_ROMAN), new PDType1Font(TIMES_BOLD),
PDType1Font.TIMES_ITALIC, PDType1Font.TIMES_BOLD_ITALIC), new PDType1Font(TIMES_ITALIC), new PDType1Font(TIMES_BOLD_ITALIC)),
COURIER(PDType1Font.COURIER, PDType1Font.COURIER_BOLD, COURIER(new PDType1Font(Standard14Fonts.FontName.COURIER), new PDType1Font(COURIER_BOLD),
PDType1Font.COURIER_OBLIQUE, PDType1Font.COURIER_BOLD_OBLIQUE), new PDType1Font(COURIER_OBLIQUE), new PDType1Font(COURIER_BOLD_OBLIQUE)),
HELVETICA(PDType1Font.HELVETICA, PDType1Font.HELVETICA_BOLD, HELVETICA(new PDType1Font(Standard14Fonts.FontName.HELVETICA), new PDType1Font(HELVETICA_BOLD),
PDType1Font.HELVETICA_OBLIQUE, PDType1Font.HELVETICA_BOLD_OBLIQUE); new PDType1Font(HELVETICA_OBLIQUE), new PDType1Font(HELVETICA_BOLD_OBLIQUE));
private final PDFont plainFont; private final PDFont plainFont;

View file

@ -2,23 +2,16 @@ package org.xbib.graphics.pdfbox.layout.font;
import org.xbib.graphics.pdfbox.layout.element.Document; import org.xbib.graphics.pdfbox.layout.element.Document;
import java.util.HashMap;
import java.util.Map;
public enum Fonts { public enum Fonts {
HELVETICA, HELVETICA,
TIMES, TIMES,
COURIER, COURIER,
NOTOSANS; NOTOSANS;
private final Map<String, Font> map = new HashMap<>();
public Font getFont(Document document) { public Font getFont(Document document) {
return map.computeIfAbsent(name(), name -> { if ("notosans".equalsIgnoreCase(name())) {
if ("notosans".equalsIgnoreCase(name())) { return new NotoSansFont(document);
return new NotoSansFont(document); }
} return BaseFont.valueOf(name());
return BaseFont.valueOf(name());
});
} }
} }

View file

@ -5,6 +5,7 @@ import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
import org.apache.pdfbox.pdmodel.graphics.color.PDDeviceRGB; 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.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationTextMarkup;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xbib.graphics.pdfbox.layout.element.Document; import org.xbib.graphics.pdfbox.layout.element.Document;
@ -66,8 +67,7 @@ public class CustomAnnotationTest {
.getAnnotationsOfType(HighlightAnnotation.class); .getAnnotationsOfType(HighlightAnnotation.class);
for (HighlightAnnotation highlightAnnotation : HighlightAnnotations) { for (HighlightAnnotation highlightAnnotation : HighlightAnnotations) {
// use PDF text markup to implement the highlight // use PDF text markup to implement the highlight
PDAnnotationTextMarkup markup = new PDAnnotationTextMarkup( PDAnnotationTextMarkup markup = new PDAnnotationHighlight();
PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT);
// use the bounding box of the drawn object to position the // use the bounding box of the drawn object to position the
// highlight // highlight
PDRectangle bounds = new PDRectangle(); PDRectangle bounds = new PDRectangle();

View file

@ -13,11 +13,11 @@ import java.io.FileOutputStream;
public class HelloNotoFontTest { public class HelloNotoFontTest {
@Test @Test
public void test() throws Exception { public void testDocumentOne() throws Exception {
Document document = new Document(PageFormats.A4_PORTRAIT); Document document = new Document(PageFormats.A4_PORTRAIT);
Font font = new NotoSansFont(document);
Paragraph paragraph = new Paragraph(); Paragraph paragraph = new Paragraph();
paragraph.add(new Indent(32, SpaceUnit.pt)); paragraph.add(new Indent(32, SpaceUnit.pt));
Font font = new NotoSansFont(document);
paragraph.addMarkup("Hello Noto Regular\n", 12, font); paragraph.addMarkup("Hello Noto Regular\n", 12, font);
paragraph.addMarkup("*Hello Noto Bold*\n", 12, font); paragraph.addMarkup("*Hello Noto Bold*\n", 12, font);
paragraph.addMarkup("_Hello Noto Italic_\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.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); paragraph.addMarkup("Hello Unicode Markup: _Zwrotki dla Dorotki : arcyksiążę fiołków_\n", 12, font);
document.add(paragraph); 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(); 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();
}
} }

View file

@ -10,13 +10,27 @@ import java.io.FileOutputStream;
public class ScriptingTest { public class ScriptingTest {
@Test @Test
public void script() throws Exception { public void scriptOne() throws Exception {
Settings settings = Settings.settingsBuilder() Settings settings = Settings.settingsBuilder()
.loadFromResource("json", getClass().getResourceAsStream("scripting.json")) .loadFromResource("json", getClass().getResourceAsStream("scripting.json"))
.build(); .build();
Engine engine = new Engine(); try (Engine engine = new Engine()) {
engine.execute(settings); engine.execute(settings);
Document document = engine.getState().getDocument(); Document document = engine.getState().getDocument();
document.render().save(new FileOutputStream("build/scripting.pdf")).close(); 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();
}
}
} }

View file

@ -108,7 +108,7 @@
"elements": [ "elements": [
{ {
"type": "text", "type": "text",
"value": "Hello World 5", "value": "Hello World 5 Zwrotki dla Dorotki : arcyksiążę fiołków",
"fontsize": 20, "fontsize": 20,
"font": "notosans" "font": "notosans"
} }

View file

@ -1,6 +1,6 @@
package org.xbib.graphics.pdfbox.print; 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.pdmodel.PDDocument;
import org.apache.pdfbox.printing.PDFPageable; import org.apache.pdfbox.printing.PDFPageable;
@ -86,7 +86,8 @@ public class PrintUtility {
} }
public static void print(InputStream inputStream, Printer printer) throws Exception { 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(); PrinterJob job = PrinterJob.getPrinterJob();
job.setPageable(new PDFPageable(document)); job.setPageable(new PDFPageable(document));
job.setPrintService(printer.getService()); job.setPrintService(printer.getService());

View file

@ -251,15 +251,14 @@ public class PdfBoxGraphics2D extends Graphics2D {
PDRectangle bbox, PDRectangle bbox,
PdfBoxGraphics2D parentGfx) PdfBoxGraphics2D parentGfx)
throws IOException { throws IOException {
this(document, createXObject(document, bbox), parentGfx); this(document, createAppearanceStream(document, bbox), parentGfx);
} }
public PdfBoxGraphics2D(PDDocument document, public PdfBoxGraphics2D(PDDocument document,
PDFormXObject xFormObject, PDAppearanceStream pdAppearanceStream,
PdfBoxGraphics2D parentGfx) PdfBoxGraphics2D parentGfx)
throws IOException { throws IOException {
this(document, xFormObject, new PDPageContentStream(document, xFormObject, this(document, pdAppearanceStream, new PDPageContentStream(document, pdAppearanceStream, pdAppearanceStream.getStream().createOutputStream(COSName.FLATE_DECODE)), parentGfx);
xFormObject.getStream().createOutputStream(COSName.FLATE_DECODE)), parentGfx);
} }
public PdfBoxGraphics2D(PDDocument document, public PdfBoxGraphics2D(PDDocument document,
@ -1152,17 +1151,17 @@ public class PdfBoxGraphics2D extends Graphics2D {
} }
private void checkNoCopyActive() { 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" 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)); + gatherDebugCopyInfo(this));
} }
} }
private static PDFormXObject createXObject(PDDocument document, PDRectangle bbox) { private static PDAppearanceStream createAppearanceStream(PDDocument document, PDRectangle bbox) {
PDFormXObject xFormObject = new PDAppearanceStream(document); PDAppearanceStream appearanceStream = new PDAppearanceStream(document);
xFormObject.setResources(new PDResources()); appearanceStream.setResources(new PDResources());
xFormObject.setBBox(bbox); appearanceStream.setBBox(bbox);
return xFormObject; return appearanceStream;
} }
private static String gatherDebugCopyInfo(PdfBoxGraphics2D gfx) { private static String gatherDebugCopyInfo(PdfBoxGraphics2D gfx) {

View file

@ -1,7 +1,6 @@
package org.xbib.graphics.pdfbox.font; package org.xbib.graphics.pdfbox.font;
import org.apache.fontbox.ttf.TrueTypeCollection; import org.apache.fontbox.ttf.TrueTypeCollection;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.font.PDType0Font;
@ -28,6 +27,21 @@ import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; 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 * Default implementation to draw fonts. You can reuse instances of this class
* within a PDDocument for more then one {@link PdfBoxGraphics2D}. * 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 { public void registerFont(String fontName, InputStream fontStream) throws IOException {
File fontFile = File.createTempFile("pdfboxgfx2dfont", ".ttf"); File fontFile = File.createTempFile("pdfboxgfx2dfont", ".ttf");
try (FileOutputStream out = new FileOutputStream(fontFile)) { try (FileOutputStream out = new FileOutputStream(fontFile)) {
IOUtils.copy(fontStream, out); fontStream.transferTo(out);
} }
fontFile.deleteOnExit(); fontFile.deleteOnExit();
tempFiles.add(fontFile); tempFiles.add(fontFile);
@ -412,10 +426,10 @@ public class DefaultFontDrawer implements FontDrawer, Closeable {
return chooseMatchingTimes(font); return chooseMatchingTimes(font);
} }
if (fontNameEqualsAnyOf(font, "Symbol")) { if (fontNameEqualsAnyOf(font, "Symbol")) {
return PDType1Font.SYMBOL; return new PDType1Font(SYMBOL);
} }
if (fontNameEqualsAnyOf(font, "ZapfDingbats", "Dingbats")) { if (fontNameEqualsAnyOf(font, "ZapfDingbats", "Dingbats")) {
return PDType1Font.ZAPF_DINGBATS; return new PDType1Font(ZAPF_DINGBATS);
} }
return null; return null;
} }
@ -429,18 +443,17 @@ public class DefaultFontDrawer implements FontDrawer, Closeable {
*/ */
public static PDFont chooseMatchingHelvetica(Font font) { public static PDFont chooseMatchingHelvetica(Font font) {
if ((font.getStyle() & (Font.ITALIC | Font.BOLD)) == (Font.ITALIC | Font.BOLD)) { 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) { if ((font.getStyle() & Font.ITALIC) == Font.ITALIC) {
return PDType1Font.HELVETICA_OBLIQUE; return new PDType1Font(HELVETICA_OBLIQUE);
} }
if ((font.getStyle() & Font.BOLD) == Font.BOLD) { 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 * 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) { public static PDFont chooseMatchingCourier(Font font) {
if ((font.getStyle() & (Font.ITALIC | Font.BOLD)) == (Font.ITALIC | Font.BOLD)) { 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) { if ((font.getStyle() & Font.ITALIC) == Font.ITALIC) {
return PDType1Font.COURIER_OBLIQUE; return new PDType1Font(COURIER_OBLIQUE);
} }
if ((font.getStyle() & Font.BOLD) == Font.BOLD) { 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) { public static PDFont chooseMatchingTimes(Font font) {
if ((font.getStyle() & (Font.ITALIC | Font.BOLD)) == (Font.ITALIC | Font.BOLD)) { 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) { if ((font.getStyle() & Font.ITALIC) == Font.ITALIC) {
return PDType1Font.TIMES_ITALIC; return new PDType1Font(TIMES_ITALIC);
} }
if ((font.getStyle() & Font.BOLD) == Font.BOLD) { 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) { public static boolean fontNameEqualsAnyOf(Font font, String... names) {

View file

@ -7,7 +7,6 @@ import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSFloat; import org.apache.pdfbox.cos.COSFloat;
import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSStream; import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.multipdf.PDFCloneUtility;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.PDResources;

View file

@ -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<COSBase, COSBase> clonedVersion = new HashMap<>();
private final Set<COSBase> 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.
* <p>
* Expert use only, dont use it if you dont 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 extends COSBase> 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<COSName, COSBase> 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<COSName, COSBase> 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. <br>
* 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<COSName, COSBase> 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;
}
}

View file

@ -1,12 +1,25 @@
package org.xbib.graphics.pdfbox.test; package org.xbib.graphics.pdfbox.test;
import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.xbib.graphics.pdfbox.font.CoreFontDrawer; import org.xbib.graphics.pdfbox.font.CoreFontDrawer;
import java.awt.Font; 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
@ -19,51 +32,29 @@ public class FontDrawerTest {
Font anyFontItalic = anyFont.deriveFont(Font.ITALIC); Font anyFontItalic = anyFont.deriveFont(Font.ITALIC);
Font anyFontBoldItalic = anyFont.deriveFont(Font.BOLD | Font.ITALIC); Font anyFontBoldItalic = anyFont.deriveFont(Font.BOLD | Font.ITALIC);
Assertions.assertEquals(PDType1Font.COURIER, assertEquals(new PDType1Font(COURIER).getName(), CoreFontDrawer.chooseMatchingCourier(anyFont).getName());
CoreFontDrawer.chooseMatchingCourier(anyFont)); assertEquals(new PDType1Font(COURIER_BOLD).getName(), CoreFontDrawer.chooseMatchingCourier(anyFontBold).getName());
assertEquals(PDType1Font.COURIER_BOLD, assertEquals(new PDType1Font(COURIER_OBLIQUE).getName(), CoreFontDrawer.chooseMatchingCourier(anyFontItalic).getName());
CoreFontDrawer.chooseMatchingCourier(anyFontBold)); assertEquals(new PDType1Font(COURIER_BOLD_OBLIQUE).getName(), CoreFontDrawer.chooseMatchingCourier(anyFontBoldItalic).getName());
assertEquals(PDType1Font.COURIER_OBLIQUE, assertEquals(new PDType1Font(HELVETICA).getName(), CoreFontDrawer.chooseMatchingHelvetica(anyFont).getName());
CoreFontDrawer.chooseMatchingCourier(anyFontItalic)); assertEquals(new PDType1Font(HELVETICA_BOLD).getName(), CoreFontDrawer.chooseMatchingHelvetica(anyFontBold).getName());
assertEquals(PDType1Font.COURIER_BOLD_OBLIQUE, assertEquals(new PDType1Font(HELVETICA_OBLIQUE).getName(), CoreFontDrawer.chooseMatchingHelvetica(anyFontItalic).getName());
CoreFontDrawer.chooseMatchingCourier(anyFontBoldItalic)); assertEquals(new PDType1Font(HELVETICA_BOLD_OBLIQUE).getName(), CoreFontDrawer.chooseMatchingHelvetica(anyFontBoldItalic).getName());
assertEquals(new PDType1Font(TIMES_ROMAN).getName(), CoreFontDrawer.chooseMatchingTimes(anyFont).getName());
assertEquals(PDType1Font.HELVETICA, assertEquals(new PDType1Font(TIMES_BOLD).getName(), CoreFontDrawer.chooseMatchingTimes(anyFontBold).getName());
CoreFontDrawer.chooseMatchingHelvetica(anyFont)); assertEquals(new PDType1Font(TIMES_ITALIC).getName(), CoreFontDrawer.chooseMatchingTimes(anyFontItalic).getName());
assertEquals(PDType1Font.HELVETICA_BOLD, assertEquals(new PDType1Font(TIMES_BOLD_ITALIC).getName(), CoreFontDrawer.chooseMatchingTimes(anyFontBoldItalic).getName());
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));
} }
@Test @Test
public void testDefaultFontMapping() { public void testDefaultFontMapping() {
assertEquals(PDType1Font.HELVETICA, assertEquals(new PDType1Font(HELVETICA).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode(Font.DIALOG)).getName());
CoreFontDrawer.mapToCoreFonts(Font.decode(Font.DIALOG))); assertEquals(new PDType1Font(HELVETICA).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode(Font.DIALOG_INPUT)).getName());
assertEquals(PDType1Font.HELVETICA, assertEquals(new PDType1Font(HELVETICA).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode("Arial")).getName());
CoreFontDrawer.mapToCoreFonts(Font.decode(Font.DIALOG_INPUT))); assertEquals(new PDType1Font(COURIER).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode(Font.MONOSPACED)).getName());
assertEquals(PDType1Font.HELVETICA, assertEquals(new PDType1Font(TIMES_ROMAN).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode(Font.SERIF)).getName());
CoreFontDrawer.mapToCoreFonts(Font.decode("Arial"))); assertEquals(new PDType1Font(ZAPF_DINGBATS).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode("Dingbats")).getName());
assertEquals(PDType1Font.COURIER, assertEquals(new PDType1Font(SYMBOL).getName(), CoreFontDrawer.mapToCoreFonts(Font.decode("Symbol")).getName());
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")));
assertNull(CoreFontDrawer.mapToCoreFonts(Font.decode("Georgia"))); assertNull(CoreFontDrawer.mapToCoreFonts(Font.decode("Georgia")));
} }
} }

View file

@ -20,6 +20,9 @@ import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException; 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 { class PdfBoxGraphics2DTestBase {
enum Mode { enum Mode {
@ -29,7 +32,7 @@ class PdfBoxGraphics2DTestBase {
void exportGraphic(String dir, String name, GraphicsExporter exporter) { void exportGraphic(String dir, String name, GraphicsExporter exporter) {
try { try {
PDDocument document = new PDDocument(); PDDocument document = new PDDocument();
PDFont helvetica = PDType1Font.HELVETICA; PDFont helvetica = new PDType1Font(HELVETICA);
File parentDir = new File("build/test/" + dir); File parentDir = new File("build/test/" + dir);
parentDir.mkdirs(); parentDir.mkdirs();
BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_4BYTE_ABGR); BufferedImage image = new BufferedImage(400, 400, BufferedImage.TYPE_4BYTE_ABGR);
@ -45,7 +48,7 @@ class PdfBoxGraphics2DTestBase {
contentStream.beginText(); contentStream.beginText();
contentStream.setStrokingColor(0f, 0f, 0f); contentStream.setStrokingColor(0f, 0f, 0f);
contentStream.setNonStrokingColor(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.setTextMatrix(Matrix.getTranslateInstance(10, 800));
contentStream.showText("Mode " + m); contentStream.showText("Mode " + m);
contentStream.endText(); contentStream.endText();

View file

@ -1,5 +1,6 @@
package org.xbib.graphics.pdfbox.test; package org.xbib.graphics.pdfbox.test;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; 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;
@ -29,6 +30,7 @@ import java.awt.Stroke;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
public class PdfRerenderTest { public class PdfRerenderTest {
@ -55,7 +57,9 @@ public class PdfRerenderTest {
File parentDir = new File("build/test"); File parentDir = new File("build/test");
parentDir.mkdirs(); parentDir.mkdirs();
PDDocument document = new PDDocument(); 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()) { for (PDPage sourcePage : sourceDoc.getPages()) {
PDRectangle mediaBox = sourcePage.getMediaBox(); PDRectangle mediaBox = sourcePage.getMediaBox();
@ -87,7 +91,9 @@ public class PdfRerenderTest {
parentDir.mkdirs(); parentDir.mkdirs();
PDDocument document = new PDDocument(); 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()) { for (PDPage sourcePage : sourceDoc.getPages()) {
PDPage rerenderedPage = new PDPage(sourcePage.getMediaBox()); PDPage rerenderedPage = new PDPage(sourcePage.getMediaBox());
@ -113,7 +119,9 @@ public class PdfRerenderTest {
parentDir.mkdirs(); parentDir.mkdirs();
PDDocument document = new PDDocument(); 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()) { for (PDPage sourcePage : sourceDoc.getPages()) {
PDPage rerenderedPage = new PDPage(sourcePage.getMediaBox()); PDPage rerenderedPage = new PDPage(sourcePage.getMediaBox());

View file

@ -39,7 +39,7 @@ dependencyResolutionManagement {
library('bytebuddy', 'net.bytebuddy', 'byte-buddy').version('1.14.4') library('bytebuddy', 'net.bytebuddy', 'byte-buddy').version('1.14.4')
library('objenesis', 'org.objenesis', 'objenesis').version('2.6') library('objenesis', 'org.objenesis', 'objenesis').version('2.6')
library('jna', 'net.java.dev.jna', 'jna').version('5.13.0') 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('zxing', 'com.google.zxing', 'javase').version('3.4.1')
library('reflections', 'org.reflections', 'reflections').version('0.9.11') library('reflections', 'org.reflections', 'reflections').version('0.9.11')
library('jfreechart', 'org.jfree', 'jfreechart').version('1.5.2') library('jfreechart', 'org.jfree', 'jfreechart').version('1.5.2')