Print PDF that contains JBIG2 images
Asked Answered
P

5

8

Please, suggest me some libraries that will help me print PDF files that contain JBIG2 encoded images. PDFRenderer, PDFBox don't help me. These libs can print simple PDF, but not PDF containing JBIG2 images. PDFRenderer tries to fix it (according to bug issue on PDFRedndrer's bug tracker), but some pages still (especially where barcodes exist) don't want to print.

P.S. I use javax.print API within applet

Thanks!

UPDATE: also tried ICEPdf, is too don't want to work.

I came to the conclusion that all these libraries(PDFRenderer, ICEPdf, PDFBox) use JPedals jbig2 decoder. Bug (some pages didn't print) come from this decoder library. The open source version of this decoder (which is used in PDFRenderer, ICEPdf, PDFBox) is no longer supported, but JPedal has a new commercial branch of the project, and they wrote that the bug has been fixed in new commercial release, which costs $9k.

Any ideas?

UPDATE 2: yesterday I tried to replace JPedal's free library with other open-source jbig2-imageio libraries. But yet I don't get any successful results, so I created a new topic on their project's page (google-code's forum - here ). Would be grateful for any help.

I also found some helpfull discussions on Apache PDFBox bug-tracker: here and here.

Protero answered 21/8, 2012 at 13:48 Comment(6)
@aravindKrishna krysalis is barcode generator library? I need library that renders PDF into image and print it. So all printers able to print any kind of PDF (not only printers that have native support pdf)Protero
sorry for prev try this itext-1.4.8Ambrogino
Can you provide a (link to a) sample PDF which exhibits the problems you describe?Quintessence
@KurtPfeifle uploaded on Google's Document service. Direct link: doc-0k-8c-docs.googleusercontent.com/docs/securesc/…Protero
@KurtPfeifle first page of this PDF is not printing by JPedal free lib. It throw IndexOutOfBound exception. Error same as here: https://idrsolutions.fogbugz.com/default.asp?support.2.4055.1 Other pages print's nice.Protero
@yms I can't. I use it within applet.Protero
I
1

There is a fork of the JPedal library by Borisvl located at

https://github.com/Borisvl/JBIG2-Image-Decoder#readme

which contains speed improvements and I believe it should also fix your bug.

EDIT : The bug is related to simple range checking. Basically you need to prevent GetPixel from accessing x,y values outside of the bitmap extents.

You need to make sure the following conditions are met before calling getPixel

col >= 0 and col < bitmap.width row >= 0 and row < bitmap.height

Here is some Delphi code with a couple of small range checks. I cannot test the Java code myself but you need to make changes to src/org/jpedal/jbig2/image/JBIG2Bitmap.java

procedure TJBIG2Bitmap.combine(bitmap: TJBIG2Bitmap; x, y: Integer; combOp: Int64);
...
...
var
begin
  srcWidth := bitmap.width;
  srcHeight := bitmap.height;
  srcRow := 0;
  srcCol := 0;

  if (x < 0) then x := 0;
  if (y < 0) then y := 0;

  for row := y to Min(y + srcHeight - 1, Self.height - 1) do   // <<<<<<<<  HERE
  begin
    for col := x to x + srcWidth - 1 do
    begin
      srcPixel := bitmap.getPixel(srcCol, srcRow);

Andrew.

Instrumentation answered 7/9, 2012 at 10:40 Comment(1)
I am able to render your PDF correctly with my Delhpi port of the JPedal code. I have posted some code from my port that will point you in the right direction.Instrumentation
S
3

As going through your comment in yms answer ie. " but what library I can use to extract images and (more importantly) put them back in PDF?"

Here is a simple demonstration of
1 ) Extracting jbig2 or you can say all images from pdf.
2 ) Converting jbig2 image to any other format, in my case its jpeg.
3 ) Creating new pdf containing the jpeg.

Using libraries jbig2-imageio and itext.

In the below example please change the resources and the directories path as per your need.
For this I had to go through several resources that I will attach in the end. Hope this helps.

import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.parser.*;

import com.levigo.jbig2.JBIG2ImageReader;
import com.levigo.jbig2.JBIG2ImageReaderSpi;
import com.levigo.jbig2.JBIG2ReadParam;
import com.levigo.jbig2.io.DefaultInputStreamFactory;

import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;

public class JBig2Image {
    private String filepath;
    private int imageIndex;

