ScreenshotNeo

BlogHow-to

How to Take Playwright Screenshots of an Indian Language Website with Unicode Text

Capture readable Indian-script text with Playwright by checking page content, waiting for fonts, choosing the right screenshot area, and keeping rendering settings consistent.

By the ScreenshotNeo team4 October 20268 min read

To capture an Indian-language website with readable Unicode text in Playwright, make sure the expected text has loaded, wait for the page’s fonts to finish loading, and use a browser environment with fonts that support the script. Then capture the viewport, full page, or a specific element. There is no special Playwright screenshot flag for Unicode: the characters must be present in the page and render correctly before the screenshot is taken.

The workflow below uses Node.js and Playwright. It checks for expected text, waits for fonts, and saves a full-page PNG. Replace the URL and sample text with values from the page you are capturing.

1. Install Playwright and choose a consistent environment

Install Playwright in your project and install its Chromium browser:

npm install -D playwright
npx playwright install chromium

For reliable visual comparisons, keep the browser engine and version, operating system, headless setting, viewport, device scale factor, and available fonts consistent between captures. Rendering can vary with the host operating system, browser version and settings, hardware, power source, and headless mode, as the Playwright visual comparisons guide explains.

2. Check the page content and wait for fonts

Navigation finishing does not necessarily mean that client-rendered text or custom web fonts are ready. Wait for a meaningful locator and for the browser’s font loading set to settle before capturing. A locator check also helps distinguish a font problem from a page that never loaded the expected content.

import { chromium } from 'playwright';

const url = 'https://example.com';
const expectedText = 'नमस्ते'; // Replace with text that should appear on the page.

const browser = await chromium.launch();
try {
  const page = await browser.newPage({
    viewport: { width: 1280, height: 900 },
    deviceScaleFactor: 1,
  });

  await page.goto(url, { waitUntil: 'domcontentloaded' });
  await page.getByText(expectedText, { exact: false }).waitFor({ state: 'visible' });
  await page.evaluate(async () => {
    await document.fonts.ready;
  });

  await page.screenshot({ path: 'indian-language-page.png', fullPage: true });
} finally {
  await browser.close();
}

Run it with node capture.mjs after saving the code as capture.mjs in a project configured for ES modules, or use the equivalent .js setup for your project. Set url to the target page and expectedText to actual content in the intended script. The locator should identify a visible piece of page content that is present when the page is ready.

document.fonts.ready waits for the document’s font loading work to complete. It does not install a font on the host, guarantee that a particular font contains every required glyph, or prove that the site selected the intended font. If the page relies on a downloadable font, inspect its request and the computed font family when glyphs look wrong.

3. Choose viewport, full-page, or element capture

Use the capture mode that preserves the evidence you need. Playwright documents these screenshot styles in its screenshots guide and Page API.

What to capture Playwright call Useful when
Current viewport await page.screenshot({ path: 'viewport.png' }) You need a screenshot of what is visible without scrolling.
Full scrollable page await page.screenshot({ path: 'full.png', fullPage: true }) You need the full page in one image, including content below the fold.
One element await page.locator('article').screenshot({ path: 'article.png' }) You need a component or content region without surrounding page material.

Output scale and image dimensions

The screenshot scale option can be css or device. CSS scale produces one output pixel per CSS pixel; device scale produces one output pixel per device pixel. The browser context’s deviceScaleFactor defaults to 1. Keep both choices fixed when comparing images. A larger device scale can produce a larger image, which may help legibility in human review but increases file size and does not fix missing glyphs.

const page = await browser.newPage({
  viewport: { width: 1280, height: 900 },
  deviceScaleFactor: 2,
});
await page.screenshot({
  path: 'page-2x.png',
  fullPage: true,
  scale: 'device',
});

Use a fixed viewport and scale for CSS-layout comparisons. Choose a larger output only when the extra pixel detail is useful to the reviewer.

4. Diagnose missing or incorrect glyphs

Unicode text can be present in the DOM and still display as tofu boxes, blanks, or substituted glyphs when the selected font lacks the needed characters or the intended font did not load. Font support depends on the particular font and script; waiting for fonts alone cannot establish that the chosen font supports the page’s language.

  1. Confirm the expected text is actually present with a locator or page text check.
  2. Wait for document.fonts.ready before capture.
  3. Inspect the browser’s network activity for failed font requests.
  4. Check the target element’s computed font-family in the browser’s developer tools or with getComputedStyle.
  5. Verify visually that the output image shows the intended characters. A successful screenshot call only means an image was captured; it does not validate glyph correctness.
  6. If a host-installed font is required, make sure the capture machine has a suitable font available, then keep that setup the same for later comparisons.
