Skip to content
BabaPDF
Learn

Why your PDF text turns into question marks

6 min readArticle

The built-in fonts physically cannot hold Cyrillic, CJK or emoji — and some tools tell you, while others just mangle it.

You convert a text file to PDF, open the result, and where your name used to be there is a row of question marks. Nothing warned you in the way you would expect to be warned — the conversion ran, the file downloaded, the document opens perfectly. The text is simply gone, swapped character for character with "?". PDF text turns into question marks because of the font, not because of anything wrong with your file, and the reason is old enough to have a certain grim dignity to it.

The fonts that come free with the format hold 218 characters

Every PDF reader is required to know fourteen fonts without being handed them: Helvetica, Times and Courier with their bold and italic variants, plus Symbol and ZapfDingbats. Using one costs nothing — the file names the font and the viewer supplies the glyphs. A whole one-page PDF written that way comes out of this site at roughly a kilobyte, because there is no font inside it to carry. The catch is the encoding. Those fonts are addressed through WinAnsi, an eight-bit encoding, and eight bits gives 256 slots. Cyrillic does not fit into 256 slots next to Latin. Neither does Chinese, or Japanese, or Hindi, or an emoji.

BabaPDF's text conversion uses Helvetica this way, and the output admits it if you open the file up: BaseFont Helvetica, Subtype Type1, Encoding WinAnsiEncoding, and no FontFile entry anywhere in the document — nothing embedded, nothing carried. So when TXT to PDF meets a character Helvetica cannot address, something has to give.

A bright compartment tray, most of its cells holding small dark blocks, with one corner group of cells standing empty
A standard font is a type case with a fixed number of compartments. Your alphabet either fits in them or it does not.

What gives is a question mark, silently

The library doing the drawing is a fork, and forks exist in order to behave differently from the thing they were forked from. I ran the identical call against both. Upstream pdf-lib, version 1.17.1, refuses outright: drawing "СЕКРЕТНО" throws an error reading WinAnsi cannot encode "С" (0x0421), and nothing is written. The fork this site actually uses does not throw. It returns a perfectly valid PDF, no error, no warning, no complaint of any kind — and the page reads "????????".

Silent substitution is the worse of those two failures by a wide margin, and it has already cost this codebase once. A comment in the source asserted that the library throws on characters it cannot encode. True of upstream; quietly false after the fork. The watermark tool was written trusting that comment, and shipped a feature that would stamp "????????" across every page of a document and report success. A watermark that says nothing looks identical to a watermark that worked.

A dark road forking into two, a bright line running away along one branch while the other ends flat
The original library refuses and tells you. The fork carries on and says nothing. Both are defensible; only one is visible.

The characters that were changed without ever being counted

Until 16 July 2026, a lookup table rewrote eighteen code points into ASCII lookalikes before anything was tested for encodability at all. Smart quotes became straight quotes. An en dash became a hyphen, an em dash became two. An ellipsis became three periods, a bullet a hyphen, guillemets angle brackets. Because the substitution happened first, those characters were never tested, never counted and never mentioned. The tool reported zero replacements and was telling the truth by its own accounting.

I checked what a reader actually received. The line "Smart “quotes” and — dash…" converted to Smart "quotes" and -- dash... with a replacement count of zero. Nothing was exactly wrong with that output — it was legible, and it is what most people would shrug at. But your typography was edited on the way through, and the number on the screen said nothing had happened.

The guard was stricter than the font it was guarding

Here is the part I did not expect to find. The encodability test admitted the line feed, printable ASCII from space to tilde, and the Latin-1 supplement from 160 to 255 — 192 code points in total, with everything else becoming a question mark. That is a Latin-1 test. But the font is not Latin-1 encoded. It is WinAnsi encoded, and WinAnsi differs from Latin-1 in precisely one region: the range from 128 to 159, which Latin-1 leaves empty as control codes and which WinAnsi fills with 27 printable characters — the euro sign, the smart quotes, the dashes, the ellipsis, the bullet, the trademark sign and their neighbours.

So the guard rejected characters the font could have drawn. I drew a euro sign directly, with no sanitising in the way, and the page read € — the library encodes it without a murmur. Sent through TXT to PDF, the same euro sign came back as a question mark with a replacement counted against it. Nobody would guess that from outside: a currency symbol off the same keyboard as the pound sign, in a font that can draw it, replaced with "?" on the way to the page.

That is a false limitation — a tool talking you out of something it can actually do — and it was fixed on 16 July 2026. The guard now admits those 27 characters, each one verified by writing a PDF and reading it back with a different library than the one that wrote it. A euro sign comes out as a euro sign. Smart quotes stay curly, dashes stay dashes, the ellipsis and the bullet arrive as themselves, and the count no longer lies by omission. The five slots WinAnsi genuinely leaves undefined still become question marks, and so does everything outside the encoding — Cyrillic, CJK, emoji. The font did not grow glyphs; the gate just stopped being narrower than the door behind it.

A dark archway with a bright round object wedged at its opening, unable to pass through
The gate was narrower than the door behind it — characters turned away from a font that would have set them perfectly, until the range test was fixed.

If you are stamping a watermark, this is where it bites hardest. Add Watermark sends its text through the same guard, so a Cyrillic or Chinese watermark comes back as question marks stretched diagonally across all of your pages. The tool warns you twice: its live preview shows the question marks before you commit, and it reports the count. Look at page one before you circulate the document anyway.

Why not just embed a font with every character in it?

Embedding a real Unicode font is the correct fix, and it is genuinely expensive. Covering Cyrillic, Greek, Arabic and CJK means delivering a font file to the browser before anything can be converted at all, and a font covering common Chinese carries tens of thousands of glyphs rather than a couple of hundred. That download would land on every visitor, including the large majority converting ASCII shopping lists, on a tool whose entire argument is that it is small and finishes before you have looked away.

Embedding also travels: the font goes inside every file you produce, which is the same trade-off that makes PDFs large in the first place. The honest position is that this is a real limitation, chosen on purpose, and not a gap waiting to be filled next quarter. If your document is not in a Latin alphabet, a browser tool built around the standard fourteen fonts is the wrong tool for it, and you want a word processor that embeds a subset of a proper font instead.

A row of ornate dark bottles on a shelf, beside plain pale identical cylinders
Curly quotes went in, straight quotes came out, and the counter said nothing happened. That silent downgrade is gone now — curly stays curly.

What to do about it

  • If your text is English or Western European, none of this touches you. The WinAnsi range covers it comfortably, typographic punctuation included.
  • If your text is Cyrillic, Greek, CJK, Hebrew, Arabic, or has an emoji in it, expect question marks — and open the converted file rather than trusting the download.
  • Watch the replacement count. A number above zero means characters were destroyed, and since the July 2026 fix a zero means your characters went through as themselves.
  • Smart quotes, dashes, ellipses, bullets and the euro sign render exactly as typed. The silent ASCII rewrite described above was removed on 16 July 2026.
  • Two lookalike substitutions remain, both outside WinAnsi: the reversed quotation mark becomes a straight quote and the true minus sign becomes a hyphen. Neither is counted, and neither loses meaning. Convert your text trusting the count you see.
Keep reading