From the blog

Why your TTS app reads section headers like body text (and how to fix it)

You are walking to a meeting. The phone is reading a proposal you should have read last night. The audio runs along comfortably for a minute and then, somewhere around the middle of a sentence, you hear this:

“…significant growth opportunity over the next four quarters. Product Roadmap We will ship the core feature set in Q3 of next year and follow with…”

You stop walking. You back up the audio fifteen seconds. You listen again. You realize that “Product Roadmap” was a section header in the original PDF — it sat alone on its line, was set in 16-point bold, had whitespace above and below — and your TTS app read it as if it were the end of the previous sentence and the start of the next one. The transition into a new section came and went and you did not register it. You have been listening to body text from the new section for thirty seconds without knowing.

This is the most common failure mode in PDF-to-speech reading, and it happens because of one specific design choice. Almost every TTS app treats your document as a flat string of characters and asks the synthesis engine to read it at a steady cadence. The visual metadata that tells your eye “this is a heading” is thrown away before the audio is ever generated.

This post is about why that happens, what the alternative looks like, and what you can do about it as a reader and as a writer.

The PDF format actually knows this

A PDF is not a stream of characters. It is a structured document where every text run carries metadata: which font, which weight (regular, bold, semi-bold), what size, what color, the exact position on the page. When a document designer creates a heading, the heading is encoded with a larger font, a heavier weight, and often a distinctive color. The PDF file remembers all of this, byte-for-byte, even after the file has been compressed and shipped.

The “Product Roadmap” line in the original proposal had:

  • Font size 16, against a body text size of 11
  • Font weight bold (weight 700), against body text at regular (weight 400)
  • A teal color, not the near-black of the rest of the document
  • Standalone position on its own line, with extra whitespace above and below
  • Left-aligned at the same indentation as the body — not centered, not indented

A human eye picks up all of this in roughly two hundred milliseconds and processes the line as a section header before consciously reading the words. The information is right there in the document. The TTS app threw it away.

Why TTS apps do this

There are two reasons, and both come from how the software is structured rather than from any deep technical limitation.

Cloud TTS doesn’t care. When an app routes the document through a cloud TTS service — and most of the household-name PDF-to-speech apps do — the cloud service receives plain text. It has no way to know that “Product Roadmap” was set in 16-point bold teal because the metadata was stripped at the client when the text was extracted for upload. The synthesis engine reads what it is given. If the input is flat text, the output is flat audio.

You could imagine routing the metadata to the server as well — sending font size, weight, color alongside each chunk of text — but the standard cloud TTS APIs do not accept that. They take a string and a voice ID. They return audio. The protocol is the constraint.

Even on-device apps mostly do plain-text extraction. It might seem that an app processing documents locally would have an easier path to structure-aware reading, since the metadata never leaves the device. In practice, most on-device readers extract text through the default API of whatever PDF library they use — PDFKit’s attributedString on iOS, PDFTextStripper’s flattened output on Android — and the default API is text-first. Font metadata is technically available but requires opting in: subclassing the text stripper, overriding the per-character callback, accumulating runs with their attributes, and writing the heuristic layer that decides what to do with all that. It is real engineering work. Most apps do not do it because nobody has been asking for it.

The two reasons compound. Cloud apps can’t do structure-aware reading because the architecture doesn’t allow it. On-device apps could but don’t because nobody else has, so nobody knows to ask for it.

What structure-aware reading actually means

A reader that respects document structure picks up the visual metadata that the PDF preserved, classifies what each block of text actually is, and converts that classification into audible cues. The heuristics are not magic. Most of them are the same rules a designer would describe when teaching a junior designer how to read a layout.

Larger font + bold → a heading. The threshold is per-document. A 14-point bold line in a document whose body is 10-point is a heading. A 14-point bold line in a document whose body is 12-point is a sub-heading at best, maybe just an emphasis. The reader has to compute the document’s own “normal” before it can decide what counts as larger-than-normal.

Colored bold text at body size → still a heading. This is the case the “Product Roadmap” example sat in. The text was the same size as body — 11 points — but it was bold and it was teal, while the rest of the document was near-black. The author chose teal precisely because it makes the line visually distinct from body text. A reader that ignores color misses this; a reader that respects it picks the line out the same way the eye does.

Bullet-glyph at the start of a line → a bullet. The common visible glyphs (•, ●, –, —, ◦, ▪, *) are the cue. A reader can strip the glyph from the audio — you do not actually want to hear “bullet point” before every item — and instead use a brief pause to signal the list rhythm. The eye does the same thing: it sees the bullet, registers “list,” moves on.

Bold prefix followed by a colon → an inline label. “Product:”, “Timeline:”, “Risks:” at the start of a paragraph or a bullet. The label is short. The colon is the separator. A reader that knows this can read the label with a slight emphasis, place a 250-millisecond pause after the colon, and continue with the body text at normal cadence. In audio, this is the difference between “Product ninety-five percent built no will we ship it risk” and “Product. (beat) Ninety-five percent built. No will we ship it risk.”

Repeating small text near the top or bottom of every page → a running header or footer. Page numbers, document titles, copyright lines. A flat-text reader will dutifully read every one of these aloud once per page, which on a fifty-page document means fifty interruptions of “page seventeen of fifty” with no useful information. A structure-aware reader detects the recurrence (same content, same position, multiple pages) and skips them entirely.

