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的学习,建议多看看源码及注释,代码量不大,加注释也就四百来行。