I'm trying to implement printing for a macOS app. I've created a custom NSView class to handle it as described in the Apple Developer documentation. To facilitate multi-page printing, I've followed the guidance at the Laying Out Page Content page.
My PrintView class code looks like this:
import Cocoa
class PrintView: NSView {
let configuration: PrintConfiguration // a struct with some margin info and helpers
let info = NSPrintInfo.shared
private(set) var pageNum = 0
private(set) var ranges: [NSRange] = [] // array of pre-calculated page content
private(set) var content: NSAttributedString
override var isFlipped: Bool {
return true
}
init(printContent: NSAttributedString, config: PrintConfiguration, ranges: [NSRange]) {
self.content = printContent
self.ranges = ranges
self.configuration = config
super.init(frame: CGRect(origin: .zero, size: info.paperSize))
self.frame.size.height = config.printHeight
info.verticalPagination = .automatic
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ dirtyRect: NSRect) {
print("Entering draw - dirtyRect: \(dirtyRect)")
let rangeNum = pageNum - 1 // adjust to 0-based for accessing array
let range = ranges[rangeNum]
let text = self.content.attributedSubstring(from: range)
let drawRect = configuration.printRect
text.draw(in: drawRect)
}
override func knowsPageRange(_ range: NSRangePointer) -> Bool {
print("Entering knowsPageRange")
range.pointee.location = 1
range.pointee.length = self.ranges.count
print("Page range: \(range.pointee)")
return true
}
override func rectForPage(_ page: Int) -> NSRect {
print("Entering rectForPage")
self.pageNum = page
let pageHeight = configuration.printHeight
let pageNum = CGFloat(page - 1)
let printRect = CGRect(x: configuration.leftMargin,
y: (pageNum * pageHeight) + configuration.topMargin,
width: configuration.printWidth,
height: configuration.printHeight)
print("Print rect: \(printRect)")
return printRect
}
override func locationOfPrintRect(_ rect: NSRect) -> NSPoint { // only included this method to see if it's called -- it doesn't appear to be required for my implementation
print("Entering locationOfPrintRect")
return NSPoint(x: self.configuration.leftMargin, y: self.configuration.topMargin)
}
}
Then from a view's print method, I can call it thusly:
let printView = PrintView(printContent: text, config: printConfiguration, ranges: ranges)
let info = NSPrintInfo.shared
let printOp = NSPrintOperation(view: printView, printInfo: info)
printOp.jobTitle = title // a view property
printOp.canSpawnSeparateThread = true
printOp.run()
And when I invoke that from my running application with text that should extend to two pages, I get the following in my console:
Entering knowsPageRange
Page range: {1, 2}
Entering knowsPageRange
Page range: {1, 2}
Entering rectForPage
Print rect: (40.0, 80.0, 532.0, 652.0)
Entering locationOfPrintRect
Entering draw - dirtyRect: (40.0, 80.0, 532.0, 572.0)
Entering rectForPage
Print rect: (40.0, 732.0, 532.0, 652.0)
Entering locationOfPrintRect
In other words, all the methods I expect to be called twice (once for each page) are called twice (or more in the case of knowsPageRange), except for the draw method. And as expected with that behavior, only the first page is rendered. The print preview dialog does show two pages to be printed, but the second page is blank.
Likewise if I choose the print dialog option to send the print job to the Preview app, it only shows the first page of text.
After writing my question initially I walked the dog and it occurred to me that I should try it with text that should extend the text to more than two pages worth. When I got back and did that, it again created the correct number of pages, but only draws text for the first page. However, with 6 pages worth of text, the pagination methods are only called three times:
Entering knowsPageRange
Page range: {1, 6}
Entering knowsPageRange
Page range: {1, 6}
Entering rectForPage
Print rect: (40.0, 80.0, 532.0, 652.0)
Entering locationOfPrintRect
Entering draw - dirtyRect: (40.0, 80.0, 532.0, 572.0)
Entering rectForPage
Print rect: (40.0, 732.0, 532.0, 652.0)
Entering locationOfPrintRect
Entering rectForPage
Print rect: (40.0, 1384.0, 532.0, 652.0)
Entering locationOfPrintRect
I'd appreciate it if somebody can tell me what I'm doing wrong.
Thanks to @Willeke steering me in the right direction, my code now works with the following change to the rectForPage
method:
override func rectForPage(_ page: Int) -> NSRect {
self.pageNum = page
let printRect = CGRect(x: configuration.leftMargin,
y: configuration.topMargin,
width: configuration.printWidth,
height: configuration.info.paperSize.height - configuration.bottomMargin)
return printRect
}