Turkish characters in HTML to PDF: fixing ğ, ş, ı and İ
Turkish text in a generated PDF can go wrong in three different ways: garbled letters such as “İstanbul”, missing characters (ğ, ş, ı, İ) that come out empty or as another letter, and a capital “I” where an “İ” should be. They have different causes and different fixes. This page covers all three, with examples you can run.
Garbled letters Missing characters The dotted İ Fonts on the server One API call FAQ
Garbled letters: “İstanbul” and “ÅŸ”
This is an encoding problem, not a font problem. The bytes of your HTML are UTF-8, but the browser read them as Windows-1252, so every Turkish letter turned into two odd characters. It usually happens when the HTML comes from a file or an HTTP response that does not say which encoding it uses.
- Save the HTML file as UTF-8 and add
<meta charset="utf-8">as the first tag inside<head>. - If a server sends the HTML, send
Content-Type: text/html; charset=utf-8. - With Puppeteer or Playwright,
page.setContent(string)already works with a normal JavaScript string. The problem appears when you read a file with the wrong encoding before that.
<!doctype html>
<html lang="tr">
<head>
<meta charset="utf-8">
</head>
<body>
<h1>Fatura – İstanbul</h1>
<p>Çalışkan öğrenci şimdi güzel İstanbul'u gezdi.</p>
</body>
</html>Missing characters: the Turkish letters that older PDF libraries skip
The standard PDF fonts (Helvetica, Times, Courier) only contain Western European characters. Turkish ç, ö, ü are among them, but ğ Ğ ş Ş ı İ are not. Libraries that use those built-in fonts, such as FPDF or TCPDF in their default setup, therefore print a blank, a question mark or a different letter in their place.
There are two ways out. Embed a Unicode font that contains the whole Turkish alphabet (for example Noto Sans or DejaVu Sans) and tell the library to use it. Or print HTML with a browser engine, where the system fonts are used and the characters appear without extra setup, as long as the machine has a suitable font (see Fonts on the server).
A good test sentence contains every special letter:
Çalışkan öğrenci şimdi güzel İstanbul'u gezdi: ÇĞİÖŞÜ çğıöşü 12345The dotted İ: set lang="tr"
Turkish has two letters “i”: dotted (i, İ) and dotless (ı, I). Their capitals are not the usual pair, so “istanbul” in capitals must be “İSTANBUL”, not “ISTANBUL”. The browser only knows to do this when the page says it is Turkish.
We tested this in Chromium with text-transform: uppercase on the text “istanbul ığdır”:
- With
<html lang="tr">: İSTANBUL IĞDIR - Without a
langattribute: ISTANBUL IĞDIR (the dot is lost)
<html lang="tr">
<body>
<h2 style="text-transform: uppercase">istanbul</h2> <!-- İSTANBUL -->
</body>
</html>The same rule applies in JavaScript. "istanbul".toUpperCase() gives “ISTANBUL”, while "istanbul".toLocaleUpperCase("tr-TR") gives “İSTANBUL”. Use the second one whenever you build capital letters yourself, for example for a header you generate before printing.
Fonts on the server
Turkish uses Latin letters, so most fonts already contain them. A slim Docker image or a CI runner sometimes has no usable font at all, though, and then every letter fails, not only the special ones. Install a complete font family in the image that runs Chrome:
FROM node:20-bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
fontconfig fonts-noto-core \
&& fc-cache -f \
&& rm -rf /var/lib/apt/lists/*Then check that the system sees a font for Turkish:
fc-list :lang=tr family | head -3If it prints at least one family, render the test sentence above and look at the PDF. Details for other scripts are in our guide to empty boxes in Puppeteer PDFs.
Skip the setup: one API call
Getprintoza is an HTML to PDF API that runs real Chromium with fonts for 12 languages already installed, Turkish included. When you send "language": "tr" and your HTML has no lang attribute, the API adds lang="tr" for you, so capital letters like İSTANBUL come out correctly. Add "footer": true and every page gets “Sayfa 1 / 3” at the bottom.
curl -X POST https://getprintoza.com/v1/convert \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"language": "tr", "footer": true, "html": "<h1 style=\"text-transform:uppercase\">İstanbul fatura</h1><p>Çalışkan öğrenci şimdi güzel İstanbul'"'"'u gezdi.</p>"}' \
--output fatura.pdfIf your HTML contains characters that none of the installed fonts can draw, the PDF is still created and the response carries an X-Getprintoza-Warnings header that lists them. Full details are in the API documentation.
The Free plan includes 50 PDFs a month and needs no card. Get a free API key or see the pricing.
FAQ
Why do I see “ı” and “ü” in my PDF?
Your HTML was read with the wrong encoding. Save it as UTF-8 and declare <meta charset="utf-8">.
Why are ğ, ş and ı missing, but ç, ö and ü are fine?
The PDF library is using a built-in font that only has Western European letters. Embed a Unicode font or print the HTML with a browser engine.
Why does “i” become “I” in capitals?
The page has no lang="tr". Set it on the <html> tag, or send language: "tr" to the API.
Do I need a special font for Turkish?
No. Any complete Latin font such as Noto Sans works, as long as it is installed on the machine that prints the PDF.