iOS长图生成的pdf性能优化记录

这篇具有很好参考价值的文章主要介绍了iOS长图生成的pdf性能优化记录。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

  某日产品拿来了一个由30多页高清长图生成的pdf,在应用中运行出现了崩溃。

排查

  经过调试发现加载长图生成的pdf时,运行内存会出现缓慢增长,直至崩溃。经过代码定位发现时pdf转成image对象的过程中由于是长图生成的pdf,这一页的pdf的size相当于正常pdfsize的30多页,转换的过程中context的fill的size也是正常pdf的30多倍。经过调研,尝试,发现对于同一页的pdf,可以通过调整context的fill的size来只把pdf中的部分内容转换成image对象,内存正常也不大。

方案

  原来的方案是每页pdf生成一个image对象,通过一个collectonViewCell来显示。调整后的方案为:根据屏幕大小来决定一个pdf页面生成多少个image对象,有多少个image对象,一个section里就有多少个cell。方案如下:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if pdfSize.height/pdfSize.width >= 3.0 { //长图生成的pdf 备注:pdfSize 即一页pdf的大小
            let visible_width = pdfSize.width
            let visible_height = visible_width * Board_height/Board_width
            let visibleSize = CGSize(width: visible_width, height: visible_height)
            let count:Int = Int(ceil(pdfSize.height/visible_height))// 计算需要分割的次数
            let index:Int
            let itemSize:CGSize
            if indexPath.item < count {
                index = indexPath.item
            } else {
                index = count - 1
            }
            if CGFloat(index) * visibleSize.height + visibleSize.height < pdfSize.height {
                itemSize = visibleSize
            } else {
             let tailHeight = pdfSize.height - CGFloat(index) * visibleSize.height
                 itemSize = CGSize(width: visibleSize.width, height: tailHeight)
            }
            return itemSize
        }
        return pdfSize
    }

在讲pdf转成image对象的过程中,方案调整核心代码如下:

