1

I'm trying to convert the content of an UITextView to PDF. The UITextView uses an NSAttributedString that contains text and images. The images are instances of NSTextAttachment.

I went through the UIPrintPageRenderer way passing the UIPrintFormatter that I get from the UITextView through its viewPrintFormatter() function and it works perfectly for the text but I can't see any of the images. It looks like the images are actually taking up space as I can see the empty space between text lines, but no image is being rendered.

I suspect it might be the formatter, but I don't know how to work around it. Any help is greatly appreciated.

The code I'm using to create the PDF from the NSAttributedString is the following:

public func createPDF(text: NSAttributedString, documentPath: URL, customPrintFormatter: UIPrintFormatter? = nil) {
    let printFormatter = customPrintFormatter == nil ? UISimpleTextPrintFormatter(attributedText: text) : customPrintFormatter!

    let renderer = UIPrintPageRenderer()
    renderer.addPrintFormatter(printFormatter, startingAtPageAt: 0)

    // A4 size
    let pageSize = CGSize(width: 595.2, height: 841.8)

    // create some sensible margins
    let pageMargins = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)

    // calculate the printable rect from the above two
    let printableRect = CGRect(x: pageMargins.left, y: pageMargins.top, width: pageSize.width - pageMargins.left - pageMargins.right, height: pageSize.height - pageMargins.top - pageMargins.bottom)

    // and here's the overall paper rectangle
    let paperRect = CGRect(x: 0, y: 0, width: pageSize.width, height: pageSize.height)

    renderer.setValue(NSValue(cgRect: paperRect), forKey: "paperRect")
    renderer.setValue(NSValue(cgRect: printableRect), forKey: "printableRect")

    let pdfData = NSMutableData()

    UIGraphicsBeginPDFContextToData(pdfData, paperRect, nil)
    renderer.prepare(forDrawingPages: NSMakeRange(0, renderer.numberOfPages))

    let bounds = UIGraphicsGetPDFContextBounds()

    for i in 0  ..< renderer.numberOfPages {
        UIGraphicsBeginPDFPage()

        renderer.drawPage(at: i, in: bounds)
    }

    UIGraphicsEndPDFContext()

    do {
        try pdfData.write(to: documentPath)
    } catch {
        print(error.localizedDescription)
    }
}

The following is the code that creates the NSTextAttachment

        let attachment = NSTextAttachment()
        attachment.image = UIImage.init(named: "1.png")
        attachment.bounds = CGRect.init(x: 0, y: 0, width: 200, height: 100)
        let img = NSMutableAttributedString.init(attachment: attachment)

        let imgRange = NSRange.init(location: match.range(at: 0).location + match.range(at: 0).length, length: img.length)
        self.beginEditing()
        backingStore.insert(img, at: match.range(at: 0).location + match.range(at: 0).length)
        let r = NSMakeRange(match.range(at: 0).location + match.range(at: 0).length, img.length)
        self.edited([.editedCharacters, .editedAttributes], range: range, changeInLength: img.length)
        self.endEditing()
Valerio Santinelli
  • 1,592
  • 2
  • 27
  • 45
  • What happens when you omit the customPrinter logic as in public func createPDF(text: NSAttributedString, documentPath: URL) let printFormatter = UISimpleTextPrintFormatter(attributedText: text)? (This appears to be the only part of your logic that isn't the same as the HackingWithSwift article so it may be the cause of the issue.) Also, can you post the logic that creates the attributed text that gets passed in? – Steve Robertson Feb 09 '19 at 16:14
  • @SteveRobertson there's no difference if I simply use the `UISimpleTextPrintFormatter`. Both produce the same output. I've added to the original question a snippet from the code of the custom `NSStorage` class that creates the `NSTextAttachment`. Nothing fancy, but I can see the image in the `UITextView` that uses that `NSStorage` object. – Valerio Santinelli Feb 10 '19 at 10:53

0 Answers0