How to Generate Greek-Language PDFs from HTML
Create Greek PDFs from HTML with reliable Unicode fonts, WeasyPrint or Chromium, complete code, troubleshooting, and validation steps.
Direct answer: Generate a Greek-language PDF by rendering Unicode HTML with a font that contains every Greek glyph your document uses. Make that font available to the renderer, wait for fonts to load, then inspect the PDF for missing glyphs, accents, wrapping and searchable text. WeasyPrint uses Pango and Fontconfig and can load fonts through the system or CSS @font-face; Chromium tools such as Puppeteer and Playwright render PDFs with print CSS media and wait for fonts before output.
A PDF renderer cannot draw a character that is absent from its available fonts. WeasyPrint documents that unsupported code points become the font’s .notdef glyph and produce a warning. Its setup guide also explains that fonts must be installed or referenced with @font-face.
1. Keep the HTML as Unicode
Store Greek text as Unicode throughout your pipeline. Preserve the original characters when reading templates, loading data, and passing HTML to the renderer. If Greek is already corrupted in the browser-rendered page, fix that input before PDF generation.
<!doctype html>
<html lang="el">
<head>
<meta charset="utf-8">
<title>Τιμολόγιο</title>
</head>
<body>
<h1>Τιμολόγιο παροχής υπηρεσιών</h1>
<p>Ευχαριστούμε για τη συνεργασία σας.</p>
</body>
</html>
The lang attribute helps document semantics and accessibility. The essential requirement for glyphs is that the selected font covers the actual Greek letters, tonos marks, punctuation and symbols in your content.
2. Choose and load a Greek-capable font
Select a font explicitly instead of relying on the target machine’s default. Keep a fallback family for characters missing from the primary face. The research sources do not identify one universally best Greek typeface, so check glyph coverage and licensing for the font your project chooses.
body {
font-family: "ProjectGreek", sans-serif;
font-size: 11pt;
line-height: 1.45;
}
@font-face {
font-family: "ProjectGreek";
src: url("fonts/project-greek.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
With WeasyPrint, install the font where Fontconfig can find it, or reference it with CSS @font-face. Its Python API uses FontConfiguration when CSS loads font resources. WeasyPrint embeds fonts in generated PDFs automatically and normally subsets them to the glyphs used in the file. See the WeasyPrint first-steps documentation.
Verify coverage before rendering
- Include Greek capitals, lowercase letters, accented vowels and final sigma if they occur.
- Check punctuation such as « » and the Greek question mark (;) plus currency symbols and non-breaking spaces.
- Test every font weight and style used by your CSS; a bold or italic face may have different coverage.
- Keep a fallback family for symbols or mixed Greek and Latin content.
3. Generate a PDF with WeasyPrint
WeasyPrint is a Python library and command-line renderer. Its documented basic API is HTML(...).write_pdf(...).
Minimal Python example
from weasyprint import HTML
HTML("document.html").write_pdf("document.pdf")
Complete Python example with an explicit font configuration
from pathlib import Path
from weasyprint import CSS, HTML
from weasyprint.text.fonts import FontConfiguration
base = Path(__file__).parent
html_path = base / "document.html"
css_path = base / "print.css"
output_path = base / "document.pdf"
font_config = FontConfiguration()
html = HTML(filename=str(html_path), base_url=str(base))
css = CSS(filename=str(css_path), font_config=font_config)
html.write_pdf(
str(output_path),
stylesheets=[css],
font_config=font_config,
)
print(f"Wrote {output_path}")
Use a base_url that lets relative font, image and stylesheet URLs resolve. Keep the font files alongside the document or provide absolute, accessible URLs.
Command line
weasyprint document.html document.pdf
For print layout, add page rules:
@page {
size: A4;
margin: 18mm 16mm 20mm;
}
h1, h2 {
break-after: avoid;
}
table {
width: 100%;
border-collapse: collapse;
}
td, th {
border: 0.2mm solid #999;
padding: 2mm;
}
4. Generate a PDF with Chromium
Use Puppeteer or Playwright when your HTML depends on browser behavior, client-side JavaScript or CSS features supported by Chromium. Both use print CSS media for PDF output. Puppeteer documents that page.pdf() waits for fonts to load by default.
Puppeteer (Node.js)
import puppeteer from "puppeteer";
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto("https://example.com/document", {
waitUntil: "networkidle2"
});
await page.emulateMediaType("print");
await page.pdf({
path: "document.pdf",
format: "A4",
printBackground: true,
preferCSSPageSize: true
});
} finally {
await browser.close();
}
For local HTML, use a file:// URL only when your deployment permits it, or serve the document from a controlled local HTTP server. Ensure font requests are reachable from the page and wait for any application-specific data to finish loading.
Playwright (Node.js)
import { chromium } from "playwright";
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.goto("https://example.com/document", {
waitUntil: "networkidle"
});
await page.pdf({
path: "document.pdf",
format: "A4",
printBackground: true,
preferCSSPageSize: true
});
} finally {
await browser.close();
}
Read the Puppeteer PDF API and Playwright page.pdf documentation for the options supported by your installed version.
5. Make fonts deterministic
- Package the exact font files with your application image or deployment artifact.
- Set an explicit
font-familyin the document stylesheet. - Use
@font-facewith a resolvable URL when the renderer does not share your system fonts. - Wait for font loading in browser automation before calling the PDF API.
- Render a fixture containing every Greek character your production templates use.
Browser-side code can make the wait explicit:
await page.evaluate(async () => {
await document.fonts.ready;
});
This does not repair missing glyphs; it only ensures that available web fonts have finished loading.
6. Inspect and validate the generated PDF
Open the PDF and check Greek glyphs, tonos marks, punctuation, line wrapping, tables, page breaks and selectable text. Search the document for representative Greek words. Review WeasyPrint logs for missing-glyph warnings. If the file must satisfy an archival or Unicode conformance target, validate it against that exact profile. WeasyPrint describes PDF/A-3u and explains that the “u” indicates Unicode text availability, while also warning that validity depends on the HTML, CSS and PDF features used.
| Check | What failure looks like | Likely cause |
|---|---|---|
| Glyphs | Squares, empty boxes or missing letters | Selected font lacks the code point or was not loaded |
| Accents | Τόνοι disappear or appear incorrectly | Font coverage or text normalization issue |
| Search | Greek text cannot be selected or found | Renderer output or conformance issue; inspect text extraction |
| Layout | Clipped lines or unexpected page breaks | Print CSS, font metrics or unsupported CSS |
7. Troubleshooting Greek PDF errors
Squares instead of Greek letters
Cause: The renderer cannot find a font containing the glyph. Fix: install the font for Fontconfig, add a working @font-face, confirm the URL resolves from the renderer, and choose a fallback family. WeasyPrint’s documentation gives the same diagnosis for empty characters and squares.
No characters are drawn
Cause: No usable font is available to the renderer, or the font resource failed to load. Fix: inspect renderer logs, verify file permissions and URLs, and render a small Greek-only fixture before processing the full document.
The browser shows Greek, but the PDF does not
Cause: The PDF process runs in a different container, operating system or font environment. Fix: package fonts with the PDF worker and avoid relying on developer-machine fonts.
Fonts load too late in Chromium
Cause: PDF generation starts before web fonts or application data finish loading. Fix: wait for navigation, document.fonts.ready and any application-specific readiness signal before calling page.pdf().
Greek text is corrupted before rendering
Cause: The HTML or source data was decoded incorrectly upstream. Fix: inspect the HTML string or response before passing it to the renderer and preserve Unicode through every file and database boundary.
PDF layout differs from the web page
Cause: Chromium applies print media by default and print styles may override screen styles. Fix: define print CSS deliberately, set page size and margins, and test page breaks. Do not assume unsupported CSS behaves identically across renderers.
8. Performance, reliability and cost
- Font size: WeasyPrint normally subsets embedded fonts to used glyphs, which can reduce output size. Keep only required font faces in the document.
- Browser startup: Reuse a Chromium process for batches instead of launching one browser per document, while isolating pages and cleaning up failed jobs.
- Network dependencies: Remote fonts and images add latency and failure points. Self-host critical assets when reproducibility matters.
- Repeatability: Pin renderer and font versions in deployment. Font updates can change line wrapping and pagination.
- Retries: Retry transient navigation or resource failures, but record the HTML, renderer version and font package used for each job.
- Cost: Self-hosted WeasyPrint and Chromium consume your own compute. Managed rendering costs depend on the service and plan; compare using your real document size, asset count and concurrency.
9. Or skip the browser setup
ScreenshotNeo provides a website screenshot API that can return PNG, JPEG, WebP or PDF. Use one GET request instead of maintaining browser installation and capture code. The API accepts options for full-page capture, custom CSS and JavaScript, waiting conditions, headers, cookies, user agents, timezone and geolocation; see the ScreenshotNeo documentation for PDF parameters and the complete option list.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/greek-document -o shot.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/greek-document"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/greek-document' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
For a PDF response, select the PDF output option documented by ScreenshotNeo and save the response with a .pdf extension. ScreenshotNeo removes cookie banners, newsletter popups and chat widgets before capture. Bot checks, blank pages and failed loads are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor and other MCP clients call take_screenshot, get_page_info and capture_pdf. The free plan includes 1,000 screenshots each month without a card; paid plans start at $5 for 3,000.
Create a free ScreenshotNeo account and start with 1,000 screenshots per month at no charge.
10. Practical checklist
- HTML is preserved as Unicode and contains the intended Greek characters.
- An explicit Greek-capable font is selected for every relevant weight and style.
- Fonts are installed or loaded with a resolvable
@font-face. - Browser jobs wait for fonts and application data.
- Print page size, margins, backgrounds and page breaks are defined.
- The output is checked visually and for selectable/searchable Greek text.
- Logs are reviewed for missing-glyph warnings.
- Required PDF/A or Unicode conformance is validated separately.
FAQ
Do I need a Greek-specific PDF library?
No. You need a renderer and a font with the required glyph coverage. WeasyPrint, Puppeteer and Playwright can all generate PDFs when configured correctly.
Should I convert Greek text to images?
No. Keep text as text so it remains selectable, searchable and accessible. Fix font loading or coverage instead.
Can one font cover Greek and Latin?
Often, but verify the exact characters, punctuation and symbols in your templates. Use a fallback family when coverage is incomplete.
Which renderer produces the best Greek typography?
The supplied sources do not establish a universal winner. Compare the exact HTML, CSS, fonts and conformance requirements in your target environment.
Why does a PDF look different from the screen?
Print media rules, page dimensions, font metrics and renderer-supported CSS can change layout. Design and test a print stylesheet explicitly.


