At the end of lesson 04, Redact knew the text on the page but not where to show it. The OCR panel listed Maya Chen; the page itself showed nothing. Before any suggestion can become a black box, we need OCR’s positions to land on exactly the right pixels in the editor. Checkpoint: redact-05-coordinates, after redact-04-connect-ocr

Two ways to describe a box

OCR and the editor describe the same rectangle in different languages. The editor uses percentages because the page is drawn at different sizes: big on a monitor, small on a phone, scaled in the safe-share preview. A mark at x: 17.5% stays on the same word at any size. OCR uses pixels because that’s all it knows: the image it was given. So every box needs converting once, from pixels to percentages, using the size of the exact image OCR read. That’s why lesson 04’s service refused images whose size didn’t match the request.

Why be so strict about bad boxes?

A wrong outline is worse than no outline. If an outline sits a line too low, a reviewer who trusts it covers the wrong words and shares the real ones. So the conversion fixes what it safely can, like corners given in the wrong order or a box that pokes past the page edge, and throws away what it can’t. More on OCR’s coordinates, including what rotation does to them: OCR boxes and rotation.

1. Get this lesson’s code

Stop the app and the service if they’re running. From the repository root, redact-course, bring in this lesson’s commit:
No packages change, so there’s nothing to install. To see every change, open the comparison on GitHub.

2. Convert one box by hand

The chat image is 1200 pixels wide and 1600 pixels high. In one run, OCR returned this box for Maya Chen:
The editor wants the top-left corner and the size, each as a percentage of the page:
Notice the subtraction. Dividing all four numbers by the page size would give you the bottom-right corner, not the size, and every mark would be far too big.

3. Read the conversion

Open src/features/redact/ocr-geometry.ts:
Read it top to bottom:
  1. A page with no size is a bug, so it throws.
  2. A box with NaN or Infinity in it is skipped (null).
  3. Math.min and Math.max put the corners in order, whichever way OCR gave them.
  4. clamp pulls each edge back onto the page.
  5. If nothing is left, because the box had no width or lay entirely off the page, it’s skipped.
  6. Otherwise it’s converted, using x2 - x1 for the width as you did by hand.
percentage rounds to six decimal places, which is why you get 17.583333 instead of a long fraction.

4. Read where it’s used

In RedactApp.tsx, each OCR block is converted as it arrives:
flatMap with an empty array is how a skipped box disappears. The block keeps rawBbox too, so you can always see what OCR originally said. DocumentSurface.tsx then draws one dashed outline per block, positioned with the percentages:
Only on Original, never on the safe-share preview: outlines show evidence, they don’t cover anything.

5. Try to break it

Run only these tests:
The first test is your hand calculation. The second feeds corners in the wrong order and far off the page, [1300, 1700, -100, -200], and expects the whole page back. The third checks that NaN, a zero-width box and a box entirely off the page come back as null, and that a page with width 0 throws.

6. See the outlines on the page

Start the service in one terminal and the app in another:
Upload public/test-files/redact/chat-private.png, choose Private, and select Prepare for review. On Original, the header now reads 12 detected regions · drag to draw, and every line of text has a thin dashed outline around it. Look at Maya Chen: the outline hugs the name. Now look at Safe-share preview. Nothing is covered. Drag across Maya Chen to cover it yourself, and notice your orange mark sits on top of OCR’s dashed outline.

If something goes wrong

  • Outlines are offset from the text: the page the browser shows isn’t the image OCR read. That’s what the size check in lesson 04 prevents; make sure you’re on this lesson’s code with git log --oneline -1.
  • No outlines and “Drag to draw · drag marks to move” in the header: no OCR blocks came back, usually because the service isn’t running. The OCR panel on the right will be missing too.

7. Check your work

Stop the app and service, then run:
npm test reports Tests 49 passed (49).

So what just happened?

OCR’s pixel boxes now land on the right words in the editor, at any zoom. Boxes that would land in the wrong place are dropped instead of drawn. Every suggestion from here on is tied to one of these outlines.

Try it yourself

Pick another block from lesson 02’s output, phone is +1 415 555 0184., and convert its bbox by hand. Then upload the chat and check your numbers against where its outline sits, on the second line of the first message bubble.

Where to next

Next: Ask Laya which lines are sensitive (lesson 06)