SwiftUI中使用ImageRenderer渲染PDF时如何添加打印安全边距
为SwiftUI渲染的PDF添加打印安全区域(页边距)
我希望在渲染PDF的过程中为页面之间添加打印安全区域(又称页边距)。以下是我的代码,目前该代码可生成无页边距的多页PDF:
static func render(view: some View) -> URL { // 1: Render View with some modifiers let renderer = ImageRenderer(content: view) let url = URL.documentsDirectory.appending(path: "output.pdf") // 2: Start the rendering process renderer.render { size, context in // 3: Tell SwiftUI our PDF should be the same size as the views we're rendering var box = CGRect(x: 0, y: 0, width: 612, height: 792) // Letter size in points // 4: Calculate how many pages we need let pageCount = Int(ceil(size.height / box.height)) guard let pdf = CGContext(url as CFURL, mediaBox: &box, nil) else { return } // Split the view into pages // Iterrate over the pages and render them for page in (0..<pageCount).reversed() { let offset = CGFloat(page) * box.height // 5: Start a new PDF page pdf.beginPDFPage(nil) //6. write chopped view to the page pdf.translateBy(x: 0, y: -offset) // 7: Render the SwiftUI view data onto the page context(pdf) // 8: End the page and close the file pdf.endPDFPage() } pdf.closePDF() } return url }
解决方案
要实现打印安全区域,需要调整页面可绘制范围、分页逻辑和渲染偏移,具体修改如下:
1. 定义页边距参数
先设置自定义的页边距大小(可根据需求调整):
let margin: CGFloat = 20.0
2. 区分页面整体尺寸与可绘制区域
保持PDF页面的整体尺寸不变,同时计算去掉页边距后的可绘制区域:
var box = CGRect(x: 0, y: 0, width: 612, height: 792) // 完整Letter页面尺寸 let drawableBox = box.insetBy(dx: margin, dy: margin) // 页边距内的可绘制区域
3. 基于可绘制区域计算页数
原来的分页逻辑是按整页高度计算,现在要改成按可绘制区域高度计算,确保每页内容不会超出安全区:
let pageCount = Int(ceil(size.height / drawableBox.height))
4. 调整渲染时的坐标偏移与裁剪
在循环渲染每页时,需要将坐标系平移到页边距位置,并裁剪到可绘制区域,避免内容溢出:
for page in (0..<pageCount).reversed() { let offset = CGFloat(page) * drawableBox.height pdf.beginPDFPage(nil) // 平移到页边距起点,裁剪到可绘制区域 pdf.translateBy(x: margin, y: margin - offset) pdf.clip(to: CGRect(x: 0, y: 0, width: drawableBox.width, height: drawableBox.height)) context(pdf) pdf.endPDFPage() }
完整修改后的代码
static func render(view: some View) -> URL { let renderer = ImageRenderer(content: view) let url = URL.documentsDirectory.appending(path: "output.pdf") renderer.render { size, context in let margin: CGFloat = 20.0 // 自定义页边距 var box = CGRect(x: 0, y: 0, width: 612, height: 792) // Letter页面尺寸 let drawableBox = box.insetBy(dx: margin, dy: margin) // 可绘制区域 // 按可绘制区域高度计算页数 let pageCount = Int(ceil(size.height / drawableBox.height)) guard let pdf = CGContext(url as CFURL, mediaBox: &box, nil) else { return } for page in (0..<pageCount).reversed() { let offset = CGFloat(page) * drawableBox.height pdf.beginPDFPage(nil) // 调整坐标并裁剪,确保内容在安全区内 pdf.translateBy(x: margin, y: margin - offset) pdf.clip(to: CGRect(x: 0, y: 0, width: drawableBox.width, height: drawableBox.height)) context(pdf) pdf.endPDFPage() } pdf.closePDF() } return url }
核心逻辑说明
- 媒体盒(mediaBox):保持为完整页面尺寸,保证PDF的页面规格正确
- 可绘制区域:通过
insetBy缩小范围,预留出打印安全的页边距 - 裁剪操作:
clip(to:)确保视图内容不会绘制到页边距区域内 - 偏移计算:基于可绘制区域高度计算分页偏移,保证内容在每页的安全区内正确衔接
内容的提问来源于stack exchange,提问作者Evgeniy Poznyak
相关产品推荐
相关产品推荐

