ScreenshotNeo

BlogHow-to

How to Make Screenshots of Indian Websites Render in Devanagari Correctly

Fix boxes, broken glyphs, and inconsistent Hindi text in website screenshots by checking Unicode, fonts, shaping, load timing, and the capture environment.

By the ScreenshotNeo team4 October 20269 min read

If Hindi or other Devanagari text appears as boxes, broken conjuncts, misplaced vowel signs, or different letters in a screenshot, the capture usually preserved a problem already present in the browser render. A screenshot tool does not repair fonts or script shaping. Check the text encoding, the font actually used, web-font load timing, the browser and operating system, and then the stability of the capture environment.

For a Hindi sans-serif page, start with a Devanagari font followed by general-script and generic fallbacks:

body {
  font-family: 'Noto Sans Devanagari', 'Noto Sans',
               'Noto Sans Symbols 2', sans-serif;
}

Make sure the page loads the declared font, and wait for that font before capturing. Then validate representative Hindi text in the same browser environment used for screenshots. Noto documents this family-stack approach for Hindi; its font specimen describes Devanagari support and OpenType features, but font inventory alone cannot guarantee that every string will render correctly. Noto’s web font guidance · Noto Sans Devanagari UI specimen

1. Identify which layer is failing

Devanagari rendering depends on Unicode text, a font with the needed glyphs, a shaping-capable browser stack, and the moment and environment in which capture happens. Work through these checks in order so that a fallback-font issue is not mistaken for a screenshot bug.

  1. Confirm the text representation. Check that the page content is Unicode Devanagari. Older pages may use a legacy font-specific encoding; if so, changing to a modern Unicode font stack may not be enough. Treat that as a separate migration or encoding diagnosis.
  2. Inspect the rendered font. A CSS declaration is only a request. If the selected font lacks coverage for a character, the browser can use a fallback. Verify the font that actually rendered the affected text and check coverage for the exact characters, punctuation, and digits on the page.
  3. Check shaping with real strings. Devanagari uses complex text layout. Test conjuncts, vowel signs, and combining marks, not only isolated characters. A font can cover code points and still have a font-specific defect in particular sequences.
  4. Confirm web-font loading before capture. A capture taken before a requested web font is ready may contain fallback text. Check font requests and failures in the page, and wait for the relevant font rather than relying on an arbitrary delay.
  5. Record the capture environment. Browser defaults and installed fonts vary by platform and can change over time. OS, browser version, settings, hardware, power source, and headless mode can all affect output.

2. Choose and load a suitable font stack

Put the script-specific family first, then suitable general-script families, then a generic family. Noto recommends a Devanagari family for Hindi and a corresponding general Noto family for punctuation and digits. For a serif design, use the script-specific serif family with its matching general Noto Serif family instead of mixing in an unrelated design.

/* Sans-serif example for Hindi and related page content */
body {
  font-family: 'Noto Sans Devanagari', 'Noto Sans',
               'Noto Sans Symbols 2', sans-serif;
}

/* Example direction for a serif design; load these families first */
.article-copy {
  font-family: 'Noto Serif Devanagari', 'Noto Serif', serif;
}

The family names in CSS do not install or download the font by themselves. Load the families through the site’s chosen web-font method, or ensure the screenshot host has the intended system fonts. Check the browser’s font and network diagnostics to see whether the requested family loaded or a fallback rendered instead.

Load only the styles the page needs. Noto advises that most sites need no more than three weights for one family. A leaner font setup reduces unnecessary downloads and makes it easier to verify which face is used.

Choice Useful when Check before relying on it
System font The capture host is controlled and the system family is known. Availability and version on every capture host; platform defaults differ.
Web font The page needs a consistent, intentional typeface across machines. Network access, successful loading, selected weight, and readiness before capture.
Fallback stack Pages include Hindi alongside Latin text, punctuation, or symbols. Coverage and visual behavior for the actual mixed-script strings.

3. Wait for fonts before taking the screenshot

There is no universal wait duration that guarantees web fonts are ready. A fixed sleep can be too short on a slow or cold connection and waste time when the font is already available. Prefer a font-readiness check in the browser, and verify that the relevant font request succeeded.

For a page you control, this browser-side check waits for the document’s font-loading set to settle before the capture step:

await document.fonts.ready;

In browser automation, evaluate that expression in the page context before requesting the screenshot. This wait addresses font readiness; it does not prove that the chosen face has correct glyph coverage or shaping. If a font fails to load, the promise can settle while fallback is still in use, so also inspect the loaded font and network errors.

4. Check platform defaults and font-specific behavior

Do not assume that a CSS generic family maps to the same Devanagari font everywhere. A Chromium source change dated March 4, 2026 documents different Devanagari defaults by platform: Nirmala UI on Windows, Devanagari MT / ITF Devanagari on macOS, and Noto families on Linux and ChromeOS. These are Chromium mappings, not a guarantee for every Chrome release, browser, user configuration, or machine. Chromium source change for Devanagari defaults

