android pdf框架-5,生成pdf

前面的文章都是如何展示pdf,这篇关于如何生成pdf文件.

使用图片生成pdf

使用mupdf生成

对于图片,生成pdf时,如果没有考虑内存的问题就比较容易了,如果考虑到内存可能会溢出,那么只能对图片切割处理,然后再生成页面.

切割后可以尽量以大的区域去展示pdf.

定义一个方法

fun createPdfFromImages(pdfPath: String?, imagePaths: List<String>): Boolean

pdf存储的路径,与图片的路径,要支持多张图片生成pdf

假如生成的页面高宽值是8.3 * 72 * 2与11.7 * 72 * 2,就是8*11英寸的页面.好像是a2

那么生成页面时要处理图片大于这个高宽的情况.

以目前手机普遍在1080*1920的分辨率之上,所以我定义了最大的高为2160.当图片的高大于这个高度,比如微博里面有好多图片是长图,那么就要进行切割.

复制代码
var mDocument: PDFDocument? = null
        try {
            mDocument = PDFDocument.openDocument(pdfPath) as PDFDocument
        } catch (e: Exception) {
            Log.d("TAG", "could not open:$pdfPath")
        }
        if (mDocument == null) {
            mDocument = PDFDocument()
        }

pdf的路径可以是新的,可以是旧的,所以先读取,如果没有读取到,可能是新的地址.如果是旧的地址,图片生成新的部分是追加到旧的后面的.

复制代码
val resultPaths = processLargeImage(imagePaths)

        //空白页面必须是-1,否则会崩溃,但插入-1的位置的页面会成为最后一个,所以追加的时候就全部用-1就行了.
        var index = -1
        for (path in resultPaths) {
            val page = addPage(path, mDocument, index++)

            mDocument.insertPage(-1, page)
        }
        mDocument.save(pdfPath, OPTS);
private const val OPTS = "compress-images;compress;incremental;linearize;pretty;compress-fonts"

大体的流程就是这样的.剩下的就是处理图片切割的方式了.

图片切割
复制代码
private fun processLargeImage(imagePaths: List<String>): List<String> {
        val options = BitmapFactory.Options()
        //默认值为false,如果设置成true,那么在解码的时候就不会返回bitmap,即bitmap = null。
        options.inJustDecodeBounds = true
        val maxHeight = PAPER_HEIGHT

        val result = arrayListOf<String>()
        for (path in imagePaths) {
            try {
                BitmapFactory.decodeFile(path, options)
                if (options.outHeight > maxHeight) {
                    //split image,maxheight=PAPER_HEIGHT
                    splitImages(result, path, options.outWidth, options.outHeight)
                } else {
                    //result.add(path)
                    val bitmapPath = compressImageFitPage(path, options.outWidth, options.outHeight)
                    result.add(bitmapPath)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
        return result
    }

这里处理了两种,一种是大于高的图片,肯定切割,另一种是不大于高,但图片可能比较大,比如正方形的1400*1400的这种,如果直接使用它的高宽,生成的pdf会比较大,所以进行了屏幕如1080宽的适配操作.生成的pdf就不那么大了.

splitImages切割就是从图片顶点开始,按高度切割,然后保存在临时目录里面.

复制代码
var top = 0f
        val right = 0 + width
        var bottom = PAPER_HEIGHT

        while (bottom < height) {
            val rect = android.graphics.Rect()
            rect.set(0, top.toInt(), right, bottom.toInt())
            splitImage(path, rect, result)

            top = bottom
            bottom += PAPER_HEIGHT
        }
        if (top < height) {
            val rect = android.graphics.Rect()
            rect.set(0, top.toInt(), right, height)
            splitImage(path, rect, result)
        }

切割图片后,与不切割的图片一样,做一次宽度适配

复制代码
val mDecoder = BitmapRegionDecoder.newInstance(path, true)
        val bm: Bitmap = mDecoder.decodeRegion(rect, null)
        val file =
            File(
                PDFUtils.getExternalCacheDir(App.instance).path
                        //FileUtils.getStorageDirPath() + "/amupdf"
                        + File.separator + "create" + File.separator + System.currentTimeMillis() + ".jpg"
            )
        PDFUtils.saveBitmapToFile(bm, file, Bitmap.CompressFormat.JPEG, 100)
        Log.d("TAG", "new file:height:${rect.bottom - rect.top}, path:${file.absolutePath}")

        //result.add(file.absolutePath)
        //splitPaths.add(file)
        val bitmapPath = compressImageFitPage(file.absolutePath, bm.width, bm.height)
        result.add(bitmapPath)

这是宽度处理

复制代码
private fun compressImageFitPage(
        path: String,
        width: Int,
        height: Int,
    ): String {
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = false
        options.outWidth = PDF_PAGE_WIDTH.toInt()
        options.outHeight = (height * PDF_PAGE_WIDTH / width).toInt()
        val bitmap = BitmapFactory.decodeFile(path, options)
        val file =
            File(
                PDFUtils.getExternalCacheDir(App.instance).path
                        + File.separator + "create" + File.separator + System.currentTimeMillis() + ".jpg"
            )
        PDFUtils.saveBitmapToFile(bitmap, file, Bitmap.CompressFormat.JPEG, 100)
        Log.d(
            "TAG",
            "bitmap.width:$width, height:$height,:${options.outWidth},${options.outHeight}, path:${file.absolutePath}"
        )
        return file.absolutePath
    }

到这,切割就完成了,图片的高宽处理也完成了.剩下的就是将这些图片添加到页面上.

添加页面addPage
复制代码
private fun addPage(
        path: String,
        mDocument: PDFDocument,
        index: Int
    ): PDFObject? {
        val image = Image(path)
        val resources = mDocument.newDictionary()
        val xobj = mDocument.newDictionary()
        val obj = mDocument.addImage(image)
        xobj.put("I", obj)
        resources.put("XObject", xobj)

        val w = image.width
        val h = image.height
        val mediabox = Rect(0f, 0f, w.toFloat(), h.toFloat())
        val contents = "q $w 0 0 $h 0 0 cm /I Do Q\n"
        val page = mDocument.addPage(mediabox, 0, resources, contents)
        Log.d("TAG", String.format("index:%s,page,%s", index, contents))
        return page
    }

mupdf的语法都是类似的,js,c也都是这个规则.创建image对象.

contents要注意的点,这里设置了高宽,如果要添加内容,就要额外的操作了.否则这段应该是固定的.

在添加页面时,如果是-1,表示追加,追加时如果是空的文档自然就放到第一页了.

相关推荐
踢球的打工仔35 分钟前
PHP面向对象(7)
android·开发语言·php
安卓理事人40 分钟前
安卓socket
android
安卓理事人6 小时前
安卓LinkedBlockingQueue消息队列
android
万能的小裴同学8 小时前
Android M3U8视频播放器
android·音视频
q***57748 小时前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober9 小时前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
城东米粉儿9 小时前
关于ObjectAnimator
android
zhangphil10 小时前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
火柴就是我11 小时前
从头写一个自己的app
android·前端·flutter
lichong95112 小时前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端