ScreenshotNeo

BlogHTML to image & PDF

Convert a Kannada Webpage to PDF with Puppeteer and Noto Fonts

Render Kannada text in a Puppeteer PDF by checking font availability, waiting for fonts, and choosing print settings that fit the page.

By the ScreenshotNeo team4 October 20268 min read

Use Puppeteer’s page.pdf() to save a webpage as a PDF. For readable Kannada, make sure Chromium can load a Kannada-capable font such as Noto Sans Kannada, wait for the page’s fonts to finish loading, and inspect the saved PDF for glyphs and layout. Puppeteer generates PDFs with print CSS by default, so print styles and page settings can change the result from what you see on screen.

1. Install Puppeteer and create a PDF

Start a Node.js project and install Puppeteer. Its package manages a compatible browser installation as part of the normal setup.

npm init -y
npm install puppeteer

Save this as save-kannada-pdf.js. Replace the example address with the page you want to capture.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com/kannada-page', {
      waitUntil: 'networkidle2',
      timeout: 60000,
    });

    await page.pdf({
      path: 'kannada-page.pdf',
      format: 'A4',
      printBackground: true,
      waitForFonts: true,
    });
  } finally {
    await browser.close();
  }
})();

Run it with node save-kannada-pdf.js. This is a documented Puppeteer workflow; a particular site may need a site-specific readiness condition if it loads content after navigation.

2. Make a Kannada font available to Chromium

