You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS中使用WKWebView生成PDF时如何避免图片被分页截断?

问题:WKWebView生成PDF时动态图片被分页截断

在iOS应用中使用WKWebView渲染HTML内容,生成PDF时遇到问题:动态加载到DOM中的图片会被分页截断。常规浏览器打印时图片可避免跨页截断,但iOS中通过WKWebView生成PDF时,图片会根据页面尺寸和位置被随机截断高度。

生成PDF的Swift代码

func createPdf(completion: @escaping (PDFDocument?) -> Void) { 
        let formatter: UIViewPrintFormatter = self.viewPrintFormatter()
        let render = UIPrintPageRenderer()
        render.addPrintFormatter(formatter, startingAtPageAt: 0)
        
        let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8)
        let printableWidth = page.width - 2 * 32
        let printableHeight = page.height - 2 * 64
        let printable = CGRect(x: 32, y: 32, width: printableWidth, height: printableHeight)
        render.setValue(NSValue(cgRect: page), forKey: "paperRect")
        render.setValue(NSValue(cgRect: printable), forKey: "printableRect")
         
        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)
        for i in 0..<render.numberOfPages {
            UIGraphicsBeginPDFPage();
            let bounds = UIGraphicsGetPDFContextBounds()
            render.drawPage(at: i, in: bounds)
            if i > 0 {
                let pageNumber = i
                let footerText = "Page \(pageNumber)"
                let attributes: [NSAttributedString.Key: Any] = [
                    .font: UIFont.systemFont(ofSize: 12),
                    .foregroundColor: UIColor.black
                ]
                let textSize = footerText.size(withAttributes: attributes)
                let textRect = CGRect(
                    x: bounds.width - textSize.width - 32,
                    y: bounds.height - textSize.height - 20,
                    width: textSize.width,
                    height: textSize.height
                )
                footerText.draw(in: textRect, withAttributes: attributes)
            }
        }
        UIGraphicsEndPDFContext()
        completion(PDFDocument(data: pdfData as Data))
    }

解决方案

1. 给图片添加CSS分页控制

在HTML样式中,给动态图片或其容器添加分页避免属性,强制WebKit不在图片内部分页:

.image-wrap {
    page-break-inside: avoid;
    break-inside: avoid; /* 兼容现代渲染逻辑 */
    display: block;
    margin: 0 0 1em 0; /* 添加底部间距,避免图片紧贴分页边界 */
}

.image-wrap img {
    max-width: 100%;
    height: auto;
}

注意:动态加载的图片需确保样式在加载完成后正确应用,可通过JS在图片加载完成后给元素添加类名。

2. 等待所有图片加载完成再生成PDF

动态图片未加载完成就触发PDF生成,会导致尺寸计算错误引发截断。可通过JS监听页面加载状态:

// 生成PDF前确认所有图片加载完成
webView.evaluateJavaScript("Array.from(document.images).every(img => img.complete) && document.readyState === 'complete'") { (result, error) in
    guard let isReady = result as? Bool, isReady else {
        // 延迟重试,确保图片加载完成
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
            // 重新检查加载状态
        }
        return
    }
    // 图片全部加载完成,执行PDF生成
    self.createPdf { pdfDoc in
        // 处理结果
    }
}

3. 改用WebKit原生PDF生成API(iOS14+)

iOS14及以上提供的WKWebView.createPDFAPI更贴近浏览器打印逻辑,对CSS分页支持更完善:

func createPdfWithWebKit(completion: @escaping (PDFDocument?) -> Void) {
    let pdfConfig = WKPDFConfiguration()
    pdfConfig.pageWidth = 595.2
    pdfConfig.pageHeight = 841.8
    pdfConfig.margins = UIEdgeInsets(top: 32, left: 32, bottom: 64, right: 32)
    
    webView.createPDF(configuration: pdfConfig) { result in
        switch result {
        case .success(let data):
            completion(PDFDocument(data: data))
        case .failure(let error):
            print("PDF生成失败:\(error.localizedDescription)")
            completion(nil)
        }
    }
}

该API会自动处理图片分页避免,无需手动调整打印渲染器逻辑。

4. 避免Swift代码中手动绘制页码干扰分页

原代码中在PDF生成后手动绘制页码,可能干扰WebKit的原始分页计算。建议通过HTML的@page规则添加页脚页码,保持渲染逻辑统一:

@page {
    @bottom-right {
        content: "Page " counter(page);
        font-size: 12px;
    }
}

内容的提问来源于stack exchange,提问作者Ariful Jannat Arif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:34:55