Using Core Graphics to create PDF. Can't write the text *banging head*

I included the relevant part of the func.

manipulatedRowsClean is an array of CSV rows. It is cleaned of unprintable characters.

When i run this code, I get a 3-page pdf doc with red cell lines deliniating the places where text ought to go. None of the text is written - when i use print debug statements, I can see that it is all properly there. In my debugging output I get a million '.notdef: no mapping.' errors. This makes me think I have bad characters or my font isn't specified properly. My string data looks good and clean, and I've tried sleecting both Helvetica and system font. I'm pretty new to Swift, so I am sure I am doing something (many things?) wrong here. Any help is greatly appreciated!

 let manipulatedRowsClean = cleanCSVRows(manipulatedRows)
        
        // Define your margins
           let topMargin: CGFloat = 50
           let bottomMargin: CGFloat = 50
        
        // Prepare to create the PDF with Core Graphics
        var pdfPageBounds = CGRect(x: 0, y: 0, width: 612, height: 792) // A4 size
        let pdfData = NSMutableData()
        let pdfConsumer = CGDataConsumer(data: pdfData as CFMutableData)!
        guard let pdfContext = CGContext(consumer: pdfConsumer, mediaBox: &pdfPageBounds, nil) else {
            print("Error: Could not create CGContext.")
            return
        }
        print("CGContext created.")

        let rowHeight: CGFloat = 20
        let columnWidths: [CGFloat] = [100, 100, 100, 100, 100, 100, 100, 100, 100] // Adjust as needed
        var currentY: CGFloat = pdfPageBounds.maxY - topMargin

        // Begin the first page
        pdfContext.beginPage(mediaBox: &pdfPageBounds)
        print("Starting first page. CurrentY is \(currentY)")

        for (index, row) in manipulatedRowsClean.enumerated() {
            print("Starting row \(index + 1)")

            if currentY - rowHeight < bottomMargin { // Adjust condition to account for rowHeight
                pdfContext.endPage() // Finish the current page
                print("Ending page due to reaching bottom margin. CurrentY is \(currentY)")
                
                pdfContext.beginPage(mediaBox: &pdfPageBounds) // Start a new page
                currentY = pdfPageBounds.maxY - topMargin // Reset the Y position
                print("Starting new page. CurrentY reset to \(currentY)")
            }

            let columns = row.components(separatedBy: ",")
            var currentX: CGFloat = pdfPageBounds.minX + 10 // Add a left margin if needed
            print("CurrentX start position is \(currentX)")
            
            for (columnIndex, text) in columns.enumerated() {
                if columnIndex < columnWidths.count {
                    let paragraphStyle = NSMutableParagraphStyle()
                    paragraphStyle.alignment = .left
                    let attributes = [
                        NSAttributedString.Key.font: NSFont(name: "Helvetica", size: 12) ?? NSFont.systemFont(ofSize: 12),
                        NSAttributedString.Key.paragraphStyle: paragraphStyle
                    ]
                    
                  let attributedText = NSAttributedString(string: text, attributes: attributes)
                    
                    //let attributedText = NSAttributedString(string: "bob", attributes: attributes)
                    
                    print("Column \(columnIndex) text is \(text)")
                    
                    let textRect = CGRect(x: currentX, y: currentY - rowHeight, width: columnWidths[columnIndex], height: rowHeight)
                    
                    pdfContext.saveGState() // Save the current graphic state
                    defer { pdfContext.restoreGState() }
                    attributedText.draw(with: textRect, options: .usesLineFragmentOrigin, context: nil)
                   
                    
                    pdfContext.saveGState()
                    defer { pdfContext.restoreGState() }
                    pdfContext.setStrokeColor(NSColor.red.cgColor)
                    pdfContext.setLineWidth(1)
                    pdfContext.addRect(textRect)
                    pdfContext.strokePath()
                  
                    
                    print("Drew text for column \(columnIndex + 1) at rect \(textRect)")
                    currentX += columnWidths[columnIndex]
                } else {
                    print("Warning: More columns in row \(index + 1) than widths specified.")
                }
            }
            
            currentY -= rowHeight // Move to the next row
            print("Finished drawing row \(index + 1). CurrentY is now \(currentY)")
        }

        pdfContext.endPage()
        pdfContext.closePDF()
        print("Finished creating PDF.")


        let sortedFileName = originalFileName.replacingOccurrences(of: ".pdf", with: "_sorted.pdf")
        if let document = PDFDocument(data: pdfData as Data) {
            print("PDFDocument created successfully.")
            savePDFDocument(document, defaultFileName: sortedFileName)
        } else {
            print("Failed to create PDF document from data.")
            addDebugMessage("Failed to create PDF document from data.")
        }
    }

Your question is Apple specific so you may get better help in the Apple dev forums rather than the Swift forums.

I'm not an expert with PDF rendering but here is some simple sample code from another forum. Rendering NSAttributedStrings with… | Apple Developer Forums.

You can compare what is different in that implementation vs yours. Keep adjusting yours until it starts working.

One difference is the use of NSGraphicsContext.current . I'd suggest adding that after you create your context and see what happens:

 NSGraphicsContext.current = NSGraphicsContext(cgContext: pdfContext, flipped: false)

Assuming the text then shows, study the other differences to see if there is anything else useful to use.

Here is a working sample code from a SwiftUI based project.
Perhaps it does help to solve your issue.

 var mediaBox = NSRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 1100, height: 600))
 if let dataConsumer = CGDataConsumer(url: saveURL as CFURL) {
	 if let pdfContext = CGContext(consumer: dataConsumer, mediaBox: &mediaBox, nil) {
		 let options: [CFString: Any] = [kCGPDFContextMediaBox: mediaBox]
		 for aDay in Weekdays.allCases {
			 pdfContext.beginPDFPage(options as CFDictionary)

			 let renderer = ImageRenderer(content: DayView(day: aDay).environmentObject(appData))
			 renderer.render { size, renderFunction in
				 pdfContext.translateBy(x: (mediaBox.width - size.width) / 2.0,
										y: (mediaBox.height - size.height) / 2.0)
				 renderFunction(pdfContext)
			 }

			 /// Add a day header to each page which is missing due to the segmented control on screen
			 /// add also the document name
			 let titleString = "\(docNAme ?? "Ohne Titel") - \(aDay.rawValue)"
			 let attrs : [NSAttributedString.Key : Any] = [.font: NSFont.boldSystemFont(ofSize: 16.0)]
			 let day  = NSAttributedString(string: titleString, attributes: attrs)
			 let path = CGMutablePath()
			 let strWidth  = day.size().width + 1
			 let strHeight = day.size().height + 1
			 let dayXPos   = (mediaBox.width - strWidth) / 2.0
			 let dayYPos   = mediaBox.height - strHeight - 35
			 path.addRect(CGRect(x: dayXPos, y: dayYPos, width: strWidth, height: strHeight))
			 let fSetter = CTFramesetterCreateWithAttributedString(day as CFAttributedString)
			 let frame = CTFramesetterCreateFrame(fSetter, CFRangeMake(0, day.length), path, nil)
			 CTFrameDraw(frame, pdfContext)

			 pdfContext.endPDFPage()
		 }
		 pdfContext.closePDF()
	 }
 }