8

Hey everyone so I am trying to build a small sample printing app on android and can't seem to print an existing pdf. There is plenty of documentation on creating a custom document with the canvas but I already have the document. Basically I just want to be a able to read in a pdf document and send it as a file output stream directly to the printer to be printed. Any help is appreciated.

Doug Ray
  • 988
  • 2
  • 9
  • 29
  • Check out those links. http://stackoverflow.com/questions/15701465/how-to-print-from-the-thermal-printer-in-android http://stackoverflow.com/questions/14530058/how-can-i-print-an-image-on-a-bluetooth-printer-in-android – Mocanu Bogdan Oct 12 '15 at 20:42

4 Answers4

21

We can simply achieve this by creating a custom PrintDocumentAdapter

PdfDocumentAdapter.java

public class PdfDocumentAdapter extends PrintDocumentAdapter {

Context context = null;
String pathName = "";
public PdfDocumentAdapter(Context ctxt, String pathName) {
    context = ctxt;
    this.pathName = pathName;
}
@Override
public void onLayout(PrintAttributes printAttributes, PrintAttributes printAttributes1, CancellationSignal cancellationSignal, LayoutResultCallback layoutResultCallback, Bundle bundle) {
    if (cancellationSignal.isCanceled()) {
        layoutResultCallback.onLayoutCancelled();
    }
    else {
        PrintDocumentInfo.Builder builder=
                new PrintDocumentInfo.Builder(" file name");
        builder.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
                .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
                .build();
        layoutResultCallback.onLayoutFinished(builder.build(),
                !printAttributes1.equals(printAttributes));
    }
}

@Override
public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor parcelFileDescriptor, CancellationSignal cancellationSignal, WriteResultCallback writeResultCallback) {
    InputStream in=null;
    OutputStream out=null;
    try {
        File file = new File(pathName);
        in = new FileInputStream(file);
        out=new FileOutputStream(parcelFileDescriptor.getFileDescriptor());

        byte[] buf=new byte[16384];
        int size;

        while ((size=in.read(buf)) >= 0
                && !cancellationSignal.isCanceled()) {
            out.write(buf, 0, size);
        }

        if (cancellationSignal.isCanceled()) {
            writeResultCallback.onWriteCancelled();
        }
        else {
            writeResultCallback.onWriteFinished(new PageRange[] { PageRange.ALL_PAGES });
        }
    }
    catch (Exception e) {
        writeResultCallback.onWriteFailed(e.getMessage());
        Logger.logError( e);
    }
    finally {
        try {
            in.close();
            out.close();
        }
        catch (IOException e) {
            Logger.logError( e);
        }
    }
}}

Now call print by using PrintManager

        PrintManager printManager=(PrintManager) getActivityContext().getSystemService(Context.PRINT_SERVICE);
    try
    {
        PrintDocumentAdapter printAdapter = new PdfDocumentAdapter(Settings.sharedPref.context,filePath );
        }
        printManager.print("Document", printAdapter,new PrintAttributes.Builder().build());
    }
    catch (Exception e)
    {
        Logger.logError(e);
    }
9

Basically I just want to be a able to read in a pdf document and send it as a file output stream directly to the printer to be printed.

That's not strictly possible, unless you find some particular printer that offers such an API for Android.