    public JBig2Image() {
        this.filepath = "/home/blackadmin/Desktop/pdf/demo18.jbig2";
        this.imageIndex = 0;
        extractImgFromPdf();
        convertJBig2ToJpeg();
        createPDF();
    }

    private void extractImgFromPdf() {
        try {
            /////////// Extract all Images from pdf /////////////////////////

            PdfReader reader = new PdfReader("/home/blackadmin/Desktop/pdf/orig.pdf");
            PdfReaderContentParser parser = new PdfReaderContentParser(reader);
            MyImageRenderListener listener = new MyImageRenderListener("/home/blackadmin/Desktop/pdf/demo%s.%s");
            for (int i = 1; i <= reader.getNumberOfPages(); i++) {
                parser.processContent(i, listener);
            }
        } catch (IOException ex) {
            System.out.println(ex);
        }
    }

    private void convertJBig2ToJpeg() {
        InputStream inputStream = null;
        try {
            ///////// Read jbig2 image ////////////////////////////////////////
            inputStream = new FileInputStream(new File(filepath));
            DefaultInputStreamFactory disf = new DefaultInputStreamFactory();
            ImageInputStream imageInputStream = disf.getInputStream(inputStream);
            JBIG2ImageReader imageReader = new JBIG2ImageReader(new JBIG2ImageReaderSpi());
            imageReader.setInput(imageInputStream);
            JBIG2ReadParam param = imageReader.getDefaultReadParam();
            BufferedImage bufferedImage = imageReader.read(imageIndex, param);

            ////////// jbig2 to jpeg ///////////////////////////////////////////
            ImageIO.write(bufferedImage, "jpeg", new File("/home/blackadmin/Desktop/pdf/demo18.jpeg"));
        } catch (IOException ex) {
            System.out.println(ex);
        } finally {
            try {
                inputStream.close();
            } catch (IOException ex) {
                System.out.println(ex);
            }
        }
    }

