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.")
}
}
gestrich
(Bill)
2
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()
}
}