Printing a PDF in Java With PDFRenderer

Printing is not an easy task.  It is easy to get tied into intricate details of environment specific configuration settings such as margins and paper size. Therefore, printing a PDF without a library is not recommended unless you have a lot of time or are really interested in that sort of thing.  There are two libraries that can perform this task already.   One will be exampled today, and another tomorrow.

Today’s library is PDFRenderer https://pdf-renderer.dev.java.net/.   This library was released by Sun a couple of years ago.   Note that this example does not map a FileChannel object as some examples do.  Using a FileChannel made the file not able to be deleted until garbage collection was run.  See the bug in the code comments for more details.

try
{
File f = null;
RandomAccessFile fis = null;
FileChannel fc = null;
ByteBuffer bb = null;
String printer = YOUR_PRINTER_NAME;
PrintService printService = PrintHelper.getPrintService(printer);

f = YOUR_PDF_FILE;
//Read only access would work too
fis = new RandomAccessFile(f, "rw");
fc = fis.getChannel();
bb = ByteBuffer.allocate((int)fc.size());
fc.read(bb);


//Do not map the file to a ByteBuffer as the examples show.
// There is a reason why in java bug #474038
// http://bugs.sun.com/view_bug.do?bug_id=4724038
//fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size());
//bb = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size());

PDFFile pdfFile = new PDFFile(bb); // Create PDF Print Page
PDFPrintPage pages = new PDFPrintPage(pdfFile);
// Create Print Job
PrinterJob pjob = PrinterJob.getPrinterJob();
pjob.setPrintService(printService);

PageFormat pf = PrinterJob.getPrinterJob().defaultPage();

pf.setOrientation(PageFormat.PORTRAIT);

Paper paper = new Paper();

//This is to fix an error in PDF-Renderer
//View http://juixe.com/techknow/index.php/2008/01/17/print-a-pdf-document-in-java/ for details
//Printing a PDF is also possible by sending the bytes directly to the printer, but
//  the printer would have to support it.

paper.setImageableArea(0,0,paper.getWidth() * 2,paper.getHeight());

pf.setPaper(paper);

pjob.setJobName(f.getName());

Book book = new Book();
book.append(pages, pf, pdfFile.getNumPages());
pjob.setPageable(book);
pjob.print();

}
catch (FileNotFoundException e)
{
//do your error action
}
catch (IOException e)
{
//do your error action
}
catch (PrinterException e)
{
//do your error action
}
finally
{
try
{
if (fc != null)
{
fc.close();
fc = null;
}
}
catch (IOException e)
{
log.error(e);
//handle error here
}
try
{
if (fis != null)
{
fis.close();
fis = null;
}
}
catch (IOException e)
{
//handle error here
}
if (bb != null)
{
bb.clear();
}
}

Because this library has the bug that makes it not want to print in half size when printing a portrait-oriented PDF,  I cannot recommend using this library unless you find it is the only thing that will do the job.    Such obvious bugs that have been around for over a year point to unmaintained code or some other upstream issues.  Tune in tomorrow for another library that is maintained that will get the job done as well.

Related posts:

