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

2024-01-22 10:44

本文主要是介绍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.widthlet visible_height = visible_width * Board_height/Board_widthlet visibleSize = CGSize(width: visible_width, height: visible_height)let count:Int = Int(ceil(pdfSize.height/visible_height))// 计算需要分割的次数let index:Intlet itemSize:CGSizeif 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.heightitemSize = 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.originalPageRectlet elaborate: CGFloat = 1.0let scale_W = pdfSize.width / originalPageRect.size.width * elaboratelet scale_H = pdfSize.height / originalPageRect.size.height * elaboratelet width = originalPageRect.size.width * scale_Wlet height = width * Board_height/Board_width // Board_height屏幕高度,Board_width屏幕宽度let visibleSize = CGSize(width: width, height: height)let rotation = ori_page.rotationori_page.rotation = 0let 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:CGRectvar tailHeight = 0.0if 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.heightscaledPageRect = 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 inUIColor.white.set()//这个核心代码,scaledPageRect就是计算好的rect,ctx.fill(scaledPageRect)let rotationAngle: CGFloatswitch 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.origindic[annotation] = annotation.boundslet 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_boundsannotation.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 = tmpImgtmpImg = nil}//将对pdfpage的修改进行还原ori_page.rotation = rotationfor (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 upnewSize.width = floor(newSize.width)newSize.height = floor(newSize.height)UIGraphicsBeginImageContextWithOptions(newSize, false, self.scale)guard let context = UIGraphicsGetCurrentContext() else {return nil}// Move origin to middlecontext.translateBy(x: newSize.width/2, y: newSize.height/2)// Rotate around middlecontext.rotate(by: CGFloat(radians))// Draw the image at its centerself.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 }
}

备注:这段代码结合了自己的项目实际业务,大家作为参考。多看下核心代码和注释

这篇关于iOS长图生成的pdf性能优化记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/632740

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n