-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
382 lines (358 loc) · 19.3 KB
/
Copy pathindex.html
File metadata and controls
382 lines (358 loc) · 19.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>oneocr — Windows 11's OCR engine, from Java</title>
<meta name="description" content="A pure-Java binding to the OCR engine built into the Windows 11 Snipping Tool, using the Foreign Function and Memory API, plus the 1ocr command line tool: images, PDFs and folders to text, JSON, SVG and semantic XHTML."/>
<link rel="icon" href="assets/favicon.svg" type="image/svg+xml"/>
<link rel="alternate icon" href="assets/favicon-32.png"/>
<link rel="apple-touch-icon" href="assets/icon-256.png"/>
<meta property="og:title" content="oneocr — Windows 11's OCR engine, from Java"/>
<meta property="og:description" content="Bind the Snipping Tool's OCR engine from Java through FFM. Images, PDFs and folders to text, JSON, SVG and browser-renderable semantic XHTML."/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://oneocr.github.io/"/>
<meta property="og:image" content="https://oneocr.github.io/assets/icon-512.png"/>
<style>
:root {
--bg: #ffffff;
--bg-alt: #f4f6f8;
--surface: #ffffff;
--fg: #14181d;
--fg-muted: #58616d;
--rule: #dde2e8;
--accent: #0b7c8c;
--accent-fg: #ffffff;
--accent-dim:#e3f2f4;
--code-bg: #131820;
--code-fg: #e6edf3;
--code-dim: #8b97a6;
--good: #0f7a4a;
--warn-bg: #fff8e8;
--warn-rule: #e6d2a0;
--shadow: 0 1px 2px rgba(16,24,40,.06), 0 8px 24px -12px rgba(16,24,40,.18);
--mono: "Cascadia Mono", "JetBrains Mono", ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
--sans: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Display", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0d1117;
--bg-alt: #121821;
--surface: #161d27;
--fg: #e8edf3;
--fg-muted: #9aa5b3;
--rule: #26303c;
--accent: #3fbdcf;
--accent-fg: #06232a;
--accent-dim:#12313a;
--code-bg: #0a0e14;
--code-fg: #e6edf3;
--code-dim: #7d8998;
--good: #3fbe86;
--warn-bg: #221d10;
--warn-rule: #4a3f22;
--shadow: 0 1px 2px rgba(0,0,0,.4), 0 8px 24px -12px rgba(0,0,0,.6);
}
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--bg);
color: var(--fg);
font: 17px/1.65 var(--sans);
-webkit-font-smoothing: antialiased;
}
.wrap { max-width: 62rem; margin: 0 auto; padding: 0 1.5rem; }
/* ---------- header ---------- */
header.site {
position: sticky; top: 0; z-index: 20;
background: var(--bg);
background: color-mix(in srgb, var(--bg) 88%, transparent);
backdrop-filter: saturate(1.6) blur(10px);
border-bottom: 1px solid var(--rule);
}
header.site .wrap { display: flex; align-items: center; gap: 1.25rem; height: 60px; }
.brand { display: flex; align-items: center; gap: .6rem; text-decoration: none; color: var(--fg); font-weight: 650; letter-spacing: -.01em; }
.brand img { width: 28px; height: 28px; display: block; }
header.site nav { margin-left: auto; display: flex; gap: 1.25rem; align-items: center; }
header.site nav a { color: var(--fg-muted); text-decoration: none; font-size: .94rem; }
header.site nav a:hover { color: var(--fg); }
.hide-sm { display: inline; }
/* ---------- hero ---------- */
.hero { padding: 4.5rem 0 3.5rem; border-bottom: 1px solid var(--rule); }
.hero h1 { font-size: clamp(2.1rem, 5vw, 3.1rem); line-height: 1.08; letter-spacing: -.03em; margin: 0 0 1rem; max-width: 34ch; }
.hero .lede { font-size: clamp(1.05rem, 2.2vw, 1.22rem); color: var(--fg-muted); max-width: 62ch; margin: 0 0 2rem; }
.eyebrow { display: inline-flex; align-items: center; gap: .5rem; font: 600 .78rem/1 var(--sans); letter-spacing: .09em; text-transform: uppercase; color: var(--accent); background: var(--accent-dim); border-radius: 999px; padding: .45rem .8rem; margin: 0 0 1.5rem; }
.cta { display: flex; flex-wrap: wrap; gap: .75rem; margin: 0 0 2.5rem; }
.btn { display: inline-block; padding: .7rem 1.15rem; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: .96rem; border: 1px solid transparent; }
.btn-primary { background: var(--accent); color: var(--accent-fg); }
.btn-primary:hover { filter: brightness(1.08); }
.btn-ghost { background: transparent; color: var(--fg); border-color: var(--rule); }
.btn-ghost:hover { border-color: var(--fg-muted); }
/* ---------- code ---------- */
pre {
background: var(--code-bg); color: var(--code-fg);
border-radius: 10px; padding: 1.1rem 1.25rem; margin: 0 0 1.25rem;
overflow-x: auto; font: .88rem/1.7 var(--mono);
font-variant-ligatures: none; font-feature-settings: "liga" 0, "calt" 0;
box-shadow: var(--shadow);
}
pre .c { color: var(--code-dim); }
code { font-family: var(--mono); font-size: .9em; font-variant-ligatures: none; font-feature-settings: "liga" 0, "calt" 0; }
p code, li code, td code { background: var(--bg-alt); border: 1px solid var(--rule); border-radius: 5px; padding: .08em .35em; }
/* ---------- sections ---------- */
section { padding: 4rem 0; border-bottom: 1px solid var(--rule); }
section.tint { background: var(--bg-alt); }
section > .wrap > h2 { font-size: clamp(1.5rem, 3vw, 2rem); letter-spacing: -.02em; margin: 0 0 .6rem; }
section > .wrap > .sub { color: var(--fg-muted); max-width: 62ch; margin: 0 0 2.25rem; }
h3 { font-size: 1.06rem; margin: 0 0 .45rem; letter-spacing: -.01em; }
/* ---------- cards ---------- */
.grid { display: grid; gap: 1rem; }
.grid-3 { grid-template-columns: 1fr; }
@media (min-width: 34rem) { .grid-3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 56rem) { .grid-3 { grid-template-columns: repeat(3, 1fr); } }
.card {
background: var(--surface); border: 1px solid var(--rule); border-radius: 11px;
padding: 1.15rem 1.25rem; display: block; text-decoration: none; color: var(--fg);
}
a.card:hover { border-color: var(--accent); }
a.card:hover h3 { color: var(--accent); }
.card p { margin: 0; color: var(--fg-muted); font-size: .95rem; }
.card .coord { display: block; margin: .55rem 0 0; font: .78rem/1.5 var(--mono); color: var(--fg-muted); overflow-wrap: anywhere; }
.ext { font-family: var(--mono); font-size: .8rem; color: var(--accent); font-weight: 600; }
/* ---------- figure: line order ---------- */
.compare { display: grid; gap: 1rem; grid-template-columns: 1fr; margin: 0 0 1rem; }
@media (min-width: 48rem) { .compare { grid-template-columns: 1fr 1fr; } }
.compare > div { border: 1px solid var(--rule); border-radius: 11px; overflow: hidden; background: var(--surface); }
.compare h4 { margin: 0; padding: .7rem 1rem; font-size: .82rem; letter-spacing: .04em; text-transform: uppercase; color: var(--fg-muted); border-bottom: 1px solid var(--rule); background: var(--bg-alt); }
.compare pre { margin: 0; border-radius: 0; box-shadow: none; background: transparent; color: var(--fg); font-size: .84rem; }
.compare .bad { color: #c0392b; }
@media (prefers-color-scheme: dark) { .compare .bad { color: #ff8a7a; } }
.compare .ok { color: var(--good); }
/* ---------- table ---------- */
.tablewrap { overflow-x: auto; border: 1px solid var(--rule); border-radius: 11px; background: var(--surface); }
table { border-collapse: collapse; width: 100%; font-size: .95rem; }
th, td { text-align: left; padding: .7rem 1rem; border-bottom: 1px solid var(--rule); white-space: nowrap; }
thead th { background: var(--bg-alt); font-size: .8rem; letter-spacing: .04em; text-transform: uppercase; color: var(--fg-muted); }
tbody tr:last-child td { border-bottom: 0; }
td.num { font-family: var(--mono); }
/* ---------- callout ---------- */
.note { background: var(--warn-bg); border: 1px solid var(--warn-rule); border-radius: 11px; padding: 1.1rem 1.25rem; margin: 1.5rem 0 0; }
.note p { margin: 0 0 .6rem; }
.note p:last-child { margin: 0; }
.note strong { font-weight: 650; }
/* ---------- footer ---------- */
footer.site { padding: 3rem 0 4rem; color: var(--fg-muted); font-size: .93rem; }
footer.site .cols { display: grid; gap: 1.5rem; grid-template-columns: 1fr; margin: 0 0 2rem; }
@media (min-width: 44rem) { footer.site .cols { grid-template-columns: 2fr 1fr 1fr; } }
footer.site h5 { font-size: .8rem; letter-spacing: .06em; text-transform: uppercase; color: var(--fg); margin: 0 0 .7rem; }
footer.site ul { list-style: none; margin: 0; padding: 0; }
footer.site li { margin: 0 0 .4rem; }
footer.site a { color: var(--fg-muted); text-decoration: none; }
footer.site a:hover { color: var(--accent); text-decoration: underline; }
footer.site .fine { border-top: 1px solid var(--rule); padding-top: 1.5rem; }
.lead-brand { display: flex; align-items: center; gap: .6rem; margin: 0 0 .7rem; font-weight: 650; color: var(--fg); }
.lead-brand img { width: 24px; height: 24px; }
@media (max-width: 30rem) { .hide-sm { display: none; } }
</style>
</head>
<body>
<header class="site">
<div class="wrap">
<a class="brand" href="#top"><img src="assets/mark.svg" alt=""/> oneocr</a>
<nav>
<a href="#output" class="hide-sm">Output</a>
<a href="#layout" class="hide-sm">Layout</a>
<a href="#scripts" class="hide-sm">Scripts</a>
<a href="#install">Install</a>
<a href="https://github.com/oneocr">GitHub</a>
</nav>
</div>
</header>
<main id="top">
<div class="hero">
<div class="wrap">
<p class="eyebrow">Java · FFM · Windows 11</p>
<h1>Windows 11 already has an OCR engine. This one calls it from Java.</h1>
<p class="lede">The Snipping Tool ships an undocumented ~50 MB Microsoft OCR model. <strong>oneocr</strong> binds it through the JDK's Foreign Function & Memory API — no JNI, nothing to compile — and adds <code>1ocr</code>, a command line tool for images, PDFs and whole folders.</p>
<div class="cta">
<a class="btn btn-primary" href="#install">Get started</a>
<a class="btn btn-ghost" href="demo/sample-page.oneocr.xhtml">See real output →</a>
<a class="btn btn-ghost" href="https://github.com/oneocr">GitHub</a>
</div>
<pre><span class="c"># a page, and everything it can tell you about it</span>
1ocr page.png
<span class="c"># -> page.png.oneocr.txt .json .svg .xhtml</span>
<span class="c"># a PDF, with the reading order recovered</span>
1ocr pdf order.pdf --layout
<span class="c"># a whole tree, resumable</span>
1ocr folder ./scans -r</pre>
</div>
</div>
<section id="output">
<div class="wrap">
<h2>One page, six files</h2>
<p class="sub">Four are written by default, beside the input. The layout export and the second pass are asked for. Nothing is overwritten, and the originals are never modified.</p>
<div class="grid grid-3">
<div class="card">
<h3><span class="ext">.txt</span> Plain text</h3>
<p>The recognised text, in the order the engine reported it.</p>
</div>
<div class="card">
<h3><span class="ext">.json</span> Compact JSON</h3>
<p>Every word with its bounding box, confidence and index. The machine-readable record.</p>
</div>
<div class="card">
<h3><span class="ext">.svg</span> Vector overlay</h3>
<p>Words placed at their true page coordinates, over the source image.</p>
</div>
<a class="card" href="demo/sample-page.oneocr.xhtml">
<h3><span class="ext">.xhtml</span> Semantic XHTML →</h3>
<p>A browser-renderable document with real elements per segment and per word. Toggle word boxes, confidence colouring and the precise SVG layer. Open the demo.</p>
</a>
<a class="card" href="demo/sample-page.layout.html">
<h3><span class="ext">.layout.html</span> Layout export →</h3>
<p>Reading order recovered: columns become tables, split lines are rejoined, alignment detected.</p>
</a>
<div class="card">
<h3><span class="ext">+tesseract</span> Second pass</h3>
<p>A parallel set of the same four, where a second engine re-read the lines OneOCR could not.</p>
</div>
</div>
</div>
</section>
<section id="layout" class="tint">
<div class="wrap">
<h2>Line order is not reading order</h2>
<p class="sub">The engine reports lines in its own sequence. On a single column of prose that is fine. On anything set in two columns it interleaves them, and a cause title comes out unusable.</p>
<div class="compare">
<div>
<h4>As the engine reports it</h4>
<pre><span class="bad">Siva Vallabhaneni
Petitioner
Union of India
Respondent</span></pre>
</div>
<div>
<h4>With <code>--layout</code></h4>
<pre><span class="ok">Siva Vallabhaneni Petitioner
Union of India Respondent</span></pre>
</div>
</div>
<p>Structure is recovered by recursive X-Y cutting over the line boxes: parallel columns become a table, one printed line detected as two boxes is rejoined, and left, centre and right alignment is detected per block. Thresholds are scale-free — expressed in multiples of the page's own median line height — so the same numbers hold at any DPI.</p>
<p>Measured on a real 83-page legal notice: <strong>145 orphaned fragments rejoined</strong> and its table of contents unscrambled. <code>1ocr layout <file>.oneocr.xhtml</code> re-renders from the saved boxes with no second OCR pass, which makes threshold tuning a sub-second loop.</p>
</div>
</section>
<section id="scripts">
<div class="wrap">
<h2>Where the engine fails silently, a second engine reads</h2>
<p class="sub">OneOCR handles Latin, Devanagari and Tamil well. On the other Indic scripts it does not error — it returns another script, or Latin gibberish, while its page-level confidence still looks healthy, because the lines it could not read are simply dropped from the average.</p>
<div class="tablewrap">
<table>
<thead>
<tr><th>On a 25-page bilingual court order</th><th>OneOCR alone</th><th>With the second pass</th></tr>
</thead>
<tbody>
<tr><td>Gujarati, character error rate</td><td class="num">100%</td><td class="num">3–8%</td></tr>
<tr><td>Latin on the same pages</td><td class="num">1.4%</td><td class="num">0.4%</td></tr>
</tbody>
</table>
</div>
<p style="margin-top:1.25rem">Latin <em>improves</em>, because some of the flagged lines were ones where OneOCR had mangled its own English alongside the Gujarati. Only the lines a per-line confidence guard flags are re-read; the script is identified by letting candidate models compete on confidence, and the results are spliced back into OneOCR's own geometry.</p>
<pre><span class="c"># candidates are always explicit — there is no default set</span>
1ocr page.png --tesseract --tess-candidates guj,hin
1ocr pdf report.pdf --tesseract --tess-lang guj <span class="c"># skip probing</span></pre>
<p>Needs Tesseract 5.4+. Language models are downloaded on first use and cached; nothing is bundled.</p>
</div>
</section>
<section id="modules" class="tint">
<div class="wrap">
<h2>Five repositories</h2>
<p class="sub">Each one builds and publishes on its own. Use the CLI, or take the binding and build your own tool on it.</p>
<div class="grid grid-3">
<a class="card" href="https://github.com/oneocr/cli">
<h3>cli →</h3>
<p>The <code>1ocr</code> command. Images, PDFs, folders, the layout export and a small GUI.</p>
<span class="coord">io.github.oneocr:oneocr-cli</span>
</a>
<a class="card" href="https://github.com/oneocr/api">
<h3>api →</h3>
<p>The FFM binding to <code>oneocr.dll</code>, with arena-scoped native memory.</p>
<span class="coord">io.github.oneocr:oneocr-api</span>
</a>
<a class="card" href="https://github.com/oneocr/tesseract">
<h3>tesseract →</h3>
<p>The optional second pass — an FFM binding to Tesseract 5 with script identification.</p>
<span class="coord">io.github.oneocr:oneocr-tesseract</span>
</a>
<a class="card" href="https://github.com/oneocr/xhtml_controls_js">
<h3>xhtml_controls_js →</h3>
<p>The viewer controls, written in Java and compiled to JavaScript by TeaVM.</p>
<span class="coord">io.github.oneocr:oneocr-xhtml_controls_js</span>
</a>
<a class="card" href="https://github.com/oneocr/semantic_xhtml">
<h3>semantic_xhtml →</h3>
<p>The CSS and JS that every generated document links, served from this domain.</p>
<span class="coord">oneocr.github.io/semantic_xhtml/</span>
</a>
</div>
</div>
</section>
<section id="install">
<div class="wrap">
<h2>Install</h2>
<p class="sub">JDK 22+, where FFM is final. JDK 21 works with <code>--enable-preview</code>. Windows 11 for now.</p>
<h3>As a library</h3>
<pre><dependency>
<groupId>io.github.oneocr</groupId>
<artifactId>oneocr-api</artifactId>
<version>2.0</version>
</dependency></pre>
<h3>As a command</h3>
<pre><span class="c"># build the shaded jar, then run it</span>
git clone https://github.com/oneocr/api && (cd api && mvn install)
git clone https://github.com/oneocr/tesseract && (cd tesseract && mvn install)
git clone https://github.com/oneocr/cli && (cd cli && mvn package)
java --enable-native-access=ALL-UNNAMED -jar cli/target/1ocr-2.0.jar page.png</pre>
<div class="note">
<p><strong>The native libraries are not distributed here.</strong> <code>oneocr.dll</code>, <code>oneocr.onemodel</code>, <code>onnxruntime.dll</code> and <code>opencv_world*.dll</code> are Microsoft components that ship with Windows, and they are not ours to redistribute.</p>
<p>Take them from your own Windows installation and place them in <code>api/src/main/resources/natives/</code> before building. The <a href="https://github.com/b1tg/win11-oneocr">b1tg project</a> documents where they live.</p>
</div>
</div>
</section>
<footer class="site">
<div class="wrap">
<div class="cols">
<div>
<p class="lead-brand"><img src="assets/mark.svg" alt=""/> oneocr</p>
<p style="margin:0;max-width:44ch">A pure-Java binding to the OCR engine in the Windows 11 Snipping Tool, and a command line tool built on it. Windows only today; Linux is the next target.</p>
</div>
<div>
<h5>Repositories</h5>
<ul>
<li><a href="https://github.com/oneocr/cli">cli</a></li>
<li><a href="https://github.com/oneocr/api">api</a></li>
<li><a href="https://github.com/oneocr/tesseract">tesseract</a></li>
<li><a href="https://github.com/oneocr/xhtml_controls_js">xhtml_controls_js</a></li>
<li><a href="https://github.com/oneocr/semantic_xhtml">semantic_xhtml</a></li>
</ul>
</div>
<div>
<h5>See it</h5>
<ul>
<li><a href="demo/sample-page.oneocr.xhtml">Semantic XHTML demo</a></li>
<li><a href="demo/sample-page.layout.html">Layout export demo</a></li>
<li><a href="demo/sample-page.svg">SVG overlay</a></li>
<li><a href="demo/sample-page.txt">Plain text</a></li>
</ul>
</div>
</div>
<div class="fine">
<p style="margin:0">Built on the reverse-engineering of <a href="https://github.com/b1tg/win11-oneocr">b1tg/win11-oneocr</a> and others. The OCR engine itself is Microsoft's, and is not distributed here.</p>
</div>
</div>
</footer>
</main>
</body>
</html>