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,表示追加,追加时如果是空的文档自然就放到第一页了.

相关推荐

  1. android pdf框架-5,生成pdf

    2024-03-13 00:30:04       44 阅读
  2. android pdf框架-6,文本生成pdf

    2024-03-13 00:30:04       44 阅读
  3. Puppeteer 生成图片 生成 PDF

    2024-03-13 00:30:04       21 阅读
  4. WKWebView生成PDF

    2024-03-13 00:30:04       34 阅读
  5. js生成pdf

    2024-03-13 00:30:04       105 阅读
  6. qt绘制生成PDF文件

    2024-03-13 00:30:04       46 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-13 00:30:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-13 00:30:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-03-13 00:30:04       87 阅读
  4. Python语言-面向对象

    2024-03-13 00:30:04       96 阅读

热门阅读

  1. 深入理解Nginx日志级别

    2024-03-13 00:30:04       41 阅读
  2. 库表设计基本字段

    2024-03-13 00:30:04       38 阅读
  3. LLM(大语言模型)常用评测指标-MAP@R

    2024-03-13 00:30:04       42 阅读
  4. 使用Docker部署debezium来监控MySQL数据库

    2024-03-13 00:30:04       46 阅读
  5. 微信小程序重新加载当前页面、刷新当前页面

    2024-03-13 00:30:04       41 阅读
  6. lmbench学习

    2024-03-13 00:30:04       49 阅读