The symptom everyone recognizes

Copy a paragraph from a two-column paper, paste it into a translator, and you get something like this:

…the model consistently underestimates variance under high noise conditions. We therefore propose a two-stage framework for robust detection. Figure 3 shows the ablation results for the proposed module. In contrast, prior work assumes homoscedastic error…

The last sentence belongs to the right column. The extractor glued it onto the left column's ending. Nothing is corrupted — the text is all there — but the reading order is wrong, so the meaning inverts.

This is worth stating precisely, because it changes what you should fix:

Two-column scrambling is a text-extraction ordering problem, not an OCR-accuracy problem and not a translation problem.

If you improve OCR accuracy, the order stays wrong. If you switch translators, the order stays wrong.

Why the ordering breaks

PDF is a display format, not a document structure. The bytes generally describe where glyphs are painted, not what a paragraph is. A two-column page therefore offers at least two plausible orderings of the same glyph sequence:

  1. Row-major — sweep left to right across the full page width, then down. This interleaves the columns.
  2. Column-major — read the left column top to bottom, then the right column top to bottom. This matches how humans read it.

Many extractors default to something close to (1), because it is the simplest geometric rule and it works fine for single-column text. Academic templates — LaTeX twocolumn, IEEE, ACM, Springer — are exactly the case where (1) fails badly.

Additional traps that compound the problem:

  • Figure captions live in a narrow band that often spans one column but is vertically centered near the page middle, so they survive in odd places.
  • Footnotes sit at the page bottom and get appended at the end of a row sweep.
  • Inline math splits a sentence into fragments that get re-ordered independently.
  • Hyphenation at line ends leaves broken words (detec- / tion) that no translator can recover after reordering.

The two clean fixes

Fix A: copy per column

If the PDF has a text layer, select only the left column, paste, then the only the right column. This is the cheapest fix for a single paragraph and it needs no tooling.

Its weakness is operational: for a 14-page paper you will do this dozens of times, and it is easy to lose your place.

Fix B: region capture with on-device OCR

Capture the region you are actually reading — a paragraph, a results table, a figure caption — and recognize the text in place. Because the capture is spatially constrained, the character stream follows the visual reading path, so column interleaving cannot occur.

The tradeoff versus Fix A: you need a capture tool, and you are re-recognizing text that already exists as a text layer (slightly redundant). The benefit: one gesture per paragraph, and it also works on scanned PDFs where no text layer exists at all.

Implementation notes that decide success or failure

If you build this yourself on macOS with VNRecognizeTextRequest, these four settings matter more than anything else:

import Vision func recognize(_ cgImage: CGImage, orientation: CGImagePropertyOrientation = .up) throws -> [String] { let request = VNRecognizeTextRequest() request.recognitionLevel = .accurate // small caption text needs .accurate request.recognitionLanguages = ["en-US", "ja-JP"] request.usesLanguageCorrection = false // keep identifiers and units intact request.minimumTextHeight = 0.0 request.customWords = ["homoscedastic", "p-value", "HR", "CI"] let handler = VNImageRequestHandler(cgImage: cgImage, orientation: orientation) try handler.perform([request]) return (request.results ?? []).compactMap { $0.topCandidates(1).first?.string } } 
  1. usesLanguageCorrection = false. With correction on, statistical notation and identifiers get "helpfully" normalized. For academic text this is worse than useless — p becomes P, subscripts drift, and symbols change class.
  2. orientation. VNImageRequestHandler(cgImage:orientation:) with a CGImagePropertyOrientation is mandatory for rotated scans. Without it you get empty results, not an error — the worst possible failure mode.
  3. Reading order from boundingBox. Observations come per visual line, in normalized coordinates with the origin at the bottom-left. To reconstruct reading order: cluster by midY (descending), then sort each cluster by minX (ascending). Use a tolerance derived from the median line height (roughly 0.6×), never a hard-coded constant — zoom level and resolution will break a constant immediately.
  4. Hyphenation on join. Drop a trailing hyphen only when the next fragment starts lowercase; otherwise you will destroy real identifiers such as --legacy-peer-deps.

Where the data goes (the part that decides adoption)

Splitting the pipeline matters more than the OCR settings for anyone reading internal documents:

Stage Runs Leaves the machine
Screen capture → text recognition On-device (Apple Vision) nothing — pixels stay local
Recognized text → translation Online the recognized string only

So: the screenshot of an unpublished manuscript is never uploaded, but the recognized text is sent for translation. Offline, recognition still works and translation does not (NSURLErrorDomain error -1009). That is a design boundary, not a defect, and it should be stated plainly rather than buried.

What this does not solve

  • Scanned papers with no text layer are recognized, but OCR of dense math remains unreliable regardless of settings.
  • Vector figures are not text; nothing here reads a plot.
  • Column detection on three-column layouts (some Nature/Nature-style formats) needs a different clustering strategy.
  • Latency is machine- and resolution-dependent; I am deliberately not quoting a number, because a figure measured on one Mac does not transfer to another.

The tool I use

I built this into a small macOS app for my own reading workflow and distribute it:

  • macOS 13.0+ / Apple Silicon (arm64) only — does not run on Intel Macs
  • No account required, free to use
  • First launch of a non-notarized build: right-click → Open

Product page: https://www.zipp-mac.com/?utm_source=devto&utm_medium=social

Source and builds: https://github.com/dayu-999/zipp-mac

Takeaway

When a two-column paper "breaks" after copy-paste, do not tune your translator. The text is intact; the order is wrong. Either copy per column, or capture the region so the reading path is preserved — and if you go the OCR route, set usesLanguageCorrection = false and derive your line-clustering tolerance from the observed text height.