{"_id":"@craftthingy-digital-innovation/cty-paddle-ocr","_rev":"5-3263c0c6c39d9d5fa3861949eb5ad7a4","name":"@craftthingy-digital-innovation/cty-paddle-ocr","dist-tags":{"latest":"1.0.4"},"versions":{"1.0.0":{"name":"@craftthingy-digital-innovation/cty-paddle-ocr","version":"1.0.0","_id":"@craftthingy-digital-innovation/cty-paddle-ocr@1.0.0","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"dist":{"shasum":"97969b1faf9ce7a3d2fb67f68d5e61dbf31cfbed","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-paddle-ocr/-/cty-paddle-ocr-1.0.0.tgz","fileCount":57,"integrity":"sha512-rAoBsGo4vcrAC0ztzGN7UgR4pxQm3U1RnPI9A4XtdDdTLQYede03Y89uDh1eA/KciGcYQ2OdWjqqOhqn86PRow==","signatures":[{"sig":"MEYCIQDDh7INHHd2KdLbZojDOBbcMSbvTJtJEoFs+jp63XxoKwIhAJJkwHvZDa77vI0M3S3zRod7y/2SZuFRa8sWQufzMyne","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180524},"main":"./dist/paddle-ocr-client.umd.js","type":"module","module":"./dist/paddle-ocr-client.es.js","exports":{".":{"import":"./dist/paddle-ocr-client.es.js","require":"./dist/paddle-ocr-client.umd.js"}},"gitHead":"8742ca5664c3da10e0524ab38a423f39f432ab4b","scripts":{"build":"vite build"},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"_npmVersion":"11.6.1","description":"[Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","directories":{},"_nodeVersion":"23.11.0","dependencies":{"@napi-rs/canvas":"^0.1.69","onnxruntime-web":"^1.21.0","path-browserify":"^1.0.1","@techstark/opencv-js":"^4.10.0-release.1"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cty-paddle-ocr_1.0.0_1783062593746_0.08837616215365407","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@craftthingy-digital-innovation/cty-paddle-ocr","version":"1.0.1","_id":"@craftthingy-digital-innovation/cty-paddle-ocr@1.0.1","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr#readme","bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr/issues"},"dist":{"shasum":"b906b45242166d92da1ed7da55296fd381b97d5a","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-paddle-ocr/-/cty-paddle-ocr-1.0.1.tgz","fileCount":57,"integrity":"sha512-giYsN+xkr3tZL87VaoX3hQxg8cXYYeABItx2Fa6C5X05xFcAH9xGhxEei6m6SI09AhTcqcHZ2X2VChrc/4Ro5A==","signatures":[{"sig":"MEUCIHC8SmNEyqA9vZUL8aJi6o5n4vdvHnp2/ODth/z1m9O+AiEA9bneIQ3nbyAFHVVqcbqAAMVOyg+K7WcYtJSP/mR6kaI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180843},"main":"./dist/paddle-ocr-client.umd.js","type":"module","module":"./dist/paddle-ocr-client.es.js","exports":{".":{"import":"./dist/paddle-ocr-client.es.js","require":"./dist/paddle-ocr-client.umd.js"}},"gitHead":"0df261d3a9b236d5767960b96c60170a50b3273e","scripts":{"build":"vite build"},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"repository":{"url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr.git","type":"git"},"_npmVersion":"11.6.1","description":"[Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","directories":{},"_nodeVersion":"23.11.0","dependencies":{"@napi-rs/canvas":"^0.1.69","onnxruntime-web":"^1.21.0","path-browserify":"^1.0.1","@techstark/opencv-js":"^4.10.0-release.1"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cty-paddle-ocr_1.0.1_1783062714873_0.9045489480859019","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@craftthingy-digital-innovation/cty-paddle-ocr","version":"1.0.2","license":"SEE LICENSE IN LICENSE","_id":"@craftthingy-digital-innovation/cty-paddle-ocr@1.0.2","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr#readme","bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr/issues"},"dist":{"shasum":"bbe641491abe50b185731be109d1ec8b617c6ebe","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-paddle-ocr/-/cty-paddle-ocr-1.0.2.tgz","fileCount":57,"integrity":"sha512-xJDWLrp3MnKyyd7A/LR+aNKewYHSf29p+kVSt4pVu78lpKwwYPZULmfW0xFbyppobymP77/6ydGNHMq5LAGcKQ==","signatures":[{"sig":"MEYCIQCui52AhBXonwE67YZu7uVtH4doLuWAAXQKb3FCruNbIQIhAMFR7+a//D08YFpTh5fEfTSGw8nfE1iO34W0EMkBxj4o","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180882},"main":"./dist/paddle-ocr-client.umd.js","type":"module","module":"./dist/paddle-ocr-client.es.js","exports":{".":{"import":"./dist/paddle-ocr-client.es.js","require":"./dist/paddle-ocr-client.umd.js"}},"gitHead":"0108122eee5ae0223d205b9d94ada33121917939","scripts":{"build":"vite build"},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"repository":{"url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr.git","type":"git"},"_npmVersion":"10.8.2","description":"[Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","directories":{},"_nodeVersion":"20.20.2","dependencies":{"@napi-rs/canvas":"^0.1.69","onnxruntime-web":"^1.21.0","path-browserify":"^1.0.1","@techstark/opencv-js":"^4.10.0-release.1"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cty-paddle-ocr_1.0.2_1783146817468_0.6405960917716031","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@craftthingy-digital-innovation/cty-paddle-ocr","version":"1.0.3","license":"SEE LICENSE IN LICENSE","_id":"@craftthingy-digital-innovation/cty-paddle-ocr@1.0.3","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr#readme","bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr/issues"},"dist":{"shasum":"6048298e9b719f45401b7bd33d844f454b8ec7db","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-paddle-ocr/-/cty-paddle-ocr-1.0.3.tgz","fileCount":57,"integrity":"sha512-+8GUquERNznl6wQVEfTc2Tqpsi/FLcrqcDwrWzwg+GRt3SPv/fWatBK76ALUx2oPx81jCy9V0z+NuYJKqeR11g==","signatures":[{"sig":"MEQCIHYiPXQyNuVeKfV0w11DMD3XwCrmBS5UIudUsN3inCS6AiAK7xenTTjogmjALeaqwpxvJc7gRPQoIj/47f4NXKDj4g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":182442},"main":"./dist/paddle-ocr-client.umd.js","type":"module","module":"./dist/paddle-ocr-client.es.js","exports":{".":{"import":"./dist/paddle-ocr-client.es.js","require":"./dist/paddle-ocr-client.umd.js"}},"gitHead":"6372f7c2aee779bbe464421e94f5655154cba3d4","scripts":{"build":"vite build"},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"repository":{"url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr.git","type":"git"},"_npmVersion":"10.8.2","description":"[Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","directories":{},"_nodeVersion":"20.20.2","dependencies":{"@napi-rs/canvas":"^0.1.69","onnxruntime-web":"^1.21.0","path-browserify":"^1.0.1","@techstark/opencv-js":"^4.10.0-release.1"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^5.2.0"},"_npmOperationalInternal":{"tmp":"tmp/cty-paddle-ocr_1.0.3_1783397753063_0.4588190601661273","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@craftthingy-digital-innovation/cty-paddle-ocr","version":"1.0.4","license":"SEE LICENSE IN LICENSE","type":"module","repository":{"type":"git","url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr.git"},"bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr/issues"},"homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr#readme","main":"./dist/paddle-ocr-client.umd.js","module":"./dist/paddle-ocr-client.es.js","exports":{".":{"import":"./dist/paddle-ocr-client.es.js","require":"./dist/paddle-ocr-client.umd.js"}},"scripts":{"build":"vite build"},"dependencies":{"@napi-rs/canvas":"^0.1.69","@techstark/opencv-js":"^4.10.0-release.1","onnxruntime-web":"^1.21.0","path-browserify":"^1.0.1"},"devDependencies":{"vite":"^5.2.0"},"_id":"@craftthingy-digital-innovation/cty-paddle-ocr@1.0.4","gitHead":"46ef5fa96c25cb928cdef7b752ae81bab82011a9","description":"[Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-9WdqYYAqHNs8jBt2Xf9HgqhLmzPO0p5FZHUnrTRFcuAosKT48VnpWRQobFX/1u/No21DYimzqBGtKK/ALH7VWw==","shasum":"6caec37393209a704365ecfd72f060a880a9717e","tarball":"https://registry.npmjs.org/@craftthingy-digital-innovation/cty-paddle-ocr/-/cty-paddle-ocr-1.0.4.tgz","fileCount":57,"unpackedSize":182425,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDeQRaYiC8MLb+CX3/8vvySSZ5P9YiV+2N/cQg3OJgdmwIhANemYlbrV0t7nHmc4O7u3NeeNyyPU60XrAlM4P/F3I56"}]},"_npmUser":{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"},"directories":{},"maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cty-paddle-ocr_1.0.4_1783398477352_0.7402509944198559"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-03T07:09:53.623Z","modified":"2026-07-07T04:27:57.655Z","1.0.0":"2026-07-03T07:09:53.890Z","1.0.1":"2026-07-03T07:11:55.002Z","1.0.2":"2026-07-04T06:33:37.601Z","1.0.3":"2026-07-07T04:15:53.240Z","1.0.4":"2026-07-07T04:27:57.530Z"},"bugs":{"url":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr/issues"},"license":"SEE LICENSE IN LICENSE","homepage":"https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr#readme","repository":{"type":"git","url":"git+https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr.git"},"description":"[Bahasa Indonesia](#bahasa-indonesia) | [English](#english)","maintainers":[{"name":"killerking69","email":"alifnurhidayatwork@gmail.com"}],"readme":"# Client-Side PaddleOCR Compiler & Bundler\n\n[Bahasa Indonesia](#bahasa-indonesia) | [English](#english)\n\n---\n\n## Bahasa Indonesia\n\nProyek ini merupakan **cty-paddle-ocr**, sebuah library (SDK) PaddleOCR mandiri, ringkas, dan berkinerja tinggi yang berjalan secara **isomorphic** (baik di sisi client/web browser menggunakan WebAssembly maupun di sisi server-side Node.js).\n\nPustaka ini tidak lagi bergantung pada dependency eksternal `ppu-paddle-ocr` atau `ppu-ocv`. Seluruh logika pemrosesan gambar OpenCV (`cty-ocv`) dan eksekusi ONNX Runtime (`cty-ocr`) telah di-porting secara lokal dan disempurnakan. Modul ini secara dinamis berpindah ke pemrosesan sekuensial (dengan jeda `setTimeout` yield) di browser untuk mencegah tab membeku, dan berjalan paralel (multi-threaded) di Node.js untuk performa server tertinggi.\n\n### 1. Cara Kerja & Shimming Engine\n\nModul dikompilasi menggunakan bundler **Vite** dalam *Library Mode* dengan konfigurasi alias khusus pada `vite.config.js`. Berikut adalah detail shim yang disematkan:\n\n1.  **Canvas Shim (`browser-canvas-shim.js`)**:\n    Menangkap impor `@napi-rs/canvas` (pustaka biner canvas Node) dan mengalihkan seluruh pemanggilan method-method gambarnya ke objek global browser asli (`HTMLCanvasElement`, `document.createElement('canvas')`, dan `new Image()`). Ini memungkinkan pembuatan canvas secara dinamis di dalam browser.\n2.  **Filesystem Shim (`browser-fs-shim.js`)**:\n    Mengganti pemanggilan sinkron Node `fs.readFileSync` dengan XHR sinkron (`XMLHttpRequest`) yang dikonfigurasikan dengan `overrideMimeType('text/plain; charset=x-user-defined')`. Teknik ini memaksa browser mengunduh biner model ONNX sebagai aliran byte raw tanpa merusak struktur filenya, menghindari kesalahan parser browser.\n3.  **URL-Aware Path Shim (`browser-path-shim.js`)**:\n    Menggantikan parser POSIX `path-browserify` dengan wrapper kustom. Ketika mendeteksi URL absolute (`http://` atau `https://`), modul ini langsung mengembalikan nilainya secara utuh tanpa merusak karakter double-slash (`//`) menjadi single-slash.\n4.  **WASM Sequential Engine (`main.js`)**:\n    Menyusun inisialisasi AI secara asinkron (`ort.InferenceSession.create(url)`).\n    **Crucial Browser Optimization**: Meng-override method `processBoxesInParallel` milik `RecognitionService` agar berjalan secara **Sekuensial** (satu-demi-satu) dan menambahkan jeda mikro `setTimeout(resolve, 10)` sebelum memproses tiap kotak gambar. Ini mencegah runtime WebAssembly (WASM) membekukan / me-lock main thread GUI browser Anda saat memproses puluhan kotak deteksi sekaligus.\n\n### 2. Cara Menginstal & Membangun Bundle\n\n#### Langkah A: Persiapan Awal\nPastikan Anda memiliki Node.js terinstal pada sistem Anda. Masuk ke folder proyek bundler:\n```bash\ncd D:\\CraftThingy\\client-side-paddle-ocr-project\n```\n\n#### Langkah B: Instal Dependencies\nUnduh dependencies standar yang diperlukan:\n```bash\nnpm install\n```\n\n#### Langkah C: Bangun Modul (Compilation)\nKompilasikan kode sumber beserta seluruh shimming-nya menjadi satu berkas JavaScript tunggal:\n```bash\nnpm run build\n```\n\nHasil kompilasi akan ditaruh di folder `dist/` dalam format:\n*   `dist/paddle-ocr-client.umd.js`: Format UMD yang siap diimpor via `<script src=\"...\">` di HTML/PHP biasa.\n*   `dist/paddle-ocr-client.es.js`: Format ES Modules untuk proyek modern (Vite, Webpack, React, Vue, dll.).\n\n### 3. Struktur Berkas Proyek\n*   `main.js`: Titik masuk utama (Entrypoint) yang membungkus `PaddleOcrService` menjadi kelas global browser `PaddleOCRClient` dan menyematkan patch sekuensial.\n*   `vite.config.js`: Berisi pemetaan alias bundler dan konfigurasi library output.\n*   `browser-canvas-shim.js`: Menjembatani fungsi canvas server ke HTML5 Canvas client.\n*   `browser-fs-shim.js`: Menjembatani fungsi `fs` ke XMLHttpRequest browser.\n*   `browser-path-shim.js`: Menjembatani fungsi manipulasi direktori ke string URL web.\n*   `browser-url-shim.js`: Menjembatani fungsi pemetaan berkas URL Node.\n\n### 4. API Reference\n\n#### **`class PaddleOCRClient`**\nPustaka pembungkus (wrapper) utama untuk menjalankan deteksi & rekognisi teks PaddleOCR di dalam browser.\n\n##### **`constructor(options)`**\n*   `options.verbose` (boolean): Menampilkan log debugger di konsol browser (default: `false`).\n*   `options.maxSideLength` (number): Skala sisi gambar maksimum untuk detektor OCR. Nilai yang lebih tinggi (seperti `2000`) meningkatkan akurasi deteksi simbol/teks kecil, namun memakan lebih banyak memori (default: `2000`).\n\n##### **`async init(modelConfig)`**\nMengunduh model ONNX/ORT dan file dictionary kamus secara asinkron lewat HTTP dan memuatnya ke runtime WebAssembly.\n*   `modelConfig.detection` (string): URL path file model deteksi ONNX/ORT (default: `'/models/en_PP-OCRv3_det_infer.onnx'`).\n*   `modelConfig.recognition` (string): URL path file model rekognisi ONNX/ORT (default: `'/models/en_PP-OCRv3_rec_infer.onnx'`).\n*   `modelConfig.charactersDictionary` (string): URL path file kamus karakter (default: `'/models/en_dict.txt'`).\n\n> [!TIP]\n> Berkas model ONNX dan ORT (teroptimasi FlatBuffers untuk browser) yang kompatibel dapat diunduh langsung dari repositori model resmi organisasi Anda:\n> **[cty-paddle-ocr-models](https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr-models)**.\n\n##### **`async recognize(imageInput)`**\nMengekstrak teks dan koordinat layout geometris dari input gambar/canvas.\n*   `imageInput` (HTMLImageElement | HTMLCanvasElement | Blob | File | ArrayBuffer): Elemen gambar DOM, elemen canvas, blob file, file lokal, atau buffer biner gambar yang akan dipindai.\n*   **Return Value**: Mengembalikan `Promise` yang menghasilkan objek berstruktur seperti berikut:\n    ```json\n    {\n      \"text\": \"CRAFTTHINGY SHOP\\nTOTAL: 15000\\nTERIMA KASIH\",\n      \"lines\": [\n        {\n          \"text\": \"CRAFTTHINGY SHOP\",\n          \"box\": {\n            \"x\": 45,\n            \"y\": 20,\n            \"width\": 210,\n            \"height\": 28\n          },\n          \"words\": [\n            {\n              \"text\": \"CRAFTTHINGY\",\n              \"box\": { \"x\": 45, \"y\": 20, \"width\": 130, \"height\": 28 }\n            },\n            {\n              \"text\": \"SHOP\",\n              \"box\": { \"x\": 180, \"y\": 20, \"width\": 75, \"height\": 28 }\n            }\n          ]\n        },\n        {\n          \"text\": \"TOTAL: 15000\",\n          \"box\": {\n            \"x\": 45,\n            \"y\": 60,\n            \"width\": 150,\n            \"height\": 24\n          },\n          \"words\": [\n            {\n              \"text\": \"TOTAL:\",\n              \"box\": { \"x\": 45, \"y\": 60, \"width\": 70, \"height\": 24 }\n            },\n            {\n              \"text\": \"15000\",\n              \"box\": { \"x\": 120, \"y\": 60, \"width\": 75, \"height\": 24 }\n            }\n          ]\n        }\n      ]\n    }\n    ```\n\n### 5. Contoh Penggunaan (Code Examples)\n\n#### Contoh A: Memindai Gambar dari Tag `<img>`\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/ort.min.js\"></script>\n<script async src=\"https://docs.opencv.org/4.5.4/opencv.js\"></script>\n<script>\n  window.cv = window.Module = {\n    onRuntimeInitialized: () => window.isOpencvReady = true\n  };\n  window.process = { env: { NODE_ENV: 'production' }, cwd: () => '/' };\n  window.setImmediate = (fn, ...args) => setTimeout(fn, 0, ...args);\n</script>\n<script src=\"/js/paddle-ocr-client.js\"></script>\n\n<script>\n  async function runOCR() {\n    const ocr = new PaddleOCRClient({ verbose: true });\n    await ocr.init({\n      detection: '/models/en_PP-OCRv3_det_infer.onnx',\n      recognition: '/models/en_PP-OCRv3_rec_infer.onnx',\n      charactersDictionary: '/models/en_dict.txt'\n    });\n\n    const img = document.getElementById('my-image');\n    const result = await ocr.recognize(img);\n    console.log(\"Hasil pemindaian:\", result.text);\n  }\n</script>\n```\n\n#### Contoh B: Memindai Halaman PDF (Menggunakan PDF.js)\n```javascript\nasync function scanPdfPage(pdfUrl, pageNum) {\n  const loadingTask = pdfjsLib.getDocument(pdfUrl);\n  const pdfDoc = await loadingTask.promise;\n  const page = await pdfDoc.getPage(pageNum);\n  \n  const viewport = page.getViewport({ scale: 1.5 });\n  const canvas = document.createElement('canvas');\n  canvas.width = viewport.width;\n  canvas.height = viewport.height;\n  const context = canvas.getContext('2d');\n  \n  await page.render({ canvasContext: context, viewport: viewport }).promise;\n\n  const ocr = new PaddleOCRClient();\n  await ocr.init();\n  \n  const result = await ocr.recognize(canvas);\n  console.log(`Teks Halaman ${pageNum}:`, result.text);\n}\n```\n\n---\n\n### 6. ModelManager API\n\nKelas `ModelManager` disediakan untuk berinteraksi langsung dengan repositori model resmi Anda di GitHub.\n\n#### **`static async listAvailableModelsFromGithub()`**\n*   Mengembalikan daftar seluruh berkas model (`.ort`, `.onnx`) dan dictionary (`.txt`) yang tersedia di repositori GitHub model Anda.\n*   **Contoh Penggunaan**:\n    ```javascript\n    import { ModelManager } from '@craftthingy-digital-innovation/cty-paddle-ocr';\n    const models = await ModelManager.listAvailableModelsFromGithub();\n    console.log(models); // [{ name: \"PP-OCRv6_medium_det.ort\", size: 62160320, downloadUrl: \"...\" }]\n    ```\n\n#### **`static async downloadModelFromGithub(fileName, destFolder)`**\n*   Mengunduh berkas model tertentu dari GitHub LFS dan menyimpannya di folder server lokal secara offline. *(Hanya didukung di lingkungan Node.js)*.\n*   **Contoh Penggunaan**:\n    ```javascript\n    import { ModelManager } from '@craftthingy-digital-innovation/cty-paddle-ocr';\n    await ModelManager.downloadModelFromGithub('PP-OCRv6_medium_det.ort', './public/models');\n    ```\n\n---\n\n### 7. Panduan Performa & Optimasi (Performance Guide)\n\n#### **A. Format Berkas Model (`.ort` vs `.onnx`)**\n*   **Sangat Direkomendasikan**: Gunakan model format `.ort` (FlatBuffers) untuk lingkungan browser.\n*   Format `.ort` menonaktifkan optimasi grafis runtime bawaan ONNX (`graphOptimizationLevel: 'disabled'`) sehingga memangkas waktu inisialisasi awal di browser hingga 3-5x lebih cepat dibanding `.onnx`.\n\n#### **B. WASM Multithreading**\n*   Untuk mengaktifkan akselerasi multi-core di browser, konfigurasikan server web Anda (seperti Apache/Nginx/CodeIgniter) untuk mengirimkan header HTTP berikut:\n    ```http\n    Cross-Origin-Opener-Policy: same-origin\n    Cross-Origin-Embedder-Policy: require-corp\n    ```\n*   Setel variabel lingkungan ONNX di JavaScript sebelum memproses gambar:\n    ```javascript\n    ort.env.wasm.numThreads = navigator.hardwareConcurrency || 4;\n    ```\n\n## English\n\nThis project is **cty-paddle-ocr**, a lightweight, high-performance, standalone PaddleOCR SDK designed for **isomorphic** execution (running anywhere JavaScript runs: Web browsers using WebAssembly and Node.js servers).\n\nIt has been decoupled from the external `ppu-paddle-ocr` and `ppu-ocv` packages. The entire OpenCV wrapper engine (`cty-ocv`) and ONNX session wrappers (`cty-ocr`) have been ported locally. It dynamically scales to run sequentially (with `setTimeout` yields) on single-threaded browser runtimes to prevent freezing, and concurrently (multi-threaded) on Node.js backends for high-performance server environments.\n\n### 1. Architecture & Shimming Engine\n\nThe compiler bundles modules using **Vite** in *Library Mode* with custom aliases defined in `vite.config.js`. Below are the detailed shims applied:\n\n1.  **Canvas Shim (`browser-canvas-shim.js`)**:\n    Reroutes `@napi-rs/canvas` methods (a native canvas library for Node) to browser-native canvas elements (`HTMLCanvasElement`, `document.createElement('canvas')`, and `new Image()`). This allows canvas elements to be created dynamically in the browser.\n2.  **Filesystem Shim (`browser-fs-shim.js`)**:\n    Replaces Node's synchronous `fs.readFileSync` with a synchronous `XMLHttpRequest` configured with `overrideMimeType('text/plain; charset=x-user-defined')` to download raw binary ONNX models without corruption, bypassing browser parser errors.\n3.  **URL-Aware Path Shim (`browser-path-shim.js`)**:\n    Patches POSIX path helpers to handle absolute URL paths (`http://` or `https://`) and prevent double-slash (`//`) paths from being converted into single slashes.\n4.  **WASM Sequential Engine (`main.js`)**:\n    Sets up asynchronous AI initialization (`ort.InferenceSession.create(url)`).\n    **Crucial Browser Optimization**: Overrides `processBoxesInParallel` inside `RecognitionService` to process bounding boxes **sequentially** instead of concurrently, yielding with `setTimeout(resolve, 10)` before each run. This prevents concurrent WebAssembly inferences from locking up the browser's main GUI thread.\n\n### 2. How to Install & Build\n\n#### Step A: Preparation\nEnsure you have Node.js installed. Navigate to the bundler directory:\n```bash\ncd D:\\CraftThingy\\client-side-paddle-ocr-project\n```\n\n#### Step B: Install Dependencies\nDownload the standard required dependencies:\n```bash\nnpm install\n```\n\n#### Step C: Build the Bundle (Compilation)\nCompile the source code and shims into a single JavaScript library file:\n```bash\nnpm run build\n```\n\nThe compiled output is created under the `dist/` directory:\n*   `dist/paddle-ocr-client.umd.js` (Universal Module Definition for script tags in legacy browsers or vanilla HTML/PHP).\n*   `dist/paddle-ocr-client.es.js` (ES Modules for modern bundlers like Vite or Webpack).\n\n### 3. Project Directory Structure\n*   `main.js`: The primary entry point. Wraps `PaddleOcrService` into a global browser class `PaddleOCRClient` and hooks the sequential run patch.\n*   `vite.config.js`: Defines the bundler alias mappings and library output config.\n*   `browser-canvas-shim.js`: Redirects canvas operations to HTML5 Canvas.\n*   `browser-fs-shim.js`: Routes Node `fs` calls to XMLHttpRequest.\n*   `browser-path-shim.js`: Routes directory manipulation to standard web URLs.\n*   `browser-url-shim.js`: Emulates URL mapping.\n\n### 4. API Reference\n\n#### **`class PaddleOCRClient`**\nThe primary library wrapper class to initialize and run PaddleOCR client-side inside the browser.\n\n##### **`constructor(options)`**\n*   `options.verbose` (boolean): Prints debug statements to browser developer tools console (default: `false`).\n*   `options.maxSideLength` (number): Scaled limit of the maximum side length for the text detector. Larger values (e.g. `2000`) increase accuracy for small/blurry characters but consume more memory (default: `2000`).\n\n##### **`async init(modelConfig)`**\nAsynchronously downloads ONNX/ORT model binaries and character files over HTTP and compiles them into WebAssembly.\n*   `modelConfig.detection` (string): URL path to the detection ONNX/ORT model file (default: `'/models/en_PP-OCRv3_det_infer.onnx'`).\n*   `modelConfig.recognition` (string): URL path to the recognition ONNX/ORT model file (default: `'/models/en_PP-OCRv3_rec_infer.onnx'`).\n*   `modelConfig.charactersDictionary` (string): URL path to the character dictionary text file (default: `'/models/en_dict.txt'`).\n\n> [!TIP]\n> The pre-converted ONNX and optimized ORT (FlatBuffers-serialized) model files can be downloaded from your organization's model repository:\n> **[cty-paddle-ocr-models](https://github.com/CraftThingy-Digital-Innovation/cty-paddle-ocr-models)**.\n\n##### **`async recognize(imageInput)`**\nExtracts text boundaries and text lines from a given graphical element.\n*   `imageInput` (HTMLImageElement | HTMLCanvasElement | Blob | File | ArrayBuffer): The source image/canvas or file binary to scan.\n*   **Return Value**: Returns a `Promise` resolving to the following populated schema:\n    ```json\n    {\n      \"text\": \"CRAFTTHINGY SHOP\\nTOTAL: 15000\\nTERIMA KASIH\",\n      \"lines\": [\n        {\n          \"text\": \"CRAFTTHINGY SHOP\",\n          \"box\": {\n            \"x\": 45,\n            \"y\": 20,\n            \"width\": 210,\n            \"height\": 28\n          },\n          \"words\": [\n            {\n              \"text\": \"CRAFTTHINGY\",\n              \"box\": { \"x\": 45, \"y\": 20, \"width\": 130, \"height\": 28 }\n            },\n            {\n              \"text\": \"SHOP\",\n              \"box\": { \"x\": 180, \"y\": 20, \"width\": 75, \"height\": 28 }\n            }\n          ]\n        },\n        {\n          \"text\": \"TOTAL: 15000\",\n          \"box\": {\n            \"x\": 45,\n            \"y\": 60,\n            \"width\": 150,\n            \"height\": 24\n          },\n          \"words\": [\n            {\n              \"text\": \"TOTAL:\",\n              \"box\": { \"x\": 45, \"y\": 60, \"width\": 70, \"height\": 24 }\n            },\n            {\n              \"text\": \"15000\",\n              \"box\": { \"x\": 120, \"y\": 60, \"width\": 75, \"height\": 24 }\n            }\n          ]\n        }\n      ]\n    }\n    ```\n\n### 5. Code Examples\n\n#### Example A: Scanning an Image element (`<img>`)\n```html\n<script src=\"https://cdn.jsdelivr.net/npm/onnxruntime-web@1.20.1/dist/ort.min.js\"></script>\n<script async src=\"https://docs.opencv.org/4.5.4/opencv.js\"></script>\n<script>\n  window.cv = window.Module = {\n    onRuntimeInitialized: () => window.isOpencvReady = true\n  };\n  window.process = { env: { NODE_ENV: 'production' }, cwd: () => '/' };\n  window.setImmediate = (fn, ...args) => setTimeout(fn, 0, ...args);\n</script>\n<script src=\"/js/paddle-ocr-client.js\"></script>\n\n<script>\n  async function runOCR() {\n    const ocr = new PaddleOCRClient({ verbose: true });\n    await ocr.init({\n      detection: '/models/en_PP-OCRv3_det_infer.onnx',\n      recognition: '/models/en_PP-OCRv3_rec_infer.onnx',\n      charactersDictionary: '/models/en_dict.txt'\n    });\n\n    const img = document.getElementById('my-image');\n    const result = await ocr.recognize(img);\n    console.log(\"Scanned Text Output:\", result.text);\n  }\n</script>\n```\n\n#### Example B: Scanning a PDF page (with PDF.js)\n```javascript\nasync function scanPdfPage(pdfUrl, pageNum) {\n  const loadingTask = pdfjsLib.getDocument(pdfUrl);\n  const pdfDoc = await loadingTask.promise;\n  const page = await pdfDoc.getPage(pageNum);\n  \n  const viewport = page.getViewport({ scale: 1.5 });\n  const canvas = document.createElement('canvas');\n  canvas.width = viewport.width;\n  canvas.height = viewport.height;\n  const context = canvas.getContext('2d');\n  \n  await page.render({ canvasContext: context, viewport: viewport }).promise;\n\n  const ocr = new PaddleOCRClient();\n  await ocr.init();\n  \n  const result = await ocr.recognize(canvas);\n  console.log(`Page ${pageNum} parsed text:`, result.text);\n}\n```\n\n---\n\n### 6. ModelManager API\n\nThe `ModelManager` class provides native utilities to interact directly with your official models repository on GitHub.\n\n#### **`static async listAvailableModelsFromGithub()`**\n*   Returns a list of all model weights (`.ort`, `.onnx`) and vocabulary dictionaries (`.txt`) hosted in your GitHub models repository.\n*   **Usage Example**:\n    ```javascript\n    import { ModelManager } from '@craftthingy-digital-innovation/cty-paddle-ocr';\n    const models = await ModelManager.listAvailableModelsFromGithub();\n    console.log(models); // [{ name: \"PP-OCRv6_medium_det.ort\", size: 62160320, downloadUrl: \"...\" }]\n    ```\n\n#### **`static async downloadModelFromGithub(fileName, destFolder)`**\n*   Downloads a specific model file from GitHub LFS and saves it to a local storage path. *(Server-side Node.js environment only)*.\n*   **Usage Example**:\n    ```javascript\n    import { ModelManager } from '@craftthingy-digital-innovation/cty-paddle-ocr';\n    await ModelManager.downloadModelFromGithub('PP-OCRv6_medium_det.ort', './public/models');\n    ```\n\n---\n\n### 7. Performance & Optimization Guide\n\n#### **A. Model File Formats (`.ort` vs `.onnx`)**\n*   **Highly Recommended**: Use the `.ort` format (FlatBuffers serialized graph) in client-side web browsers.\n*   The `.ort` graph representation bypasses ONNX Runtime Web's graphic optimization step (`graphOptimizationLevel: 'disabled'`), loading the network in the browser 3x to 5x faster than conventional `.onnx` files.\n\n#### **B. WASM Multithreading**\n*   To unlock multi-core CPU inference inside web browsers, configure your hosting server (Apache/Nginx/Express) to serve the following headers:\n    ```http\n    Cross-Origin-Opener-Policy: same-origin\n    Cross-Origin-Embedder-Policy: require-corp\n    ```\n*   Initialize the ONNX thread pool size in your frontend JavaScript code:\n    ```javascript\n    ort.env.wasm.numThreads = navigator.hardwareConcurrency || 4;\n    ```\n\n## Asal Usul & Kredit / Origins & Credits\n\n### Bahasa Indonesia\nProyek ini dikembangkan oleh **CraftThingy Digital Innovation (Alif Nurhidayat)**. Proyek ini dibangun di atas fondasi inovasi open-source berikut:\n1.  **Baidu PaddleOCR**: Model deteksi & pengenalan teks kelas dunia yang menjadi inti dari sistem OCR ini.\n2.  **cty-paddle-ocr-models**: Repositori model resmi kami tempat menampung dan mendistribusikan model ONNX/ORT secara mandiri.\n3.  **ONNX Runtime Web (Microsoft)**: Engine eksekusi WebAssembly yang menjalankan model neural network di browser.\n4.  **OpenCV.js**: Pustaka pengolahan citra komputer yang menangani transformasi geometris dan cropping karakter.\n5.  **PDF.js (Mozilla)**: Pustaka rendering dokumen PDF yang memproses halaman dokumen menjadi frame canvas.\n\n### English\nThis project is developed by **CraftThingy Digital Innovation (Alif Nurhidayat)**. It is built upon the following open-source projects and innovations:\n1.  **Baidu PaddleOCR**: The world-class OCR system providing the core deep learning models for text detection and recognition.\n2.  **cty-paddle-ocr-models**: Our official model repository hosting pre-converted ONNX and optimized ORT models.\n3.  **ONNX Runtime Web (Microsoft)**: The WebAssembly execution runtime that powers the model inference in browser clients.\n4.  **OpenCV.js**: The computer vision engine handling character cropping and geometry conversions.\n5.  **PDF.js (Mozilla)**: The document rendering library enabling multi-page PDF scanning inside the browser canvas.\n\n","readmeFilename":"README.md"}