The full classification cascade — the one Audris uses, in priority order — is described in the structure-aware reading post. The rules above are the most important ones. There are a handful more for quotes, code blocks, and table cells, but the headline cases are the bullet glyphs, the bold-with-color heading, and the labeled bullet pattern.

Why pauses matter so much for listening

The fix for the “Product Roadmap” problem is mostly about pauses. Bigger pauses before a major heading. Shorter pauses before a sub-heading. A beat after a label. Almost nothing else — the voice itself doesn’t need to change much.

This is because reading visually and listening are two different perceptual problems.

When you read a page visually, the eye does the pacing. Whitespace is information. A blank line says “new paragraph.” Extra whitespace plus a bigger font says “new section.” The eye registers the visual structure and the brain uses it to keep track of where you are in the document. You can skim, jump back to re-read a sentence, drop into a sub-heading and read just that part. The structure is laid out in two dimensions and your eye navigates it.

Listening collapses all of that into a one-dimensional stream. You can pause and rewind, but you cannot skim and jump in the same way. Your brain does not get the visual cues. Pauses are the only way to encode structure in the audio signal — and they have to be the right length, in the right places, or the structure is invisible.

Research on prosody and parsing — the field that studies how listeners process spoken language — describes this in some detail. The short version: listeners use pause length to segment incoming speech into clauses, sentences, and larger units. A 200-millisecond pause is heard as a comma. A 600-millisecond pause is heard as a sentence end. A 1.5-second pause is heard as a major boundary — section, chapter, topic shift. A reader that places pauses arbitrarily produces audio that the listener’s brain has to work harder to follow; a reader that places pauses where the document’s structure says they should go produces audio that lands naturally.

This is why “just speed up the audio” does not fix the flat-text problem. The pauses are missing. Speeding up the audio compresses the missing pauses further. Slowing it down expands the body text proportionally without restoring the structural breaks. The fix is not a rate adjustment. It is recovering the structure and using the right pause for each transition.

How to fix it

If you are a reader: there are a few options.

The simplest is to know what your current app does and does not handle, and to read accordingly. If your app reads flat text — and most do — pick documents where the flat-text reading does not matter much. Articles, essays, the body of a chapter. Skip documents where structure carries information. Save those for visual reading.

The slightly less simple option is to switch apps. Voice Dream Reader on iOS has some heading awareness — it will pause a beat for some structural transitions, particularly in well-tagged EPUB files. It is not full structure-aware reading, but it is better than the flat-text default. Audris is an on-device, structure-aware reader built around this specific problem — every step of the pipeline described above is implemented and tuned per-document. If structure-aware reading matters to you, this is what we built it for, and the home page has an audio demo of the same business proposal paragraph read two ways. The difference is the entire pitch.

If you are a writer of PDFs — and most knowledge workers are — there is a simpler fix that helps every assistive technology, not just TTS apps. Use proper heading styles in your word processor. In Microsoft Word and Google Docs and Apple Pages, the “Heading 1” and “Heading 2” styles do two things: they apply the visual styling (size, weight, color) and they tag the heading semantically in the document structure. When the file is exported as a PDF, the semantic tag is preserved. Any assistive technology that reads tagged PDFs — screen readers for blind users, navigation tools for users with motor disabilities, and yes, structure-aware TTS apps — can use the tag directly. No font heuristics needed.

The mistake is to fake a heading with size and bold without using the heading style. The line looks right visually but it carries no semantic tag, and the assistive technology has to infer the structure from the visual cues. A structure-aware reader like Audris does that inference for you, but it is fundamentally easier when the document is tagged in the first place.

What this enables, beyond not missing section breaks

Recovering the structure does more than fix the “Product Roadmap” problem. Once the audio knows where one section ends and the next begins, several things become possible that flat-text readers cannot do.

The headphone “next chapter” button can actually skip to the next section. On flat-text TTS, that button skips a fixed amount of audio — usually thirty seconds — which lands somewhere in the middle of the current paragraph. On structure-aware audio, the same button takes you to the start of the next H2 heading. You can navigate the document by section the same way you would scroll through it visually.

The sleep timer can stop at the end of a section instead of mid-sentence. If you fall asleep listening to a chapter, the audio finishes the section and pauses cleanly. You wake up at the start of the next section and pick up where the structure resumes, not in the middle of a paragraph you have to re-listen to in order to get the context.

A tap-to-jump table of contents becomes possible. The reader can present the structure visually as a list of section titles, and you can jump to any section directly. On a flat-text TTS, the same feature has to fake it with timestamps and offsets and usually lands a few seconds off.

The structure is the foundation for almost every “listening as a first-class activity” feature. Flat-text readers cannot offer these features because they do not know where they are in the document. Structure-aware readers can.

Documents that breathe

The opening example in this post was about a proposal that read “Product Roadmap” as if it were the end of the previous sentence. That moment is the small, repeating failure that adds up over a long document. Three or four of those per page and you cannot follow the argument. Twenty of them per chapter and you stop using audio for that kind of document at all.

Documents that breathe is the short version of what structure-aware reading produces. Headings get the breath they have on the page. Bullets get the rhythm. Labels get the brief tempo shift that says “this is the name of the thing, the rest is the description.” Section breaks land where the writer put them. Audio that respects the document’s own structure is the difference between listening for forty seconds and giving up, and listening for the entire walk to the meeting.

The fix is not exotic. It is a pipeline that reads the visual metadata the PDF already preserves and converts it into the audio cues a listener already needs. Every PDF you have ever opened contains the information. Most TTS apps just throw it away.