概述
在之前的文章中介绍了macOS中如何调用打印功能,用那种方式如果只打印一页的内容,则无妨,如果是多页,则会出现文本前后被截断的问题如下图的效果:
出现这样的问题,最大的原因就是打印view的时候没有处理分页,而是让系统自动分页,导致一个view绘制下来边界会被截断,所以,解决的这个问题的关键在于,如何在每一页只绘制一页所能展示的内容,数据上要做切割,然后还有一点是如何将切割好的数据,转移到对应的页面上去,关于这个问题我研究了好几天,找遍了stackoverflow也没有找到对应的解决方案,都是比较模糊的指向自定义view,去实现分页,我也尝试了,并没有成功,可能是我的做法有问题吧,虽然能分页但是绘制的内容有问题,后面我自己又另辟蹊径找到了别的替代方法。
解决方案
这个问题的解决方案还是的归功于PDFKit,之前我知道这个库是专门处理PDF的,但是不知道如何将编辑框的内容加载到里面去,后面我发现他的API中有能将view转化为data的方法:
var data = view.dataWithPDF(inside: NSMakeRect(0, 0, contentWidth, onePageHeight))
既然能转为PDF data数据,那就好办了。直接创PDFDocument。
let pdfDocument = PDFDocument(data: data)!
能创建PDFDocument,就能通过对应的API调起打印功能:
let print = pdfDocument.printOperation(for: printInfo, scalingMode: .pageScaleNone, autoRotate: true)
print?.showsPrintPanel = true
print?.showsProgressPanel = true
print?.run()
这和创建NSPrintOperation一样,是这个框架自带的打印功能,如果你只是想生存PDF文件,直接调用write方法就行了。不得不说在Mac上,只要你找对了方法实现起来还是很简单的,主要是资料太少了,做的人也不多,一些方法鲜有人知。
我这边项目的需求是将大的文本编辑框中的内容打印或者输出位PDF,如果有相同需求的,可以照搬代码,首先拿到编辑框的内容:
//我这边还需要在最开始的位置添加一些内容
let headerStr = self.addHeaderInfo()
//为了让编辑框内容不受添加头信息的影响,editBoxContent的内容是双向绑定的,更改了也会影响编辑框里面的内容变化。
var readyPrintContent: String = editBoxContent
let index = readyPrintContent.startIndex
//插入到开始位置。
readyPrintContent.insert(contentsOf: headerStr, at: index)
然后将单行内容切割出来,后面要根据每行的高度计算一页的高度,对整体内容进行分页切割。
var printContent: String = ""
let compt = readyPrintContent.components(separatedBy: "\n")
let contentWidth = printInfo.paperSize.width - printInfo.leftMargin - printInfo.rightMargin
let onePageHeight = printInfo.paperSize.height - printInfo.topMargin - printInfo.bottomMargin
var currentPageHeight:CGFloat = 0
//以空一行作为页的切割标志
for i in compt.indices {
//计算文本高度的方法,前面的文章有提到过
let textHeight = compt[i].getheightForComment(fontSize: 12, width: contentWidth)
currentPageHeight = currentPageHeight + textHeight
if currentPageHeight >= onePageHeight - 30 {
currentPageHeight = 0
printContent.append("\n")
}
printContent.append(compt[i]+"\n")
}
然后切割页:
let splitPages = printContent.components(separatedBy: "\n\n")
这样处理之后,页的数据就已经切割好了,然后关键的步骤将页的数据,添加到每一页。
先取第一页的内容,创建PDFDocument:
//PrintView是自己定义的展示内容的view,起始里面就包了个Text。通过NSHostingView转换一下。
let view = NSHostingView(rootView: PrintView(text: splitPages[0]))
//必须要设置frame的size
view.frame.size = CGSize(width: contentWidth, height: onePageHeight)
//转换为PDF data
var data = view.dataWithPDF(inside: NSMakeRect(0, 0, contentWidth, onePageHeight))
//创建pdfDocument
let pdfDocument = PDFDocument(data: data)!
下面是关键步骤,将内容添加到每一页PDF上:
//前面已经绘制了一页了,要从第二页开始。
if splitPages.count > 1 {
for page in 1 ..< splitPages.count - 1{
let view = NSHostingView(rootView: PrintView(text: splitPages[page]))
view.frame.size = CGSize(width: contentWidth, height: onePageHeight)
data = view.dataWithPDF(inside: NSMakeRect(0, 0, contentWidth, onePageHeight))
let pdfPage = PDFPage(image: NSImage(data: data)!)!
pdfDocument.insert(pdfPage, at: pdfDocument.pageCount)
}
}
最后调起打印功能,则大功告成,是不是很简单。
let print = pdfDocument.printOperation(for: printInfo, scalingMode: .pageScaleNone, autoRotate: true)
print?.showsPrintPanel = true
print?.showsProgressPanel = true
print?.run()
发现了方法,做起来就很简单了,可以说找方法用了很长时间,解决问题之用了几分钟,留下解决方案也是希望有同样需求的开发者能少走弯路,我们都在寻着别人的脚步,别人的经验,快速的解决我们遇到的问题,如果我的方案解决了你的问题,别忘了点个赞哦。有问题可以私信我。