From 2b93eacf0dd610268a31534b56e1d9bc1463882d Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 7 Nov 2021 11:10:47 -0700 Subject: [PATCH 1/5] Add sample code to exercise printing --- .../printing/SimplifiedPdfBoxPdfPrinter.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java diff --git a/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java b/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java new file mode 100644 index 00000000000..7972199e0aa --- /dev/null +++ b/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java @@ -0,0 +1,83 @@ +/** + * + */ +package org.apache.pdfbox.printing; + +import java.awt.print.PrinterJob; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import javax.print.PrintService; +import javax.print.PrintServiceLookup; +import javax.print.attribute.HashPrintRequestAttributeSet; + +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.io.MemoryUsageSetting; +import org.apache.pdfbox.pdmodel.PDDocument; + +/** + * @author Kevin + * + */ +public class SimplifiedPdfBoxPdfPrinter { + + private final String printerName; + + /** + * + */ + public SimplifiedPdfBoxPdfPrinter(String printerName) { + this.printerName = printerName; + } + + private static void forceFileExist(File f) throws IOException{ + new FileOutputStream(f).close(); + } + + private static PrintService getPrintService(String name){ + PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null); + for (PrintService service : services) { + if (service.getName().equalsIgnoreCase(name)){ + return service; + } + } + return null; + } + + + public void print(File pdf) throws Exception { + if (!pdf.exists()) + throw new IOException(pdf + " does not exist before printing - conversion to prn not possible"); + + PrintService printService = getPrintService(printerName); + + PrinterJob job = PrinterJob.getPrinterJob(); + job.setPrintService(printService); + + try(PDDocument doc = Loader.loadPDF(pdf, MemoryUsageSetting.setupMixed(5000000L))){ + PDFPageable pageable = new PDFPageable(doc); + + job.setPageable(pageable); + + // create a new HashPrintRequestAttributeSet and initialize it with the printer's default attributes + HashPrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet(); + + File tempPrn = File.createTempFile("tempPrn", ""); + + // set the output file as a destination + attributes.add(new javax.print.attribute.standard.Destination(tempPrn.toURI())); + + + // print with the attributes + job.print(attributes); + } + + } + + + public static void main(String[] args) throws Exception { + SimplifiedPdfBoxPdfPrinter svc = new SimplifiedPdfBoxPdfPrinter("\\\\Moby\\Cust Suc Dell C2660dn"); + svc.print(new File("S:\\ClientData\\ClientData\\XPRIA-TPT100892\\JrachvUniverse_H21.pdf")); + } +} From 98958281ee9cdcd23deaaf0348a89bae0a3d01fa Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 7 Nov 2021 11:11:17 -0700 Subject: [PATCH 2/5] Use BigBufferedImage for tiling paint --- .../pdfbox/printing/BigBufferedImage.java | 280 ++++++++++++++++++ .../apache/pdfbox/rendering/TilingPaint.java | 5 +- 2 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 pdfbox/src/main/java/org/apache/pdfbox/printing/BigBufferedImage.java diff --git a/pdfbox/src/main/java/org/apache/pdfbox/printing/BigBufferedImage.java b/pdfbox/src/main/java/org/apache/pdfbox/printing/BigBufferedImage.java new file mode 100644 index 00000000000..fd8d6fa2893 --- /dev/null +++ b/pdfbox/src/main/java/org/apache/pdfbox/printing/BigBufferedImage.java @@ -0,0 +1,280 @@ +/** + * + */ +package org.apache.pdfbox.printing; + +/* + * This class is part of MCFS (Mission Control - Flight Software) a development + * of Team Puli Space, official Google Lunar XPRIZE contestant. + * This class is released under Creative Commons CC0. + * @author Zsolt Pocze, Dimitry Polivaev + * Please like us on facebook, and/or join our Small Step Club. + * http://www.pulispace.com + * https://www.facebook.com/pulispace + * http://nyomdmegteis.hu/en/ + * https://www.freeplane.org + */ +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.color.ColorSpace; +import java.awt.image.BandedSampleModel; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; +import sun.nio.ch.DirectBuffer; +public class BigBufferedImage extends BufferedImage { + private static final String TMP_DIR = System.getProperty("java.io.tmpdir"); + public static final int MAX_PIXELS_IN_MEMORY = 1024 * 1024; + public static BufferedImage create(int width, int height, int imageType) { + if (width * height > MAX_PIXELS_IN_MEMORY) { + try { + final File tempDir = new File(TMP_DIR); + return createBigBufferedImage(tempDir, width, height, imageType); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + return new BufferedImage(width, height, imageType); + } + } + public static BufferedImage create(File inputFile, int imageType) throws IOException { + try (ImageInputStream stream = ImageIO.createImageInputStream(inputFile);) { + Iterator readers = ImageIO.getImageReaders(stream); + if (readers.hasNext()) { + try { + ImageReader reader = readers.next(); + reader.setInput(stream, true, true); + int width = reader.getWidth(reader.getMinIndex()); + int height = reader.getHeight(reader.getMinIndex()); + BufferedImage image = create(width, height, imageType); + int cores = Math.max(1, Runtime.getRuntime().availableProcessors() / 2); + int block = Math.min(MAX_PIXELS_IN_MEMORY / cores / width, (int) (Math.ceil(height / (double) cores))); + ExecutorService generalExecutor = Executors.newFixedThreadPool(cores); + List> partLoaders = new ArrayList<>(); + for (int y = 0; y < height; y += block) { + partLoaders.add(new ImagePartLoader( + y, width, Math.min(block, height - y), inputFile, image)); + } + generalExecutor.invokeAll(partLoaders); + generalExecutor.shutdown(); + return image; + } catch (InterruptedException ex) { + Logger.getLogger(BigBufferedImage.class.getName()).log(Level.SEVERE, null, ex); + } + } + } + return null; + } + private static BufferedImage createBigBufferedImage(File tempDir, int width, int height, int imageType) + throws FileNotFoundException, IOException { + FileDataBuffer buffer = new FileDataBuffer(tempDir, width * height, 4); + ColorModel colorModel = null; + BandedSampleModel sampleModel = null; + switch (imageType) { + case TYPE_INT_RGB: + colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + new int[]{8, 8, 8, 0}, + false, + false, + ComponentColorModel.TRANSLUCENT, + DataBuffer.TYPE_BYTE); + sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 3); + break; + case TYPE_INT_ARGB: + colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + new int[]{8, 8, 8, 8}, + true, + false, + ComponentColorModel.TRANSLUCENT, + DataBuffer.TYPE_BYTE); + sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 4); + break; + default: + throw new IllegalArgumentException("Unsupported image type: " + imageType); + } + SimpleRaster raster = new SimpleRaster(sampleModel, buffer, new Point(0, 0)); + BigBufferedImage image = new BigBufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null); + return image; + } + private static class ImagePartLoader implements Callable { + private final int y; + private final BufferedImage image; + private final Rectangle region; + private final File file; + public ImagePartLoader(int y, int width, int height, File file, BufferedImage image) { + this.y = y; + this.image = image; + this.file = file; + region = new Rectangle(0, y, width, height); + } + @Override + public ImagePartLoader call() throws Exception { + Thread.currentThread().setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2); + try (ImageInputStream stream = ImageIO.createImageInputStream(file);) { + Iterator readers = ImageIO.getImageReaders(stream); + if (readers.hasNext()) { + ImageReader reader = readers.next(); + reader.setInput(stream, true, true); + ImageReadParam param = reader.getDefaultReadParam(); + param.setSourceRegion(region); + BufferedImage part = reader.read(0, param); + Raster source = part.getRaster(); + WritableRaster target = image.getRaster(); + target.setRect(0, y, source); + } + } + return ImagePartLoader.this; + } + } + private BigBufferedImage(ColorModel cm, SimpleRaster raster, boolean isRasterPremultiplied, Hashtable properties) { + super(cm, raster, isRasterPremultiplied, properties); + } + public void dispose() { + ((SimpleRaster) getRaster()).dispose(); + } + public static void dispose(RenderedImage image) { + if (image instanceof BigBufferedImage) { + ((BigBufferedImage) image).dispose(); + } + } + private static class SimpleRaster extends WritableRaster { + public SimpleRaster(SampleModel sampleModel, FileDataBuffer dataBuffer, Point origin) { + super(sampleModel, dataBuffer, origin); + } + public void dispose() { + ((FileDataBuffer) getDataBuffer()).dispose(); + } + } + private static final class FileDataBufferDeleterHook extends Thread { + static { + Runtime.getRuntime().addShutdownHook(new FileDataBufferDeleterHook()); + } + private static final HashSet undisposedBuffers = new HashSet<>(); + @Override + public void run() { + final FileDataBuffer[] buffers = undisposedBuffers.toArray(new FileDataBuffer[0]); + for (FileDataBuffer b : buffers) { + b.disposeNow(); + } + } + } + private static class FileDataBuffer extends DataBuffer { + private final String id = "buffer-" + System.currentTimeMillis() + "-" + ((int) (Math.random() * 1000)); + private File dir; + private String path; + private File[] files; + private RandomAccessFile[] accessFiles; + private MappedByteBuffer[] buffer; + public FileDataBuffer(File dir, int size) throws FileNotFoundException, IOException { + super(TYPE_BYTE, size); + System.out.println("Size = " + size); + this.dir = dir; + init(); + } + public FileDataBuffer(File dir, int size, int numBanks) throws FileNotFoundException, IOException { + super(TYPE_BYTE, size, numBanks); + System.out.println("Size = " + size); + this.dir = dir; + init(); + } + private void init() throws FileNotFoundException, IOException { + FileDataBufferDeleterHook.undisposedBuffers.add(this); + if (dir == null) { + dir = new File("."); + } + if (!dir.exists()) { + throw new RuntimeException("FileDataBuffer constructor parameter dir does not exist: " + dir); + } + if (!dir.isDirectory()) { + throw new RuntimeException("FileDataBuffer constructor parameter dir is not a directory: " + dir); + } + path = dir.getPath() + "/" + id; + File subDir = new File(path); + subDir.mkdir(); + buffer = new MappedByteBuffer[banks]; + accessFiles = new RandomAccessFile[banks]; + files = new File[banks]; + for (int i = 0; i < banks; i++) { + File file = files[i] = new File(path + "/bank" + i + ".dat"); + final RandomAccessFile randomAccessFile = accessFiles[i] = new RandomAccessFile(file, "rw"); + System.out.println("Created map file " + file); + buffer[i] = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, getSize()); + } + } + @Override + public int getElem(int bank, int i) { + return buffer[bank].get(i) & 0xff; + } + @Override + public void setElem(int bank, int i, int val) { + buffer[bank].put(i, (byte) val); + } + @Override + protected void finalize() throws Throwable { + dispose(); + } + private void disposeNow() { + final MappedByteBuffer[] disposedBuffer = this.buffer; + this.buffer = null; + disposeNow(disposedBuffer); + } + public void dispose() { + final MappedByteBuffer[] disposedBuffer = this.buffer; + this.buffer = null; + new Thread() { + @Override + public void run() { + disposeNow(disposedBuffer); + } + }.start(); + } + private void disposeNow(final MappedByteBuffer[] disposedBuffer) { +System.out.println("Cleaning up " + path); + FileDataBufferDeleterHook.undisposedBuffers.remove(this); + if (disposedBuffer != null) { + for (MappedByteBuffer b : disposedBuffer) { + ((DirectBuffer) b).cleaner().clean(); + } + for (RandomAccessFile file : accessFiles) { + try { + file.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + accessFiles = null; + for (File file : files) { + file.delete(); + } + files = null; + new File(path).delete(); + path = null; + } + } + } +} diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java index 1003a793f99..9ab79b18821 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java @@ -36,6 +36,7 @@ import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; import org.apache.pdfbox.pdmodel.graphics.pattern.PDTilingPattern; +import org.apache.pdfbox.printing.BigBufferedImage; import org.apache.pdfbox.util.Matrix; /** @@ -140,8 +141,10 @@ private BufferedImage getImage(PageDrawer drawer, PDTilingPattern pattern, PDCol int rasterWidth = Math.max(1, ceiling(width)); int rasterHeight = Math.max(1, ceiling(height)); - BufferedImage image = new BufferedImage(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); + //BufferedImage image = new BufferedImage(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); + BufferedImage image = BigBufferedImage.create(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = image.createGraphics(); // flip a -ve YStep around its own axis (see gs-bugzilla694385.pdf) From 8970265235a116c80c078302d6100f6cda6887ba Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 7 Nov 2021 11:12:06 -0700 Subject: [PATCH 3/5] Fix cache issue, add caching of PageDrawer in PDFRendere --- pdfbox/pom.xml | 7 ++++++ .../apache/pdfbox/rendering/PDFRenderer.java | 17 ++++++++++++- .../pdfbox/rendering/TilingPaintFactory.java | 25 +++++++++---------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/pdfbox/pom.xml b/pdfbox/pom.xml index 5d90c2306c2..79d37a1e3d3 100644 --- a/pdfbox/pom.xml +++ b/pdfbox/pom.xml @@ -41,6 +41,13 @@ + + + org.apache.commons + commons-collections4 + 4.4 + + org.apache.pdfbox fontbox diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java index 3911ea402db..0237680f3d0 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java @@ -24,8 +24,12 @@ import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.IOException; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; + +import org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceStrength; +import org.apache.commons.collections4.map.ReferenceMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.pdfbox.cos.COSName; @@ -71,6 +75,11 @@ public class PDFRenderer private float imageDownscalingOptimizationThreshold = 0.5f; private final PDPageTree pageTree; + + private final Map pageDrawerCache = new ReferenceMap<>(ReferenceStrength.HARD, ReferenceStrength.WEAK); + + + /** * Creates a new PDFRenderer. @@ -436,6 +445,7 @@ public void renderPageToGraphics(int pageIndex, Graphics2D graphics, float scale * @param destination controlling visibility of optional content groups * @throws IOException if the PDF cannot be read */ + public void renderPageToGraphics(int pageIndex, Graphics2D graphics, float scaleX, float scaleY, RenderDestination destination) throws IOException { @@ -453,7 +463,12 @@ public void renderPageToGraphics(int pageIndex, Graphics2D graphics, float scale PageDrawerParameters parameters = new PageDrawerParameters(this, page, subsamplingAllowed, destination, actualRenderingHints, imageDownscalingOptimizationThreshold); - PageDrawer drawer = createPageDrawer(parameters); + PageDrawer drawer = pageDrawerCache.get(pageIndex); + if (drawer == null) { + drawer = new PageDrawer(parameters); + pageDrawerCache.put(pageIndex, drawer); + } + drawer.drawPage(graphics, cropBox); } diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaintFactory.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaintFactory.java index 93fa71641a7..0f16644d531 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaintFactory.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaintFactory.java @@ -20,11 +20,11 @@ import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Map; -import java.util.WeakHashMap; +import org.apache.commons.collections4.map.AbstractReferenceMap.ReferenceStrength; +import org.apache.commons.collections4.map.ReferenceMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.apache.pdfbox.cos.COSDictionary; import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; @@ -41,8 +41,8 @@ class TilingPaintFactory private static final Log LOG = LogFactory.getLog(TilingPaintFactory.class); private final PageDrawer drawer; - private final Map> weakCache - = new WeakHashMap<>(); + private final Map weakCache + = new ReferenceMap<>(ReferenceStrength.HARD, ReferenceStrength.WEAK); TilingPaintFactory(PageDrawer drawer) { @@ -55,16 +55,13 @@ Paint create(PDTilingPattern pattern, PDColorSpace colorSpace, Paint paint = null; TilingPaintParameter tilingPaintParameter = new TilingPaintParameter(drawer.getInitialMatrix(), pattern.getCOSObject(), colorSpace, color, xform); - WeakReference weakRef = weakCache.get(tilingPaintParameter); - if (weakRef != null) - { - // PDFBOX-4058: additional WeakReference makes gc work better - paint = weakRef.get(); - } + + paint = weakCache.get(tilingPaintParameter); + if (paint == null) { paint = new TilingPaint(drawer, pattern, colorSpace, color, xform); - weakCache.put(tilingPaintParameter, new WeakReference<>(paint)); + weakCache.put(tilingPaintParameter, paint); } return paint; } @@ -140,7 +137,8 @@ public boolean equals(Object obj) LOG.debug("Couldn't convert color to RGB - treating as not equal", ex); return false; } - return !(this.xform != other.xform && (this.xform == null || !this.xform.equals(other.xform))); + + return !(this.xform != other.xform && (this.xform == null || !( this.xform.getScaleX() == other.xform.getScaleX() && this.xform.getScaleY() == other.xform.getScaleY() ))); } @Override @@ -151,7 +149,8 @@ public int hashCode() hash = 23 * hash + (this.patternDict != null ? this.patternDict.hashCode() : 0); hash = 23 * hash + (this.colorSpace != null ? this.colorSpace.hashCode() : 0); hash = 23 * hash + (this.color != null ? this.color.hashCode() : 0); - hash = 23 * hash + (this.xform != null ? this.xform.hashCode() : 0); + hash = 23 * hash + (this.xform != null ? Double.hashCode(this.xform.getScaleX()) : 0); + hash = 23 * hash + (this.xform != null ? Double.hashCode(this.xform.getScaleY()) : 0); return hash; } From df32d691c3008ed199ab265eb3a0fc437346536c Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 7 Nov 2021 21:06:17 -0700 Subject: [PATCH 4/5] Checking performance with gs-bugzilla692158-schleuse-veryslow.pdf --- .../java/org/apache/pdfbox/rendering/TilingPaint.java | 6 +++--- .../pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java index 9ab79b18821..a090c25c1d5 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java @@ -142,9 +142,9 @@ private BufferedImage getImage(PageDrawer drawer, PDTilingPattern pattern, PDCol int rasterHeight = Math.max(1, ceiling(height)); - //BufferedImage image = new BufferedImage(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); - BufferedImage image = BigBufferedImage.create(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); - + BufferedImage image = new BufferedImage(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); + //BufferedImage image = BigBufferedImage.create(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); +System.out.println("New BufferedImage in TilingPaint"); Graphics2D graphics = image.createGraphics(); // flip a -ve YStep around its own axis (see gs-bugzilla694385.pdf) diff --git a/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java b/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java index 7972199e0aa..d22032817d7 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java @@ -77,7 +77,13 @@ public void print(File pdf) throws Exception { public static void main(String[] args) throws Exception { + long start = System.currentTimeMillis(); + SimplifiedPdfBoxPdfPrinter svc = new SimplifiedPdfBoxPdfPrinter("\\\\Moby\\Cust Suc Dell C2660dn"); - svc.print(new File("S:\\ClientData\\ClientData\\XPRIA-TPT100892\\JrachvUniverse_H21.pdf")); + //svc.print(new File("S:\\ClientData\\ClientData\\XPRIA-TPT100892\\JrachvUniverse_H21.pdf")); + + svc.print(new File("C:\\Users\\kevin\\Downloads\\gs-bugzilla692158-schleuse-veryslow.pdf")); + + System.out.println("Elapsed: " + (System.currentTimeMillis() - start)/1000L + " secs"); } } From 624ff8042c0f290c720d27031b46876be8c6e3a9 Mon Sep 17 00:00:00 2001 From: Kevin Date: Sun, 7 Nov 2021 22:34:07 -0700 Subject: [PATCH 5/5] Remove BigBufferedImage, refine MAXEDGE handling in TilingPaint --- .../pdfbox/printing/BigBufferedImage.java | 280 ------------------ .../apache/pdfbox/rendering/TilingPaint.java | 27 +- .../printing/SimplifiedPdfBoxPdfPrinter.java | 6 +- 3 files changed, 20 insertions(+), 293 deletions(-) delete mode 100644 pdfbox/src/main/java/org/apache/pdfbox/printing/BigBufferedImage.java diff --git a/pdfbox/src/main/java/org/apache/pdfbox/printing/BigBufferedImage.java b/pdfbox/src/main/java/org/apache/pdfbox/printing/BigBufferedImage.java deleted file mode 100644 index fd8d6fa2893..00000000000 --- a/pdfbox/src/main/java/org/apache/pdfbox/printing/BigBufferedImage.java +++ /dev/null @@ -1,280 +0,0 @@ -/** - * - */ -package org.apache.pdfbox.printing; - -/* - * This class is part of MCFS (Mission Control - Flight Software) a development - * of Team Puli Space, official Google Lunar XPRIZE contestant. - * This class is released under Creative Commons CC0. - * @author Zsolt Pocze, Dimitry Polivaev - * Please like us on facebook, and/or join our Small Step Club. - * http://www.pulispace.com - * https://www.facebook.com/pulispace - * http://nyomdmegteis.hu/en/ - * https://www.freeplane.org - */ -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.color.ColorSpace; -import java.awt.image.BandedSampleModel; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.ComponentColorModel; -import java.awt.image.DataBuffer; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.WritableRaster; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.imageio.ImageIO; -import javax.imageio.ImageReadParam; -import javax.imageio.ImageReader; -import javax.imageio.stream.ImageInputStream; -import sun.nio.ch.DirectBuffer; -public class BigBufferedImage extends BufferedImage { - private static final String TMP_DIR = System.getProperty("java.io.tmpdir"); - public static final int MAX_PIXELS_IN_MEMORY = 1024 * 1024; - public static BufferedImage create(int width, int height, int imageType) { - if (width * height > MAX_PIXELS_IN_MEMORY) { - try { - final File tempDir = new File(TMP_DIR); - return createBigBufferedImage(tempDir, width, height, imageType); - } catch (IOException e) { - throw new RuntimeException(e); - } - } else { - return new BufferedImage(width, height, imageType); - } - } - public static BufferedImage create(File inputFile, int imageType) throws IOException { - try (ImageInputStream stream = ImageIO.createImageInputStream(inputFile);) { - Iterator readers = ImageIO.getImageReaders(stream); - if (readers.hasNext()) { - try { - ImageReader reader = readers.next(); - reader.setInput(stream, true, true); - int width = reader.getWidth(reader.getMinIndex()); - int height = reader.getHeight(reader.getMinIndex()); - BufferedImage image = create(width, height, imageType); - int cores = Math.max(1, Runtime.getRuntime().availableProcessors() / 2); - int block = Math.min(MAX_PIXELS_IN_MEMORY / cores / width, (int) (Math.ceil(height / (double) cores))); - ExecutorService generalExecutor = Executors.newFixedThreadPool(cores); - List> partLoaders = new ArrayList<>(); - for (int y = 0; y < height; y += block) { - partLoaders.add(new ImagePartLoader( - y, width, Math.min(block, height - y), inputFile, image)); - } - generalExecutor.invokeAll(partLoaders); - generalExecutor.shutdown(); - return image; - } catch (InterruptedException ex) { - Logger.getLogger(BigBufferedImage.class.getName()).log(Level.SEVERE, null, ex); - } - } - } - return null; - } - private static BufferedImage createBigBufferedImage(File tempDir, int width, int height, int imageType) - throws FileNotFoundException, IOException { - FileDataBuffer buffer = new FileDataBuffer(tempDir, width * height, 4); - ColorModel colorModel = null; - BandedSampleModel sampleModel = null; - switch (imageType) { - case TYPE_INT_RGB: - colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), - new int[]{8, 8, 8, 0}, - false, - false, - ComponentColorModel.TRANSLUCENT, - DataBuffer.TYPE_BYTE); - sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 3); - break; - case TYPE_INT_ARGB: - colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), - new int[]{8, 8, 8, 8}, - true, - false, - ComponentColorModel.TRANSLUCENT, - DataBuffer.TYPE_BYTE); - sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, width, height, 4); - break; - default: - throw new IllegalArgumentException("Unsupported image type: " + imageType); - } - SimpleRaster raster = new SimpleRaster(sampleModel, buffer, new Point(0, 0)); - BigBufferedImage image = new BigBufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null); - return image; - } - private static class ImagePartLoader implements Callable { - private final int y; - private final BufferedImage image; - private final Rectangle region; - private final File file; - public ImagePartLoader(int y, int width, int height, File file, BufferedImage image) { - this.y = y; - this.image = image; - this.file = file; - region = new Rectangle(0, y, width, height); - } - @Override - public ImagePartLoader call() throws Exception { - Thread.currentThread().setPriority((Thread.MIN_PRIORITY + Thread.NORM_PRIORITY) / 2); - try (ImageInputStream stream = ImageIO.createImageInputStream(file);) { - Iterator readers = ImageIO.getImageReaders(stream); - if (readers.hasNext()) { - ImageReader reader = readers.next(); - reader.setInput(stream, true, true); - ImageReadParam param = reader.getDefaultReadParam(); - param.setSourceRegion(region); - BufferedImage part = reader.read(0, param); - Raster source = part.getRaster(); - WritableRaster target = image.getRaster(); - target.setRect(0, y, source); - } - } - return ImagePartLoader.this; - } - } - private BigBufferedImage(ColorModel cm, SimpleRaster raster, boolean isRasterPremultiplied, Hashtable properties) { - super(cm, raster, isRasterPremultiplied, properties); - } - public void dispose() { - ((SimpleRaster) getRaster()).dispose(); - } - public static void dispose(RenderedImage image) { - if (image instanceof BigBufferedImage) { - ((BigBufferedImage) image).dispose(); - } - } - private static class SimpleRaster extends WritableRaster { - public SimpleRaster(SampleModel sampleModel, FileDataBuffer dataBuffer, Point origin) { - super(sampleModel, dataBuffer, origin); - } - public void dispose() { - ((FileDataBuffer) getDataBuffer()).dispose(); - } - } - private static final class FileDataBufferDeleterHook extends Thread { - static { - Runtime.getRuntime().addShutdownHook(new FileDataBufferDeleterHook()); - } - private static final HashSet undisposedBuffers = new HashSet<>(); - @Override - public void run() { - final FileDataBuffer[] buffers = undisposedBuffers.toArray(new FileDataBuffer[0]); - for (FileDataBuffer b : buffers) { - b.disposeNow(); - } - } - } - private static class FileDataBuffer extends DataBuffer { - private final String id = "buffer-" + System.currentTimeMillis() + "-" + ((int) (Math.random() * 1000)); - private File dir; - private String path; - private File[] files; - private RandomAccessFile[] accessFiles; - private MappedByteBuffer[] buffer; - public FileDataBuffer(File dir, int size) throws FileNotFoundException, IOException { - super(TYPE_BYTE, size); - System.out.println("Size = " + size); - this.dir = dir; - init(); - } - public FileDataBuffer(File dir, int size, int numBanks) throws FileNotFoundException, IOException { - super(TYPE_BYTE, size, numBanks); - System.out.println("Size = " + size); - this.dir = dir; - init(); - } - private void init() throws FileNotFoundException, IOException { - FileDataBufferDeleterHook.undisposedBuffers.add(this); - if (dir == null) { - dir = new File("."); - } - if (!dir.exists()) { - throw new RuntimeException("FileDataBuffer constructor parameter dir does not exist: " + dir); - } - if (!dir.isDirectory()) { - throw new RuntimeException("FileDataBuffer constructor parameter dir is not a directory: " + dir); - } - path = dir.getPath() + "/" + id; - File subDir = new File(path); - subDir.mkdir(); - buffer = new MappedByteBuffer[banks]; - accessFiles = new RandomAccessFile[banks]; - files = new File[banks]; - for (int i = 0; i < banks; i++) { - File file = files[i] = new File(path + "/bank" + i + ".dat"); - final RandomAccessFile randomAccessFile = accessFiles[i] = new RandomAccessFile(file, "rw"); - System.out.println("Created map file " + file); - buffer[i] = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, getSize()); - } - } - @Override - public int getElem(int bank, int i) { - return buffer[bank].get(i) & 0xff; - } - @Override - public void setElem(int bank, int i, int val) { - buffer[bank].put(i, (byte) val); - } - @Override - protected void finalize() throws Throwable { - dispose(); - } - private void disposeNow() { - final MappedByteBuffer[] disposedBuffer = this.buffer; - this.buffer = null; - disposeNow(disposedBuffer); - } - public void dispose() { - final MappedByteBuffer[] disposedBuffer = this.buffer; - this.buffer = null; - new Thread() { - @Override - public void run() { - disposeNow(disposedBuffer); - } - }.start(); - } - private void disposeNow(final MappedByteBuffer[] disposedBuffer) { -System.out.println("Cleaning up " + path); - FileDataBufferDeleterHook.undisposedBuffers.remove(this); - if (disposedBuffer != null) { - for (MappedByteBuffer b : disposedBuffer) { - ((DirectBuffer) b).cleaner().clean(); - } - for (RandomAccessFile file : accessFiles) { - try { - file.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - accessFiles = null; - for (File file : files) { - file.delete(); - } - files = null; - new File(path).delete(); - path = null; - } - } - } -} diff --git a/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java b/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java index a090c25c1d5..add703ac2ba 100644 --- a/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java +++ b/pdfbox/src/main/java/org/apache/pdfbox/rendering/TilingPaint.java @@ -30,13 +30,13 @@ import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.color.PDColor; import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; import org.apache.pdfbox.pdmodel.graphics.pattern.PDTilingPattern; -import org.apache.pdfbox.printing.BigBufferedImage; import org.apache.pdfbox.util.Matrix; /** @@ -50,20 +50,26 @@ class TilingPaint implements Paint private final Paint paint; private final Matrix patternMatrix; private static final int MAXEDGE; - private static final String DEFAULTMAXEDGE = "3000"; + private static final String DEFAULTMAXEDGE_64 = "3000"; + private static final String DEFAULTMAXEDGE_32 = "500"; static { - String s = System.getProperty("pdfbox.rendering.tilingpaint.maxedge", DEFAULTMAXEDGE); + String defaultMaxEdge = DEFAULTMAXEDGE_64; + if ("32".equals(System.getProperty("sun.arch.data.model"))) { + defaultMaxEdge = DEFAULTMAXEDGE_32; + } + + String s = System.getProperty("pdfbox.rendering.tilingpaint.maxedge", defaultMaxEdge); int val; try { - val = Integer.parseInt(s); + val = Integer.parseInt(s); } catch (NumberFormatException ex) { LOG.error("Default will be used", ex); - val = Integer.parseInt(DEFAULTMAXEDGE); + val = Integer.parseInt(defaultMaxEdge); } MAXEDGE = val; } @@ -141,10 +147,8 @@ private BufferedImage getImage(PageDrawer drawer, PDTilingPattern pattern, PDCol int rasterWidth = Math.max(1, ceiling(width)); int rasterHeight = Math.max(1, ceiling(height)); - BufferedImage image = new BufferedImage(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); - //BufferedImage image = BigBufferedImage.create(rasterWidth, rasterHeight, BufferedImage.TYPE_INT_ARGB); -System.out.println("New BufferedImage in TilingPaint"); + Graphics2D graphics = image.createGraphics(); // flip a -ve YStep around its own axis (see gs-bugzilla694385.pdf) @@ -236,8 +240,11 @@ private Rectangle2D getAnchorRect(PDTilingPattern pattern) throws IOException LOG.info("bbox: " + bbox); LOG.info("pattern matrix: " + pattern.getMatrix()); LOG.info("concatenated matrix: " + patternMatrix); - width = Math.min(MAXEDGE, Math.abs(width)) * Math.signum(width); - height = Math.min(MAXEDGE, Math.abs(height)) * Math.signum(height); + + float scale = (float)Math.sqrt( (MAXEDGE * MAXEDGE) / (width * height) ); + width = width * scale; + height = height * scale; + //TODO better solution needed } diff --git a/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java b/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java index d22032817d7..420c86c0d40 100644 --- a/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java +++ b/pdfbox/src/test/java/org/apache/pdfbox/printing/SimplifiedPdfBoxPdfPrinter.java @@ -57,7 +57,7 @@ public void print(File pdf) throws Exception { try(PDDocument doc = Loader.loadPDF(pdf, MemoryUsageSetting.setupMixed(5000000L))){ PDFPageable pageable = new PDFPageable(doc); - + job.setPageable(pageable); // create a new HashPrintRequestAttributeSet and initialize it with the printer's default attributes @@ -80,9 +80,9 @@ public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); SimplifiedPdfBoxPdfPrinter svc = new SimplifiedPdfBoxPdfPrinter("\\\\Moby\\Cust Suc Dell C2660dn"); - //svc.print(new File("S:\\ClientData\\ClientData\\XPRIA-TPT100892\\JrachvUniverse_H21.pdf")); + svc.print(new File("S:\\ClientData\\ClientData\\XPRIA-TPT100892\\JrachvUniverse_H21.pdf")); - svc.print(new File("C:\\Users\\kevin\\Downloads\\gs-bugzilla692158-schleuse-veryslow.pdf")); + //svc.print(new File("C:\\Users\\kevin\\Downloads\\gs-bugzilla692158-schleuse-veryslow.pdf")); System.out.println("Elapsed: " + (System.currentTimeMillis() - start)/1000L + " secs"); }