Files
OneCAD-Draft/crates/dwg-thumbnailer/macos/ThumbnailProvider.swift
T
Felix RiestraandClaude Sonnet 5 79028bcee4 Fix ad-hoc appex signing and thumbnail scaling for QuickLook (#365)
The ad-hoc codesign path signed the whole bundle with one --deep pass,
which never carried --entitlements to the QuickLook appex, so PlugInKit
silently rejected it as unsandboxed. Sign nested code individually
instead, matching the Developer ID path.

Separately, ThumbnailProvider drew the extracted preview at its native
pixel size anchored at the CGContext origin (bottom-left) instead of
scaling it to fill request.maximumSize, producing a small thumbnail
stuck in one corner instead of a properly sized, centered icon.

Note: Gatekeeper still rejects an ad-hoc or unnotarized Developer-ID
signature outright, and PlugInKit periodically re-validates and drops
any registered plugin that fails that check — so these fixes are
necessary but not sufficient for thumbnails to render from a build
that isn't both signed and notarized.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 16:43:28 +02:00

60 lines
2.5 KiB
Swift

// macOS QuickLook thumbnail extension for DWG files.
//
// Calls the Rust core (`dwg_thumbnail_png`, linked from libdwg_thumbnailer.a)
// to extract the embedded preview as PNG, then hands it to QuickLook.
//
// Add this file to a "Thumbnail Extension" target, expose the C ABI through a
// bridging header that #includes `dwg_thumbnailer.h`, and link the static lib
// (see README). QLSupportedContentTypes must list the DWG UTI (com.autodesk.dwg).
import QuickLookThumbnailing
import CoreGraphics
import ImageIO
import Foundation
final class ThumbnailProvider: QLThumbnailProvider {
override func provideThumbnail(
for request: QLFileThumbnailRequest,
_ handler: @escaping (QLThumbnailReply?, Error?) -> Void
) {
let maxDim = UInt32(max(request.maximumSize.width, request.maximumSize.height))
var ptr: UnsafeMutablePointer<UInt8>? = nil
var len: Int = 0
let ok = request.fileURL.path.withCString { cpath in
dwg_thumbnail_png(cpath, maxDim, &ptr, &len)
}
guard ok, let p = ptr, len > 0 else {
handler(nil, nil) // no preview → QuickLook falls back
return
}
let data = Data(bytes: p, count: len)
dwg_thumbnail_free(p, len)
guard
let src = CGImageSourceCreateWithData(data as CFData, nil),
let cg = CGImageSourceCreateImageAtIndex(src, 0, nil)
else {
handler(nil, nil)
return
}
// The embedded DWG preview bitmap is often much smaller than the
// requested icon size. Scale it up (or down) to fit request.maximumSize
// and center it — otherwise QuickLook draws it at its native pixel size
// anchored at the CGContext origin (bottom-left), producing a small
// thumbnail stuck in the corner instead of filling the icon (#365).
let maxSize = request.maximumSize
let nativeSize = CGSize(width: cg.width, height: cg.height)
let scale = min(maxSize.width / nativeSize.width, maxSize.height / nativeSize.height)
let drawSize = CGSize(width: nativeSize.width * scale, height: nativeSize.height * scale)
let origin = CGPoint(x: (maxSize.width - drawSize.width) / 2, y: (maxSize.height - drawSize.height) / 2)
let reply = QLThumbnailReply(contextSize: maxSize) { (ctx: CGContext) -> Bool in
ctx.draw(cg, in: CGRect(origin: origin, size: drawSize))
return true
}
handler(reply, nil)
}
}