将文本绘制到 CGContext for Quartz PDF 不起作用

Drawing Text to a CGContext for Quartz PDF not working

这适用于 Swift 5 on macOS

我正在尝试将一些文本写入生成的 PDF。

我能够将背景图像加载到页面上,但是当我调用我的 drawText 方法时,它没有进入任何一个页面。

我尝试通过 .draw() 方法将 NSString 绘制到上下文中,但这也不起作用。我希望它能正常工作,这样我就可以添加更多文本,包括文本框等。

我做错了什么?感谢指点。

import Cocoa
import CoreText
import Quartz

extension NSImage {
    /*
        Converts an NSImage to a CGImage for rendering in a CGContext
        Credit - Xue Yu
        - https://gist.github.com/KrisYu/83d7d97cae35a0b10fd238e5c86d288f
     */
    var toCGImage: CGImage {
        var imageRect = NSRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
        guard let image =  cgImage(forProposedRect: &imageRect, context: nil, hints: nil) else {
            abort()
        }
        return image
    }
}

class PDFText {

    /*
     Create a non-nil CGContext
     Credit - hmali - 3/15/2019
        
     */
    var pdfContext = CGContext(data: nil,
                              width: 0,
                              height: 0,
                              bitsPerComponent: 1,
                              bytesPerRow: 1,
                              space: CGColorSpace.init(name: CGColorSpace.sRGB)!,
                              bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)

    let textRect = CGRect(x: 295, y: 350, width: 100, height: 100)
    
    func createPDF() {
        let filePath = "/Users/Shared/Text.pdf"
        let fileURL = NSURL(fileURLWithPath: filePath)
        pdfContext = CGContext(fileURL, mediaBox: &backgroundRect, nil)
        pdfContext!.beginPDFPage(nil)
        drawBackground()
        drawText("This is page 1")
        pdfContext!.endPDFPage()
        pdfContext!.beginPDFPage(nil)
        drawBackground()
        drawText("This is page 1")
        pdfContext!.endPDFPage()
        pdfContext!.closePDF()
    }
    
    func drawBackground() {
        
        let cgImage = NSImage(contentsOfFile: "/Users/Shared/background.png")?.toCGImage
        pdfContext?.draw(cgImage!, in: CGRect(x: 0, y: 0, width: Int(72*8.5), height: Int(72*11)))
    }
    
    func drawText(_ text:String) {
    
        let style = NSMutableParagraphStyle()
        style.alignment = .center
        let attr = [NSAttributedString.Key.font: NSFont(name: "Helvetica", size: 16.0),
                    NSAttributedString.Key.foregroundColor: NSColor.purple,
                    NSAttributedString.Key.backgroundColor: NSColor.clear,
                    NSAttributedString.Key.paragraphStyle: style]
        let attrText = NSAttributedString(string: text, attributes: attr as [NSAttributedString.Key : Any])
        pdfContext?.saveGState()
        pdfContext?.translateBy(x: attrText.size().width, y: attrText.size().height)
        attrText.draw(with: textRect)
        pdfContext?.restoreGState()
    }
}

关闭我解决的一个悬而未决的问题(完整代码)。

Swift macOS 上的 5.4

import Cocoa
import CoreText
import Quartz

var pageWidth: CGFloat = 72*8.5
var pageHeight: CGFloat = 72*11.0
var pageRect: CGRect = CGRect(x:0, y:0, width: pageWidth, height: pageHeight)

extension NSImage {
    /*
        Converts an NSImage to a CGImage for rendering in a CGContext
        Credit - Xue Yu
        - https://gist.github.com/KrisYu/83d7d97cae35a0b10fd238e5c86d288f
     */
    var toCGImage: CGImage {
        var imageRect = NSRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
        guard let image =  cgImage(forProposedRect: &imageRect, context: nil, hints: nil) else {
            abort()
        }
        return image
    }
}

class PDFText {

    /*
     Create a non-nil empty CGContext
     Credit - hmali - 3/15/2019
        
     */
    var pdfContext = CGContext(data: nil,
                               width: 0,
                               height: 0,
                               bitsPerComponent: 1,
                               bytesPerRow: 1,
                               space: CGColorSpace.init(name: CGColorSpace.sRGB)!,
                               bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)

    // Set a rectangle to be in the center of the page
    let textRect = CGRect(x: pageRect.midX-50, y: pageRect.midY-50, width: 100, height: 100)
    
    func createPDF() {
        let filePath = "/Users/Shared/Text.pdf"
        let fileURL = NSURL(fileURLWithPath: filePath)
        pdfContext = CGContext(fileURL, mediaBox: &pageRect, nil)
        // This must be called to begin a page in a PDF document
        pdfContext!.beginPDFPage(nil)
        drawBackground()
        drawText(text: "This is page 1")
        // This has to be called prior to writing another page to the PDF document
        pdfContext!.endPDFPage()
        pdfContext!.beginPDFPage(nil)
        drawBackground()
        drawText(text: "This is page 2")
        // Call this or before closing the document.
        pdfContext!.endPDFPage()
        pdfContext!.closePDF()
    }
    
    func drawBackground() {
        // Draws an image into the graphics context.
        // NOTE: If the image is not sized for the specified rectangle it will be
        //       scaled (up/down) automatically to fit within the rectangle.
        let cgImage = NSImage(contentsOfFile: "/Users/Shared/background.png")?.toCGImage
        pdfContext?.draw(cgImage!, in: pageRect)
    }
    
    func drawText(text:String) {
        // Credit: Nutchaphon Rewik, https://github.com/nRewik/SimplePDF
        
        // Create a paragraph style to be used with the atributed string
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = .center
        // Set up the sttributes to be applied to the attributed text
        let stringAttributes = [NSAttributedString.Key.font: NSFont(name: "Helvetica", size: 16.0),
                    NSAttributedString.Key.foregroundColor: NSColor.purple,
                    NSAttributedString.Key.backgroundColor: NSColor.clear,
                    NSAttributedString.Key.paragraphStyle: paragraphStyle]
        // Create the attributed string
        let attributedString = NSAttributedString(string: text, attributes: stringAttributes as [NSAttributedString.Key : Any])
        // Set up a CoreText frame that encloses the attributed string
        let frameSetter = CTFramesetterCreateWithAttributedString(attributedString)
        // Get the frame size for the attributed string
        let frameSize = CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0, attributedString.string.count), nil, textRect.size, nil)
        // Save the Graphics state of the context
        pdfContext!.saveGState()
        // Put the text matrix into a known state. This ensures that no old scaling
        // factors are left in place.
        pdfContext!.textMatrix = CGAffineTransform.identity
        // Create a path object to enclose the text.
        let framePath = CGPath(rect: CGRect(x: textRect.minX, y: textRect.midY-frameSize.height/2, width: textRect.width, height: frameSize.height), transform: nil)
        // Get the frame that will do the rendering. The currentRange variable specifies
        // only the starting point. The framesetter lays out as much text as will fit into
        // the frame or until it runs out of text.
        let frameRef = CTFramesetterCreateFrame(frameSetter, CFRange(location: 0, length: 0), framePath, nil)
        // Draw the CoreText frame (that includes the text) into the graphics context.
        CTFrameDraw(frameRef, pdfContext!)
        // Restore the previous Graphics state.
        pdfContext?.restoreGState()
    }
}

let pdf = PDFText()

pdf.createPDF()