iOS Swift中使用CGPDFDocument将两页PDF半尺寸并排至新PDF单页的实现问题
iOS Swift中使用CGPDFDocument将两页PDF半尺寸并排至新PDF单页的实现问题
你之前用UIImage转格式的方式遇到分辨率损失太正常了——毕竟转成位图就丢了PDF的矢量特性,直接操作CGPDFDocument才是正确的方向,能完美保留原PDF的清晰度!
你的现有代码是单独绘制单页的逻辑,要把两页并排到同一个新页面,核心问题是没正确管理绘图上下文的状态和坐标变换,导致第二页没画到正确位置。我给你梳理下关键思路和修正后的代码:
核心思路
- 先计算好新页面的尺寸:因为要把两页旋转90度后缩放到一半并排,新页面的宽度应该是原页面高度的1倍(两个半宽相加),高度是原页面宽度的一半;
- 在同一个新页面的上下文里,分别对两页做坐标变换(平移、缩放、旋转),用
saveGState()和restoreGState()隔离每个页面的变换,避免互相干扰; - 处理PDF坐标系和Core Graphics坐标系的翻转问题(PDF原点在左下角,CG默认是左上角)。
修正后的完整代码
// 确保能获取到原PDF的两页 guard let page1 = theCGPdfDocument.page(at: 1), let page2 = theCGPdfDocument.page(at: 2) else { print("无法获取PDF页面") return } // 获取原页面的媒体框尺寸 let originalPageRect = page1.getBoxRect(.mediaBox) // 计算单页缩放旋转后的尺寸:旋转90度后宽高互换,再缩放到1/2 let scaledSingleWidth = originalPageRect.size.height * 0.5 let scaledSingleHeight = originalPageRect.size.width * 0.5 // 新页面的媒体框:宽度是两个缩放页面的宽度之和,高度是单页缩放后的高度 var newPageRect = CGRect(x: 0, y: 0, width: scaledSingleWidth * 2, height: scaledSingleHeight) // 开启新页面 writeContextInDestination!.beginPage(mediaBox: &newPageRect) // ========== 绘制第一页 ========== writeContextInDestination!.saveGState() // 1. 先翻转坐标系,适配PDF的左下角原点 writeContextInDestination!.translateBy(x: scaledSingleWidth, y: scaledSingleHeight) writeContextInDestination!.scaleBy(x: -1, y: -1) // 2. 缩放到原尺寸的1/2,再旋转90度(顺时针) writeContextInDestination!.scaleBy(x: 0.5, y: 0.5) writeContextInDestination!.rotate(by: .pi/2) // 3. 绘制第一页 writeContextInDestination!.drawPDFPage(page1) // 恢复上下文状态,不影响第二页的绘制 writeContextInDestination!.restoreGState() // ========== 绘制第二页 ========== writeContextInDestination!.saveGState() // 1. 平移到第二页的起始位置(x轴偏移一个单页宽度),再翻转坐标系 writeContextInDestination!.translateBy(x: scaledSingleWidth * 2, y: scaledSingleHeight) writeContextInDestination!.scaleBy(x: -1, y: -1) // 2. 同样的缩放和旋转 writeContextInDestination!.scaleBy(x: 0.5, y: 0.5) writeContextInDestination!.rotate(by: .pi/2) // 3. 绘制第二页 writeContextInDestination!.drawPDFPage(page2) writeContextInDestination!.restoreGState() // 结束当前页面并关闭PDF writeContextInDestination!.endPage() writeContextInDestination!.closePDF()
关键注意点
saveGState()和restoreGState()必须配对使用:每绘制一页前保存当前上下文状态,绘制完恢复,这样第一页的变换不会影响第二页的坐标计算;- 坐标变换的顺序很重要:Core Graphics的变换是矩阵叠加,顺序是从后往前生效,所以我们先做坐标系翻转,再做缩放旋转,最后绘制页面;
- 新页面的尺寸一定要计算准确,不然会出现页面被裁剪或者留白过多的问题。
这样操作出来的新PDF完全是矢量格式,不会有任何分辨率损失,完美解决你之前的问题!
备注:内容来源于stack exchange,提问作者claude31
相关产品推荐
相关产品推荐

