OCR It

OCR It is een Chrome-extensie voor het lezen van gepagineerde documenten die vastzitten in een viewer — zoals gescande boeken, slide-decks, PDF's of readers die geen tekstselectie toestaan.

U sleept één keer het capture-gebied vast. Daarna maakt elke druk op de sneltoets een screenshot van precies dat rechthoekige vlak, voert OCR uit en voegt de tekst toe aan een doorlopend transcript. U kunt het proces ook volledig automatiseren: $\text{⌥⇧A}$ start een run die screenshots maakt, de pagina omslaat en dit herhaalt tot het document eindigt.

De resultaten kunnen vervolgens worden geplakt waar ze nuttig zijn. Een LLM is hiervoor de meest voor de hand liggende optie; honderden pagina's die u niet kon selecteren, zijn nu een tekstbestand dat u aan Claude of ChatGPT kunt geven om samen te vatten, te doorzoeken of om vragen over te stellen.

De OCR draait lokaal met een gebundelde Tesseract-build. Er is geen API-sleutel nodig, er is geen netwerkverbinding vereist en er verlaten geen afbeeldingen uw machine — de extensie maakt helemaal geen uitgaande verzoeken.

Installatie

  1. Download deze repository of gebruik git clone.
  2. Open chrome://extensions en schakel de Ontwikkelaarsmodus (Developer mode) in.
  3. Kies Uitgepakte extensie laden (Load unpacked) → selecteer de map.
  4. Pin de extensie — het icoon in de werkbalk dient tevens als paginateller.

Alles wat nodig is, is reeds toegevoegd aan de repository. Er is geen build-stap vereist: npm install is alleen nodig voor het uitvoeren van tests of het opnieuw integreren (re-vendoring) van Tesseract.

Controleer vervolgens chrome://extensions/shortcuts om te bevestigen dat de sneltoetsen zijn geladen — Chrome laat deze stilzwijgend leeg als ze al door iets anders worden gebruikt.

Bij installatie wordt er niet gevraagd om toegang tot websites. Enkele captures maken gebruik van activeTab, die Chrome overhandigt wanneer u de sneltoets indrukt of de popup opent. Twee functies vereisen een duurzame toestemming — een automatische run die langer duurt dan het laden van een pagina, en het omslaan van pagina's binnen een cross-origin iframe — en de popup biedt een 'Allow'-knop voor de site waar u zich bevindt wanneer dat nodig is.

Sneltoetsen

SneltoetsFunctie
$\text{⌥⇧S}$Leg de regio één keer vast (Capture)
$\text{⌥⇧A}$Start / stop een automatische run
$\text{⌥⇧R}$Teken of herteken de regio

Gebruik

1. De regio vastpinnen

Druk op $\text{⌥⇧R}$ en sleep een kader over de tekst. Voordat u opslaat, kunt u het kader verslepen, de handgrepen trekken of pixel voor pixel verschuiven met de pijltjestoetsen (houd $\text{⇧}$ ingedrukt om te schalen). Druk op Enter om het vast te zetten.

Teken het kader iets binnen de tekstmarges — alles in de rechthoek wordt gelezen, inclusief paginanummers en headers.

2. Vastleggen (Capture)

Druk één keer per pagina op $\text{⌥⇧S}$. Het screenshot wordt direct gemaakt en de OCR draait op de achtergrond, zodat u nooit hoeft te wachten tussen pagina's. Captures worden in een wachtrij geplaatst en de badge telt hoeveel er nog gelezen worden.

3. Automatisch laten lopen

Stel een next-page-besturing in (zie hieronder) en $\text{⌥⇧A}$ neemt het volledig over: vastleggen, omslaan, vastleggen, omslaan, totdat het document eindigt. Druk op Esc op de pagina om dit te stoppen.

4. Exporteren

Elke pagina wordt vermeld met een thumbnail van precies wat is uitgesneden, zodat een verschoven regio direct zichtbaar is in plaats van tachtig pagina's later. Tekst is ter plekke bewerkbaar; een foutieve lezing kan apart opnieuw worden uitgevoerd.

'Copy all' en 'Download .txt' exporteren de pagina's in volgorde met --- page N --- scheidingstekens.

