NSView 和 PDF 不同的输出
Posted
技术标签:
【中文标题】NSView 和 PDF 不同的输出【英文标题】:NSView and PDF different output 【发布时间】:2020-08-24 16:45:57 【问题描述】:适用于 macOS 和 Swift 5.3
我正在使用 Quartz 生成 PDF,通过使用屏幕外 CALayers 并通过我找到的 CALayer 扩展将组合层转换为 NSImage here。
但是,cgpath 行的屏幕质量与 PDF 质量相差很大。
如您所见,左侧的图像(均以 100% 缩放)(屏幕输出)对于我的 cgpath 有窄线,而右侧的图像(pdf 输出)路径更大且模糊。此外,这两种情况下的文本都是模糊的。什么会导致这种情况发生,我该如何解决它以使 cgpath 线条清晰(并且像屏幕上一样小)并且文本清晰?如果我减小路径的 lineWidth 不会使它们在 PDF 中变小,它会使它们更加不透明。我正在使用系统字体 Helvetica 作为文本。
支持层是一个单独的 CALayer,我添加了一些 CAShapeLayers、CATextLayers 和 CALayers 来制作一个视图,然后按照上述方法将组合层转换为 NSImage,并将其保存为 PDF 文档。
这是我用来生成 PDF 的代码:
NSBezierPath 扩展:
extension NSBezierPath
// Credit - Henrick - 9/18/2016
// https://***.com/questions/1815568/how-can-i-convert-nsbezierpath-to-cgpath
public var cgPath: CGPath
let path = CGMutablePath()
var points = [CGPoint](repeating: .zero, count: 3)
for i in 0 ..< self.elementCount
let type = self.element(at: i, associatedPoints: &points)
switch type
case .moveTo:
path.move(to: points[0])
case .lineTo:
path.addLine(to: points[0])
case .curveTo:
path.addCurve(to: points[2], control1: points[0], control2: points[1])
case .closePath:
path.closeSubpath()
@unknown default:
print("Error occurred in NSBezierPath extension.")
return path
CALayer 扩展:
extension CALayer
/*
Credit - Robert Myran - 12/29/2016
https://***.com/questions/41386423/get-image-from-calayer-or-nsview-swift-3
*/
/// Get `NSImage` representation of the layer.
///
/// - Returns: `NSImage` of the layer.
func image() -> NSImage
let width = 612
let height = 792
let imageRepresentation = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: width, pixelsHigh: height, bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSColorSpaceName.deviceRGB, bytesPerRow: 0, bitsPerPixel: 0)!
imageRepresentation.size = CGSize(width: width, height: height)
let context = NSGraphicsContext(bitmapImageRep: imageRepresentation)!
render(in: context.cgContext)
return NSImage(cgImage: imageRepresentation.cgImage!, size: bounds.size)
NSImage 扩展:
extension NSImage
/*
Credit - Xue Yu - 7/9/2017
https://gist.github.com/KrisYu/83d7d97cae35a0b10fd238e5c86d288f
*/
var toCGImage: CGImage
var imageRect = NSRect(x: 0, y: 0, width: size.width, height: size.height)
guard let image = cgImage(forProposedRect: &imageRect, context: nil, hints: nil) else
abort()
return image
网格绘制方法:
func insertGrid() -> CALayer
/*
Draws a single table grid of 25 boxes (5 high by 5 wide)
centered on a letter sized page
*/
// Create a new shape layer for the grid
let gridLayer = CAShapeLayer()
// Assign the grid fill and stroke colors
gridLayer.strokeColor = NSColor.black.cgColor
gridLayer.fillColor = NSColor.clear.cgColor
// Create the path
let gridPath = NSBezierPath()
gridPath.lineWidth = 0.25
// Draw the paths for the grid
// Create the outside box
gridPath.move(to: CGPoint(x: col1X, y: bottomY)) // Bottom left corner
gridPath.line(to: CGPoint(x: col1X, y: topY)) // Column 1, left line
gridPath.line(to: CGPoint(x: rightX, y: topY)) // Top line of row
gridPath.line(to: CGPoint(x: rightX, y: bottomY)) // Column 3 right line
gridPath.line(to: CGPoint(x: col1X, y: bottomY)) // Bottom line of row
// Add in column lines
gridPath.move(to: CGPoint(x: col2X, y: topY)) // Between columns 1 & 2
gridPath.line(to: CGPoint(x: col2X, y: bottomY)) // Line between columns 1 & 2
gridPath.move(to: CGPoint(x: col3X, y: topY)) // Between columns 2 & 3
gridPath.line(to: CGPoint(x: col3X, y: bottomY)) // Line between columns 2 & 3
// Close the path
gridPath.close()
// Add grid to layer (note the use of the cgPath extension)
gridLayer.path = gridPath.cgPath
return gridLayer
创建PDF方法:
func createPDF(image: NSImage) -> NSData
/*
Credit - Xue Yu - 7/9/2017
https://gist.github.com/KrisYu/83d7d97cae35a0b10fd238e5c86d288f
*/
let pdfData = NSMutableData()
let pdfConsumer = CGDataConsumer(data: pdfData as CFMutableData)!
var mediaBox = NSRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height)
let pdfContext = CGContext(consumer: pdfConsumer, mediaBox: &mediaBox, nil)!
pdfContext.beginPage(mediaBox: &mediaBox)
pdfContext.draw(image.toCGImage , in: mediaBox)
pdfContext.endPage()
return pdfData
我这样称呼它:
createPDF(image: pdfBaseLayer.image())?.write(to: pdfFileURL, atomically: true)
有没有办法将其构建为矢量图像,还是没有必要?
这就是我的工作:
-
将 pdfBaseLayer 创建为 CALayer
将 gridLayer 创建为 CAShapeLayer,然后执行
pdfBaseLayer.addSublayer(gridLayer)
将 textLayer 创建为三个单独的 CATextLayer 子层和
然后是 pdfBaseLayer.addSubLayer(textLayer)。
如果您需要更多代码,请告诉我。
【问题讨论】:
稍微想一想,在开始生成 PDF 之前,我就知道它有多少页。为了获得清晰的线条和更清晰的文本,我是否应该只创建一个具有count
空白页长的 PDF 文档,然后将路径和文本直接绘制到 PDF 中?
【参考方案1】:
问题已解决。我将 CALayer 作为 CGContext 插入到 pdf 中。本质上,据我所知,它是将 CGContext 转换为图像,然后将其放入 pdf 中。从那以后,我改变了我的方法,放弃创建 CALayer 直接写入 CGContext,然后将其写入 pdf。这种方式让一切都超级清晰。
【讨论】:
以上是关于NSView 和 PDF 不同的输出的主要内容,如果未能解决你的问题,请参考以下文章
从具有相同名称但不同扩展名的文件(*.pdf 和 *.rtf)中,如果 *.rtf 较新,则在控制台上输出消息