ScreenshotNeo

BlogHow-to

How to Capture a Puppeteer Screenshot of a Marathi Website with Correct Fonts

Wait for Marathi content and web fonts before capture, then verify glyph coverage and runtime differences when Devanagari still renders incorrectly.

By the ScreenshotNeo team4 October 20268 min read

To capture a Puppeteer screenshot of a Marathi website with the intended fonts, first wait for the Marathi content to appear, then wait for the document’s fonts to finish loading, check that the intended font and required Devanagari glyphs are available, and capture. document.fonts.ready reports that font loading and related layout work have settled; it does not prove that the intended family was selected or that it contains every glyph in the text.

This guide uses Puppeteer’s JavaScript API. It covers full-page and element screenshots, practical font checks, environment differences, troubleshooting, and an API option when you do not want to manage a browser runtime. Puppeteer documents Page.screenshot() and screenshots from an ElementHandle in its screenshot guide.

1. Install Puppeteer and prepare the capture

Install Puppeteer in a project directory. The Puppeteer package downloads a compatible browser by default; if your deployment supplies its own Chrome or Chromium, use the matching executable and record its version when diagnosing font differences.

npm install puppeteer

Save the following as capture-marathi.mjs. Replace the URL and readiness selector with those for the page. The selector should indicate that the actual Marathi text to capture is present, not merely that the initial document shell has loaded.

import puppeteer from 'puppeteer';

const url = 'https://example.com/marathi-page';
const readySelector = '[data-render-complete]';

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' });
  await page.waitForSelector(readySelector, { timeout: 15000 });

  const fontStatus = await page.evaluate(async () => {
    await document.fonts.ready;
    const sample = document.querySelector('[data-marathi]')?.textContent
      ?? document.body.innerText;
    const style = getComputedStyle(
      document.querySelector('[data-marathi]') ?? document.body
    );
    return {
      status: document.fonts.status,
      family: style.fontFamily,
      weight: style.fontWeight,
      sample: sample.slice(0, 180),
    };
  });
  console.log('Font state:', fontStatus);

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

Run it with node capture-marathi.mjs. If the application inserts or changes Marathi text after the readiness selector appears, wait for that update and then await document.fonts.ready again before capturing.

2. Wait for the right thing, in the right order

  1. Navigate. networkidle2 is a useful navigation milestone, but pages with analytics, polling, or long-lived requests may not reach it consistently. Choose a milestone suitable for the page, such as domcontentloaded, and pair it with a page-specific readiness signal.
  2. Wait for content. Use waitForSelector() for a visible content element or an application-provided state. A generic load event cannot tell Puppeteer that client-rendered Marathi text is ready.
  3. Wait for fonts. After the final text exists, await document.fonts.ready. MDN describes this promise as resolving when font loading and related layout operations are complete. See the FontFaceSet.ready reference.
  4. Capture. Call page.screenshot() for the page or an element handle’s screenshot() for one region.

Do not use an arbitrary delay as proof that fonts are ready. A delay may be useful for a known animation or delayed update, but it does not establish that the expected content, font file, or glyphs are present.

3. Verify font selection and Devanagari coverage

A resolved font promise only means the document’s current font work has settled. Check the computed family and weight on the element that contains the Marathi text. Then confirm in browser developer tools or network logs that the intended web-font request succeeded. A CSS family name can be present in computed style even when the browser falls back because that face failed to load or lacks a character.

For a focused diagnostic, run this in the page after the content is present:

const result = await page.evaluate(async () => {
  await document.fonts.ready;
  const el = document.querySelector('[data-marathi]');
  if (!el) return { error: 'Marathi target element not found' };

  const style = getComputedStyle(el);
  const sample = el.textContent.trim();
  return {
    family: style.fontFamily,
    weight: style.fontWeight,
    style: style.fontStyle,
    fontsStatus: document.fonts.status,
    sample,
    declaredFaceAvailable: document.fonts.check(
      `${style.fontWeight} 16px ${style.fontFamily.split(',')[0]}`,
      sample
    ),
  };
});
console.log(result);

document.fonts.check() is a useful diagnostic, not a visual certification. Inspect the actual rendered screenshot and font requests if characters are missing or visually wrong. The selected face must cover the Marathi content, including conjuncts and diacritics. Google’s documentation notes that web-font delivery and browser behavior affect when text appears; see Google Fonts technical considerations.

If the site’s intended family lacks a needed glyph, correct the site’s font stack or font asset rather than expecting Puppeteer to create the glyph. Noto Sans Devanagari is one family you can evaluate as a candidate, but confirm its appearance and coverage against the site’s real text and styling using the Noto Sans Devanagari specimen.

4. Capture only the Marathi region

For a component screenshot, wait for the specific target and use its element handle. This avoids capturing unrelated page content and is useful when the full page has changing elements outside the target.

await page.waitForSelector('[data-marathi-card]', { visible: true });
await page.evaluate(() => document.fonts.ready);
const card = await page.$('[data-marathi-card]');
if (!card) throw new Error('Marathi card not found');
await card.screenshot({ path: 'marathi-card.png' });

Element screenshots capture the selected element’s rendered bounds. If the element is clipped or hidden by page layout, inspect its dimensions and visibility before capture; waiting for fonts does not fix layout constraints.

5. Common font and capture problems

Symptom Likely cause What to check or change
Marathi text is blank in the image The web font is still loading, failed to load, or the page was captured before the text appeared. Wait for the content first, then document.fonts.ready. Inspect font requests and console/network errors. Google notes that Chrome can show blank space for web-font text while the font is loading.
Text appears in an unexpected typeface The declared face did not load, the selected weight is unavailable, or the browser used fallback. Inspect computed family and weight, verify the font URL and response, and compare the page’s browser font inventory.
Only some characters or conjuncts look wrong The chosen face may not contain all characters in the actual Marathi text. Test the exact text with the intended face. Choose a font with the needed coverage and verify the site’s fallback stack.
Local output works but container output does not The environments can differ in browser version, installed system fonts, font URL access, or timing. Record and compare Puppeteer and Chromium versions, OS/container image, installed fonts, network access, selected weight, exact text, and readiness condition.
waitForSelector times out The selector is incorrect, never becomes present, or the page did not reach the application state. Check the selector in the live DOM, wait for the actual app-ready signal, and inspect navigation errors. Do not remove the wait without confirming content readiness.
networkidle2 never completes The page keeps requests open or continually starts new ones. Use an appropriate earlier navigation event and wait for a page-specific selector or state instead.
Screenshot succeeds but content is stale The selector appeared before a subsequent text update. Wait for the final text or app state, then await fonts again and capture.

Historical Puppeteer issue reports describe environment-specific font and fallback problems, including cases where readiness waits did not resolve the visual issue. They are examples of possible variation, not proof of a universal Puppeteer defect: reported Arabic font rendering issue and reported font fallback issue.

6. Improve repeatability, performance, and reliability

  • Keep the capture environment stable. Pin or record Puppeteer, Chromium, and the operating system/container. Provision the fonts your pages require when using system fonts.
  • Wait on signals instead of generous sleeps. A selector and font readiness promise usually express the required state more clearly than a fixed delay. Add a timeout so a broken page fails visibly instead of producing a misleading image.
  • Use the smallest capture target that meets the need. Full-page screenshots can require more rendering and produce larger files than a single element capture.
  • Make failures observable. Log the URL, browser versions, readiness selector, font state, computed family/weight, and navigation error. Keep the exact text sample available for diagnosing glyph-specific issues.
  • Retry selectively. A transient navigation or resource failure may merit a bounded retry. Repeating a deterministic missing-glyph or missing-font problem will not fix it; correct the font delivery or environment.

No universal runtime or timing benchmark applies to every Marathi site. Measure your own capture with its real fonts, content, viewport, and deployment environment.

7. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. A single GET request can return a PNG, JPEG, WebP, or PDF. Its screenshot endpoint can simplify capture when you do not want to install and operate Puppeteer; see the ScreenshotNeo API documentation.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/marathi-page"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/marathi-page'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) =>
  writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);
  • Cookie banners and consent prompts, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
  • Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Responses include X-Page-Verdict and X-Billed headers.
  • An MCP server gives AI agents, including Claude, Cursor, and other MCP clients, tools for screenshots, page information, and PDF capture.
  • The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots.

For Marathi font accuracy, still inspect the returned image against the site’s intended family and glyph coverage. Sign up for 1,000 free screenshots a month, with no card.

8. Frequently asked questions

Does document.fonts.ready guarantee the correct Marathi font?

No. It indicates that font loading and related layout have settled. It does not prove that the preferred face was selected or that it supports every character.

Should I install a Devanagari font in the container?

If the page depends on a local system font, make sure the capture environment contains it. If the site serves a web font, verify that the browser can fetch that asset. The correct remedy depends on which font the page actually uses.

Can a fixed sleep replace the font readiness wait?

No. A delay cannot confirm that the needed content and fonts have loaded. Wait for page content, then use the font-loading signal.

Which font should I choose?

Use the website’s intended typeface when possible. If its coverage is insufficient, evaluate a Devanagari-capable candidate such as Noto Sans Devanagari against the actual page styling and text.

Sources