Een pagina gemarkeerd als DUPLICATE had tekst die identiek was aan de vorige — dit gebeurt bijna altijd omdat het document niet daadwerkelijk is omgeslagen.

Pagina's voor u omslaan

Schakel Turn the page automatically after capture in, en vervolgens:

  • Klik op een besturingselement — klik op 'Pick control' en klik op de knop voor de volgende pagina van de viewer. Wat wordt opgeslagen is een punt, geen CSS-selector.
  • Druk op een toets — verzendt een toetsenbordevenement (standaard ArrowRight) naar het frame dat het midden van uw capture-regio bezit, zodat de reader de toets ontvangt en niet de hostpagina.

De knop 'Test' voert direct een advance uit, zonder capture, en rapporteert wat er is gebeurd — dit is aanbevolen voordat u een lange run start.

Waarom een punt in plaats van een selector?

Een opgeslagen punt overleeft DOM re-renders die routinematig een CSS-selector ongeldig maken, en het bereikt twee plekken waar een selector dat niet kan:

  1. Cross-origin iframes: De meeste ingebedde readers zijn iframes, en niets wat het bovenste frame kan uitdrukken, kan een element binnen een iframe adresseren.
  2. Shadow DOM: document.querySelector kan niet in een shadow root kijken.

Bij het omslaan van de pagina wordt het punt aangeboden aan elk frame en het frame dat het daadwerkelijk bezit, voert de actie uit. Een frame bepaalt waar het zich bevindt binnen de viewport van het bovenste niveau door zijn same-origin voorouders te volgen; over een origin-boundary geeft de parent de offset door via postMessage. ( window.screenX is hierbij niet nuttig — binnen een iframe rapporteert dit het browservenster, niet het frame.) Het bezittende frame lost het punt op via eventuele shadow roots, gaat omhoog naar de dichtstbijzijnde echte besturing en verzendt de volledige pointerdownmousedownpointerupmouseupclick sequentie, zodat viewers die reageren op pointerdown zich hetzelfde gedragen als diegene die luisteren naar click.

Wanneer het niet omslaat

Elke poging registreert een verdict, getoond in de popup en als een on-page toast:

VerdictBetekenis
no next-page control picked yetAuto-advance staat aan, maar er is niets gekozen
an embedded viewer owns that pointChrome's PDF-viewer of een plugin — onbereikbaar voor elke extensie
only the page background is at that pointHet besturingselement is verschoven; kies het opnieuw
a nested frame owns that pointEen frame waarin niet geïnjecteerd kon worden

Omdat het doel een vast punt op het scherm is, verbreekt het wijzigen van de venstergrootte of de zoomfactor tijdens een run de werking, precies zoals dat gebeurt bij de capture-regio.

Volautomatische runs

$\text{⌥⇧A}$ — of 'Start auto-run' — legt vast, slaat om en herhaalt dit zelfstandig. Elke cyclus wacht tot de OCR van die pagina terugkomt voordat er wordt omgeslagen. In de praktijk kost dit niets (OCR is sneller dan het omslaan van een pagina) en het biedt het enige wat een onbeheerde loop nodig heeft: betrouwbare detectie van het einde. Een run die alleen op basis van een timer screenshots zou maken, zou voorbij de laatste pagina schieten en het transcript vullen met kopieën daarvan.

Stop de run met Esc op de pagina, de sneltoets of de popup. Hij stopt ook automatisch wanneer:

ConditieStandaardinstelling
De tekst stopt met veranderenNa 2 identieke pagina's — u heeft het einde bereikt
De pagina kan niet worden omgeslagenOnmiddellijk, met vermelding van de reden
OCR faalt of stoktOnmiddellijk
Paginalimiet bereikt300 pagina's
Het tabblad sluit, of Chrome herstartOnmiddellijk

Wat de run beëindigde, wordt gemeld in de popup, zodat een run waar u van weg bent gelopen nooit mysterieus stopt. Een run weigert te starten zonder een werkende next-page-besturing.

PDF's

De ingebouwde PDF-viewer van Chrome werkt — tekst komt er direct uit. Teken de regio over het pagina-gedeelte (niet de zijbalk met thumbnails) en blader handmatig met $\downarrow$ / PageDown.

