ScreenshotNeo

BlogHow-to

Use Go chromedp to capture screenshots of Hindi government forms

Capture a Hindi government form with Go chromedp, choose element, viewport, or full-page output, and check Devanagari rendering before saving it.

By the ScreenshotNeo team4 October 20269 min read

Use chromedp to open the form in Chrome, wait for the form content to render, capture the element, viewport, or full page, then save the returned bytes with os.WriteFile. For Hindi text, inspect the resulting image in the same browser and container environment used for production: check Devanagari glyphs, clipped labels, line wrapping, and layout. There is no universal font setting that fixes every government site.

1. Install chromedp and prepare Chrome

Start with a Go module and add chromedp:

go mod init example.com/hindi-form-shot
go get github.com/chromedp/chromedp

chromedp drives Chrome through the Chrome DevTools Protocol. Make Chrome or Chromium available in the runtime where the program will run. In containers and servers, verify that the browser can start there; a local desktop browser and a production container can have different fonts, installed packages, permissions, and network access.

Set the target page URL through an environment variable so it is easy to change without editing the code:

export FORM_URL='https://example.gov.in/path/to/form'

Replace the example with the actual form URL. The page may render its form after navigation, fetch data with JavaScript, load content in an iframe, or wait on web fonts. Waiting only for navigation to complete may therefore capture an incomplete page.

2. Runnable Go example: capture the whole page

This program navigates to the configured URL, waits for a form element to become visible, captures beyond the viewport as PNG, and writes the bytes to disk:

package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/chromedp/chromedp"
)

func main() {
	formURL := os.Getenv("FORM_URL")
	if formURL == "" {
		fmt.Fprintln(os.Stderr, "set FORM_URL to the government form URL")
		os.Exit(2)
	}

	ctx, cancel := chromedp.NewContext(context.Background())
	defer cancel()

	// Bound the operation so a stalled navigation or page does not run forever.
	ctx, cancel = context.WithTimeout(ctx, 90*time.Second)
	defer cancel()

	var image []byte
	err := chromedp.Run(ctx,
		chromedp.Navigate(formURL),
		// Change the selector if the form uses a different structure.
		chromedp.WaitVisible("form", chromedp.ByQuery),
		// A quality of 100 selects PNG in chromedp's FullScreenshot helper.
		chromedp.FullScreenshot(&image, 100),
	)
	if err != nil {
		fmt.Fprintln(os.Stderr, "capture failed:", err)
		os.Exit(1)
	}

	if err := os.WriteFile("form.png", image, 0o644); err != nil {
		fmt.Fprintln(os.Stderr, "write form.png:", err)
		os.Exit(1)
	}
	fmt.Printf("wrote form.png (%d bytes)\n", len(image))
}

Run it with FORM_URL set. The form selector is only an example: use a selector that identifies the actual form or a reliable readiness condition on the target page. If the form is inside an iframe, a top-level form selector may not find it; inspect the page structure and target the frame content appropriately.

The capture action produces image bytes; os.WriteFile stores them directly. The code does not claim that a successful capture means every delayed field, font, or image has finished rendering. Confirm those conditions against the page before relying on the output.

3. Choose the capture scope

Need Action Behavior
One field group, panel, or form element chromedp.Screenshot(selector, &image, ...) Captures the first element matching the selector.
Only what is currently visible chromedp.CaptureScreenshot(&image) Captures the current browser viewport.
Page content extending below the viewport chromedp.FullScreenshot(&image, quality) Captures beyond the viewport; this helper changes device emulation settings.

For a specific form element, replace the full-page action in the example with:

chromedp.Screenshot("#application-form", &image, chromedp.ByQuery)

Use a selector that matches the desired element. The helper captures the first matching node, so make the selector specific if the page contains multiple forms or repeated panels. For the visible viewport, use:

chromedp.CaptureScreenshot(&image)

Choose the scope based on what the screenshot is meant to show. A viewport shot can omit fields below the fold. A full-page shot can become very tall for long forms. An element shot can omit instructions or context outside that element.

4. Wait for the actual form and check Hindi rendering

Navigation completion and form readiness are different conditions. A more reliable capture waits for a page-specific signal: the target form is visible, a known field has appeared, a loading indicator has disappeared, or a page-specific completion condition is true. Use a fixed delay only when the page offers no useful readiness signal; delays make captures slower and still cannot guarantee that a slow request or font has finished.

Inspect the captured image in the runtime where the job will run. Check:

  • Expected Devanagari characters render instead of blank boxes or missing glyphs.
  • Hindi labels and values are not clipped at the edge of fields or the page.
  • Line wrapping has not pushed labels into neighboring controls or changed the form layout unexpectedly.
  • Dynamic sections, validation messages, and required instructions are present when they should be.
  • The output file extension matches the actual encoding.