A font-family declaration only asks the browser to use a font. It does not install that font. Chromium must be able to find a suitable installed font or fetch a font file referenced by the page. The [Noto Fonts Kannada project](https://github.com/notofonts/kannada) provides project resources for Kannada fonts; its existence does not guarantee that a font is installed in your environment or selected by a page.

For a page or template you control, serve a font file and declare it explicitly. Adjust the path, format, and family name for the actual font file you use.

@font-face {
  font-family: "Noto Sans Kannada";
  src: url("/fonts/NotoSansKannada-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
}

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

If you do not control the page, you cannot reliably make its CSS select your preferred family through page.pdf() alone. You can inspect which fonts the page uses, install a Kannada-capable system font in the browser environment, or use Puppeteer to inject CSS before printing. Font availability and the target page’s styles both matter.

Check font readiness

Puppeteer’s PDF options default waitForFonts to true. It waits for document.fonts.ready, which is useful for remotely loaded webfonts. You can also make readiness explicit and check whether the desired family is available to the document:

await page.evaluate(async () => {
  await document.fonts.ready;
  return document.fonts.check('16px "Noto Sans Kannada"');
});

The returned boolean indicates whether the document reports the requested font as available for that check. It does not prove that every Kannada character will render as intended, so inspect the PDF itself. In a background page, font readiness waiting may require bringing the page to the foreground.

3. Choose print styles, paper size, and margins

page.pdf() uses the CSS print media type by default. A webpage can therefore hide navigation, change widths, or add page-break rules for printing. If the screen version is what you need, emulate screen media before generating the PDF:

await page.emulateMediaType('screen');
await page.pdf({ path: 'screen-layout.pdf', format: 'A4' });

For an article, print styling is usually a better starting point. You can control it in the page’s stylesheet or inject print CSS when capturing:

await page.addStyleTag({
  content: `
    @media print {
      body { font-family: "Noto Sans Kannada", sans-serif; }
      article { max-width: none; }
      .no-print { display: none !important; }
      h1, h2, h3 { break-after: avoid; }
      p, li { orphans: 3; widows: 3; }
    }
    @page { size: A4; margin: 16mm; }
  `,
});

Set up the browser page first, then call page.pdf(). The relevant options include:

Option What it controls Practical note
format Named paper size, such as A4. Defaults to Letter. When supplied, it takes priority over width and height.
width, height Custom paper dimensions. Use these when a named format is not appropriate. CSS page sizing can take priority if configured.
margin Top, right, bottom, and left page margins. The API default is no margins. Set explicit margins if text is too close to the paper edge.
preferCSSPageSize Whether CSS @page sizing takes priority. Without it, content is scaled to fit the selected paper size. Enable it when your stylesheet should define the paper size.
printBackground Whether to include background graphics and colors. Defaults to false. Enable it only when those backgrounds belong in the PDF.
scale PDF content scale. The documented range is 0.1 to 2. Prefer fixing width or margins before shrinking text.
waitForFonts Wait for document fonts before printing. Defaults to true. Set false only when you intentionally do not want to wait.
tagged Tagged PDF output. Experimental in the referenced API version; check the API documentation for the version you install.

For exact colors, the Puppeteer documentation points to the CSS property -webkit-print-color-adjust. For example, add print-color-adjust: exact; and -webkit-print-color-adjust: exact; to the relevant print styles, then verify the output. Printer-style color adjustments and background printing affect appearance, so confirm the PDF matches your needs.

4. Handle pages that load content after navigation

networkidle2 waits for network activity to settle, but it is not a guarantee that a page’s application has finished rendering. Pages that use delayed API calls, client-side routing, or lazy content may need a condition tied to the content you expect.

await page.goto('https://example.com/kannada-page', {
  waitUntil: 'domcontentloaded',
  timeout: 60000,
});

await page.waitForSelector('article', { timeout: 30000 });
await page.evaluate(async () => {
  await document.fonts.ready;
});

await page.pdf({
  path: 'kannada-page.pdf',
  format: 'A4',
  printBackground: true,
  waitForFonts: true,
});

Choose a selector that appears only when the content you need is present. If the page has a known loading indicator, wait for it to disappear as well. Avoid waiting for complete network inactivity on pages with persistent connections; use an application-specific condition instead.

5. Inspect the result

Open the generated PDF and check the actual output. In particular, look for:

  • Missing squares, blank glyphs, or substituted characters in Kannada text.
  • Incorrectly shaped or separated characters.
  • Text clipped at the edges or overlapping headers and footers.
  • Unexpected blank pages, awkward page breaks, or content omitted by print CSS.
  • Backgrounds and colors that differ from the intended output.

Compare a few representative passages, including words with conjuncts and punctuation, against the webpage. A PDF that opens successfully is not proof that its font and layout are correct.

6. Troubleshooting

Symptom Likely cause Fix
Kannada appears as boxes or missing glyphs. Chromium cannot find or load a font covering the characters, or the page selected a different fallback. Make a Kannada-capable font available to Chromium, verify the font URL or installation, wait for fonts, and inspect the saved PDF.
Font is declared but the PDF still uses another face. The font file failed to load, its family or weight does not match the CSS declaration, or page styles override it. Check the actual served file and CSS family/weight; inspect document.fonts.check() and test a controlled page.
Text looks different in the PDF than in the browser. page.pdf() uses print CSS, and print font substitution or color adjustment may differ from screen rendering. Review print styles; use emulateMediaType('screen') only when screen styling is required. Check shaping and glyphs in the PDF.
Some text or images are missing. The page had not finished loading dynamic content or lazy-loaded material before capture. Wait for a content-specific selector or app-ready signal. Scroll or trigger lazy content if the site requires it, then wait for the expected content before printing.
Navigation times out on a page that appears usable. The page maintains network connections, so a network-idle condition may never be reached. Use domcontentloaded or another navigation event, then wait for a page-specific selector or readiness signal.
Content is cut off or unexpectedly scaled. Paper size, margins, CSS @page, or scale conflict. Choose one paper-size source, set explicit margins, and check preferCSSPageSize. Reduce scale only after correcting layout.
Background colors are absent. printBackground defaults to false, or print color adjustment changes colors. Set printBackground: true when needed and use print color adjustment CSS where appropriate.
PDF creation waits indefinitely for fonts. A font resource is slow, blocked, or the page is in the background while font readiness is pending. Check font requests and browser logs, bring the page to the foreground if applicable, and only disable waitForFonts when fallback rendering is acceptable.

7. Performance, reliability, and cost

PDF creation time depends on the page, its resources, font loading, and browser runtime. Reuse a browser process for multiple pages in a controlled job rather than launching one browser for each page, and close pages and browsers reliably when work completes. Set navigation and selector timeouts so a stalled page does not hold a worker indefinitely.

For repeatable output, pin the Puppeteer dependency and deploy a known browser environment with the required fonts. Remote font availability can change or fail, so serving a font from a controlled origin or installing it in the runtime can reduce that dependency. Still validate the output after browser, font, or page-style changes; no single setting guarantees identical rendering across arbitrary sites and environments.

Chromium rendering consumes CPU and memory, and larger pages or high-resolution resources can take longer and use more memory. Keep concurrency within the capacity of the machine, and record whether failures occur during navigation, font readiness, or PDF creation. Puppeteer and Noto fonts are software; the reviewed documentation does not establish a required paid purchase for this workflow.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its PDF endpoint can capture a URL in one request; see the API documentation. This is useful when you need a hosted capture call rather than maintaining your own browser environment. The API’s listed options include PDF paper size, margins, landscape orientation, and page ranges.

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

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account to get 1,000 screenshots a month without a card.

FAQ

Will Noto fonts fix every Kannada PDF?

No. The browser must load and select a suitable font, and the page’s CSS and content still affect rendering. Check the generated PDF.

Should I use print or screen media?

Use print media for a document-oriented layout. Emulate screen media when preserving the on-screen styling is more important than print-specific layout.

Does waitForFonts mean the PDF is correct?

No. It waits for document fonts to be ready; it does not confirm that the desired font was selected or that every glyph and page break is correct.

Can I use a custom paper size?

Yes. Set width and height, or define a CSS @page size and enable preferCSSPageSize when CSS should take priority.

References