    public void createPDF() {
        Document document = new Document();
        try {
            PdfWriter.getInstance(document,
                    new FileOutputStream("/home/blackadmin/Desktop/pdf/output.pdf"));
            document.open();
            PdfPTable table = new PdfPTable(1); //1 column.    
            Image image = Image.getInstance("/home/blackadmin/Desktop/pdf/demo18.jpeg");
            image.scaleToFit(800f, 600f);
            image.scaleAbsolute(800f, 600f);   // Give the size of image you want to print on pdf
            PdfPCell nestedImgCell = new PdfPCell(image);
            table.addCell(nestedImgCell);
            document.add(table);
            document.close();
            System.out.println(
                    "======== PDF Created Successfully =========");
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    public static void main(String[] args) throws IOException {
        new JBig2Image();
    }
}



class MyImageRenderListener implements RenderListener {

    /**
     * The new document to which we've added a border rectangle.
     */
    protected String path = "";

    /**
     * Creates a RenderListener that will look for images.
     */
    public MyImageRenderListener(String path) {
        this.path = path;
    }

    /**
     * @see com.itextpdf.text.pdf.parser.RenderListener#beginTextBlock()
     */
    public void beginTextBlock() {
    }

    /**
     * @see com.itextpdf.text.pdf.parser.RenderListener#endTextBlock()
     */
    public void endTextBlock() {
    }

    /**
     * @see com.itextpdf.text.pdf.parser.RenderListener#renderImage(
     * com.itextpdf.text.pdf.parser.ImageRenderInfo)
     */
    public void renderImage(ImageRenderInfo renderInfo) {
        try {
            String filename;
            FileOutputStream os;
            PdfImageObject image = renderInfo.getImage();
            if (image == null) {
                return;
            }
            filename = String.format(path, renderInfo.getRef().getNumber(), image.getFileType());
            os = new FileOutputStream(filename);
            os.write(image.getImageAsBytes());
            os.flush();
            os.close();
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * @see com.itextpdf.text.pdf.parser.RenderListener#renderText(
     * com.itextpdf.text.pdf.parser.TextRenderInfo)
     */
    public void renderText(TextRenderInfo renderInfo) {
    }
}

References :
1 ) Extracting jbig2 from pdf (extract images) (MyImageRenderListener).
2 ) Converting jbig2 (JBIG2ImageReaderDemo)

Smackdab answered 30/8, 2012 at 18:12 Comment(1)
I used Ghost4J as problem solution.But I think your answer is work, and it more optimized way to solve problem (in opposition to Ghost4J, which use native code). Sorry for overdue bounty worthProtero
D
1

How about using AcrobatReader itself? It's a bit muddy getting it to work, and not a robust solution I guess. But will probably print all of it perfectly. And be free

Some info about this route;

http://vineetreynolds.blogspot.nl/2005/12/silent-print-pdf-print-pdf.html http://www.codeproject.com/Questions/98586/Programmatically-print-PDF-documents http://forums.adobe.com/message/2336723

Dragone answered 23/8, 2012 at 15:59 Comment(0)
R
1

As an alternative, you could try doing this server-side:

Approach 1:
Convert the PDF files to raster images using an external application and print that instead.

Approach 2:
Adjust your PDF files by recompressing JBIG2 images:

1- Extracting the images compressed as JBIG2 from your files.

2- Re-compress them with some other algorithm (jpeg, png, etc). In order to do this you might need to go outside of Java using either JNI or calling an external application. You can try with jbig2dec or ImageMagic for example if the GPL lincense suits your needs.

3- Put the recompressed images back in your PDF.


This approach will imply some quality loss on those images, but at least you will be able to print the files.

You can do this in Java with iText, there is a chapter about resizing images in the book iText in Action (with sample code). The idea there is to extract the image, resize it (including recompression) and put it back. You can use this as starting point. Be aware that iText is an AGPL project, hence you cannot use it for free in commercial closed-source applications.

If you are using a Windows-based server and you can afford a commercial tool, you can also achieve this with Amyuni PDF Creator either with C#/VB.Net or C++ (Usual disclaimer applies for this suggestion). You just need to go though all objects of type acObjectTypePicture and set the attribute Compression to acJPegHigh, this approach does not require any external JBIG2 decoder, (I can include some sample code here if you are interested).

If you are using an applet just to print your PDF files, you could also try generating a PDF file that shows the print dialog when opened

Renitarenitent answered 24/8, 2012 at 14:4 Comment(3)
It's idea, but what library I can use to extract images and (more importantly) put them back in PDF? thanks.Protero
but I still need to decode image (from JBIG2), and after decoding I can to re-compress it with another algorithm (jpeg, png, etc)..Protero
You will need to do that part outside of java, either with JNI using a native library or by calling an external applicationRenitarenitent
R
1

You have tools as ImageMagick which handle images and convert them to a lot of formats. I used it some years ago so I can't tell you if the jbig2 format is properly handled by default or if you have to install some plugin. You can try the following to have a list of supported formats beginning with J like the JBIG2 you are searching for:

$ convert -list format | grep -i J

It is really obvious to convert to pdf with with tool too, coupled with gs tool aka GhostScript.

If fact nothing prevent you to display a PNG/JPEG version of the image and provide a download link to the original JBIG2 file with its own metadatas.

Rosenblast answered 27/8, 2012 at 15:44 Comment(0)
I
1

There is a fork of the JPedal library by Borisvl located at

https://github.com/Borisvl/JBIG2-Image-Decoder#readme

which contains speed improvements and I believe it should also fix your bug.

EDIT : The bug is related to simple range checking. Basically you need to prevent GetPixel from accessing x,y values outside of the bitmap extents.

You need to make sure the following conditions are met before calling getPixel

col >= 0 and col < bitmap.width row >= 0 and row < bitmap.height

Here is some Delphi code with a couple of small range checks. I cannot test the Java code myself but you need to make changes to src/org/jpedal/jbig2/image/JBIG2Bitmap.java

procedure TJBIG2Bitmap.combine(bitmap: TJBIG2Bitmap; x, y: Integer; combOp: Int64);
...
...
var
begin
  srcWidth := bitmap.width;
  srcHeight := bitmap.height;
  srcRow := 0;
  srcCol := 0;

  if (x < 0) then x := 0;
  if (y < 0) then y := 0;

  for row := y to Min(y + srcHeight - 1, Self.height - 1) do   // <<<<<<<<  HERE
  begin
    for col := x to x + srcWidth - 1 do
    begin
      srcPixel := bitmap.getPixel(srcCol, srcRow);

Andrew.

Instrumentation answered 7/9, 2012 at 10:40 Comment(1)
I am able to render your PDF correctly with my Delhpi port of the JPedal code. I have posted some code from my port that will point you in the right direction.Instrumentation

© 2022 - 2024 — McMap. All rights reserved.