diff --git a/Sources/Kapture/App.swift b/Sources/Kapture/App.swift index 6e43cd6..d6ff6d3 100644 --- a/Sources/Kapture/App.swift +++ b/Sources/Kapture/App.swift @@ -119,33 +119,47 @@ enum LoginItem { } } -func runOCRSelfTest() { - let text = "Kapture OCR test 1234, hello world" - let size = NSSize(width: 1600, height: 220) +func renderTestImage(_ text: String, fontSize: CGFloat = 56) -> CGImage? { + let size = NSSize(width: 1900, height: 220) guard let rep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0 - ) else { exit(1) } - + ) else { return nil } NSGraphicsContext.saveGraphicsState() NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep) NSColor.white.setFill() NSRect(origin: .zero, size: size).fill() let attrs: [NSAttributedString.Key: Any] = [ - .font: NSFont.boldSystemFont(ofSize: 56), + .font: NSFont.systemFont(ofSize: fontSize), .foregroundColor: NSColor.black, ] text.draw(at: NSPoint(x: 60, y: 75), withAttributes: attrs) NSGraphicsContext.restoreGraphicsState() + return rep.cgImage +} - guard let cgImage = rep.cgImage else { exit(1) } - OCRService.shared.recognize(cgImage) { recognized in - print("expected: \(text)") - print("got: \(recognized)") - let ok = recognized == text - print(ok ? "PASS" : "FAIL") - exit(ok ? 0 : 1) +func runOCRSelfTest() { + let cases: [(expected: String, contains: String)] = [ + ("Kapture OCR test 1234, hello world", "Kapture OCR test 1234"), + ("你好世界,这是一个测试", "你好世界"), + ] + var index = 0 + + func runNext() { + guard index < cases.count else { exit(0) } + let current = cases[index] + index += 1 + guard let image = renderTestImage(current.expected) else { exit(1) } + OCRService.shared.recognize(image) { recognized in + print("case: \(current.expected)") + print("got: \(recognized)") + let ok = recognized.contains(current.contains) + print(ok ? "PASS" : "FAIL") + if !ok { exit(1) } + runNext() + } } + runNext() RunLoop.main.run() } diff --git a/Sources/Kapture/OCRService.swift b/Sources/Kapture/OCRService.swift index e64d11b..ca321ff 100644 --- a/Sources/Kapture/OCRService.swift +++ b/Sources/Kapture/OCRService.swift @@ -12,6 +12,11 @@ final class OCRService { 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 { diff --git a/build-app.sh b/build-app.sh index a7510d9..523f98d 100755 --- a/build-app.sh +++ b/build-app.sh @@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST' CFBundleIconFile Icon CFBundleShortVersionString - 1.9.0 + 1.9.1 CFBundleVersion - 90 + 91 LSUIElement NSHighResolutionCapable