Auto-advance werkt niet in de PDF-viewer, in geen enkele modus: de viewer is een plugin waarin geen enkele extensie kan injecteren, dus een klik landt op de <embed>, en het bladeren is native scrolling waar synthetische toetsen-events geen grip op hebben. Aangezien u toch per pagina een sneltoets indrukt, kost het indrukken van uw eigen page-down toets niets extra's.

Voor een PDF op schijf (file:///…), open chrome://extensions → Details van OCR It → schakel Toegang tot bestand-URL's toestaan (Allow access to file URLs) in. Chrome weigert file:// voor elke extensie totdat u dit doet.

Instellingen

InstellingFunctie
LanguageEngels, Portugees en Spaans zijn standaard aanwezig — zie hieronder om meer toe te voegen
LayoutTesseract's paginasegmentatie. 'Single block' is geschikt voor één kolom hoofdtekst; 'Auto' handelt gemengde lay-outs af
Sharpen crop before OCRSchaalt de uitsnede op naar $\sim 2\times$ en vlakt deze af naar een uitgerekte grijstinten-ramp. Helpt enorm bij non-retina schermen; laat dit aanstaan
Flag pages identical to the previous oneMarkeert herhalingen als DUPLICATE en beëindigt in een run het proces
Auto-runPauze tussen pagina's, hoeveel herhalingen een run beëindigen, en de harde paginalimiet

Een taal toevoegen

Er worden drie talen meegeleverd — Engels, Portugees en Spaans. Elke andere van de $\sim 100$ talen van Tesseract kan worden toegevoegd, maar omdat er niets tijdens runtime wordt opgehaald, moet het model eerst in de extensie worden geïntegreerd (vendored).

npm install                    # eenmalig, voor de tooling
npm run vendor -- fra deu jpn  # eventuele tesseract taalcodes

Dit plaatst elke <code>.traineddata.gz in vendor/lang/. Voeg daarna de codes toe aan LANGUAGES in src/shared.js zodat ze verschijnen in het dropdown-menu van de popup:

export const LANGUAGES = [
  { code: 'eng', label: 'English' },
  { code: 'por', label: 'Portuguese' },
  { code: 'spa', label: 'Spanish' },
  { code: 'fra', label: 'French' },       // toegevoegd
];

Laad de extensie opnieuw bij chrome://extensions en de nieuwe optie is beschikbaar. De codes zijn de drieletterige codes die Tesseract gebruikt: fra Frans, deu Duits, ita Italiaans, nld Nederlands, rus Russisch, jpn Japans, chi_sim vereenvoudigd Chinees, ara Arabisch. De volledige lijst staat in de tessdata repository.

Twee talen tegelijk werken ook — gebruik een code als eng+por en Tesseract laadt beide modellen in één worker om een pagina te lezen waar ze gemengd voorkomen:

{ code: 'eng+por', label: 'English + Portuguese' },

Dit kost een beetje snelheid en nauwkeurigheid, dus geef de voorkeur aan één taal wanneer het document slechts één taal bevat.

Grootte: Elke taal voegt ongeveer 0,7–3 MB toe aan de extensie — Engels is de grootste met 2,9 MB, Frans een van de kleinste met 0,7 MB. De modellen komen van @tesseract.js-data/<code>/4.0.0bestint: de "best" modellen gekwantiseerd naar integers, wat significant nauwkeuriger is dan de fast-varianten.

Om een taal te verwijderen, verwijdert u de .gz uit vendor/lang/ en de entry uit LANGUAGES.

Hoe het werkt

MV3 service workers hebben geen DOM en geen Worker, dus het zware werk vindt plaats in een offscreen document.

Pipeline: run loop (⌥⇧A: capture → turn → repeat) → hotkeybackground.js → verberg eigen HUD, wacht op paint → chrome.tabs.captureVisibleTab (volledige viewport) → offscreen: crop naar de regio, upscale, greyscale → sla pagina + thumbnail op, sla pagina om → wachtrij → offscreen: Tesseract → tekst naar storage.

Bestandsstructuur

PadRol
src/background.jsSneltoetsen, capture-pipeline, seriële OCR-wachtrij, auto-advance, de run loop
src/offscreen/Canvas cropping en de Tesseract worker
src/content/overlay.jsRegion picker, point picker, on-page HUD, cross-frame offset cascade
src/popup/Paginalijst, bewerken, instellingen, export
src/shared.jsStorage schema en helpers gedeeld door de worker en de popup
vendor/Tesseract runtime + .traineddata, gecommitteerd zodat er geen build is
tools/Icoon-generator, vendoring, screenshots, end-to-end test

Belangrijke details:

  • De regio wordt opgeslagen in CSS-pixels relatief aan de viewport. Bij capture wordt de eigen breedte van het screenshot gedeeld door de actuele innerWidth, zodat zoomwijzigingen en retina/non-retina verschillen correct worden verwerkt zonder te vertrouwen op een opgeslagen DPR.
  • De HUD wordt verborgen en krijgt twee animatie-frames om te verdwijnen voordat het screenshot wordt gemaakt, zodat de toast van de extensie nooit in de uitsnede terechtkomt.
  • Captures worden geserialiseerd en OCR draait één taak per keer, zodat het herhaaldelijk indrukken van de sneltoets werk in de wachtrij plaatst in plaats van de paginalijst te corrumperen.
  • Full-size uitsnedes worden alleen bewaard tot een pagina succesvol is gelezen, daarna worden ze verwijderd; de thumbnail blijft voor verificatie.
  • Een run wordt geannuleerd door een token te wijzigen dat de loop bij elke await controleert, zodat stoppen gebeurt bij een checkpoint in plaats van midden in een schrijfactie. Storage-uitlezingen binnen de loop dienen tevens als keep-alive voor de service worker, en een alarm van één minuut herstart de loop als de worker toch wordt gerecycled.

Tests

npm install
npm test            # voeg -- --headed toe om mee te kijken
npm run shots       # genereer de screenshots in docs/ opnieuw

De suite installeert de uitgepakte extensie in een echte headless Chrome via het DevTools protocol, serveert testdocumenten en bestuurt het product: het sleept een regio met synthetische muis-events, voert captures uit, controleert de OCR-tekst tegen wat er is gerenderd, verifieert dat niets buiten de regio is gelekt, controleert dat het manifest geen host-toegang vereist en dat één toolbar-klik genoeg is voor een simpele capture, test duplicate-detectie, oefent auto-advance uit tegen drie DOM-vormen (een gewone pagina, een cross-origin iframe en een open shadow root), bevestigt dat een verkeerd geconfigureerde auto-advance zichzelf rapporteert in plaats van stilzwijgend faalt, draait een onbeheerde loop tot het einde van een eindig document en controleert of deze op eigen kracht stopt met alle pagina's in de juiste volgorde, en controleert of een run direct stopt op verzoek.

Chrome 137+ negeert --load-extension, dus de harness installeert via CDP met Extensions.loadUnpacked en --enable-unsafe-extension-debugging. Headless Chrome kan de toestemmingsprompt niet tonen, dus de gedragstests installeren een kopie van de extensie waarbij de toestemming is ingebakken — de status van een gebruiker die op 'Allow' heeft geklikt — terwijl de permissies-sectie het echte manifest controleert en bewijst dat het niet-toegestane pad nog steeds werkt via Extensions.triggerAction, wat een echte toolbar-klik is.

Beperkingen

  • chrome:// pagina's, de Web Store en pagina's van andere extensies zijn verboden terrein voor elke extensie, inclusief deze.
  • Alleen de zichtbare viewport kan worden vastgelegd — de regio moet op het scherm staan.
  • Chrome beperkt screenshots tot een paar per seconde; captures proberen het opnieuw met backoff, dus snel indrukken plaatst ze simpelweg in de wachtrij.
  • Nauwkeurigheid hangt af van de bron. Scherpe gerenderde tekst wordt gelezen met $\ge 90\%$ betrouwbaarheid; scans met een lage resolutie en handgeschreven tekst vereisen handmatige correctie.
  • Lokale file:/// documenten vereisen dat 'Toegang tot bestand-URL's toestaan' is ingeschakeld.
  • activeTab bereikt geen cross-origin iframes. Als uw reader in een iframe staat, verleen dan toestemming voor de site via de popup voordat u auto-advance instelt.

Licentie

MIT — zie LICENSE. Gebundelde Tesseract-componenten behouden hun eigen licenties: vendor/LICENSE.tesseract-core en vendor/tesseract.min.js.LICENSE.txt.

Gebouwd op tesseract.js.