File size: 6,704 Bytes
db720e4
f29eea6
 
 
 
 
bbcf032
 
f29eea6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbcf032
f29eea6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbcf032
f29eea6
 
bbcf032
 
 
 
 
f29eea6
 
 
 
 
 
 
bbcf032
f29eea6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bbcf032
d9ca766
f29eea6
d9ca766
f29eea6
 
 
db720e4
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
<!doctype html>
<!-- translate="no": a browser's page translation must not rewrite the text read from a file (or the page around it) -->
<html lang="en" translate="no">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Chrome still offers to translate a page marked translate="no" (on a phone set to another language); this stops the offer -->
  <meta name="google" content="notranslate">
  <meta name="color-scheme" content="light">
  <title>litert-ocr: photo, scan or PDF to text, Markdown and JSON</title>
  <meta name="description" content="Drop a photo, screenshot, scan or PDF: the text with its line boxes, Markdown with tables, and JSON, read in this browser tab. Nothing is uploaded.">
  <link rel="icon" href="data:,">
  <link rel="stylesheet" href="style.css">
  <script>
    // coi-serviceworker sets the two headers that WebAssembly threads need (GitHub Pages cannot): it registers a
    // service worker and reloads the page once. The page waits for that before it loads the model.
    window.coi = { quiet: true };
  </script>
  <script src="coi-serviceworker.js"></script>
  <script type="module" src="app.js"></script>
</head>
<body>
  <header class="top">
    <h1>Photo, scan or PDF <span class="arrow">→</span> text, Markdown and JSON</h1>
    <p class="lede">Read in this tab with <a href="https://github.com/john-rocky/litert-ocr">litert-ocr</a>. Your files never leave this device.</p>
    <p id="status" class="status" role="status" aria-live="polite"><span class="dot"></span><span id="status-text">Starting…</span></p>
    <p id="detail" class="detail"></p>
    <p id="framed" class="framed" hidden></p>
    <ul id="notes" class="notes"></ul>
  </header>

  <main>
    <section class="input" aria-label="Input">
      <div id="drop" class="drop">
        <label class="pick">
          <input id="file" type="file" accept="image/*,application/pdf" disabled>
          <span id="pick-label">Choose a photo or PDF</span>
        </label>
        <p class="hint">or drop a file here, or paste a screenshot</p>
      </div>
      <div class="examples">
        <span class="examples-label">Examples:</span>
        <button type="button" class="example" data-file="receipt.jpg" disabled>Receipt photo</button>
        <button type="button" class="example" data-file="inventory-scan.pdf" disabled>Scanned table (PDF)</button>
        <button type="button" class="example" data-file="sign-ja.jpg" disabled>Sign in Japanese</button>
        <button type="button" class="example" data-file="screenshot.png" disabled>Screenshot</button>
      </div>
      <figure id="figure" class="figure" hidden>
        <div class="stage">
          <img id="image" alt="The file being read">
          <canvas id="page" class="page" role="img" aria-label="The PDF page being read" hidden></canvas>
          <svg id="boxes" class="boxes" aria-hidden="true"></svg>
        </div>
        <figcaption>
          <span id="caption"></span>
          <span id="pager" class="pager" hidden><button type="button" id="prev" aria-label="Previous page">‹</button><button type="button" id="next" aria-label="Next page">›</button></span>
          <span id="legend" class="legend" hidden></span>
        </figcaption>
      </figure>
    </section>

    <section id="result" class="result" aria-label="Result" hidden>
      <div class="headline">
        <p id="took" class="took"></p>
        <p id="took-detail" class="took-detail"></p>
        <p id="reading" class="took-detail" hidden></p>
      </div>
      <div id="error" class="error" role="alert" hidden>
        <p id="error-title" class="error-title"></p>
        <p id="error-text" class="error-text"></p>
        <form id="password-form" class="password" hidden>
          <label>Password <input id="password" type="password" autocomplete="off"></label>
          <button type="submit">Open</button>
        </form>
      </div>
      <div id="output" class="output" hidden>
        <div class="tabs" role="tablist">
          <button type="button" role="tab" class="tab" data-tab="text" aria-selected="true">Text</button>
          <button type="button" role="tab" class="tab" data-tab="md" aria-selected="false">Markdown</button>
          <button type="button" role="tab" class="tab" data-tab="json" aria-selected="false">JSON</button>
          <span class="tab-tools">
            <label id="source-toggle" class="toggle" hidden><input id="source" type="checkbox"> Source</label>
            <button type="button" id="copy" class="tool">Copy</button>
            <button type="button" id="download" class="tool">Download</button>
          </span>
        </div>
        <p id="tab-note" class="tab-note" hidden></p>
        <div id="pane-text" class="pane" role="tabpanel"></div>
        <div id="pane-md" class="pane md" role="tabpanel" hidden></div>
        <pre id="pane-md-source" class="pane source" role="tabpanel" hidden></pre>
        <pre id="pane-json" class="pane source" role="tabpanel" hidden></pre>
      </div>
    </section>

    <section id="about" class="about" aria-label="About this page">
      <h2>What this page does</h2>
      <p>Photos and screenshots: every text line and its box, in reading order (<code>worker.recognize(file)</code>). PDFs, scans and the Markdown and JSON tabs: pages with a text layer are read from it, scanned pages by OCR, and the lines become headings, paragraphs, lists and tables (<code>convert(file)</code>). A PDF's page is shown with a frame around each heading, paragraph, list item and table that <code>convert()</code> found on it, colored by type; the arrows under the page turn the pages read.</p>
      <p>Your files are read in this tab. The page downloads only the litert-ocr library, its LiteRT.js runtime and pdf.js from jsDelivr, and the model files from Hugging Face, once: about 80 MB over the network on the first visit (litert-ocr 0.1.0 in Chrome 155 on a Mac, 2026-10-22; 87 MB uncompressed, the size the status line shows). The browser keeps them. A page that is already open goes on reading with no network (tested in Chrome on a Mac and on an Android phone in airplane mode). Opening the page again with no network worked in Chrome on a Mac, not in Chrome on Android.</p>
      <p>The time shown is from handing the file to the library to the result, with the model already loaded. PDFs are read up to 20 pages per file here.</p>
      <p><a href="https://github.com/john-rocky/litert-ocr">Source and documentation</a> · <a href="https://github.com/john-rocky/litert-ocr/tree/main/demo">This page's code</a> · <span id="version">litert-ocr 0.2.0</span></p>
    </section>
  </main>
</body>
</html>