//index就是pdf中的从上往下被分割后的image的索引值
 private func subImageFromLongPDF(ori_page:PDFPage, pdfSize:CGSize, index:Int) -> UIImage {
        guard let page = ori_page.pageRef else {
            return UIImage()
        }
        var dic = [PDFAnnotation:CGRect]()
        let originalPageRect = ori_page.originalPageRect
        let elaborate: CGFloat = 1.0
        let scale_W = pdfSize.width / originalPageRect.size.width * elaborate
        let scale_H = pdfSize.height / originalPageRect.size.height * elaborate
        let width = originalPageRect.size.width * scale_W
        let height = width * Board_height/Board_width // Board_height屏幕高度,Board_width屏幕宽度
        let visibleSize = CGSize(width: width, height: height)

        let rotation = ori_page.rotation
        ori_page.rotation = 0

        let scaledOrigin = CGPoint(x: originalPageRect.origin.x * scale_W, y: originalPageRect.origin.y * scale_H)

        let scaledPageSize = CGSize(width: originalPageRect.size.width * scale_W, height: originalPageRect.size.height * scale_H)
        let scaledPageRect:CGRect
        var tailHeight = 0.0
        if CGFloat(index) * visibleSize.height + height < scaledPageSize.height {
            scaledPageRect = CGRect(origin: CGPoint(x: 0, y: CGFloat(index) * visibleSize.height), size: visibleSize)
        } else {
            tailHeight = scaledPageSize.height - CGFloat(index) * visibleSize.height
            scaledPageRect = CGRect(origin: CGPoint(x: 0, y: CGFloat(index) * visibleSize.height), size: CGSize(width: visibleSize.width, height: tailHeight))
        }

        var img:UIImage?
        autoreleasepool {
            let renderer = UIGraphicsImageRenderer(size: scaledPageRect.size)
            var tmpImg:UIImage? = renderer.image {
                ctx in
                UIColor.white.set()
                //这个核心代码,scaledPageRect就是计算好的rect,
                ctx.fill(scaledPageRect)
                let rotationAngle: CGFloat
                switch page.rotationAngle {//保持和安卓一致,强制为0了
                case 90:
                    rotationAngle = 270
                    //平移 以用户空间为单位,指定上下文的坐标空间 x 轴的位移量。
                    ctx.cgContext.translateBy(x: -scaledPageRect.origin.x, y: 0)
                case 180:
                    rotationAngle = 180
                    //平移
                    ctx.cgContext.translateBy(x: scaledPageRect.width,y: 0)

                case 270:
                    rotationAngle = 90
                    //平移
                    ctx.cgContext.translateBy(x: scaledPageRect.origin.x, y: scaledPageRect.size.height - scaledPageRect.origin.y)
                default:
                    rotationAngle = 0
                    //平移 以用户空间为单位,指定上下文的坐标空间 x 轴的位移量。
                    //指定上下文的坐标空间 y 轴的位移量(以用户空间为单位)。
                    if rotation == 180 {
                        if tailHeight > 0 {//尾部不足一屏的特殊处理逻辑
                            ctx.cgContext.translateBy(x: 0 - scaledOrigin.x, y: 0 + scaledOrigin.y + CGFloat(index+1) * visibleSize.height - (visibleSize.height - tailHeight))//翻转180度正常
                        } else {
                            ctx.cgContext.translateBy(x: 0 - scaledOrigin.x, y: 0 + scaledOrigin.y + CGFloat(index+1) * visibleSize.height)//翻转180度正常
                        }
                        
                    } else {
                        ctx.cgContext.translateBy(x: 0 - scaledOrigin.x, y: scaledPageSize.height + scaledOrigin.y - scaledPageRect.origin.y)
                    }

                }
                //Rotate是以原点为圆心旋转,Quartz创建的图形上下文旋转圆心为左下角,角度值正数为逆时针旋转,负数为顺时针旋转
                //UIKit创建的图像上下文旋转圆心为左上角,角度值正数为顺时针旋转,负数为逆时针旋转。

                // Flip the context vertically because the Core Graphics coordinate system starts from the bottom.
                ctx.cgContext.scaleBy(x:1.0, y: -1.0)//垂直翻转上下文,因为核心图形坐标系从底部开始
                //旋转 正值逆时针旋转,负值顺时针旋转
                ctx.cgContext.rotate(by: rotationAngle.degreesToRadians)
                ctx.cgContext.scaleBy(x: scale_W, y: scale_H)//缩放
                // Draw the PDF page.
                // 此处仍然是正常的绘制pdf,因为前面设置了context的fillSize,因此pdf绘制的时候只在前面指定的rect才会生效。
                ctx.cgContext.drawPDFPage(page)
                for annotation in ori_page.annotations {
                    let origin = annotation.bounds.origin
                    dic[annotation] = annotation.bounds
                    let annotation_fill_bounds = CGRect(x: origin.x + originalPageRect.origin.x, y: origin.y + originalPageRect.origin.y, width: annotation.bounds.size.width, height: annotation.bounds.size.height)
                    annotation.bounds = annotation_fill_bounds
                    annotation.draw(with: .cropBox, in: ctx.cgContext)
                }
            }

            if rotation%360 != 0 {
                let scale:Float =  Float(rotation) / Float(180)
                tmpImg = tmpImg?.rotate(radians: Float.pi * scale) ?? UIImage.init()
            }
            
            img = tmpImg
            tmpImg = nil
            
        }
        //将对pdfpage的修改进行还原
        ori_page.rotation = rotation
        for (annotation,bounds) in dic {
           annotation.bounds = bounds
        }
        return img ?? UIImage()
    }

extension PDFPage {
    
    var originalPageRect: CGRect {
        switch rotation {
        case 90, 270:
            let originalRect = bounds(for: PDFDisplayBox.cropBox)
            let rotatedSize = CGSize(width: originalRect.height, height: originalRect.width)
            return CGRect(origin: originalRect.origin, size: rotatedSize)
        default:
            return bounds(for: PDFDisplayBox.cropBox)
        }
    }
}

extension UIImage {
    func rotate(radians: Float) -> UIImage? {
        var newSize = CGRect(origin: CGPoint.zero, size: self.size).applying(CGAffineTransform(rotationAngle: CGFloat(radians))).size
        // Trim off the extremely small float value to prevent core graphics from rounding it up
        newSize.width = floor(newSize.width)
        newSize.height = floor(newSize.height)

        UIGraphicsBeginImageContextWithOptions(newSize, false, self.scale)
        guard let context = UIGraphicsGetCurrentContext() else {
            return nil
        }

        // Move origin to middle
        context.translateBy(x: newSize.width/2, y: newSize.height/2)
        // Rotate around middle
        context.rotate(by: CGFloat(radians))
        // Draw the image at its center
        self.draw(in: CGRect(x: -self.size.width/2, y: -self.size.height/2, width: self.size.width, height: self.size.height))

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }
}


