Skip to content

About

Tap one article in a newspaper or magazine PDF and read just that column, laid out to fit your phone. 100% AI-generated, from an idea on Somebody Build This.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

This is a 100% AI-generated project. This app was 100% generated by AI (the Somebody Build This steward): designed, written, tested and deployed by Claude (Anthropic's Claude Opus 5.5, working in Claude Code), from this idea on the board: A way to tap one article in a newspaper PDF and read just that column on a phone.

Built from an idea on Somebody Build This

Column Reader

Open a newspaper or magazine PDF on your phone, tap the article you want, and read just that column's text, laid out to fit the screen at a size you choose.

Use it: https://column-reader.vercel.app

What it does

  • Open a PDF from your phone. It stays on the device: the app never uploads it. No account, no cookies, no analytics scripts, no third-party scripts, and no tracking by the app. Like any website's host, Vercel keeps request logs that can include IP addresses and referring sites. We only look at weekly totals of requests to load the app, grouped by referring site, to see whether people find it; these count requests, not people. We don't look at individual requests.
  • Tap a column. Each page is shown small; tap the part you want to read. Screen readers and keyboards get one button per column, in reading order, named by its first words.
  • Read it laid out for the screen. The column's text is shown as plain text that fills the screen width, at a size you set with A− and A+ (remembered). Words split across lines with a hyphen are joined back up, and paragraphs are kept.
  • Next column / Previous column move along the page without going back. A small picture of the page marks where you are.
  • Show the original draws that column as it was printed, at screen width, for whenever the text looks wrong. Back to the page is always at the top.
  • Remembers your place in each paper (by file name, size and page count, on the device only), so reopening it goes back to the same page.
  • Works offline once it has loaded, and can be added to a phone's home screen.

Honest limits

  • Text PDFs only. A scanned or photographed page has no text in it, so there's nothing to pick out; the app says so plainly on that page and points to the first page that has text (a picture-only cover doesn't stop you reading the rest). If no page has any text, it says the whole PDF has none.
  • Clear columns work best. Busy pages (crosswords, ads, tables, text wrapped around odd shapes) can come out in the wrong order or mixed. Captions and photo credits between two columns show up as their own short "column" when you press Next column.
  • Hidden text. Some PDFs carry invisible leftover text under the visible page; it can get mixed in, because the app can't yet tell it apart from visible text.
  • It does not follow "continued on page 6" jumps for you; go to that page and tap the column.

It was checked on three real papers (a weekly community newspaper, an Army installation newspaper and a computer magazine): 12 of 13 articles in different positions read in the right order; the one failure was a page with hidden leftover text. It was also checked on made-up sample pages with justified text and narrow gutters, including columns of different widths.

Needs a current browser (tested in Chrome at phone and desktop sizes, including Chrome 123 from 2024). Works from 375px wide, follows your system's light or dark mode, respects "reduce motion", and has visible keyboard focus.

Develop

Needs Node 22+ and pnpm.

pnpm install
pnpm dev       # local dev server
pnpm test      # unit tests (vitest)
pnpm check     # typecheck + tests
pnpm build     # production build into dist/

PDFs are read with pdf.js (pdfjs-dist), bundled with the app together with its worker, fonts, character maps and image decoders, so the app never fetches anything from another site. src/polyfills.ts adds two small things pdf.js expects that browsers from a year or two ago lack.

The interesting logic is small and pure, in src/logic/, with tests built from synthetic text positions:

  • items.ts turns pdf.js text pieces into page positions, drops text drawn twice, and spots pages with no text (scans).
  • layout.ts finds the columns. Pieces on one baseline are joined into lines, but never across a gutter: a gap counts as a gutter only when a clear channel runs through it in the lines above and below too, in the same size and typeface (so a stretched word space in justified text, even one wider than the gutter, doesn't split a line, while two big headlines that almost touch do get split). Lines stacked in one column, in the same size and font, become a block (a column of one article, a headline, a caption). Blocks are put in reading order by cutting the page at clear vertical space first (columns, left to right), then at clear horizontal space (top to bottom). Drop caps are joined to their word.
  • reflow.ts turns a block back into paragraphs (indents, blank lines, short lines ending a sentence) and joins words broken with a hyphen at the end of a line, keeping real hyphens like "90-minute".
  • memory.ts holds the saved text size and reading places, read back safely.
  • datafiles.ts loads pdf.js's data files for one PDF and notes if any failed.

The offline logic is in src/sw/worker.js (tested in worker.test.ts), the same approach as Picture Story; the build inlines it into /sw.js with the list of files to cache. At install, each version fetches the page, checks that every script and stylesheet it loads is in that version's own file list (during a deploy, the page can briefly come from a different release), caches those files, and only then stores the page. A mismatch fails the install, so the previous version and its cache stay in charge. The stored page is never overwritten afterwards, so the offline app always matches its scripts, and a new version is used offline only after it has installed completely. A new version also waits until every open page of the old one is closed (no skipWaiting), and until then the app's page is always answered from the active version's own cache, even online, so a reload or a new tab gets the same version as the open tabs. Every file a page loads is named by its content: scripts and styles by Vite, and pdf.js's data files (fonts, character maps, decoders) live under /pdfjs/<hash of their contents>/. A page therefore only ever asks for its own version's files, and versions can't be mixed whatever the timing. On a first visit the page loaded before the offline copy existed; once it is installed, the page asks to be covered, and the service worker takes it over if the page's scripts are its own version, so the first visit works offline without a reload (another page caught by the same takeover still only loads its own version's files, from the network). pdf.js's data files are loaded by the page through the service worker; if one fails for a PDF (say, offline before the copy was installed), that PDF's empty-looking pages say part of the app didn't load and offer a reload, rather than claiming it has no text, and the next PDF starts afresh. The pdf.js worker is started as soon as the app loads, and if it can't load at all, opening a PDF says so and offers a reload instead of waiting forever.

It is a static site (Vite + vanilla TypeScript) with no environment variables or secrets, hosted on Vercel, which builds it from this repository (vercel.json sets a strict Content Security Policy: only this site's own files, no inline scripts or styles).

Maintenance

This app is looked after, not left behind. Dependabot proposes dependency updates. The Somebody Build This steward checks on the app each month and reviews security alerts in a weekly security check. Updates go through review and automated checks before they are merged.

Found a problem? Open an issue on this repository and describe what happened.

Build your own version

Want to build your own version? Ideas never lock: anyone can build this idea too. This is just one take on the idea, and anyone is welcome to build their own version and ship it there too.

License

MIT (see LICENSE). pdf.js and the files bundled with it keep their own licenses: Apache 2.0 for pdf.js, and their own licenses for the fonts, character maps and image decoders. The build copies each license file next to those files, under /pdfjs/<hash>/ on the live site.

About

Tap one article in a newspaper or magazine PDF and read just that column, laid out to fit your phone. 100% AI-generated, from an idea on Somebody Build This.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages