ScreenshotNeo

BlogHow-to

How to Take a Puppeteer Screenshot of a Gujarati Web Page with Correct Fonts

Make Gujarati text render correctly in Puppeteer screenshots by loading a Gujarati-capable font, waiting for it, and checking the result.

By the ScreenshotNeo team4 October 20268 min read

To capture a Gujarati web page with correct fonts in Puppeteer, make a Gujarati-capable font available to the same Chromium runtime that takes the screenshot, apply that font to the relevant text, and wait for the page’s fonts to finish loading before capture. Noto Sans Gujarati is a practical general-purpose sans-serif option; Noto Sans Gujarati UI is designed for interfaces with limited vertical space. Include a fallback family for Latin text and other characters.

Gujarati is an Indic script that needs complex text shaping. A font file merely existing on the host does not guarantee that Chromium can use it, or that the requested family contains every character on the page. CSS can silently fall back to another font. Make the runtime, font loading, and rendered output part of your check.

1. Make a Gujarati font available to Chromium

Choose a font that suits the page’s typography. Noto’s guidance distinguishes script-specific document fonts from UI fonts; use the UI family when compact interface labels and vertical space are important. Check the family name supplied by the font or stylesheet and use that exact name in CSS. [Noto Sans Gujarati; Noto Sans Gujarati UI]

There are two common ways to provide it:

  • Use a web font: load a stylesheet or font file that Chromium can reach. This avoids packaging a font into the container, but requires network access and a successful font request during capture.
  • Install the font in the runtime: add the font files to the Docker image or machine that launches Chromium. This can make repeated captures more consistent, provided the browser sees the installed fonts.

For managed browser sessions, check whether the provider supports adding an @font-face rule before capture. Cloudflare Browser Run documents adding page styles with Puppeteer’s page.addStyleTag(). [Cloudflare Browser Run documentation]

2. Set the font family in page CSS

Apply the script font to the element containing Gujarati. Put the most important script’s family first, then provide fallbacks for other characters. For example, add this CSS to your page or inject it before capture:

body {
  font-family: "Noto Sans Gujarati", "Noto Sans", sans-serif;
}

The exact family name depends on the font file or web-font stylesheet you load. If Gujarati text is in a specific component, target that component instead of changing the whole page. When the first family lacks a character, the browser tries subsequent families and may eventually use a system default. That fallback behavior can make a page appear to load successfully while using an unintended font. [Noto Fonts: font fallback guidance]

3. Capture after fonts are ready

Install Puppeteer in your project with npm install puppeteer. Save the following as screenshot.js, then run it with node screenshot.js https://example.com. Replace the example URL with the page you need to capture.

const puppeteer = require('puppeteer');

async function main() {
  const url = process.argv[2];
  if (!url) {
    throw new Error('Usage: node screenshot.js <url>');
  }

  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: 'networkidle0', timeout: 60000 });

    // Wait for fonts requested by the document to load or fail.
    await page.evaluate(() => document.fonts.ready);

    // Inspect the CSS family applied to Gujarati text if the result looks wrong.
    const fontFamily = await page.$eval('body', el => getComputedStyle(el).fontFamily);
    console.log('Body font-family:', fontFamily);

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

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

Puppeteer documents page navigation, page evaluation, and screenshots in its API reference. document.fonts.ready is a useful safeguard for waiting on browser font loading; it does not prove that the intended family supplied every glyph. Inspect the actual PNG and verify font requests and computed styles when diagnosing a fallback. [Puppeteer Page API]

Use an explicit web font when the page does not already load one

If you control the page, define and apply an @font-face rule with the URL for a Gujarati font that Chromium can reach. For example, the CSS structure is:

@font-face {
  font-family: "CaptureGujarati";
  src: url("/fonts/gujarati-font.woff2") format("woff2");
  font-display: swap;
}

body {
  font-family: "CaptureGujarati", "Noto Sans", sans-serif;
}

Replace the example font URL with the actual font asset. If the target page is not under your control, inject the rule before waiting for fonts:

await page.addStyleTag({
  content: `
    @font-face {
      font-family: "CaptureGujarati";
      src: url("https://your-host.example/fonts/gujarati-font.woff2") format("woff2");
      font-display: swap;
    }
    body { font-family: "CaptureGujarati", "Noto Sans", sans-serif; }
  `
});
await page.evaluate(() => document.fonts.ready);

Use a font URL your browser session can access. Cross-origin restrictions, authentication, redirects, or network policy may prevent a font from loading. For a local font installed in the runtime, use its installed family name instead of assuming that an arbitrary file on disk will be discovered automatically.

4. Validate the actual screenshot

