import AppKit import Vision final class OCRService { static let shared = OCRService() private let queue = DispatchQueue(label: "dev.tzgyn.kapture.ocr", qos: .userInitiated) func recognize(_ cgImage: CGImage, completion: @escaping (String) -> Void) { queue.async { let request = VNRecognizeTextRequest() request.recognitionLevel = .accurate request.usesLanguageCorrection = true request.recognitionLanguages = ["en-US", "zh-Hans", "zh-Hant", "ja-JP", "ko-KR"] // Without this, Vision defaults to the first listed language and // renders CJK as Latin garbage (verified: 你好世界 -> "177WR"). if #available(macOS 13.0, *) { request.automaticallyDetectsLanguage = true } let handler = VNImageRequestHandler(cgImage: cgImage, options: [:]) do { try handler.perform([request]) } catch { completion("") return } let observations = request.results ?? [] let lines = observations .sorted { a, b in let ay = a.boundingBox.midY let by = b.boundingBox.midY if abs(ay - by) > 0.02 { return ay > by } return a.boundingBox.minX < b.boundingBox.minX } .compactMap { $0.topCandidates(1).first?.string } completion(lines.joined(separator: "\n")) } } }