Use a small visual fixture containing the kinds of sequences your site actually displays: conjuncts, vowel signs, combining marks, punctuation, and digits. Compare the output at the intended viewport in the target browser. Font bugs can be specific: for example, a Google Fonts issue filed in February 2026 reports a Google Sans Devanagari combining-mark problem on macOS and React Native on iOS. That report does not establish a general problem with other fonts or browsers. Google Fonts issue report

5. Make screenshot output reproducible

Once the text looks correct in a browser, keep the capture conditions consistent. Record or pin the operating system, browser version, browser settings, headless or headed mode, viewport, and relevant hardware or display settings. Keep font files and web-font availability stable for visual baselines.

Playwright notes that screenshot output can vary with the host OS, browser version, settings, hardware, power source, and headless mode, and recommends using the same environment for visual comparisons. Its screenshot assertion waits for consecutive matching captures to address stability. A stable image only means the capture stopped changing; it does not establish that the typeface or shaping is correct. Playwright visual comparisons · Playwright PageAssertions API

6. Capture with a reproducible DIY setup

For a page you control, use browser automation so you can set the viewport, wait for font readiness, and save the rendered result. This Playwright example is runnable in a Node.js project after installing Playwright and its browser. It waits for the page to load and for document fonts to settle before taking a full-page PNG.

import { chromium } from 'playwright';

const url = process.argv[2] ?? 'https://example.com';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });

try {
  await page.goto(url, { waitUntil: 'networkidle', timeout: 60_000 });
  await page.evaluate(() => document.fonts.ready);
  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

Save the file as capture.mjs and run node capture.mjs https://your-site.example. Install dependencies with npm install playwright and install the browser binary with npx playwright install chromium. If the site keeps long-lived network connections open, networkidle may not arrive; use an appropriate page-ready condition for that site, then still wait for document.fonts.ready. This code makes the capture sequence repeatable, but it does not install Noto fonts on the host or diagnose glyph correctness automatically.

7. Troubleshoot common failures

Symptom Likely cause What to do
Boxes or missing characters The chosen and fallback fonts lack glyph coverage, or the font did not load. Inspect the rendered font and font request; load a Devanagari family and verify the exact characters.
Letters are present but conjuncts or marks look broken Shaping support or a font-specific sequence defect. Test representative strings in the target browser and try a different Devanagari-capable font. Do not infer a universal browser defect from one font report.
Screenshot has different text from the local browser Different OS defaults, installed fonts, browser version, headless mode, or settings. Align and record the capture environment; explicitly load the intended web font where possible.
First capture is wrong, later capture is correct Screenshot ran before the web font became available. Wait for the font readiness promise and inspect whether the font request succeeded; avoid guessing with a fixed delay.
Punctuation or digits use a visibly different style The script font covers Devanagari but not all other characters. Add a suitable general-script fallback after the Devanagari family and test mixed-script lines.
Visual test is stable but still looks wrong Capture stability is being confused with typographic correctness. Review the glyph shapes and shaping separately; consecutive matching screenshots do not validate the font.
Hindi appears corrupted even with a suitable font The source may use a legacy font-specific encoding rather than Unicode Devanagari. Inspect the underlying text representation and treat conversion as a separate content issue.

8. Performance, reliability, and cost considerations

Font downloads add network work and can make a cold capture differ from a warm one. Load only needed families and weights, keep the font source dependable for the capture host, and wait on readiness instead of adding a large unconditional sleep. For visual regression, a controlled environment and stable font files reduce unrelated differences between runs.

Keep two checks separate: whether the screenshot is repeatable, and whether the Devanagari is correct. A retry may help with a transient failed font request, but repeatedly capturing the same broken fallback output will not fix coverage or shaping. If consistent output matters, explicitly choose and load the font rather than relying on a platform default.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. A screenshot service captures what its browser renders, so provide a page that loads the intended Devanagari font; use the font-readiness wait option when the page needs time before capture. The API returns an image or PDF from one GET request. See the ScreenshotNeo API documentation for request options.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
  • Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are never billed. Response headers report the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
  • The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.

Sign up for ScreenshotNeo’s free 1,000 screenshots a month, with no card required.

Frequently asked questions

Will embedding a font in the page guarantee correct Hindi screenshots?

It removes dependence on the host’s system font for that family, but you still need successful loading, adequate glyph coverage, and correct shaping for the actual text.

Does a matching visual baseline mean the letters are correct?

No. It means the image is stable under the comparison process. Inspect the Devanagari itself for typographic correctness.

Should I use a fixed sleep before every capture?

Usually a readiness condition is more dependable. A fixed delay can be too short on a slow connection and unnecessarily long when fonts are already ready.

Do Chromium’s platform defaults apply to every browser?

No. The documented mappings are Chromium-specific and can depend on release, platform, and user configuration. Explicitly load the intended family when repeatability matters.