Blank pages (B&W JPEG) in converted PDF in normal PDF viewer
Status: Beta
Brought to you by:
roskakori
Not the same as "Blank pages when viewing converted PDF in normal PDF viewer - ID: 1721414" , maybe related to "Make B&W JPEG files compatible - ID: 1456026"? : When converting PDFs, 8bit B&W JPEGs appears as blank pages in normal PDFs viewer. PDF viewers tested: OSX viewer, Kindle2. Jomic 0.9.30 (Apr 20, 2008) in OSX.
I have this problem too, and I've figured out what is causing the PDF viewer to fail to display the generated PDF: the image is a 256-color jpeg sure enough, and manually chopping off the PDF header allows it to be viewed as a jpeg. The problem is that the pdf stream header contains the line "/ColorSpace /DeviceRGB", which is incorrect. Manually changing it to "DeviceGray" in a hex editor made the PDF viewable! So this should be easy to fix :)
I spoke too soon. The problem appears to be a weakness of the pdfbox library; org.pdfbox.pdmodel.graphics.xobject.PDJPeg unconditionally calls "setColorSpace( PDDeviceRGB.INSTANCE );" in several places, and has no interface to specify a b'n'w jpeg.
Actually, it's ok, we can change the PDJpeg's color space setting after creating it and before rendering it, I think, like so ...
Index: source/net/sf/jomic/comic/CreatePdfComicTask.java
--- source/net/sf/jomic/comic/CreatePdfComicTask.java (revision 2132)
+++ source/net/sf/jomic/comic/CreatePdfComicTask.java (working copy)
@@ -34,6 +34,7 @@
import org.pdfbox.pdmodel.PDPage;
import org.pdfbox.pdmodel.common.PDRectangle;
import org.pdfbox.pdmodel.edit.PDPageContentStream;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceGray;
import org.pdfbox.pdmodel.graphics.xobject.PDJpeg;
/**
@@ -106,6 +107,11 @@
try {
PDPage page = new PDPage();
PDJpeg jpeg = new PDJpeg(pdfDocument, imageStream);
+ if (!needsTrim && isJpegImage) {
+ // Check if B'n'W image.
+ // If so, call:
+ jpeg.setColorSpace( new PDDeviceGray() );
+ }
PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page);
try {
... but I don't know how to detect the format from the raw FileInputStream or File yet.
I have come up with a patch that fixes the problem for me. I'm not 100% sure about reusing the "if (!needsTrim && isJpegImage)" condition before checking for a b'n'w image, I did it because I guessed that in the case where it has to create a RenderedImage that it would be implicitly promoted to rgb, but I could be wrong, in which case the test should be removed from the line after the PDJpeg is constructed.
I don't seem to be able to access the "attach file" feature here, though. I'll paste it into the comment, but the whitespace will get mangled; for an unmangled version, see http://pastebin.com/1qjRjf9m
Thank you Thomas for such a useful bit of software! :-)
Index: source/net/sf/jomic/tools/ImageTools.java
--- source/net/sf/jomic/tools/ImageTools.java (revision 2132)
+++ source/net/sf/jomic/tools/ImageTools.java (working copy)
@@ -399,6 +399,60 @@
return (ImageReader) ImageIO.getImageReaders(imageStream).next();
}
+ /**
+ * Get an image type specifier for <code>imageStream</code> or <code>null</code> if no image
+ * stream or reader is available.
+ *
+ * @see ImageIO#getImageReaders(java.lang.Object)
+ * @see ImageReader#setInput(java.lang.Object)
+ * @see ImageReader#getNumImages(boolean)
+ * @see ImageReader#getRawImageType(int)
+ */
+ public ImageTypeSpecifier getImageTypeSpecifier(ImageInputStream imageStream)
+ throws IOException {
+ ImageTypeSpecifier result = null;
+
+ if (imageStream != null) {
+ Iterator readerRider = ImageIO.getImageReaders(imageStream);
+
+ while ((result == null) && readerRider.hasNext()) {
+ ImageReader reader = (ImageReader) readerRider.next();
+
+ reader.setInput(imageStream);
+ if (reader.getNumImages(true) <= 0) {
+ continue;
+ }
+ result = reader.getRawImageType(reader.getMinIndex());
+ assert result != null;
+ if (logger.isDebugEnabled()) {
+ logger.debug("image format=" + result);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get an image type specifier for <code>imageFile</code> or <code>null</code> if no image
+ * reader is available for it.
+ *
+ * @see ImageIO#createImageInputStream(java.lang.Object)
+ */
+ public ImageTypeSpecifier getImageTypeSpecifier(File imageFile)
+ throws IOException {
+ ImageTypeSpecifier result = null;
+ ImageInputStream imageStream = ImageIO.createImageInputStream(imageFile);
+
+ if (imageStream != null) {
+ try {
+ result = getImageTypeSpecifier(imageStream);
+ } finally {
+ imageStream.close();
+ }
+ }
+ return result;
+ }
+
public int getLeftRotation(int rotation) {
assertIsValidRotation(rotation);
Index: source/net/sf/jomic/comic/CreatePdfComicTask.java
--- source/net/sf/jomic/comic/CreatePdfComicTask.java (revision 2132)
+++ source/net/sf/jomic/comic/CreatePdfComicTask.java (working copy)
@@ -34,6 +34,7 @@
import org.pdfbox.pdmodel.PDPage;
import org.pdfbox.pdmodel.common.PDRectangle;
import org.pdfbox.pdmodel.edit.PDPageContentStream;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceGray;
import org.pdfbox.pdmodel.graphics.xobject.PDJpeg;
/**
@@ -106,6 +107,14 @@
try {
PDPage page = new PDPage();
PDJpeg jpeg = new PDJpeg(pdfDocument, imageStream);
+
+ if (!needsTrim && isJpegImage) {
+ // Check if B'n'W image.
+ if (imageTools.getImageTypeSpecifier(imageFile).getNumComponents() == 1) {
+ // If so, set color space; PDJpeg ctor can't do it for us.
+ jpeg.setColorSpace(new PDDeviceGray());
+ }
+ }
PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page);
try {
Updated version of the patch now available at http://pastebin.com/mgZx9Zx8
I found a bug in the original version; it is apparently possible for ImageReader.getRawImageType() to return NULL, when handling what appeared to be an ordinary-seeming jpeg file.
In the updated version of the patch, this possibility is handled by allowing for null returns from ImageTools.getImageTypeSpecifier() and falling back to the old behaviour (i.e. letting pdfbox assume this is a colour jpeg) in CreatePdfComicTask.addImageFile() if so.
Whitespace-mangled version of the updated patch follows, JFTR:
Index: source/net/sf/jomic/tools/ImageTools.java
--- source/net/sf/jomic/tools/ImageTools.java (revision 2132)
+++ source/net/sf/jomic/tools/ImageTools.java (working copy)
@@ -399,6 +399,59 @@
return (ImageReader) ImageIO.getImageReaders(imageStream).next();
}
+ /**
+ * Get an image type specifier for <code>imageStream</code> or <code>null</code> if no image
+ * stream or reader is available.
+ *
+ * @see ImageIO#getImageReaders(java.lang.Object)
+ * @see ImageReader#setInput(java.lang.Object)
+ * @see ImageReader#getNumImages(boolean)
+ * @see ImageReader#getRawImageType(int)
+ */
+ public ImageTypeSpecifier getImageTypeSpecifier(ImageInputStream imageStream)
+ throws IOException {
+ ImageTypeSpecifier result = null;
+
+ if (imageStream != null) {
+ Iterator readerRider = ImageIO.getImageReaders(imageStream);
+
+ while ((result == null) && readerRider.hasNext()) {
+ ImageReader reader = (ImageReader) readerRider.next();
+
+ reader.setInput(imageStream);
+ if (reader.getNumImages(true) <= 0) {
+ continue;
+ }
+ result = reader.getRawImageType(reader.getMinIndex());
+ if (result != null && logger.isDebugEnabled()) {
+ logger.debug("image format=" + result);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get an image type specifier for <code>imageFile</code> or <code>null</code> if no image
+ * reader is available for it.
+ *
+ * @see ImageIO#createImageInputStream(java.lang.Object)
+ */
+ public ImageTypeSpecifier getImageTypeSpecifier(File imageFile)
+ throws IOException {
+ ImageTypeSpecifier result = null;
+ ImageInputStream imageStream = ImageIO.createImageInputStream(imageFile);
+
+ if (imageStream != null) {
+ try {
+ result = getImageTypeSpecifier(imageStream);
+ } finally {
+ imageStream.close();
+ }
+ }
+ return result;
+ }
+
public int getLeftRotation(int rotation) {
assertIsValidRotation(rotation);
Index: source/net/sf/jomic/comic/CreatePdfComicTask.java
--- source/net/sf/jomic/comic/CreatePdfComicTask.java (revision 2132)
+++ source/net/sf/jomic/comic/CreatePdfComicTask.java (working copy)
@@ -25,6 +25,7 @@
import java.util.Map;
import javax.imageio.ImageIO;
+import javax.imageio.ImageTypeSpecifier;
import net.sf.jomic.tools.ImageTools;
import net.sf.jomic.tools.StringTools;
@@ -34,6 +35,7 @@
import org.pdfbox.pdmodel.PDPage;
import org.pdfbox.pdmodel.common.PDRectangle;
import org.pdfbox.pdmodel.edit.PDPageContentStream;
+import org.pdfbox.pdmodel.graphics.color.PDDeviceGray;
import org.pdfbox.pdmodel.graphics.xobject.PDJpeg;
/**
@@ -106,6 +108,15 @@
try {
PDPage page = new PDPage();
PDJpeg jpeg = new PDJpeg(pdfDocument, imageStream);
+
+ if (!needsTrim && isJpegImage) {
+ // Check if B'n'W image.
+ ImageTypeSpecifier its = imageTools.getImageTypeSpecifier(imageFile);
+ if (its != null && its.getNumComponents() == 1) {
+ // If so, set color space; PDJpeg ctor can't do it for us.
+ jpeg.setColorSpace(new PDDeviceGray());
+ }
+ }
PDPageContentStream contentStream = new PDPageContentStream(pdfDocument, page);
try {
I found the patch tracker. The diff is now item 3055577.
https://sourceforge.net/tracker/?func=detail&aid=3055577&group_id=103788&atid=635945
I updated the nightly build to use PDFBox 1.5, which has a lot of improvements and can open a lot more PDF documents than the version included with Jomic 0.9.30.
Does that fix your problem?