Check the PNG in the same environment that will run in production or CI. Look closely at Gujarati conjuncts and vowel marks, mixed Gujarati and Latin text, punctuation, and line wrapping. Font metrics can change element sizes and wrapping, so a screenshot may differ even when the page content is unchanged.

  • Confirm the computed font-family on the text-containing element.
  • Check browser console errors and network responses for the font stylesheet and font file.
  • Compare the capture in the target Chromium runtime, not only on a workstation with a different font inventory.
  • Keep the viewport, browser version, installed fonts, page content, and CSS consistent when comparing captures.

5. Choose a loading strategy that fits the page

Approach Useful when Check
Web font You want to avoid packaging font files with the browser image. The runtime can reach the font URL and the font request succeeds before capture.
Font installed in the container You control the Chromium image and want its font inventory to be part of deployment. The font is installed in the image that launches Chromium, and the CSS family name matches.
Managed browser font injection Your browser provider supports adding styles to a page. The provider supports the needed style injection and can fetch or otherwise access the font asset.

These are operational tradeoffs, not a performance ranking. An installed font gives you control over the runtime’s font files; a web font avoids custom image packaging but depends on network loading; managed environments can simplify browser operations while still requiring the right font to be available.

Common errors and fixes

Symptom Likely cause Fix
Gujarati appears in a different style than expected. The requested family is misspelled, unavailable, or lacks some characters, so CSS selected a fallback. Check the exact family name, computed style, font coverage, and font request. Put the intended Gujarati family first in the stack.
It works locally but fails in Docker or CI. The host has fonts that are absent from the container where Chromium runs. Install the Gujarati font in the container image or load a reachable web font. Confirm the font in the exact runtime.
The font stylesheet loads, but text still looks wrong. The font file may fail to load, lack the needed glyphs, or be unsuitable for the script’s shaping needs. Inspect the font file request and test the rendered Gujarati sample in the target Chromium build. A stylesheet response alone does not prove the font was used.
networkidle0 never completes. The page may keep network requests open, such as analytics or other persistent activity. Use a navigation condition appropriate to the page, then wait for the page’s own ready signal and document.fonts.ready before capture.
The screenshot is clipped or wraps differently from the browser. The viewport or device scale differs, or a different font changed text metrics. Set the viewport explicitly, wait for fonts, and compare in a consistent browser and runtime.
page.addStyleTag() does not change the font. The injected selector misses the text, the font URL is unreachable, or a more specific page rule overrides it. Target the actual text element, check CSS specificity and computed style, and inspect the font request.
Chromium fails to launch in a minimal Linux image. The runtime may lack browser system dependencies, in addition to the font itself. Follow Puppeteer’s deployment guidance for the Linux environment and include the needed system packages and Gujarati font files. [Puppeteer troubleshooting]

Reliability, performance, and cost notes

  • Reliability: Pin or otherwise standardize the browser runtime and font inventory across local, CI, and production captures. Wait for fonts and verify the image; a successful page load is not proof that the intended font rendered.
  • Performance: Font loading adds work when a font must be fetched. Installing a font in a controlled image or reusing browser resources may change operational overhead, but the research sources establish no universal timing advantage. Avoid waiting for total network idleness on pages that keep requests open; use a page-specific readiness condition.
  • Cost: Self-hosted Puppeteer has no per-screenshot API charge described here, but it uses your compute, browser deployment, and maintenance time. A managed browser service may charge under its own terms; check its current pricing and whether custom fonts are supported before choosing it.

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. Its capture workflow accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents.

Use this one-call request for a Gujarati page, replacing the URL with your target. ScreenshotNeo supports PNG, JPEG, WebP, and PDF output. See the ScreenshotNeo API documentation for request options and format configuration.

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

The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for ScreenshotNeo free and get 1,000 screenshots a month with no card.

FAQ

Is Noto Sans Gujarati UI always the right choice?

No. It is intended for interface use where vertical space is limited. Choose the document or UI style that fits the page, and verify the result with its actual Gujarati content.

Does waiting for document.fonts.ready guarantee the correct font?

No. It waits for the document’s font loading to settle, but a missing font or glyph can still trigger fallback. Check computed styles, font requests, and the screenshot.

Why does a Latin fallback matter on a Gujarati page?

A page can contain Gujarati, Latin text, punctuation, and other characters. A font stack gives the browser options for characters that the Gujarati family does not cover.

Can Puppeteer fix broken Gujarati shaping by itself?

Puppeteer controls Chromium; it does not supply a suitable font simply by taking a screenshot. The browser runtime needs a Gujarati-capable font and shaping support.