const diagnostics = await page.locator('article').evaluate((element) => ({
  text: element.textContent,
  fontFamily: getComputedStyle(element).fontFamily,
}));
console.log(diagnostics);

This diagnostic reports the element’s text and declared computed font-family stack. It does not identify which font supplied each glyph or prove complete script coverage.

5. Keep captures repeatable

  • Pin or otherwise control the Playwright and browser versions used for the baseline and later captures.
  • Use the same browser engine, operating system, headless mode, viewport, and device scale factor.
  • Keep the same font availability and site font-loading conditions.
  • Wait for a page-specific readiness signal, such as the expected text, rather than assuming navigation completion means the page is ready.
  • Use networkidle only when it suits the site. Persistent connections or polling can keep a page from reaching network idle; in those cases, wait for the expected content and relevant fonts instead.
  • Inspect the saved image when script legibility matters. A successful API call is not a visual assertion.

6. Troubleshooting

Symptom Likely cause What to do
Expected text never appears The page has not rendered the content, the selector is wrong, or the content differs from the expected string. Check the locator and page state; use a selector or text that actually identifies the loaded content. Handle redirects or application errors before capture.
Boxes or missing characters appear The selected font may lack glyphs, or a web font may have failed to load. Inspect font requests and the computed font family. Ensure the environment has a suitable font and verify the result visually.
Screenshot contains fallback-font text Capture happened before the custom font was ready, or the font request failed. Await document.fonts.ready, inspect failed requests, and confirm the page selected its intended font.
networkidle does not finish The site may keep network connections open or poll continuously. Use a navigation condition that fits the site, then wait for the specific text or element and fonts needed for the capture.
Baseline and current images differ across machines Browser version, operating system, fonts, headless mode, viewport, scale, or other environment details differ. Run both captures with a controlled and matching browser and host setup.
Full-page image is unexpectedly large The document is tall, or device scale multiplies the output pixels. Capture a viewport or a relevant element when that is sufficient; use a fixed viewport and CSS scale for stable layout comparisons.

7. Reliability, performance, and cost considerations

Browser startup, page loading, font downloads, and full-page image size all contribute to capture time and resource use. Reuse a browser process for batches of captures where appropriate, while creating isolated pages or contexts for separate page state. Set practical navigation and operation timeouts for your workload, and close the browser in a finally block so failures do not leave it running.

Readiness checks improve reliability because they tie capture to the page content and fonts you need. They cannot guarantee that every glyph is correct: font coverage and visual output still need verification. Full-page and high-scale captures can use more memory and produce larger files than viewport captures. For repeatable visual tests, prioritize a fixed environment and settings over changing scale between runs.

With a local Playwright setup, costs depend on the machines and infrastructure you run it on; this workflow does not require a particular paid font or device. If managing browsers and fonts for a capture pipeline is undesirable, ScreenshotNeo offers a hosted screenshot API with a free tier and paid plans described below.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. For an ordinary page capture, make one GET request with the target URL. See the ScreenshotNeo API documentation for request options. For this Indian-language use case, verify the returned image visually if exact glyph rendering is essential; do not assume a hosted capture proves that the page’s chosen font supports every character.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

The Node.js example uses Bun’s file writer. In a Node.js project, save the response bytes with await writeFile('shot.webp', Buffer.from(await res.arrayBuffer())) from node:fs/promises.

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan. Create a free account and get 1,000 screenshots a month with no card.

FAQ

Does Unicode require a special Playwright option?

No. The browser must have the text and a font that can render its characters. Wait for page content and fonts, then inspect the image.

Does document.fonts.ready install missing fonts?

No. It waits for the document’s font loading work to settle. It does not add fonts to the host or guarantee script coverage.

Should I use full-page capture for every screenshot?

No. Use viewport capture for visible content, full-page capture for the scrollable document, and element capture when only one component matters.

Why can two screenshots differ when the page code has not changed?

Browser and host rendering conditions can differ. Keep the engine, version, operating system, font setup, headless setting, viewport, and device scale consistent.