ScreenshotNeo

BlogHow-to

How to Capture Indian Language Websites Correctly with Puppeteer Screenshots

Prevent missing glyphs and inconsistent text in Puppeteer screenshots by checking fonts, page metadata, locale, viewport, and the browser environment.

By the ScreenshotNeo team4 October 20269 min read

Puppeteer screenshots show what Chrome or Firefox actually renders. To capture Indian-language websites correctly, make sure the browser environment has fonts that cover the page’s scripts, the document uses suitable encoding and language metadata, and the page has finished rendering before capture. Set locale separately when the page depends on language negotiation or locale-sensitive formatting: locale settings do not install fonts or add missing glyphs.

This guide uses Puppeteer with Chrome. Its principles also apply when you capture a page in another browser, but the exact browser configuration may differ. Puppeteer’s page.screenshot() API captures the rendered page; typography problems must be addressed before that call. Puppeteer screenshot API · Puppeteer overview from Chrome for Developers.

1. Start with the right mental model

There is no single “Indian font” switch in Puppeteer. India’s websites can use different scripts and fonts, and the font available on your development machine may not be available in a container or deployment runtime. Correct capture depends on the full rendering chain:

  1. The page’s text and metadata: character encoding, language tags, styles, and any web fonts the page loads.
  2. The browser’s available fonts: installed system fonts and fonts successfully loaded by the page.
  3. Capture timing: whether the page content, styles, and required fonts are ready when the screenshot is taken.
  4. Capture conditions: browser runtime, viewport, device scale factor, and any locale-sensitive page behavior.

If a character appears as an empty box or the wrong symbol, investigate font coverage and font availability first. If the text is correct but the page chooses the wrong language, date format, or number format, investigate locale and language negotiation separately.

2. Prepare the page and choose a deterministic viewport

Use a fixed viewport that matches the output you want to produce. For mobile output, test at the actual mobile dimensions and device scale factor: scripts can look different at small sizes, and the India Roadmap on Universal Acceptance and the Multilingual Internet notes that some fonts and scripts may not render well on low-resolution mobile screens. That report recommends UTF-8, accurate language attributes, embedded fonts for uniform display, and locale-specific CSS handling. These are general recommendations, not a certification of a particular current browser or font package. India Roadmap on Universal Acceptance and Multilingual Internet (PDF).

For a site you control, check that the HTML declares UTF-8 and accurate language metadata. For example:

<meta charset="utf-8">
<html lang="hi">

Use the appropriate language tag for the page or text rather than copying hi for every Indian-language page. If a page mixes languages, mark important sections with their respective language tags. When a website relies on embedded web fonts, ensure they load successfully before capture.

3. Install fonts in the browser environment

When a page depends on fonts not available in the browser environment, install or provide fonts that cover the scripts you need. Choose based on the actual language and script being rendered, then validate the result in the deployed runtime. A generic package list is not a complete Indian-language font matrix, and this guide does not assume that any one font package covers every script.

Puppeteer’s troubleshooting documentation treats system dependencies and fonts as part of running Chrome in containers and gives examples of font packages for several major character sets. Follow the guidance for your base image and select fonts for your target scripts. The same documentation warns that Chrome needs writable profile, configuration, and cache paths in read-only containers, and that Chrome is not supported on Alpine out of the box. Puppeteer troubleshooting: dependencies, fonts, and containers.

After adding fonts, restart the browser process and capture again. A browser already running may not reflect changes made to its environment. Compare captures from the exact production image or runtime, not only from a developer workstation.

4. Runnable Puppeteer example

Install Puppeteer in your project with npm install puppeteer. The following CommonJS script opens a page, applies a fixed viewport, waits for document loading and web fonts, then saves a full-page PNG. Set PAGE_URL to the page you are permitted to capture.

const puppeteer = require('puppeteer');

