前文介绍如何使用图片生成pdf,这里介绍如何使用文本生成pdf
使用mupdf生成
mupdf生成的pdf略大,字体可以自定义.
生成的代码不复杂,也有好几种,以story的方式生成为例
fun createPdfFromText(sourcePath: String, destPath: String): Boolean {
val text = EncodingDetect.readFile(sourcePath)
val mediabox = Rect(0f, 0f, 500f, 707f) //A2
val margin = 10f
var writer = DocumentWriter(destPath, "PDF", "")
var snark = "<!DOCTYPE html>" +
"<style>" +
"#body { font-family: \"Droid Sans\", sans-serif; }" +
"</style>" +
"<body>" +
text +
"</body></html>"
val story = Story(snark, "", 12f)
var more: Boolean
do {
val filled = Rect()
val where = Rect(
mediabox.x0 + margin,
mediabox.y0 + margin,
mediabox.x1 - margin,
mediabox.y1 - margin
)
val dev: Device = writer.beginPage(mediabox)
more = story.place(where, filled)
story.draw(dev, Matrix.Identity())
writer.endPage()
} while (more)
writer.close()
writer.destroy()
story.destroy()
return true
}
文档的读取,对于中文,可能会涉及到编码的处理.
先定义mediabox,就是纸张大小,这里是a2
然后用html生成story,最后写入就完成了
如果要自定义样式,颜色这些,api要去查如何使用.并不那么方便.
使用系统sdk生成
系统sdk是基于福昕的代码.但生成时可以使用view来设置样式,然后生成页面.样式比较容易控制,毕竟view长什么样是容易修改的.
读取文本是一样的.
val pdfDocument = PdfDocument()
val pageWidth = PDF_PAGE_WIDTH.toInt()
val pageHeight = PDF_PAGE_HEIGHT.toInt()
val contentView =
LayoutInflater.from(context).inflate(R.layout.pdf_content, parent, false) as TextView
contentView.text = content
val measureWidth = View.MeasureSpec.makeMeasureSpec(pageWidth, View.MeasureSpec.EXACTLY)
val measuredHeight = View.MeasureSpec.makeMeasureSpec(pageHeight, View.MeasureSpec.EXACTLY)
contentView.measure(measureWidth, measuredHeight)
contentView.layout(0, 0, pageWidth, pageHeight)
建立文档对象,然后加载布局
val lineCount = contentView.lineCount
var lineHeight = contentView.lineHeight
if (contentView.lineSpacingMultiplier > 0) {
lineHeight = (lineHeight * contentView.lineSpacingMultiplier).toInt()
}
val layout = contentView.layout
var start = 0
var end: Int
var pageH = 0
val paddingTopAndBottom: Int = Utils.dipToPixel(context, 40f)
设置边距40dp,读取行间距与行数.
因为设置了内容后,由textview自己就能计算出这些,然后根据这些信息,计算我们一页的高宽可以分成几页,然后每一页再用一个布局去生成就行了.
while (i < lineCount) {
end = layout.getLineEnd(i)
val line = content.substring(start, end) //指定行的内容
start = end
sb.append(line)
pageH += lineHeight
if (pageH >= pageHeight - paddingTopAndBottom) {
Log.d(
"TextView",
String.format(
"============page line:%s,lh:%s,ph:%s==========",
i,
lineHeight,
pageHeight
)
)
createTxtPage(
context,
parent,
pdfDocument,
pageWidth,
pageHeight,
i + 1,
sb.toString()
)
pageH = 0
sb.setLength(0)
}
i++
}
if (sb.length > 0) {
Log.d("TextView", "last line ===")
createTxtPage(context, parent, pdfDocument, pageWidth, pageHeight, i, sb.toString())
}
这就是计算的过程,最后要再处理剩下的部分.
对于每一页的显示字符数处理完,接着就是对这些生成页面
private fun createTxtPage(
context: Context?,
parent: ViewGroup?,
pdfDocument: PdfDocument,
pageWidth: Int,
pageHeight: Int,
pageNo: Int,
content: String?
) {
val contentView =
LayoutInflater.from(context).inflate(R.layout.pdf_content, parent, false) as TextView
contentView.text = content
val pageInfo: PdfDocument.PageInfo =
PdfDocument.PageInfo.Builder(pageWidth, pageHeight, pageNo)
.create()
val page: PdfDocument.Page = pdfDocument.startPage(pageInfo)
val pageCanvas: Canvas = page.getCanvas()
val measureWidth = View.MeasureSpec.makeMeasureSpec(pageWidth, View.MeasureSpec.EXACTLY)
val measuredHeight = View.MeasureSpec.makeMeasureSpec(pageHeight, View.MeasureSpec.EXACTLY)
contentView.measure(measureWidth, measuredHeight)
contentView.layout(0, 0, pageWidth, pageHeight)
contentView.draw(pageCanvas)
// finish the page
pdfDocument.finishPage(page)
}
页面同样用之前的布局,这样保持是一致的,否则高宽计算就不对了.这里因为不需要显示,所以我们要调用measure与layout再draw画出来
保存就简单了
private fun savePdf(path: String?, document: PdfDocument): Boolean {
val outputStream = FileOutputStream(path)
try {
document.writeTo(outputStream)
return true
} catch (e: IOException) {
e.printStackTrace()
} finally {
document.close()
}
return false
}