在Android上打印现有的PDF文件

8

大家好,我正在尝试在Android上构建一个小型示例打印应用程序,但似乎无法打印现有的PDF文件。有许多关于使用画布创建自定义文档的文档,但我的文档已经存在。基本上,我只想读取一个PDF文档,并将其作为文件输出流直接发送到打印机进行打印。任何帮助都将不胜感激。


请查看以下链接。 https://dev59.com/7WUo5IYBdhLWcg3w3ymi https://dev59.com/b-o6XIcBkEYKwwoYORwV - Mocanu Bogdan
4个回答

22
我们可以通过创建一个自定义的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);
        }
    }
}}
现在通过使用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);
    }

那非常有帮助。 - Vahid Akbari
@KostyaM 我尝试了这段代码,我得到了正确页数的打印界面,但无法在页面上呈现文档。可能出了什么问题? - Aalap Patel
除了注释无法打印到纸上之外,其他都很好。有什么解决办法吗? - Du Huynh
谢谢你,除此之外,你是否在共享首选项中保存了上下文? :-O - martinseal1987
1
一切都运行良好,但点击“保存PDF”时,它会以0字节的大小保存。 - Girish

9
基本上,我只想阅读 PDF 文档并将其作为文件输出流直接发送到打印机进行打印。除非您找到某些为 Android 提供此类 API 的特定打印机,否则这是不严格可能的。
如果您希望使用 Android 打印框架,则需要创建一个可以处理现有 PDF 文件的 PrintDocumentAdapter。此示例项目演示了一个这样的 PrintDocumentAdapter,但它不是通用型的。(参考链接1) (参考链接2)

谢谢,这个示例应用程序非常好用,只是我有点迷惑。我一直在按照Android开发人员的示例操作,并且在onWrite方法中像这样写: PdfDocument.PageInfo newPage = new PdfDocument.PageInfo.Builder(pageWidth, pageHeight, i).create(); 但看起来你只是打开此PDF文件流in=ctxt.getAssets().open("cover.pdf"); 这可以更改为从文件系统获取PDF吗? - Doug Ray
@DougRay:在我的书中,解释PdfDocumentAdapter所涉及的所有内容需要大约八页,因此这对于Stack Overflow的回答来说太长了。简而言之,ThreadedPrintDocumentAdapteronLayout()onWrite()调用捆绑到LayoutJobWriteJob对象中,并将它们放入工作队列以供后台线程使用。PdfDocumentAdapter创建自定义作业子类,编写一个罐装的PDF文件(来自assets/)。 - CommonsWare
哈哈,好的,非常感谢你的帮助,我会再仔细研究一下! - Doug Ray
1
@DougRay:“能否将其更改为从文件系统中获取PDF?”--原则上,这应该可以正常工作。将 in=ctxt.getAssets().open("cover.pdf"); 更改为使用 FileInputStream 的内容即可。 - CommonsWare
1
据我所知,很抱歉那是不可能的。 - CommonsWare
显示剩余6条评论

4

对于那些对Karthik Bollisetti的答案的kotlin版本感兴趣的人,这是它。

PdfDocumentAdapter被重写为:

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)
    }
}
}

然后在您的代码中像这样调用PrintManager
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)
}

0

对于支持ipp和pdf的打印机使用情况,您可以使用我的库https://github.com/gmuth/ipp-client-kotlin直接将pdf发送到打印机:

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

不涉及打印对话框。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接