我在Swift应用程序中从一些HTML生成PDF.我使用UIMarkupTextPrintFormatter,代码类似于this gist.我得到的PDF文件是NSData,并将其附加到邮箱中.在附加PDF之前,应用程序不会向用户显示PDF.

我现在想包括一些图片.用我目前的PDF生成策略添加他们的NSURL个HTML格式是行不通的.如何获得与添加了图像的HTML相对应的PDF格式?以下是我try 过的一些事情:

  1. This answer建议将base64图像嵌入HTML并使用UIPrintInteractionController.这确实给了我一个正确嵌入图像的打印预览,但我如何从那里转到对应于PDF输出的NSData

  2. 我在UIWebView篇文章中看到了一些类似的建议,但这些都导致了同样的问题——我不想向用户展示预览.

推荐答案

UIMarkupTextPrintFormatter似乎不支持HTMLimg标记.苹果的文档在这里并没有提供太多信息,它只是说明初始化参数是"The HTML markup text for the print formatter".对于打印格式化程序支持哪些标签,目前还没有明确的说明.

很多图片显示了我只能支持UIMarkupTextPrintFormatter次测试后得出的结论.

那么,对于那些希望从HTML内容中创建PDF的人来说,这会给他们带来什么便利呢?

因此,我发现实现这一点的唯一方法是使用一个隐藏的web视图,加载html内容,然后使用web视图的UIViewPrintFormatter.这是可行的,但感觉真的像黑客.

它确实可以工作,它会将图像嵌入到你的PDF文档中,但是如果是我,我会倾向于使用CoreTextQuartz 2D,因为你会对PDF生成过程有更多的控制,虽然我说我理解这可能有些过分,但我不知道你的html内容的大小或复杂性.

下面是一个有效的例子...

Setup

定义一个基本url非常有用,这样我就可以只传入我想要使用的图像的文件名.映射到应用程序包中图像所在目录的基本url.你也可以定义自己的位置.

Bundle.main.resourceURL + "www/"

Copy Files Build Phase

然后我创建了一个协议来处理与文档相关的功能.默认实现由一个扩展提供,如下面的代码所示.

protocol DocumentOperations {

    // Takes your image tags and the base url and generates a html string
    func generateHTMLString(imageTags: [String], baseURL: String) -> String

    // Uses UIViewPrintFormatter to generate pdf and returns pdf location
    func createPDF(html: String, formmatter: UIViewPrintFormatter, filename: String) -> String

    // Wraps your image filename in a HTML img tag
    func imageTags(filenames: [String]) -> [String]
}


extension DocumentOperations  {

    func imageTags(filenames: [String]) -> [String] {

        let tags = filenames.map { "<img src=\"\($0)\">" }

        return tags
    }


    func generateHTMLString(imageTags: [String], baseURL: String) -> String {

        // Example: just using the first element in the array
        var string = "<!DOCTYPE html><head><base href=\"\(baseURL)\"></head>\n<html>\n<body>\n"
        string = string + "\t<h2>PDF Document With Image</h2>\n"
        string = string + "\t\(imageTags[0])\n"
        string = string + "</body>\n</html>\n"

        return string
    }


    func createPDF(html: String, formmatter: UIViewPrintFormatter, filename: String) -> String {
        // From: https://gist.github.com/nyg/b8cd742250826cb1471f

        print("createPDF: \(html)")

        // 2. Assign print formatter to UIPrintPageRenderer
        let render = UIPrintPageRenderer()
        render.addPrintFormatter(formmatter, startingAtPageAt: 0)

        // 3. Assign paperRect and printableRect
        let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
        let printable = page.insetBy(dx: 0, dy: 0)

        render.setValue(NSValue(cgRect: page), forKey: "paperRect")
        render.setValue(NSValue(cgRect: printable), forKey: "printableRect")

        // 4. Create PDF context and draw
        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)

        for i in 1...render.numberOfPages {

            UIGraphicsBeginPDFPage();
            let bounds = UIGraphicsGetPDFContextBounds()
            render.drawPage(at: i - 1, in: bounds)
        }

        UIGraphicsEndPDFContext();

        // 5. Save PDF file
        let path = "\(NSTemporaryDirectory())\(filename).pdf"
        pdfData.write(toFile: path, atomically: true)
        print("open \(path)")

        return path
    }

}

然后我让视图控制器采用了这个协议.实现这一点的关键在于,您的视图控制器需要采用UIWebViewDelegate

class ViewController: UIViewController, DocumentOperations {    
    @IBOutlet private var webView: UIWebView!

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        webView.delegate = self
        webView.alpha = 0

        if let html = prepareHTML() {
            print("html document:\(html)")
            webView.loadHTMLString(html, baseURL: nil)

        }
    }

    fileprivate func prepareHTML() -> String? {

        // Create Your Image tags here
        let tags = imageTags(filenames: ["PJH_144.png"])
        var html: String?

        // html
        if let url = Bundle.main.resourceURL {

            // Images are stored in the app bundle under the 'www' directory
            html = generateHTMLString(imageTags: tags, baseURL: url.absoluteString + "www/")
        }

        return html
    }
}


extension ViewController: UIWebViewDelegate {

    func webViewDidFinishLoad(_ webView: UIWebView) {
        if let content = prepareHTML() {
            let path = createPDF(html: content, formmatter: webView.viewPrintFormatter(), filename: "MyPDFDocument")
            print("PDF location: \(path)")
        }


    }


}

Swift相关问答推荐

如何根据列表中的项目数量创建多个变量?

Form中的Divider在macOS上并不完全扩展

如何在visionOS中进行购买?&# 39;购买(选项:)在visionOS中不可用

如何在自定义视图中读取修改符子元素?

并发访问SWIFT中数组的非重叠子范围

是否可以循环遍历SWIFT子类并访问它们的静态变量S?

SwiftUI:为什么@State在子视图中持续存在?

如何在SWIFT任务中判断当前任务是否已取消(异步/等待)

从SwiftUI使用UIPageView时,同一页面连续显示两次的问题

Swift通过设置变量B自动设置变量A的值

Swift数据关系

设备上ScrollView为什么会将内容高度更改为随机值,而在预览上不会? - 优化后的标题:设备上ScrollView内容高度随机变化问题解决

暂停我的 AR 会话并清除变量和锚点的功能

如何在 SwiftUI 中为过渡动画保持相同的标识

防止带有背景的文本扩展到 maxWidth

TextField 键入未完成

快速更改tableView中的数据

如何在 ZStack 中单独下移卡片?

SwiftUI 4 列表初始化程序在 iOS 中不可用

如何将 Int 拆分为其各个数字?