If you wish to use the Android printing framework, you will need to create a PrintDocumentAdapter that can handle your existing PDF file. This sample project demonstrates one such PrintDocumentAdapter, though it is not general-purpose.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks the sample app works great, I am a little lost on how though. I was following the example from android developers and had something like this in the onWrite method PdfDocument.PageInfo newPage = new PdfDocument.PageInfo.Builder(pageWidth, pageHeight, i).create(); but it looks like your just opening this pdf from a file stream with in=ctxt.getAssets().open("cover.pdf"); Could this be changed to to grab a pdf from the file system ? – Doug Ray Oct 12 '15 at 22:13
  • @DougRay: Explaining everything involved in my `PdfDocumentAdapter` takes about eight pages in [my book](https://commonsware.com/Android), and so it's too long for a Stack Overflow answer. In a nutshell, `ThreadedPrintDocumentAdapter` bundles up `onLayout()` and `onWrite()` calls into `LayoutJob` and `WriteJob` objects and puts them on a work queue for a background thread. `PdfDocumentAdapter` creates custom job subclasses that writes a canned PDF file (from `assets/`). – CommonsWare Oct 12 '15 at 22:17
  • @_@ haha well thanks for all the help I will check it out some more ! – Doug Ray Oct 12 '15 at 22:20
  • 1
    @DougRay: "Could this be changed to to grab a pdf from the file system ?" -- in principle, that should work fine. Change `in=ctxt.getAssets().open("cover.pdf");` to something that uses a `FileInputStream`. – CommonsWare Oct 12 '15 at 22:23
  • I love you @CommonsWare, you helped me in this situation. – Vishal Rabadiya Aug 18 '17 at 12:54
  • Hi @CommonsWare i can see in above comment lots fans anyways i checked your example but i'm not quite sure i want to change my all coding part but you have very good knowledge in this topic so can you help me to solve my problem here https://stackoverflow.com/questions/54870164/convert-html-into-pdf-using-webview-can-not-created-multiple-pages – Rucha Bhatt Joshi Feb 28 '19 at 14:53
  • Please use permanent links when linking to GitHub. Correct link to PrintDocumentAdapter: https://github.com/commonsguy/cw-omnibus/blob/f2ffeb687d002f4a41b52a6ef5bb2580eb6a4ed6/Printing/PrintManager/app/src/main/java/com/commonsware/android/print/PdfDocumentAdapter.java – TomTasche May 23 '19 at 10:58
  • @TomTasche: That is not a good link either. I have updated the answer with permanent links. – CommonsWare May 23 '19 at 11:01
  • The link I provided is in fact a permanent link - linking to a specific git commit. Your link points to a git tag, which is of course fine too. Thanks for updating your answer! – TomTasche May 23 '19 at 12:16
  • Is it possible to print pdf with no dialog? Just print to default settings.. – iz25 Feb 06 '20 at 09:49
  • 1
    @iz25: AFAIK, that is not possible, sorry. – CommonsWare Feb 06 '20 at 11:58
4

For those interested in the kotlin version of the Karthik Bollisetti answer here is it.

The PdfDocumentAdapter is re-written as this

class PdfDocumentAdapter(private val pathName: String) : PrintDocumentAdapter() {

override fun onLayout(
    oldAttributes: PrintAttributes?,
    newAttributes: PrintAttributes,
    cancellationSignal: CancellationSignal?,
    callback: LayoutResultCallback,
    bundle: Bundle
) {
    if (cancellationSignal?.isCanceled == true) {
        callback.onLayoutCancelled()
        return
    } else {
        val builder = PrintDocumentInfo.Builder(" file name")
        builder.setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
            .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
            .build()

        callback.onLayoutFinished(builder.build(), newAttributes == oldAttributes)
    }
}

override fun onWrite(
    pageRanges: Array<out PageRange>,
    destination: ParcelFileDescriptor,
    cancellationSignal: CancellationSignal?,
    callback: WriteResultCallback
) {
    try {
        // copy file from the input stream to the output stream
        FileInputStream(File(pathName)).use { inStream ->
            FileOutputStream(destination.fileDescriptor).use { outStream ->
                inStream.copyTo(outStream)
            }
        }

        if (cancellationSignal?.isCanceled == true) {
            callback.onWriteCancelled()
        } else {
            callback.onWriteFinished(arrayOf(PageRange.ALL_PAGES))
        }

    } catch (e: Exception) {
        callback.onWriteFailed(e.message)
    }
}
}

then call the PrintManager in your code like this

val printManager : PrintManager = requireContext().getSystemService(Context.PRINT_SERVICE) as PrintManager
try {
    val printAdapter = PdfDocumentAdapter(file.absolutePath)
    printManager.print("Document", printAdapter, PrintAttributes.Builder().build())
} catch (e : Exception) {
    Timber.e(e)
}
ilatyphi95
  • 555
  • 5
  • 22
0

For use cases with printers supporting ipp and pdf you can send pdfs straight to printers using my library https://github.com/gmuth/ipp-client-kotlin:

Kotlin:
val printer = IppPrinter(URI.create("ipp://myprinter/ipp"))
val job = printer.printJob(File("mydocument.pdf"))
job.waitForTermination()

No print dialog is involved.

IPP Nerd
  • 992
  • 9
  • 25