The reviewed sources do not identify a font installation or CSS change that universally fixes Hindi rendering across government sites. If glyphs are missing, inspect the browser environment, page styles, loaded fonts, and network requests; verify again in the actual production environment. A visually correct screenshot also does not establish that the form is accessible. GIGW publishes separate government website guidance and accessibility resources, including considerations for form labels, structure, reading order, and documents: GIGW guidelines and accessibility guidance.

5. Output formats and full-page emulation behavior

The Chrome DevTools Protocol screenshot method supports PNG, JPEG, and WebP; PNG is the default. CDP also supports a quality value for JPEG, a clipping rectangle, and capture beyond the viewport. chromedp’s FullScreenshot helper selects PNG when its quality argument is 100 and JPEG otherwise. Name files accordingly: quality 100 should use a .png extension, while a non-100 quality uses JPEG and should use .jpg or .jpeg.

The example uses quality 100 and writes form.png. If you change the quality, change the output name and account for lossy JPEG compression, which can make small text less crisp. PNG is a sensible default for forms with fine text. Choose another format only when its size or compatibility suits the workflow, and inspect small Devanagari text at the intended viewing size.

FullScreenshot overrides device emulation settings. If your capture pipeline configured a particular device or viewport profile, reset or reapply those settings after the full-page capture before continuing with another capture that depends on emulation.

6. cURL, Python, and Node.js alternatives

If you need an HTTP-based screenshot instead of managing Chrome and chromedp, these examples call ScreenshotNeo. They use the API’s documented endpoint and save the returned image response; see the ScreenshotNeo API documentation for parameters and response details.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.gov.in/path/to/form -o form.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.gov.in/path/to/form"},
    timeout=90,
)
r.raise_for_status()
open("form.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.gov.in/path/to/form'
});
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('form.webp', res);

These API examples are an alternative workflow, not chromedp code. Keep the API key private and follow the service documentation for supported options and response handling.

7. Troubleshooting

Symptom Likely cause What to change
Wait for selector times out The selector does not match, the page has not rendered the form, or the form is inside a frame. Inspect the DOM, choose a selector for the actual content, wait for a page-specific ready state, and account for iframe content.
Screenshot is blank or missing fields The page is still loading data or rendering delayed content when capture begins. Wait for a form-specific condition or for a loading marker to disappear; avoid assuming navigation alone means the form is ready.
Hindi characters appear as boxes or are absent The page’s font or browser environment may not provide the expected glyphs, or font loading has not completed. Inspect the page and font loading in the capture runtime, wait for the relevant page state, and verify in the same production container or host.
Labels or values are clipped The selected element bounds, viewport, or layout differs from expectations. Capture the correct element or whole page, inspect responsive layout and line wrapping, and use the same viewport settings as production.
Output will not open as PNG A non-100 quality value was used with FullScreenshot, which produces JPEG. Use quality 100 for PNG or use a JPEG extension for non-100 quality.
Later captures use unexpected device dimensions FullScreenshot overrides device emulation settings. Reset or reapply the emulation configuration after the full-page capture.
Navigation or capture hangs The target request or page may be stalled, or the operation has no deadline. Set a context timeout, surface the returned error, and investigate whether the page is reachable from the runtime.
Screenshot file is empty or missing The capture returned an error before writing, or file writing failed. Check and report both the chromedp.Run and os.WriteFile errors; do not treat a zero-byte file as a successful capture.

8. Performance, reliability, and cost

Capture time is affected by page navigation, delayed requests, fonts, and the readiness condition. Waiting on the actual form avoids saving too early; unbounded waits can leave jobs stuck, while arbitrary long sleeps add latency without proving the page is ready. Put a deadline around each capture and return the underlying error so callers can decide whether to retry.

Full-page images can consume more memory and disk than viewport or element captures, especially for long forms. Capture only the required scope, avoid unnecessary repeat captures, and use JPEG only when its compression tradeoff is acceptable for the small text in the form. chromedp itself runs the browser in your environment, so account for browser setup and runtime resources in your deployment. No benchmark or universal capture time is established by the sources.

For an official form, preserve the original URL and capture outcome alongside the image if your workflow needs traceability, and review the result before using it as evidence. A screenshot records a rendered view; it does not prove the form is current, complete, or accessible.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. Make a single request with the form URL to get an image or PDF; its API options also include full-page and element captures, custom waits, headers, cookies, and viewport settings. See the documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.gov.in/path/to/form -o form.webp

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 take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Does capturing a Hindi form show that it is accessible?

No. A screenshot can help review visual rendering, but it does not check semantic labels, reading order, keyboard access, or assistive technology behavior. Consult the separate government accessibility guidance for those reviews.

Should I capture a form before or after entering data?

That depends on the purpose of the capture. For a blank-form reference, capture after the form has loaded but before entry. For a workflow record, make sure the intended state is visible and that sensitive personal data is handled according to your requirements.

Can I use an arbitrary delay instead of waiting for a selector?

You can, but a delay is only elapsed time, not proof that the form or its fonts are ready. Prefer a condition tied to the content whenever the page provides one.