是否可以访问桌面视图将其绘制为 PDF?

Is it possible to access the Desktop view to draw it to PDF?

提问人:Peter R 提问时间:11/5/2023 更新时间:11/5/2023 访问量:47

问:

我有一个应用程序,我想为其制作 Mac App Store 屏幕截图。这是一个菜单栏应用程序,因此定期截取屏幕截图会导致图像分辨率非常低。我已经弄清楚了如何将我的应用程序视图截图为 PDF,然后将 PDF 转换为 SVG,然后将 SVG 转换为 PNG。获取屏幕截图的方式有点迂回,但它有效,并且可以以高分辨率制作,这正是我想要的。我的问题是,除了我的电脑桌面之外,还有没有类似的事情?我基本上想截取桌面视图的屏幕截图,并将其写入 PDF,就像我为应用程序视图所做的那样。

以下是我目前为我自己的应用程序视图执行此操作的方法。

这是我的 NSPopover 子类上的一个函数,所以这就是它的来源。contentViewController!.view

let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("\(Int(Date().timeIntervalSince1970 * 1000)).pdf") as CFURL
let size = Defaults[.windowSize]
var mediaBox = NSRect(origin: .zero, size: size)
let gc = CGContext(url, mediaBox: &mediaBox, nil)!
let nsgc = NSGraphicsContext(cgContext: gc, flipped: false)
NSGraphicsContext.current = nsgc
gc.beginPDFPage(nil); do {
  let view = contentViewController!.view as! NSHostingView<ContentView>
  gc.saveGState(); do {
    gc.drawFlipped(rect: mediaBox) {
      view.layer?.render(in: gc)
    }
  }; gc.restoreGState()

}; gc.endPDFPage()
NSGraphicsContext.current = nil
gc.closePDF()

我基本上想知道是否有某种方法可以替换为访问桌面视图的东西(最好是菜单栏。contentViewController!.view

Swift macOS AppKit

评论

0赞 jnpdx 11/5/2023
我不确定我是否理解将其写入 PDF 的意义,或者为什么“菜单栏应用程序如此常规截屏会导致分辨率非常低的图像”,但 ScreenCaptureKit 现在包含用于获取屏幕截图的 API(您的应用程序或其他应用程序,包括桌面)
0赞 Peter R 11/5/2023
@jnpdx 将其写入 PDF 的目的是我得到一个矢量图像。菜单应用程序具有较低分辨率图像的原因是窗口很小,而不是全屏。因此,如果我打开应用程序,并截取屏幕的屏幕截图,应用程序本身仅占屏幕的 5-10% 左右。如果我然后放大它以便在应用商店图像中更清晰地显示内容,分辨率现在非常低。使用PDF矢量图像,我可以使它达到帝国大厦的大小,并且仍然清晰。
1赞 jnpdx 11/5/2023
你无法获取 Mac 桌面的位图(或任何位图)并以某种方式将其转换为矢量 - 你得到的最好的是写入 PDF 的位图
1赞 jnpdx 11/5/2023
您无法获得 Mac 桌面的矢量。
2赞 jnpdx 11/5/2023
不,桌面不仅仅是“视图”。此外,它不归你的应用所有,因此你无权访问任何基础 NSView。API (ScreenCaptureKit 和 CoreGraphics,如下面的答案中引用)返回位图图像。即使您确实有权访问 NSView,文本桌面图像也是位图,不会进行矢量化。

答:

0赞 Keyboard Corporation 11/5/2023 #1

也许这在你身上行不通,但我希望是这样。因此,通过这样做,请使用 Core Graphics 框架中的函数。此函数创建指定窗口的图像。CGWindowListCreateImage

例:

let windowListOption = CGWindowListOption(arrayLiteral: .optionIncludingWindow)
let windowID = CGWindowID(yourWindow.windowNumber)
let imageOption = CGWindowImageOption(arrayLiteral: .boundsIgnoreFraming)

let image = CGWindowListCreateImage(CGRect.null, windowListOption, windowID, imageOption)

在上面的代码中,是要捕获的窗口。您可以从实例中获取它。该函数返回一个,然后您可以在上下文中绘制该函数。yourWindowNSWindowCGWindowListCreateImageCGImage

如果要捕获整个屏幕(包括菜单栏),可以使用该类创建屏幕的位图表示形式,然后在上下文中绘制它。NSBitmapImageRep

例如:

let screenRect = NSScreen.main!.frame
let bitmap = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(screenRect.width), pixelsHigh: Int(screenRect.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .calibratedRGB, bytesPerRow: 0, bitsPerPixel: 0)!

let context = NSGraphicsContext(bitmapImageRep: bitmap)!
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = context

yourWindow.contentView!.draw(screenRect)

NSGraphicsContext.restoreGraphicsState()

yourWindow是要捕获的窗口。的方法在目标的指定矩形中绘制视图。然后,您可以使用 创建图像并在上下文中绘制它。drawNSViewbitmap

*由于沙盒限制,此方法不会捕获不属于应用程序的窗口内容。此外,无法捕获菜单栏,因为它是一个单独的进程,无法由应用程序捕获。

评论

1赞 jnpdx 11/5/2023
这些是已弃用的 API,但它们仍然有效。对于高分辨率图像,应将标志包含在bestResolutionCGWindowListCreateImage
0赞 Keyboard Corporation 11/5/2023
谢谢你,我也这么认为,但由于我不再使用它,很抱歉我无法准确地提供帮助。