async function main() {
  const url = process.env.PAGE_URL || 'https://example.com';
  const browser = await puppeteer.launch({ headless: true });

  try {
    const page = await browser.newPage();
    await page.setViewport({
      width: 1440,
      height: 1000,
      deviceScaleFactor: 1
    });

    await page.goto(url, {
      waitUntil: 'networkidle2',
      timeout: 60000
    });

    // Wait for document fonts, including web fonts, when the page exposes them.
    await page.evaluate(async () => {
      if (document.fonts && document.fonts.ready) {
        await document.fonts.ready;
      }
    });

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

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

networkidle2 is a useful starting point, not a guarantee that every site is visually settled. Some sites keep network connections open, load content after scrolling, or update the page after navigation. If network-idle waiting times out, use a site-specific readiness condition or a bounded delay after the essential content appears. For lazy-loaded images, scroll the relevant content into view before capturing or use a site-specific loading strategy.

The Puppeteer API documents page.screenshot() options, including full-page capture and output types. PNG is a useful debugging format because it preserves sharp text. Choose JPEG or WebP only when those formats suit your downstream use. Screenshot method and options.

5. Configure locale only when the page needs it

Locale can affect language negotiation, browser language values, and formatting APIs such as Intl. It does not make missing glyphs appear. A third-party Puppeteer locale guide describes setting Chrome’s locale override through the Chrome DevTools Protocol before navigation, alongside separate handling for request language and browser language values. Check the recipe against the Puppeteer and browser versions in your project. Puppeteer Guide: Language and Locale.

For Chrome, an illustrative CDP setup is:

const client = await page.createCDPSession();
await client.send('Emulation.setLocaleOverride', { locale: 'hi-IN' });
await page.setExtraHTTPHeaders({ 'Accept-Language': 'hi-IN,hi;q=0.9,en;q=0.8' });
await page.goto(url, { waitUntil: 'domcontentloaded' });

Use a locale appropriate to the target content and verify the result in your installed browser. These settings address different things: the locale override affects browser locale behavior, while Accept-Language is a request header that can influence server responses. Neither installs a font. Avoid changing navigator.language through page scripts unless the page specifically requires that behavior; it is a separate concern and can make the emulated environment less representative.

6. Inspect failures systematically

When the output is wrong, reduce the problem to a representative page and a small set of representative characters in the target script. Check the screenshot at the target viewport and inspect the same page in the same browser runtime. Then work through these layers:

  1. Text and encoding: confirm that the response contains the intended characters and the document declares UTF-8.
  2. Language metadata: check the document’s lang value and any language tags around mixed-language content.
  3. Font loading: check computed font-family styles and whether web-font requests succeeded. A declared family does not prove its font file loaded or includes the needed glyphs.
  4. Runtime font coverage: verify that the deployed browser has fonts covering the relevant script, especially in containers.
  5. Capture timing: wait for the content and fonts actually required by the page before taking the screenshot.
  6. Output conditions: compare at the production viewport, scale factor, browser, and container configuration.

The reviewed sources do not prescribe one universal diagnostic script or font package for all Indian scripts. Treat this checklist as a way to isolate the cause, then validate the chosen font and runtime against your pages.

7. Troubleshooting common problems

Symptom Likely cause What to check or change
Boxes, blank squares, or replacement symbols The selected font lacks glyph coverage, or the required font did not load. Inspect computed font families and font requests. Install or provide a font covering the target script; capture again in the production browser environment.
Text is correct locally but broken in Docker The container does not have the fonts or system dependencies available on the workstation. Review Puppeteer’s container troubleshooting guide, add suitable dependencies and script-covering fonts, and test in the built image.
Locale-sensitive content appears in the wrong language or format Request language, browser locale, and page metadata are not aligned. Check lang, the site’s language negotiation, and any required locale override separately from font coverage.
Some text is styled differently after capture begins Web fonts or styles have not finished loading before the screenshot. Wait for document.fonts.ready where available and verify that the font requests succeeded.
Page navigation never reaches network idle The page maintains connections or continues background requests. Wait for a meaningful selector or document state, then wait for the specific content and fonts needed. Keep a finite timeout.
Chrome fails to start in a container Missing system dependencies, unsupported base image, or unwritable profile/configuration/cache paths. Apply the Puppeteer troubleshooting guidance for dependencies and writable paths. The guide warns that Chrome is not supported on Alpine out of the box.
Mobile screenshot text is hard to read Small viewport or low device scale factor can affect legibility and script rendering. Capture at the intended mobile viewport and scale factor; inspect actual output and adjust page styles or capture dimensions as needed.

8. Reliability, performance, and cost considerations

Reliability

  • Pin and record the browser and Puppeteer versions used by the capture environment, and keep the production runtime reproducible.
  • Use a bounded navigation timeout and a page-specific readiness condition when a site does not become network idle.
  • Keep the browser’s required profile and cache locations writable in containers.
  • Validate font availability and representative script samples after changing a base image, browser, font set, or viewport.

Performance

  • Full-page screenshots can take longer and use more memory than viewport screenshots, especially on very tall pages.
  • Waiting for all network activity can add time or stall on sites with persistent connections. Waiting for the content and fonts needed for the capture can be more appropriate.
  • Installing only the fonts and dependencies needed by your target pages can keep a browser image more manageable, but validate coverage rather than assuming a small font set is sufficient.

Cost

With self-hosted Puppeteer, account for the compute, storage, maintenance, and engineering time required to operate browser workers and keep fonts and system dependencies current. The cited sources provide no benchmark for capture speed or cost, so measure your own pages and runtime if those numbers matter.

9. Or skip the browser setup

With ScreenshotNeo, one GET request captures a URL as an image or PDF. It is a screenshot API and MCP server from Yorker Media. Its clean-shot steps accept cookie or consent banners like a visitor and remove more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Only clean shots are billed: bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and the response includes X-Page-Verdict and X-Billed headers.

For Indian-language pages, an API does not remove the need to confirm that the page and capture environment render the target script correctly. This option is useful when you want to avoid operating browser workers yourself; use a representative page to confirm the returned image suits your needs.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -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"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js:

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}`);
const bytes = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', bytes);

See the ScreenshotNeo API documentation for request options. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for AI agents and MCP clients. Plans include 1,000 screenshots a month free with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card required.

10. FAQ

Does setting lang="hi-IN" install Hindi fonts?

No. Language metadata describes the content; fonts must be available to the browser or loaded by the page.

Should I use a different font package for each language?

Choose fonts based on the scripts your pages use and verify glyph coverage in your runtime. The sources do not establish one universal package for all Indian languages.

Can a higher device scale factor fix missing characters?

No. It changes output density, not font coverage. Use it to match the intended capture resolution after glyphs render correctly.

Will waiting for network idle guarantee the final visual state?

No. It is a navigation condition, not proof that every lazy-loaded image, animation, or application update has completed. Wait for the page-specific content required by your screenshot.

Sources