Android生成pdf文件之PdfDocument及踩过的坑

有时候项目中可能会遇到这样的需求,如何将android中界面显示的内容生成pdf,这里讲述的是使用android原生的PdfDocument,并没有使用框架,其一是使用起来非常的简单,但是也会也到一些坑,下面将一一道来。

首先,先来说下这个类的使用,使用非常的简单,直接参考官方文档或是在源码中都能查看到简单的实例,这里就先看下源码中介绍的简单使用:

 * // create a new document
 * PdfDocument document = new PdfDocument();
 *
 * // crate a page description
 * PageInfo pageInfo = new PageInfo.Builder(new Rect(0, 0, 100, 100), 1).create();
 *
 * // start a page
 * Page page = document.startPage(pageInfo);
 *
 * // draw something on the page
 * View content = getContentView();
 * content.draw(page.getCanvas());
 *
 * // finish the page
 * document.finishPage(page);
 * . . .
 * // add more pages
 * . . .
 * // write the document content
 * document.writeTo(getOutputStream());
 *
 * // close the document
 * document.close();

这是android源码中的介绍(API 27),但是在构建PageInfo的时候却没有上面Builder()这样的构造方法,而是:

PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(pageWidth,pageHeight,pageNumber).create();

这里说下Builder()中这几个参数的作用:

        pageWidth:pdf页面的宽度(文档介绍:The page width in PostScript );

        pageHeight:pdf页面的高度(文档介绍:The page height in PostScript );

        pageNumber:pdf页面的页数,在实际使用过程,发现这个参数并没起到什么作用;

说到这,再来看下我在实际应用中的使用:

    private void pdfModel(){
        PdfDocument document = new PdfDocument();
        // ll_model是一个LinearLayout
        PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(ll_model.getWidth(),ll_model.getHeight(),1).create();
        PdfDocument.Page page = document.startPage(pageInfo);
        ll_model.draw(page.getCanvas());
        document.finishPage(page);
        File file = new File(getPdfFilePath(pdfName));
        FileOutputStream outputStream = new FileOutputStream(file);
        try {
            document.writeTo(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        document.close();
    }

这里就是将一个LinearLayout中的内容生成了pdf,如果LinearLayout中内容很长,可以使用ScrollView在包裹一下。这里生成的pdf只是一页,当文字内容在三百行以内时(根据自己生成的内容而定),生成的pdf显示是没什么问题的,但是内容在多一些时,你就会发现,pdf依旧可以生成,但是查看的时候,pdf中的内容却显示不出来了。

遇到这问题的时候,当时就在想是不是生成一页的pdf高度太高了导致的,所以这时就在想是不是可以分页,但当时我的一个TextView显示的内容可能就有上千行,所以还涉及对这一个view的内容生成的pdf进行分页,结合代码发现,将view中的内容生成pdf文件,实际是会将view中的内容先绘制在PageInfo中的一块画布上(canvas),所以就想着可不可以对这块画布做些处理,跟着这个思路走,就有了对TextView中内容进行分页的做法:

    private void pdfInterviewContent(PdfDocument document){
        // 一页pdf的高度
        int onePageHeight = tv_content.getLineHeight()*30;
        // TextView中总共有多少行
        int lineCount = tv_content.getLineCount();
        // 计算这个TextView需要分成多少页
        int pdfCount = lineCount % 30 == 0 ? lineCount/30 : lineCount/30+1;
        for (int i = 0; i < pdfCount; i++) {
            PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(tv_content.getWidth(),onePageHeight+120,1)
                    .setContentRect(new Rect(0,60,tv_content.getWidth(),onePageHeight+60))
                    .create();
            PdfDocument.Page page = document.startPage(pageInfo);
            Canvas canvas = page.getCanvas();
            canvas.translate(0,-onePageHeight*i);
            tv_content.draw(canvas);
            document.finishPage(page);
        }
        File file = new File(getPdfFilePath(pdfName));
        FileOutputStream outputStream = new FileOutputStream(file);
        try {
            document.writeTo(outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        document.close();
    }

这里在说下构建PageInfo中多的setContentRect()这个方法,这个方法去掉也是不影响生产pdf的,这个方法影响的是生成pdf的显示效果,比如这里的60就是说每一页生成的pdf距离这一页的顶部和底部各是60,如果这里把这个方法去电,那么生成的pdf内容将是顶着页面的顶部和底部的。在构建完PageInfo后,就可以拿到画布(canvas)了,为了生成对应位置的内容,这里就需要对画布进行一个平移操作了,这个平移操作就看你是怎么去划分了,这里是将一个页面的高度作为基本的平移单位。

至此,生成pdf以及分页就全都ok了,不过这里还有一个问题没怎么弄明白,就是生成一页文字的pdf的大小差不多是7M多,但我生成30页文字的pdf也就多了1M左右,目前还不明白这其中原因。

对于PdfDocument的学习,建议多看看源码及注释,代码量不大,加注释也就四百来行。

 

你可能感兴趣的:(Android)