extension FloatingPoint {
    var degreesToRadians: Self { return self * .pi / 180 }
    var radiansToDegrees: Self { return self * 180 / .pi }
}

备注:这段代码结合了自己的项目实际业务,大家作为参考。多看下核心代码和注释文章来源地址https://www.toymoban.com/news/detail-814198.html

到了这里,关于iOS长图生成的pdf性能优化记录的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Cocos 微信小游戏内存与性能优化指南(iOS端)

    前言 由于微信小游戏普通模式是基于 C++ 渲染层与 JS 编译引擎在原生平台模拟 HTMLCanvas 渲染能力的方案,其中 JS 代码的编译执行效率会极大影响游戏运行的性能。通常 JS 引擎都提供了 JIT 能力用于提高编译速度,这使得小游戏能够在 Android 端取得优异的性能。但是在 IOS 端由

    2024年02月08日
    浏览(40)
  • iOS UI掉帧和卡顿优化解决方案记录

    UI卡顿原理 在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到

    2024年01月22日
    浏览(44)
  • Android性能优化系列-腾讯matrix-IO监控-IOCanaryPlugin源码分析

    作者:秋去无痕 matrix 对io的监控包括四个方面 监控在主线程执行 IO 操作的问题 监控缓冲区过小的问题 监控重复读同一文件 监控内存泄漏问题 IOCanaryPlugin,内部由IOCanaryCore完成真正的操作。 根据配置进行hook的安装 取消hook 底层hook安装包函几个步骤,加载so,设置hook内容,

    2024年02月09日
    浏览(81)
  • 【python】PDF转长图

    步骤: 下载依赖文件poppler 上代码

    2024年02月04日
    浏览(38)
  • Linux性能学习(3.2):IO_磁盘IO

    参考资料: 1. Linux I/O模型 2. 判断磁盘I/O是否饱和与%util指标的意义 3. 磁盘利用率和饱和度 4. 辩证看待 I/Ostat 在上一篇中,大致了解了文件系统的一些知识,了解了不同的文件系统以及VFS的概念,其实在存储介质上也是有这个情况,在嵌入式开发中,会根据不同的项目使用不

    2024年02月08日
    浏览(51)
  • 使用ComPDFKit PDF SDK 构建iOS PDF阅读器

    在当今以移动为先的世界中,为企业和开发人员创建一个iOS应用程序是必不可少的。随着对PDF文档处理需求的增加,使用ComPDFKit这个强大的PDF软件开发工具包(SDK)来构建iOS PDF阅读器和编辑器可以让最终用户轻松查看和编辑PDF文档。 在本博客中,我们将首先探讨整合ComPDFK

    2024年02月15日
    浏览(36)
  • iOS性能指标和性能测试工具

    作为一名软件测试工程师,在测试 iOS 应用的性能时,需要关注以下几个方面: 1. 响应时间:应用的启动时间、页面加载速度、接口响应时间等。 2. CPU 使用率:应用在各种操作下的 CPU 占用情况。 3. 内存使用:应用在各种操作下的内存占用情况。 4. 网络性能:应用在各种

    2024年02月13日
    浏览(47)
  • 查看ios 应用程序性能

    目录 摘要 前言 性能概括 CPU内存监控 内存监控 磁盘监控 网络监控 GPU fps 本篇博文将介绍一款重量级性能测试工具——克魔助手,针对iOS应用程序的性能监控进行详细介绍。通过克魔助手,开发者可以方便地查看应用程序的CPU、内存、GPU性能情况,以及网络监控和抓包等功能

    2024年02月03日
    浏览(57)
  • 小程序下载PDF文件并保存在本地(适用IOS安卓)

    项目需要要做个下载功能,下载PDF版发票并保存在本地文件中。 下载文件:用uni.downloadFile或wx.downloadFile下载文件,拿到返回文件的本地临时路径。 保存文件: 1.IOS端 ios不能直接下载在本地,需用用户自己复制下载链接,后在Safari浏览器中预览下载。js功能代码如下: 2.安卓

    2024年02月04日
    浏览(55)
  • 克魔助手 - iOS性能检测平台

    众所周知,如今的用户变得越来越关心app的体验,开发者必须关注应用性能所带来的用户流失问题。目前危害较大的性能问题主要有:闪退、卡顿、发热、耗电快、网络劫持等,但是做过iOS开发的人都知道,在开发过程中我们没有一个很直观的工具可以实时的知道开发者写出

    2024年02月19日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包