update to pdfbox 2.0.21, clean up pdfbox graphics io

This commit is contained in:
Jörg Prante 2020-11-10 09:32:04 +01:00
parent b993f70a3d
commit 4dfde95054
9 changed files with 104 additions and 153 deletions

View file

@ -25,7 +25,6 @@ ext {
subprojects {
apply plugin: 'java-library'
apply from: rootProject.file('gradle/ide/idea.gradle')
apply from: rootProject.file('gradle/compile/java.gradle')
apply from: rootProject.file('gradle/test/junit5.gradle')

View file

@ -3,7 +3,7 @@ name = graphics
version = 3.0.0
gradle.wrapper.version = 6.6.1
pdfbox.version = 2.0.19
pdfbox.version = 2.0.21
zxing.version = 3.3.1
reflections.version = 0.9.11
jfreechart.version = 1.5.1

View file

@ -1,5 +1,5 @@
dependencies {
implementation "org.apache.pdfbox:pdfbox:${project.property('pdfbox.version')}"
api "org.apache.pdfbox:pdfbox:${project.property('pdfbox.version')}"
testImplementation "org.jfree:jfreechart:${project.property('jfreechart.version')}"
testImplementation "org.apache.xmlgraphics:batik-swing:${project.property('batik.version')}"
}

View file

@ -77,7 +77,7 @@ public class DefaultPaintApplier implements PaintApplier {
state.nestedTransform = null;
PDShading shading = applyPaint(paint, state);
if (state.pdExtendedGraphicsState != null) {
contentStream.setGraphicsStateParameters(extGStateCache.makeUnqiue(state.pdExtendedGraphicsState));
contentStream.setGraphicsStateParameters(extGStateCache.makeUnique(state.pdExtendedGraphicsState));
}
return shading;
}
@ -113,19 +113,19 @@ public class DefaultPaintApplier implements PaintApplier {
if (paint instanceof Color) {
applyAsStrokingColor((Color) paint, state);
} else if (simpleName.equals("LinearGradientPaint")) {
return shadingCache.makeUnqiue(buildLinearGradientShading(paint, state));
return shadingCache.makeUnique(buildLinearGradientShading(paint, state));
} else if (simpleName.equals("RadialGradientPaint")) {
return shadingCache.makeUnqiue(buildRadialGradientShading(paint, state));
return shadingCache.makeUnique(buildRadialGradientShading(paint, state));
} else if (simpleName.equals("PatternPaint")) {
applyPatternPaint(paint, state);
} else if (simpleName.equals("TilingPaint")) {
logger.log(Level.WARNING, "no tiling paint available");
} else if (paint instanceof GradientPaint) {
return shadingCache.makeUnqiue(buildGradientShading((GradientPaint) paint, state));
return shadingCache.makeUnique(buildGradientShading((GradientPaint) paint, state));
} else if (paint instanceof TexturePaint) {
applyTexturePaint((TexturePaint) paint, state);
} else if (paint instanceof ShadingPaint) {
return shadingCache.makeUnqiue(importPDFBoxShadingPaint((ShadingPaint<?>) paint, state));
return shadingCache.makeUnique(importPDFBoxShadingPaint((ShadingPaint<?>) paint, state));
} else {
logger.log(Level.WARNING, "Don't know paint " + paint.getClass().getName());
}
@ -148,12 +148,10 @@ public class DefaultPaintApplier implements PaintApplier {
PDTilingPattern pattern = new PDTilingPattern();
pattern.setPaintType(PDTilingPattern.PAINT_COLORED);
pattern.setTilingType(PDTilingPattern.TILING_CONSTANT_SPACING_FASTER_TILING);
pattern.setBBox(new PDRectangle((float) anchorRect.getX(), (float) anchorRect.getY(),
(float) anchorRect.getWidth(), (float) anchorRect.getHeight()));
pattern.setXStep((float) anchorRect.getWidth());
pattern.setYStep((float) anchorRect.getHeight());
AffineTransform patternTransform = new AffineTransform();
if (paintPatternTransform != null) {
paintPatternTransform = new AffineTransform(paintPatternTransform);
@ -766,7 +764,7 @@ public class DefaultPaintApplier implements PaintApplier {
protected abstract int getKey(TObject obj);
TObject makeUnqiue(TObject state) {
TObject makeUnique(TObject state) {
int key = getKey(state);
List<TObject> pdExtendedGraphicsStates = states.computeIfAbsent(key, k -> new ArrayList<>());
for (TObject s : pdExtendedGraphicsStates) {

View file

@ -68,10 +68,10 @@ import java.util.Map;
*/
public class PdfBoxGraphics2D extends Graphics2D {
private final PDFormXObject xFormObject;
private final Graphics2D calcGfx;
private final PDFormXObject xFormObject;
private final PDPageContentStream contentStream;
private BufferedImage calcImage;
@ -106,12 +106,10 @@ public class PdfBoxGraphics2D extends Graphics2D {
private Color backgroundColor;
private final CopyInfo copyInfo;
private final PDRectangle bbox;
private int saveCounter = 0;
private final CopyInfo copyInfo;
private final List<CopyInfo> copyList = new ArrayList<>();
private final PaintApplierEnvImpl paintEnv = new PaintApplierEnvImpl();
@ -163,8 +161,9 @@ public class PdfBoxGraphics2D extends Graphics2D {
@Override
public void applyPaint(Paint paint, Shape shapeToDraw) throws IOException {
PDShading pdShading = PdfBoxGraphics2D.this.applyPaint(paint, shapeToDraw);
if (pdShading != null)
if (pdShading != null) {
applyShadingAsColor(pdShading);
}
}
@Override
@ -174,7 +173,7 @@ public class PdfBoxGraphics2D extends Graphics2D {
@Override
public PDRectangle getGraphicsBBox() {
return bbox;
return xFormObject.getBBox();
}
@Override
@ -202,7 +201,8 @@ public class PdfBoxGraphics2D extends Graphics2D {
* @throws IOException if something goes wrong with writing into the content stream of
* the {@link PDDocument}.
*/
public PdfBoxGraphics2D(PDDocument document, int pixelWidth, int pixelHeight) throws IOException {
public PdfBoxGraphics2D(PDDocument document, int pixelWidth, int pixelHeight)
throws IOException {
this(document, new PDRectangle(pixelWidth, pixelHeight));
}
@ -235,15 +235,20 @@ public class PdfBoxGraphics2D extends Graphics2D {
this(document, bbox, null);
}
PdfBoxGraphics2D(PDDocument document, PDRectangle bbox, PdfBoxGraphics2D parentGfx)
public PdfBoxGraphics2D(PDDocument document,
PDRectangle bbox,
PdfBoxGraphics2D parentGfx)
throws IOException {
this(document, createXObject(document, bbox), parentGfx);
}
public PdfBoxGraphics2D(PDDocument document,
PDFormXObject xFormObject,
PdfBoxGraphics2D parentGfx)
throws IOException {
this.document = document;
this.bbox = bbox;
PDAppearanceStream appearance = new PDAppearanceStream(document);
xFormObject = appearance;
xFormObject.setResources(new PDResources());
xFormObject.setBBox(bbox);
contentStream = new PDPageContentStream(document, appearance,
this.xFormObject = xFormObject;
this.contentStream = new PDPageContentStream(document, xFormObject,
xFormObject.getStream().createOutputStream(COSName.FLATE_DECODE));
contentStreamSaveState();
if (parentGfx != null) {
@ -253,7 +258,7 @@ public class PdfBoxGraphics2D extends Graphics2D {
this.paintApplier = parentGfx.paintApplier;
}
baseTransform = new AffineTransform();
baseTransform.translate(0, bbox.getHeight());
baseTransform.translate(0, xFormObject.getBBox().getHeight());
baseTransform.scale(1, -1);
calcImage = new BufferedImage(100, 100, BufferedImage.TYPE_4BYTE_ABGR);
calcGfx = calcImage.createGraphics();
@ -261,35 +266,58 @@ public class PdfBoxGraphics2D extends Graphics2D {
copyInfo = null;
}
private PdfBoxGraphics2D(PdfBoxGraphics2D gfx) throws IOException {
public PdfBoxGraphics2D(PDDocument document,
PDFormXObject xFormObject,
PDPageContentStream contentStream,
PdfBoxGraphics2D parentGfx)
throws IOException {
this.document = document;
this.xFormObject = xFormObject;
this.contentStream = contentStream;
contentStreamSaveState();
if (parentGfx != null) {
this.colorMapper = parentGfx.colorMapper;
this.fontTextDrawer = parentGfx.fontTextDrawer;
this.imageEncoder = parentGfx.imageEncoder;
this.paintApplier = parentGfx.paintApplier;
}
baseTransform = new AffineTransform();
baseTransform.translate(0, xFormObject.getBBox().getHeight());
baseTransform.scale(1, -1);
calcImage = new BufferedImage(100, 100, BufferedImage.TYPE_4BYTE_ABGR);
calcGfx = calcImage.createGraphics();
font = calcGfx.getFont();
copyInfo = null;
}
private PdfBoxGraphics2D(PdfBoxGraphics2D pdfBoxGraphics2D) throws IOException {
CopyInfo info = new CopyInfo();
info.creatingContextInfo = null;
info.copy = this;
info.sourceGfx = gfx;
gfx.copyList.add(info);
info.sourceGfx = pdfBoxGraphics2D;
pdfBoxGraphics2D.copyList.add(info);
this.copyInfo = info;
this.hasPathOnStream = false;
this.document = gfx.document;
this.bbox = gfx.bbox;
this.xFormObject = gfx.xFormObject;
this.contentStream = gfx.contentStream;
this.baseTransform = gfx.baseTransform;
this.transform = (AffineTransform) gfx.transform.clone();
this.calcGfx = gfx.calcGfx;
this.calcImage = gfx.calcImage;
this.font = gfx.font;
this.stroke = gfx.stroke;
this.paint = gfx.paint;
this.clipShape = gfx.clipShape;
this.backgroundColor = gfx.backgroundColor;
this.colorMapper = gfx.colorMapper;
this.fontTextDrawer = gfx.fontTextDrawer;
this.imageEncoder = gfx.imageEncoder;
this.paintApplier = gfx.paintApplier;
this.drawControl = gfx.drawControl;
this.composite = gfx.composite;
this.renderingHints = new HashMap<>(gfx.renderingHints);
this.xorColor = gfx.xorColor;
this.document = pdfBoxGraphics2D.document;
this.xFormObject = pdfBoxGraphics2D.xFormObject;
this.contentStream = pdfBoxGraphics2D.contentStream;
this.baseTransform = pdfBoxGraphics2D.baseTransform;
this.transform = (AffineTransform) pdfBoxGraphics2D.transform.clone();
this.calcGfx = pdfBoxGraphics2D.calcGfx;
this.calcImage = pdfBoxGraphics2D.calcImage;
this.font = pdfBoxGraphics2D.font;
this.stroke = pdfBoxGraphics2D.stroke;
this.paint = pdfBoxGraphics2D.paint;
this.clipShape = pdfBoxGraphics2D.clipShape;
this.backgroundColor = pdfBoxGraphics2D.backgroundColor;
this.colorMapper = pdfBoxGraphics2D.colorMapper;
this.fontTextDrawer = pdfBoxGraphics2D.fontTextDrawer;
this.imageEncoder = pdfBoxGraphics2D.imageEncoder;
this.paintApplier = pdfBoxGraphics2D.paintApplier;
this.drawControl = pdfBoxGraphics2D.drawControl;
this.composite = pdfBoxGraphics2D.composite;
this.renderingHints = new HashMap<>(pdfBoxGraphics2D.renderingHints);
this.xorColor = pdfBoxGraphics2D.xorColor;
this.saveCounter = 0;
contentStreamSaveState();
}
@ -1043,7 +1071,7 @@ public class PdfBoxGraphics2D extends Graphics2D {
PDTilingPattern pattern = new PDTilingPattern();
pattern.setPaintType(PDTilingPattern.PAINT_COLORED);
pattern.setTilingType(PDTilingPattern.TILING_CONSTANT_SPACING_FASTER_TILING);
PDRectangle anchorRect = bbox;
PDRectangle anchorRect = xFormObject.getBBox();
pattern.setBBox(anchorRect);
pattern.setXStep(anchorRect.getWidth());
pattern.setYStep(anchorRect.getHeight());
@ -1135,9 +1163,17 @@ public class PdfBoxGraphics2D extends Graphics2D {
}
private void checkNoCopyActive() {
if (copyList.size() > 0)
if (copyList.size() > 0) {
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) {
PDFormXObject xFormObject = new PDAppearanceStream(document);
xFormObject.setResources(new PDResources());
xFormObject.setBBox(bbox);
return xFormObject;
}
private static String gatherDebugCopyInfo(PdfBoxGraphics2D gfx) {

View file

@ -1,77 +0,0 @@
package org.xbib.graphics.io.pdfbox;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.io.pdfbox.PdfBoxGraphics2D;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
public class DanglingGfxCaseTest {
@Test
public void testDanglingGfx() throws IOException {
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
PdfBoxGraphics2D pdfBoxGraphics2D = new PdfBoxGraphics2D(document, 400, 400);
PdfBoxGraphics2D child = pdfBoxGraphics2D.create(10, 10, 40, 40);
child.setColor(Color.RED);
child.fillRect(0, 0, 100, 100);
PdfBoxGraphics2D child2 = child.create(20, 20, 10, 10);
child2.setColor(Color.GREEN);
child2.drawOval(0, 0, 5, 5);
child.create();
pdfBoxGraphics2D.disposeDanglingChildGraphics();
pdfBoxGraphics2D.dispose();
PDFormXObject appearanceStream = pdfBoxGraphics2D.getXFormObject();
Matrix matrix = new Matrix();
matrix.translate(0, 20);
contentStream.transform(matrix);
contentStream.drawForm(appearanceStream);
contentStream.close();
File file = new File("build/test/dangling_test.pdf");
file.getParentFile().mkdirs();
document.save(file);
document.close();
}
@Test
public void testDanglingDisposeException() {
Assertions.assertThrows(IllegalStateException.class, () -> {
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PdfBoxGraphics2D pdfBoxGraphics2D = new PdfBoxGraphics2D(document, 400, 400);
pdfBoxGraphics2D.create();
pdfBoxGraphics2D.dispose();
});
}
@Test
public void testDanglingDisposeException2() {
Assertions.assertThrows(IllegalStateException.class, () -> {
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PdfBoxGraphics2D pdfBoxGraphics2D = new PdfBoxGraphics2D(document, 400, 400);
pdfBoxGraphics2D.create().disposeDanglingChildGraphics();
});
}
}

View file

@ -7,12 +7,15 @@ import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix;
import org.jfree.chart.ChartFactory;
//import org.jfree.chart.ChartUtilities;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
import org.jfree.chart.plot.*;
import org.jfree.chart.plot.MultiplePiePlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.SpiderWebPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
@ -28,10 +31,7 @@ import org.jfree.data.time.SimpleTimePeriod;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
//import org.jfree.ui.RectangleEdge;
//import org.jfree.util.TableOrder;
import org.junit.jupiter.api.Test;
import org.xbib.graphics.io.pdfbox.PdfBoxGraphics2D;
import java.awt.Color;
import java.awt.Font;
@ -46,29 +46,24 @@ public class MultiPageTest {
@Test
public void testMultiPageJFreeChart() throws IOException {
File parentDir = new File("build/test/multipage");
// noinspection ResultOfMethodCallIgnored
parentDir.mkdirs();
File targetPDF = new File(parentDir, "multipage.pdf");
PDDocument document = new PDDocument();
for (int i = 0; i < 6; i++) {
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
PdfBoxGraphics2D pdfBoxGraphics2D = new PdfBoxGraphics2D(document, 800, 400);
drawOnGraphics(pdfBoxGraphics2D, i);
pdfBoxGraphics2D.dispose();
PDFormXObject appearanceStream = pdfBoxGraphics2D.getXFormObject();
Matrix matrix = new Matrix();
matrix.translate(0, 30);
matrix.scale(0.7f, 1f);
contentStream.saveGraphicsState();
contentStream.transform(matrix);
contentStream.drawForm(appearanceStream);
contentStream.restoreGraphicsState();
contentStream.close();
}
document.save(targetPDF);

View file

@ -81,6 +81,7 @@ public class VectorGraphics2D extends Graphics2D implements Cloneable {
private final Processor processor;
private final PageSize pageSize;
/**
* List of operations that were performed on this graphics object and its
* derived objects.
@ -171,7 +172,7 @@ public class VectorGraphics2D extends Graphics2D implements Cloneable {
if ((clipNew == null || debugValidateGraphics.getClip() == null) && clipNew != debugValidateGraphics.getClip()) {
throw new IllegalStateException("clip() validation failed: clip(" + clipOld + ", " + s + ") => " + clipNew + " != " + debugValidateGraphics.getClip());
}
if (clipNew != null && !equals(clipNew, debugValidateGraphics.getClip())) {
if (clipNew != null && notEquals(clipNew, debugValidateGraphics.getClip())) {
throw new IllegalStateException("clip() validation failed: clip(" + clipOld + ", " + s + ") => " + clipNew + " != " + debugValidateGraphics.getClip());
}
}
@ -238,7 +239,6 @@ public class VectorGraphics2D extends Graphics2D implements Cloneable {
emit(new DrawStringCommand(str, x, y));
debugValidateGraphics.drawString(str, x, y);
}
}
@Override
@ -744,7 +744,7 @@ public class VectorGraphics2D extends Graphics2D implements Cloneable {
throw new IllegalStateException("setClip() validation failed: clip=null, validation=" +
debugValidateGraphics.getClip());
}
} else if (!equals(getClip(), debugValidateGraphics.getClip())) {
} else if (notEquals(getClip(), debugValidateGraphics.getClip())) {
throw new IllegalStateException("setClip() validation failed: clip=" + getClip() + ", validation=" +
debugValidateGraphics.getClip());
}
@ -865,11 +865,11 @@ public class VectorGraphics2D extends Graphics2D implements Cloneable {
return op.filter(bufferedImage, null);
}
private static boolean equals(Shape shapeA, Shape shapeB) {
private static boolean notEquals(Shape shapeA, Shape shapeB) {
PathIterator pathAIterator = shapeA.getPathIterator(null);
PathIterator pathBIterator = shapeB.getPathIterator(null);
if (pathAIterator.getWindingRule() != pathBIterator.getWindingRule()) {
return false;
return true;
}
double[] pathASegment = new double[6];
double[] pathBSegment = new double[6];
@ -877,17 +877,17 @@ public class VectorGraphics2D extends Graphics2D implements Cloneable {
int pathASegmentType = pathAIterator.currentSegment(pathASegment);
int pathBSegmentType = pathBIterator.currentSegment(pathBSegment);
if (pathASegmentType != pathBSegmentType) {
return false;
return true;
}
for (int segmentIndex = 0; segmentIndex < pathASegment.length; segmentIndex++) {
if (pathASegment[segmentIndex] != pathBSegment[segmentIndex]) {
return false;
return true;
}
}
pathAIterator.next();
pathBIterator.next();
}
return pathBIterator.isDone();
return !pathBIterator.isDone();
}
/**

View file

@ -1,3 +1,3 @@
dependencies {
implementation "org.apache.pdfbox:pdfbox:${project.property('pdfbox.version')}"
api "org.apache.pdfbox:pdfbox:${project.property('pdfbox.version')}"
}