Tags: , ,

  • Ed Malone

    I have used the above code and I’m trying to insert a 1/2 inch margin onto my page however the setImageableArea() function doesn’t work. Have you successfully implemented a margin on your printout using this method?

    thanks,
    Ed.

    • lyz

      In my case, the file that was being loaded already had the margins set and was set for the same size as the paper to be printed on. The setImageableArea() method on the Paper object seemed to have more of a scaling effect for the file I was working with then providing a margin. Perhaps using setImageableArea() on the PageFormat object will do what you want.

  • Leila Holmann

    Have you heard about jPDFPrint? We have developed our rendering code in the last 10 years and PDF documents are not easy to render, the PDF format specifications are over 1000 pages!

    Here is a sample code to print using our library:

    import com.qoppa.pdfPrint.PDFPrint;

    public class SimplePrint
    {
    public static void main (String [] args)
    {
    try
    {
    PDFPrint pdfPrint = new PDFPrint(“input.pdf”, null);
    pdfPrint.print (null);
    }
    catch (Throwable t)
    {
    t.printStackTrace();
    }
    }

    }

  • miguel

    I use your code and works fine but sometimes I obtain the following error:

    Problem in WPrinterJob_drawDIBImage
    at sun.awt.windows.WPrinterJob.drawDIBImage(Native Method)
    at sun.awt.windows.WPrinterJob.drawDIBImage(WPrinterJob.java:1170)
    at sun.awt.windows.WPathGraphics.drawImageToPlatform(WPathGraphics.java:1091)
    at sun.print.PathGraphics.drawImage(PathGraphics.java:1701)
    at com.sun.pdfview.PDFRenderer.drawImage(PDFRenderer.java:286)
    at com.sun.pdfview.PDFImageCmd.execute(PDFPage.java:643)
    at com.sun.pdfview.PDFRenderer.iterate(PDFRenderer.java:570)
    at com.sun.pdfview.BaseWatchable.run(BaseWatchable.java:101)
    at com.sun.pdfview.PDFPrintPage.print(PDFPrintPage.java:232)
    at sun.print.RasterPrinterJob.printPage(RasterPrinterJob.java:1964)
    at sun.print.RasterPrinterJob.print(RasterPrinterJob.java:1431)
    at sun.print.RasterPrinterJob.print(RasterPrinterJob.java:1247)
    at DetalleHojaBean.imprimir(DetalleHojaBean.java:1285)

  • Diya

    Need help with my PDFRenderer its not working, show the following error – “Call requires API level 21 (current min is 19): new android.graphics.pdf.PdfRenderer”

    CODE –

    package com.example.andro_pdf_two;

    import java.io.File;
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.Matrix;
    import android.graphics.Rect;
    import android.graphics.pdf.PdfRenderer;
    import android.os.Bundle;
    import android.os.ParcelFileDescriptor;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    import android.widget.ImageView;

    public class MainActivity extends Activity {

    private ImageView imageView;
    private int currentPage = 0;
    private Button next, previous;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    next = (Button) findViewById(R.id.next);
    next.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
    currentPage++;
    render();
    }
    });

    previous = (Button) findViewById(R.id.previous);
    previous.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
    currentPage–;
    render();
    }
    });
    render();
    }

    private void render() {
    try {
    Log.e(“In render()”,”render”);
    imageView = (ImageView) findViewById(R.id.imageView);
    int REQ_WIDTH = 1;
    int REQ_HEIGHT = 1;
    REQ_WIDTH = imageView.getWidth();
    REQ_HEIGHT = imageView.getHeight();
    Log.e(“In render2()”,”render2″);
    System.out.println(“req_height”+REQ_HEIGHT+” “+”REQ_WIDTH”+REQ_WIDTH);

    Bitmap bitmap = Bitmap.createBitmap(REQ_WIDTH, REQ_HEIGHT, Bitmap.Config.ARGB_4444);

    File file = new File(“/home/woigames/Desktop/PDF/DebitCards-tc.pdf”);

    PdfRenderer renderer = new PdfRenderer(ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));

    if(renderer != null)
    {
    Log.i(“renderer not null”,”check”);
    }
    else
    {
    Log.i(“renderer is null”,”check”);
    }
    final int pageCount = renderer.getPageCount();

    if (currentPage renderer.getPageCount()) {
    currentPage = renderer.getPageCount() – 1;
    }

    Matrix m = imageView.getImageMatrix();
    Rect rect = new Rect(0, 0, REQ_WIDTH, REQ_HEIGHT);
    renderer.openPage(currentPage).render(bitmap, rect, m, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);

    imageView.setImageMatrix(m);
    imageView.setImageBitmap(bitmap);
    imageView.invalidate();
    renderer.close();

    } catch (Exception e) {
    e.printStackTrace();
    }

    }
    }