{"id":82228,"date":"2024-11-01T19:49:36","date_gmt":"2024-11-01T16:19:36","guid":{"rendered":"https:\/\/nabfollower.com\/blog\/how-to-capture-web-page-screenshots-with-nextjs-and-puppeteer-o7p\/"},"modified":"2024-11-01T19:49:36","modified_gmt":"2024-11-01T16:19:36","slug":"how-to-capture-web-page-screenshots-with-nextjs-and-puppeteer-o7p","status":"publish","type":"post","link":"https:\/\/nabfollower.com\/blog\/how-to-capture-web-page-screenshots-with-nextjs-and-puppeteer-o7p\/","title":{"rendered":"\u0646\u062d\u0648\u0647 \u06af\u0631\u0641\u062a\u0646 \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0627\u0632 \u0635\u0641\u062d\u0647 \u0648\u0628 \u0628\u0627 Next.js \u0648 Puppeteer"},"content":{"rendered":"<p>Summarize this content to 400 words in Persian Lang<br \/>\n            \u06af\u0631\u0641\u062a\u0646 \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0627\u0632 \u0635\u0641\u062d\u0627\u062a \u0648\u0628 \u0628\u0647 \u0635\u0648\u0631\u062a \u0628\u0631\u0646\u0627\u0645\u0647 \u0627\u06cc \u0645\u06cc \u062a\u0648\u0627\u0646\u062f \u0628\u0631\u0627\u06cc \u062a\u0648\u0644\u06cc\u062f \u067e\u06cc\u0634 \u0646\u0645\u0627\u06cc\u0634\u060c \u0627\u06cc\u062c\u0627\u062f \u06af\u0632\u0627\u0631\u0634 \u0647\u0627\u06cc \u0645\u0628\u062a\u0646\u06cc \u0628\u0631 \u062a\u0635\u0648\u06cc\u0631 \u0648 \u0645\u0648\u0627\u0631\u062f \u062f\u06cc\u06af\u0631 \u0628\u0633\u06cc\u0627\u0631 \u0645\u0641\u06cc\u062f \u0628\u0627\u0634\u062f. \u062f\u0631 \u0627\u06cc\u0646 \u0631\u0627\u0647\u0646\u0645\u0627\u060c \u0645\u0627 \u06cc\u06a9 \u0645\u0633\u06cc\u0631 API Next.js \u062e\u0648\u0627\u0647\u06cc\u0645 \u0633\u0627\u062e\u062a \u06a9\u0647 \u06cc\u06a9 URL \u0631\u0627 \u0645\u06cc \u06af\u06cc\u0631\u062f \u0648 \u06cc\u06a9 \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a PNG \u0627\u06cc\u062c\u0627\u062f \u0645\u06cc \u06a9\u0646\u062f. \u0631\u0627\u0647\u200c\u0627\u0646\u062f\u0627\u0632\u06cc \u0645\u0627 \u0627\u0632 Puppeteer \u0648 chrome-aws-lambda \u0628\u0631\u0627\u06cc \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u0645\u0631\u0648\u0631\u06af\u0631 \u0628\u062f\u0648\u0646 \u0647\u062f Chrome \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0645\u06cc\u200c\u06a9\u0646\u062f \u0648 \u0622\u0646 \u0631\u0627 \u0647\u0645\u0647\u200c\u06a9\u0627\u0631\u0647 \u0648 \u0622\u0645\u0627\u062f\u0647 \u062a\u0648\u0644\u06cc\u062f \u0645\u06cc\u200c\u06a9\u0646\u062f.<\/p>\n<p>\u0645\u0627 \u0628\u0627 \u0631\u0627\u0647\u200c\u0627\u0646\u062f\u0627\u0632\u06cc \u06cc\u06a9 \u067e\u0631\u0648\u0698\u0647 Next.js \u0648 \u0642\u062f\u0645 \u0628\u0647 \u0642\u062f\u0645 \u06a9\u062f\u0647\u0627 \u0631\u0627 \u0634\u0631\u0648\u0639 \u0645\u06cc\u200c\u06a9\u0646\u06cc\u0645 \u062a\u0627 \u0628\u0641\u0647\u0645\u06cc\u0645 \u06a9\u0647 API \u0686\u06af\u0648\u0646\u0647 \u0627\u0633\u06a9\u0631\u06cc\u0646\u200c\u0634\u0627\u062a\u200c\u0647\u0627 \u0631\u0627 \u0645\u06cc\u200c\u06af\u06cc\u0631\u062f.<\/p>\n<p>\u067e\u06cc\u0634 \u0646\u06cc\u0627\u0632\u0647\u0627<\/p>\n<p>\u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 Next.js<br \/>\n\u067e\u06cc\u06a9\u0631\u0628\u0646\u062f\u06cc \u0645\u0633\u06cc\u0631 API \u0628\u0627 Puppeteer<br \/>\n\u0627\u06cc\u062c\u0627\u062f \u06a9\u0627\u0645\u067e\u0648\u0646\u0646\u062a React \u0628\u0631\u0627\u06cc \u0631\u0627\u0628\u0637 \u06a9\u067e\u0686\u0631<br \/>\n\u062a\u0648\u0636\u06cc\u062d \u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u0645\u062d\u0644\u06cc \u062f\u0631 \u0645\u0642\u0627\u0628\u0644 \u0627\u0633\u062a\u0642\u0631\u0627\u0631 \u0628\u0631\u0627\u06cc Puppeteer<\/p>\n<p>\u0634\u0631\u0648\u0639 \u06a9\u0627\u0631 \u0628\u0627 \u06cc\u06a9 \u067e\u0631\u0648\u0698\u0647 Next.js \u062c\u062f\u06cc\u062f<\/p>\n<p>\u06cc\u06a9 \u0628\u0631\u0646\u0627\u0645\u0647 Next.js \u062c\u062f\u06cc\u062f \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f:<\/p>\n<p>npx create-next-app@latest capture-image-app<br \/>\ncd capture-image-app<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>\u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0647\u0627\u06cc \u0644\u0627\u0632\u0645 \u0631\u0627 \u0646\u0635\u0628 \u06a9\u0646\u06cc\u062f:<\/p>\n<p>npm install puppeteer puppeteer-core chrome-aws-lambda busboy<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>\u0645\u0631\u062d\u0644\u0647 2: \u0645\u0633\u06cc\u0631 API \u0631\u0627 \u0628\u0631\u0627\u06cc \u0627\u06cc\u062c\u0627\u062f \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f<\/p>\n<p>\u0627\u06a9\u0646\u0648\u0646\u060c \u06cc\u06a9 \u0646\u0642\u0637\u0647 \u067e\u0627\u06cc\u0627\u0646\u06cc API \u0628\u0631\u0627\u06cc \u06af\u0631\u0641\u062a\u0646 \u0648 \u0628\u0631\u06af\u0631\u062f\u0627\u0646\u062f\u0646 \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0647\u0627 \u0628\u0631 \u0627\u0633\u0627\u0633 URL \u0627\u0631\u0627\u0626\u0647 \u0634\u062f\u0647\u060c \u062a\u0646\u0638\u06cc\u0645 \u0645\u06cc \u06a9\u0646\u06cc\u0645.<\/p>\n<p>\u062f\u0631 pages\/api \u067e\u0648\u0634\u0647\u060c \u06cc\u06a9 \u0641\u0627\u06cc\u0644 \u062c\u062f\u06cc\u062f \u0628\u0647 \u0646\u0627\u0645 \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f generate-png.ts \u0648 \u0627\u06cc\u0646 \u06a9\u062f \u0631\u0627 \u0627\u0636\u0627\u0641\u0647 \u06a9\u0646\u06cc\u062f:<\/p>\n<p>import { NextApiRequest, NextApiResponse } from &#8220;next&#8221;;<br \/>\nimport busboy, { Busboy } from &#8220;busboy&#8221;; \/\/ Use busboy for multipart parsing<br \/>\nimport chromium from &#8220;chrome-aws-lambda&#8221;;<br \/>\nimport puppeteerCore from &#8220;puppeteer-core&#8221;; \/\/ Import puppeteer-core directly<br \/>\nimport puppeteer from &#8220;puppeteer&#8221;; \/\/ Import puppeteer directly<\/p>\n<p>\/\/ Conditional import for Puppeteer based on the environment<br \/>\nconst puppeteerModule = process.env.NODE_ENV === &#8220;production&#8221; ? puppeteerCore : puppeteer;<\/p>\n<p>export const config = {<br \/>\n  api: {<br \/>\n    bodyParser: false, \/\/ Disable default body parsing to handle raw binary data (Blob)<br \/>\n  },<br \/>\n};<\/p>\n<p>const delay = (ms: number): Promise =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));<\/p>\n<p>export default async function handler(<br \/>\n  req: NextApiRequest,<br \/>\n  res: NextApiResponse<br \/>\n): Promise {<br \/>\n  try {<br \/>\n    if (req.method === &#8220;POST&#8221;) {<br \/>\n      const bb: Busboy = busboy({ headers: req.headers });<br \/>\n      let width: number = 1920; \/\/ Default width<br \/>\n      let height: number = 0; \/\/ Default height<br \/>\n      let delayTime: number = 6000;<br \/>\n      const buffers: Buffer[] = [];<\/p>\n<p>      bb.on(&#8220;file&#8221;, (_name: string, file: NodeJS.ReadableStream) =&gt; {<br \/>\n        file.on(&#8220;data&#8221;, (data: Buffer) =&gt; buffers.push(data));<br \/>\n      });<\/p>\n<p>      bb.on(&#8220;field&#8221;, (name: string, value: string) =&gt; {<br \/>\n        if (name === &#8220;width&#8221;) width = parseInt(value, 10) || 1920;<br \/>\n        if (name === &#8220;height&#8221;) height = parseInt(value, 10) || 0;<br \/>\n        if (name === &#8220;delay&#8221;) delayTime = parseInt(value, 10) || 6000;<br \/>\n      });<\/p>\n<p>      bb.on(&#8220;finish&#8221;, async () =&gt; {<br \/>\n        const blobBuffer: Buffer = Buffer.concat(buffers);<br \/>\n        const htmlContent: string = blobBuffer.toString(&#8220;utf-8&#8221;);<\/p>\n<p>        const browser = await puppeteerModule.launch({<br \/>\n          args: [&#8220;&#8211;start-maximized&#8221;],<br \/>\n          executablePath: process.env.NODE_ENV === &#8220;production&#8221;<br \/>\n            ? await chromium.executablePath || &#8220;\/usr\/bin\/chromium-browser&#8221;<br \/>\n            : undefined,  \/\/ No custom executable path needed for local<br \/>\n          headless: true,<br \/>\n        });<\/p>\n<p>        const page = await browser.newPage();<\/p>\n<p>        \/\/ Load the HTML content directly<br \/>\n        await page.setContent(htmlContent, { waitUntil: &#8220;networkidle0&#8221; });<\/p>\n<p>        \/\/@ts-expect-error todo<br \/>\n        const bodyHeight = await page.evaluate(() =&gt; {<br \/>\n          return document.body.scrollHeight; \/\/ Get the full scrollable height of the body<br \/>\n        });<\/p>\n<p>        await page.setViewport({<br \/>\n          width: Number(width),<br \/>\n          height: height || bodyHeight, \/\/ Use the provided height or fallback to the full body height<br \/>\n          deviceScaleFactor: 2,<br \/>\n        });<\/p>\n<p>        await delay(delayTime);<\/p>\n<p>        const screenshotBuffer = await page.screenshot({<br \/>\n          fullPage: !height,<br \/>\n          type: &#8220;png&#8221;,<br \/>\n          omitBackground: false,<br \/>\n        });<\/p>\n<p>        await browser.close();<\/p>\n<p>        res.setHeader(&#8220;Content-Type&#8221;, &#8220;image\/png&#8221;);<br \/>\n        res.setHeader(<br \/>\n          &#8220;Content-Disposition&#8221;,<br \/>\n          &#8220;attachment; filename=screenshot.png&#8221;<br \/>\n        );<br \/>\n        res.status(200).end(screenshotBuffer);<br \/>\n      });<\/p>\n<p>      req.pipe(bb); \/\/ Pipe the request stream to busboy<br \/>\n    } else {<br \/>\n      res.setHeader(&#8220;Allow&#8221;, [&#8220;POST&#8221;]);<br \/>\n      res.status(405).end(`Method ${req.method} Not Allowed`);<br \/>\n    }<br \/>\n  } catch (error) {<br \/>\n    console.error(&#8220;ERROR&#8221;, error);<br \/>\n    res.status(500).end(&#8220;Internal Server Error&#8221;);<br \/>\n  }<br \/>\n}<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>*\u062a\u0648\u0636\u06cc\u062d: \u0627\u0646\u062a\u062e\u0627\u0628 \u0639\u0631\u0648\u0633\u06a9 \u06af\u0631\u062f\u0627\u0646 \u0628\u0631\u0627\u06cc \u0645\u062d\u06cc\u0637 \u0647\u0627\u06cc \u0645\u062d\u0644\u06cc \u062f\u0631 \u0645\u0642\u0627\u0628\u0644 \u062a\u0648\u0644\u06cc\u062f*\u062f\u0631 \u0627\u06cc\u0646 \u06a9\u062f\u060c \u06cc\u06a9 Import \u067e\u0648\u06cc\u0627 \u0628\u0631\u0627\u06cc Puppeteer \u062a\u0646\u0638\u06cc\u0645 \u06a9\u0631\u062f\u0647 \u0627\u06cc\u0645:<\/p>\n<p>\u062a\u0648\u0633\u0639\u0647 \u0645\u062d\u0644\u06cc: \u0627\u06af\u0631 NODE_ENV \u0646\u06cc\u0633\u062a production\u060c \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0645\u06cc \u06a9\u0646\u062f puppeteer\u060c \u06a9\u0647 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u0622\u0646 \u0633\u0627\u062f\u0647 \u062a\u0631 \u0627\u0633\u062a \u0648 \u0646\u06cc\u0627\u0632\u06cc \u0646\u062f\u0627\u0631\u062f chrome-aws-lambda.<br \/>\n\u062a\u0648\u0644\u06cc\u062f: \u0628\u0631\u0627\u06cc \u0627\u0633\u062a\u0642\u0631\u0627\u0631\u0647\u0627\u06cc \u0628\u062f\u0648\u0646 \u0633\u0631\u0648\u0631\u060c \u0645\u062d\u06cc\u0637 \u0634\u0646\u0627\u0633\u0627\u06cc\u06cc \u062e\u0648\u0627\u0647\u062f \u06a9\u0631\u062f NODE_ENV \u0628\u0647 \u0639\u0646\u0648\u0627\u0646 production \u0648 Puppeteer-core \u0647\u0645\u0631\u0627\u0647 \u0628\u0627 chrome-aws-lambda\u060c \u06a9\u0647 \u0628\u0647 \u0622\u0646 \u0627\u062c\u0627\u0632\u0647 \u0645\u06cc \u062f\u0647\u062f \u062f\u0631 AWS Lambda \u0648 \u0633\u0627\u06cc\u0631 \u0645\u062d\u06cc\u0637 \u0647\u0627\u06cc \u0645\u0634\u0627\u0628\u0647 \u06a9\u0627\u0631 \u06a9\u0646\u062f. \u062f\u0631 \u0627\u06cc\u0646 \u062a\u0646\u0638\u06cc\u0645\u0627\u062a\u060c chrome-aws-lambda \u0645\u0633\u06cc\u0631 \u0635\u062d\u06cc\u062d Chromium \u0631\u0627 \u0641\u0631\u0627\u0647\u0645 \u0645\u06cc \u06a9\u0646\u062f \u0648 \u0627\u0632 \u0633\u0627\u0632\u06af\u0627\u0631\u06cc \u0628\u0627 \u0627\u0631\u0627\u0626\u0647 \u062f\u0647\u0646\u062f\u06af\u0627\u0646 \u0628\u062f\u0648\u0646 \u0633\u0631\u0648\u0631 \u0627\u0637\u0645\u06cc\u0646\u0627\u0646 \u0645\u06cc \u062f\u0647\u062f.<\/p>\n<p>\u0645\u0631\u062d\u0644\u0647 3: \u06cc\u06a9 \u06a9\u0627\u0645\u067e\u0648\u0646\u0646\u062a Simple React \u0628\u0631\u0627\u06cc \u0631\u0627\u0628\u0637 \u06a9\u0627\u0631\u0628\u0631\u06cc \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f<\/p>\n<p>\u062f\u0631 \u0627\u06cc\u0646\u062c\u0627\u060c \u06cc\u06a9 \u0641\u0631\u0645 \u0633\u0627\u062f\u0647 \u0627\u06cc\u062c\u0627\u062f \u0645\u06cc\u200c\u06a9\u0646\u06cc\u0645 \u06a9\u0647 \u0628\u0647 \u06a9\u0627\u0631\u0628\u0631\u0627\u0646 \u0627\u062c\u0627\u0632\u0647 \u0645\u06cc\u200c\u062f\u0647\u062f \u0645\u0642\u0627\u062f\u06cc\u0631 \u0648\u0631\u0648\u062f\u06cc \u0635\u0641\u062d\u0647 \u0648\u0628 \u0631\u0627 \u062b\u0628\u062a \u06a9\u0646\u0646\u062f. \u0627\u06cc\u0646 \u0641\u0631\u0645 \u062a\u0627\u0628\u0639 \u062a\u0648\u0644\u06cc\u062f \u0631\u0627 \u0628\u0631\u0627\u06cc \u06af\u0631\u0641\u062a\u0646 \u0648 \u062f\u0627\u0646\u0644\u0648\u062f \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u062f\u0631 \u0642\u0627\u0644\u0628 PDF \u0641\u0639\u0627\u0644 \u0645\u06cc \u06a9\u0646\u062f.<\/p>\n<p>import { useState } from &#8220;react&#8221;;<\/p>\n<p>export default function ScreenCaptureComponent() {<br \/>\n  const [isProcessing, setProcessing] = useState(false);<br \/>\n  const [width, setWidth] = useState(&#8220;1920&#8221;);<br \/>\n  const [height, setHeight] = useState(&#8220;1000&#8221;);<br \/>\n  const [delay, setDelay] = useState(&#8220;6000&#8221;);<\/p>\n<p>  \/\/ Function to clone HTML and prepare for capture<br \/>\n  function takeScreenshot() {<br \/>\n    const clonedElement = document.body.cloneNode(true) as HTMLElement;<br \/>\n    const blob = new Blob([clonedElement.outerHTML], { type: &#8220;text\/html&#8221; });<br \/>\n    return blob;<br \/>\n  }<\/p>\n<p>  \/\/ Function to capture screenshot by sending cloned HTML to API<br \/>\n  async function generateCapture() {<br \/>\n    setProcessing(true);<\/p>\n<p>    const htmlBlob = takeScreenshot();<\/p>\n<p>    if (!htmlBlob) {<br \/>\n      setProcessing(false);<br \/>\n      return;<br \/>\n    }<\/p>\n<p>    try {<br \/>\n      const formData = new FormData();<br \/>\n      formData.append(&#8220;file&#8221;, htmlBlob);<br \/>\n      formData.append(&#8220;width&#8221;, width);<br \/>\n      formData.append(&#8220;height&#8221;, height);<br \/>\n      formData.append(&#8220;delay&#8221;, delay);<br \/>\n      const response = await fetch(&#8220;\/api\/generate-png&#8221;, {<br \/>\n        method: &#8220;POST&#8221;,<br \/>\n        body: formData,<br \/>\n      });<\/p>\n<p>      if (!response.ok) throw new Error(&#8220;Capture failed&#8221;);<\/p>\n<p>      const blob = await response.blob();<br \/>\n      const downloadUrl = URL.createObjectURL(blob);<br \/>\n      const link = document.createElement(&#8220;a&#8221;);<br \/>\n      link.href = downloadUrl;<br \/>\n      link.download = &#8220;capture.png&#8221;;<br \/>\n      link.click();<br \/>\n      URL.revokeObjectURL(downloadUrl);<br \/>\n    } catch (error) {<br \/>\n      console.error(&#8220;Failed to capture screenshot&#8221;, error);<br \/>\n    } finally {<br \/>\n      setProcessing(false);<br \/>\n    }<br \/>\n  }<\/p>\n<p>  return (<\/p>\n<p>        Webpage Screenshot Capture<\/p>\n<p>       {<br \/>\n          e.preventDefault();<br \/>\n          generateCapture();<br \/>\n        }}<br \/>\n        style={{<br \/>\n          display: &#8220;flex&#8221;,<br \/>\n          flexDirection: &#8220;column&#8221;,<br \/>\n          alignItems: &#8220;center&#8221;,<br \/>\n          marginBottom: &#8220;16px&#8221;,<br \/>\n        }}<br \/>\n      &gt;<\/p>\n<p>          Width (px)<\/p>\n<p>         setWidth(e.target.value)}<br \/>\n          style={{<br \/>\n            width: &#8220;100%&#8221;,<br \/>\n            padding: &#8220;8px&#8221;,<br \/>\n            marginBottom: &#8220;16px&#8221;,<br \/>\n            borderRadius: &#8220;4px&#8221;,<br \/>\n            border: &#8220;1px solid #ccc&#8221;,<br \/>\n            outline: &#8220;none&#8221;,<br \/>\n          }}<br \/>\n        &gt;<br \/>\n          1920 (Full HD)<br \/>\n          1366 (Laptop)<br \/>\n          1280 (Desktop)<br \/>\n          1024 (Tablet Landscape)<br \/>\n          768 (Tablet Portrait)<br \/>\n          375 (Mobile)<\/p>\n<p>          Height (px)<\/p>\n<p>         setHeight(e.target.value)}<br \/>\n          required<br \/>\n          style={{<br \/>\n            width: &#8220;100%&#8221;,<br \/>\n            padding: &#8220;8px&#8221;,<br \/>\n            marginBottom: &#8220;16px&#8221;,<br \/>\n            borderRadius: &#8220;4px&#8221;,<br \/>\n            border: &#8220;1px solid #ccc&#8221;,<br \/>\n            outline: &#8220;none&#8221;,<br \/>\n          }}<br \/>\n        \/&gt;<\/p>\n<p>          Delay (ms)<\/p>\n<p>         setDelay(e.target.value)}<br \/>\n          required<br \/>\n          style={{<br \/>\n            width: &#8220;100%&#8221;,<br \/>\n            padding: &#8220;8px&#8221;,<br \/>\n            marginBottom: &#8220;16px&#8221;,<br \/>\n            borderRadius: &#8220;4px&#8221;,<br \/>\n            border: &#8220;1px solid #ccc&#8221;,<br \/>\n            outline: &#8220;none&#8221;,<br \/>\n          }}<br \/>\n        \/&gt;<\/p>\n<p>          {isProcessing ? &#8220;Capturing&#8230;&#8221; : &#8220;Capture Screenshot&#8221;}<\/p>\n<p>      {\/* Example HTML Element to Capture *\/}<\/p>\n<p>          Content to Capture<\/p>\n<p>        This is an example of the HTML content that will be captured.<\/p>\n<p>  );<br \/>\n}<\/p>\n<p>    \u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/p>\n<p>    \u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/p>\n<p>\u0646\u062a\u06cc\u062c\u0647 \u06af\u06cc\u0631\u06cc<\/p>\n<p>\u0627\u06cc\u0646 \u0622\u0645\u0648\u0632\u0634 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u06cc\u06a9 \u0627\u0628\u0632\u0627\u0631 \u0636\u0628\u0637 \u0635\u0641\u062d\u0647 \u0648\u0628 \u062f\u0631 Next.js\u060c \u0645\u062f\u06cc\u0631\u06cc\u062a \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0647\u0627 \u0631\u0627 \u067e\u0648\u0634\u0634 \u0645\u06cc \u062f\u0647\u062f Puppeteer\u060c \u0648 \u0627\u06cc\u062c\u0627\u062f \u06cc\u06a9 \u0645\u0624\u0644\u0641\u0647 frontend \u062a\u0639\u0627\u0645\u0644\u06cc. \u0628\u0647 \u06cc\u0627\u062f \u062f\u0627\u0634\u062a\u0647 \u0628\u0627\u0634\u06cc\u062f \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u06a9\u0646\u06cc\u062f puppeteer \u0628\u0647 \u0635\u0648\u0631\u062a \u0645\u062d\u0644\u06cc \u0648 \u062a\u063a\u06cc\u06cc\u0631 \u0628\u0647 puppeteer-core \u062f\u0631 \u062a\u0648\u0644\u06cc\u062f \u0628\u0631\u0627\u06cc \u06a9\u0627\u0647\u0634 \u0627\u0646\u062f\u0627\u0632\u0647 \u0628\u0627\u0646\u062f\u0644 \u0648 \u0628\u0647\u06cc\u0646\u0647 \u0633\u0627\u0632\u06cc \u0628\u0631\u0627\u06cc \u0645\u062d\u06cc\u0637 \u0647\u0627\u06cc \u0628\u062f\u0648\u0646 \u0633\u0631\u0648\u0631. \u06a9\u062f \u0646\u0648\u06cc\u0633\u06cc \u0645\u0628\u0627\u0631\u06a9!<\/p>\n<div data-article-id=\"2070704\" id=\"article-body\">\n<p>\u06af\u0631\u0641\u062a\u0646 \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0627\u0632 \u0635\u0641\u062d\u0627\u062a \u0648\u0628 \u0628\u0647 \u0635\u0648\u0631\u062a \u0628\u0631\u0646\u0627\u0645\u0647 \u0627\u06cc \u0645\u06cc \u062a\u0648\u0627\u0646\u062f \u0628\u0631\u0627\u06cc \u062a\u0648\u0644\u06cc\u062f \u067e\u06cc\u0634 \u0646\u0645\u0627\u06cc\u0634\u060c \u0627\u06cc\u062c\u0627\u062f \u06af\u0632\u0627\u0631\u0634 \u0647\u0627\u06cc \u0645\u0628\u062a\u0646\u06cc \u0628\u0631 \u062a\u0635\u0648\u06cc\u0631 \u0648 \u0645\u0648\u0627\u0631\u062f \u062f\u06cc\u06af\u0631 \u0628\u0633\u06cc\u0627\u0631 \u0645\u0641\u06cc\u062f \u0628\u0627\u0634\u062f. \u062f\u0631 \u0627\u06cc\u0646 \u0631\u0627\u0647\u0646\u0645\u0627\u060c \u0645\u0627 \u06cc\u06a9 \u0645\u0633\u06cc\u0631 API Next.js \u062e\u0648\u0627\u0647\u06cc\u0645 \u0633\u0627\u062e\u062a \u06a9\u0647 \u06cc\u06a9 URL \u0631\u0627 \u0645\u06cc \u06af\u06cc\u0631\u062f \u0648 \u06cc\u06a9 \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a PNG \u0627\u06cc\u062c\u0627\u062f \u0645\u06cc \u06a9\u0646\u062f. \u0631\u0627\u0647\u200c\u0627\u0646\u062f\u0627\u0632\u06cc \u0645\u0627 \u0627\u0632 Puppeteer \u0648 chrome-aws-lambda \u0628\u0631\u0627\u06cc \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0627\u0632 \u0645\u0631\u0648\u0631\u06af\u0631 \u0628\u062f\u0648\u0646 \u0647\u062f Chrome \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0645\u06cc\u200c\u06a9\u0646\u062f \u0648 \u0622\u0646 \u0631\u0627 \u0647\u0645\u0647\u200c\u06a9\u0627\u0631\u0647 \u0648 \u0622\u0645\u0627\u062f\u0647 \u062a\u0648\u0644\u06cc\u062f \u0645\u06cc\u200c\u06a9\u0646\u062f.<\/p>\n<p>\u0645\u0627 \u0628\u0627 \u0631\u0627\u0647\u200c\u0627\u0646\u062f\u0627\u0632\u06cc \u06cc\u06a9 \u067e\u0631\u0648\u0698\u0647 Next.js \u0648 \u0642\u062f\u0645 \u0628\u0647 \u0642\u062f\u0645 \u06a9\u062f\u0647\u0627 \u0631\u0627 \u0634\u0631\u0648\u0639 \u0645\u06cc\u200c\u06a9\u0646\u06cc\u0645 \u062a\u0627 \u0628\u0641\u0647\u0645\u06cc\u0645 \u06a9\u0647 API \u0686\u06af\u0648\u0646\u0647 \u0627\u0633\u06a9\u0631\u06cc\u0646\u200c\u0634\u0627\u062a\u200c\u0647\u0627 \u0631\u0627 \u0645\u06cc\u200c\u06af\u06cc\u0631\u062f.<\/p>\n<p>\u067e\u06cc\u0634 \u0646\u06cc\u0627\u0632\u0647\u0627<\/p>\n<ul>\n<li>\u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u0628\u0631\u0646\u0627\u0645\u0647 Next.js<\/li>\n<li>\u067e\u06cc\u06a9\u0631\u0628\u0646\u062f\u06cc \u0645\u0633\u06cc\u0631 API \u0628\u0627 Puppeteer<\/li>\n<li>\u0627\u06cc\u062c\u0627\u062f \u06a9\u0627\u0645\u067e\u0648\u0646\u0646\u062a React \u0628\u0631\u0627\u06cc \u0631\u0627\u0628\u0637 \u06a9\u067e\u0686\u0631<\/li>\n<li>\u062a\u0648\u0636\u06cc\u062d \u062a\u0646\u0638\u06cc\u0645\u0627\u062a \u0645\u062d\u0644\u06cc \u062f\u0631 \u0645\u0642\u0627\u0628\u0644 \u0627\u0633\u062a\u0642\u0631\u0627\u0631 \u0628\u0631\u0627\u06cc Puppeteer<\/li>\n<\/ul>\n<p><strong>\u0634\u0631\u0648\u0639 \u06a9\u0627\u0631 \u0628\u0627 \u06cc\u06a9 \u067e\u0631\u0648\u0698\u0647 Next.js \u062c\u062f\u06cc\u062f<\/strong><\/p>\n<ol>\n<li>\u06cc\u06a9 \u0628\u0631\u0646\u0627\u0645\u0647 Next.js \u062c\u062f\u06cc\u062f \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f:\n<\/li>\n<\/ol>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>npx create-next-app@latest capture-image-app\ncd capture-image-app\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<ol>\n<li>\u0648\u0627\u0628\u0633\u062a\u06af\u06cc \u0647\u0627\u06cc \u0644\u0627\u0632\u0645 \u0631\u0627 \u0646\u0635\u0628 \u06a9\u0646\u06cc\u062f:\n<\/li>\n<\/ol>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>npm install puppeteer puppeteer-core chrome-aws-lambda busboy\n<\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><strong>\u0645\u0631\u062d\u0644\u0647 2: \u0645\u0633\u06cc\u0631 API \u0631\u0627 \u0628\u0631\u0627\u06cc \u0627\u06cc\u062c\u0627\u062f \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f<\/strong><\/p>\n<p>\u0627\u06a9\u0646\u0648\u0646\u060c \u06cc\u06a9 \u0646\u0642\u0637\u0647 \u067e\u0627\u06cc\u0627\u0646\u06cc API \u0628\u0631\u0627\u06cc \u06af\u0631\u0641\u062a\u0646 \u0648 \u0628\u0631\u06af\u0631\u062f\u0627\u0646\u062f\u0646 \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0647\u0627 \u0628\u0631 \u0627\u0633\u0627\u0633 URL \u0627\u0631\u0627\u0626\u0647 \u0634\u062f\u0647\u060c \u062a\u0646\u0638\u06cc\u0645 \u0645\u06cc \u06a9\u0646\u06cc\u0645.<\/p>\n<p>\u062f\u0631 <code>pages\/api<\/code> \u067e\u0648\u0634\u0647\u060c \u06cc\u06a9 \u0641\u0627\u06cc\u0644 \u062c\u062f\u06cc\u062f \u0628\u0647 \u0646\u0627\u0645 \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f <code>generate-png.ts<\/code> \u0648 \u0627\u06cc\u0646 \u06a9\u062f \u0631\u0627 \u0627\u0636\u0627\u0641\u0647 \u06a9\u0646\u06cc\u062f:<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>import { NextApiRequest, NextApiResponse } from \"next\";\nimport busboy, { Busboy } from \"busboy\"; \/\/ Use busboy for multipart parsing\nimport chromium from \"chrome-aws-lambda\";\nimport puppeteerCore from \"puppeteer-core\"; \/\/ Import puppeteer-core directly\nimport puppeteer from \"puppeteer\"; \/\/ Import puppeteer directly\n\n\/\/ Conditional import for Puppeteer based on the environment\nconst puppeteerModule = process.env.NODE_ENV === \"production\" ? puppeteerCore : puppeteer;\n\nexport const config = {\n  api: {\n    bodyParser: false, \/\/ Disable default body parsing to handle raw binary data (Blob)\n  },\n};\n\nconst delay = (ms: number): Promise<void> =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));\n\nexport default async function handler(\n  req: NextApiRequest,\n  res: NextApiResponse\n): Promise<void> {\n  try {\n    if (req.method === \"POST\") {\n      const bb: Busboy = busboy({ headers: req.headers });\n      let width: number = 1920; \/\/ Default width\n      let height: number = 0; \/\/ Default height\n      let delayTime: number = 6000;\n      const buffers: Buffer[] = [];\n\n      bb.on(\"file\", (_name: string, file: NodeJS.ReadableStream) =&gt; {\n        file.on(\"data\", (data: Buffer) =&gt; buffers.push(data));\n      });\n\n      bb.on(\"field\", (name: string, value: string) =&gt; {\n        if (name === \"width\") width = parseInt(value, 10) || 1920;\n        if (name === \"height\") height = parseInt(value, 10) || 0;\n        if (name === \"delay\") delayTime = parseInt(value, 10) || 6000;\n      });\n\n      bb.on(\"finish\", async () =&gt; {\n        const blobBuffer: Buffer = Buffer.concat(buffers);\n        const htmlContent: string = blobBuffer.toString(\"utf-8\");\n\n        const browser = await puppeteerModule.launch({\n          args: [\"--start-maximized\"],\n          executablePath: process.env.NODE_ENV === \"production\"\n            ? await chromium.executablePath || \"\/usr\/bin\/chromium-browser\"\n            : undefined,  \/\/ No custom executable path needed for local\n          headless: true,\n        });\n\n        const page = await browser.newPage();\n\n        \/\/ Load the HTML content directly\n        await page.setContent(htmlContent, { waitUntil: \"networkidle0\" });\n\n        \/\/@ts-expect-error todo\n        const bodyHeight = await page.evaluate(() =&gt; {\n          return document.body.scrollHeight; \/\/ Get the full scrollable height of the body\n        });\n\n        await page.setViewport({\n          width: Number(width),\n          height: height || bodyHeight, \/\/ Use the provided height or fallback to the full body height\n          deviceScaleFactor: 2,\n        });\n\n        await delay(delayTime);\n\n        const screenshotBuffer = await page.screenshot({\n          fullPage: !height,\n          type: \"png\",\n          omitBackground: false,\n        });\n\n        await browser.close();\n\n        res.setHeader(\"Content-Type\", \"image\/png\");\n        res.setHeader(\n          \"Content-Disposition\",\n          \"attachment; filename=screenshot.png\"\n        );\n        res.status(200).end(screenshotBuffer);\n      });\n\n      req.pipe(bb); \/\/ Pipe the request stream to busboy\n    } else {\n      res.setHeader(\"Allow\", [\"POST\"]);\n      res.status(405).end(`Method ${req.method} Not Allowed`);\n    }\n  } catch (error) {\n    console.error(\"ERROR\", error);\n    res.status(500).end(\"Internal Server Error\");\n  }\n}\n\n\n<\/void><\/void><\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p>*<em>\u062a\u0648\u0636\u06cc\u062d: \u0627\u0646\u062a\u062e\u0627\u0628 \u0639\u0631\u0648\u0633\u06a9 \u06af\u0631\u062f\u0627\u0646 \u0628\u0631\u0627\u06cc \u0645\u062d\u06cc\u0637 \u0647\u0627\u06cc \u0645\u062d\u0644\u06cc \u062f\u0631 \u0645\u0642\u0627\u0628\u0644 \u062a\u0648\u0644\u06cc\u062f<br \/>*<\/em><br \/>\u062f\u0631 \u0627\u06cc\u0646 \u06a9\u062f\u060c \u06cc\u06a9 Import \u067e\u0648\u06cc\u0627 \u0628\u0631\u0627\u06cc Puppeteer \u062a\u0646\u0638\u06cc\u0645 \u06a9\u0631\u062f\u0647 \u0627\u06cc\u0645:<\/p>\n<ul>\n<li>\n<p>\u062a\u0648\u0633\u0639\u0647 \u0645\u062d\u0644\u06cc: \u0627\u06af\u0631 <code>NODE_ENV<\/code> \u0646\u06cc\u0633\u062a <code>production<\/code>\u060c \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u0645\u06cc \u06a9\u0646\u062f <code>puppeteer<\/code>\u060c \u06a9\u0647 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u0622\u0646 \u0633\u0627\u062f\u0647 \u062a\u0631 \u0627\u0633\u062a \u0648 \u0646\u06cc\u0627\u0632\u06cc \u0646\u062f\u0627\u0631\u062f <code>chrome-aws-lambda<\/code>.<\/p>\n<\/li>\n<li>\n<p>\u062a\u0648\u0644\u06cc\u062f: \u0628\u0631\u0627\u06cc \u0627\u0633\u062a\u0642\u0631\u0627\u0631\u0647\u0627\u06cc \u0628\u062f\u0648\u0646 \u0633\u0631\u0648\u0631\u060c \u0645\u062d\u06cc\u0637 \u0634\u0646\u0627\u0633\u0627\u06cc\u06cc \u062e\u0648\u0627\u0647\u062f \u06a9\u0631\u062f <code>NODE_ENV<\/code> \u0628\u0647 \u0639\u0646\u0648\u0627\u0646 <code>production<\/code> \u0648 Puppeteer-core \u0647\u0645\u0631\u0627\u0647 \u0628\u0627 c<code>hrome-aws-lambda<\/code>\u060c \u06a9\u0647 \u0628\u0647 \u0622\u0646 \u0627\u062c\u0627\u0632\u0647 \u0645\u06cc \u062f\u0647\u062f \u062f\u0631 AWS Lambda \u0648 \u0633\u0627\u06cc\u0631 \u0645\u062d\u06cc\u0637 \u0647\u0627\u06cc \u0645\u0634\u0627\u0628\u0647 \u06a9\u0627\u0631 \u06a9\u0646\u062f. \u062f\u0631 \u0627\u06cc\u0646 \u062a\u0646\u0638\u06cc\u0645\u0627\u062a\u060c <code>chrome-aws-lambda<\/code> \u0645\u0633\u06cc\u0631 \u0635\u062d\u06cc\u062d Chromium \u0631\u0627 \u0641\u0631\u0627\u0647\u0645 \u0645\u06cc \u06a9\u0646\u062f \u0648 \u0627\u0632 \u0633\u0627\u0632\u06af\u0627\u0631\u06cc \u0628\u0627 \u0627\u0631\u0627\u0626\u0647 \u062f\u0647\u0646\u062f\u06af\u0627\u0646 \u0628\u062f\u0648\u0646 \u0633\u0631\u0648\u0631 \u0627\u0637\u0645\u06cc\u0646\u0627\u0646 \u0645\u06cc \u062f\u0647\u062f.<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0645\u0631\u062d\u0644\u0647 3: \u06cc\u06a9 \u06a9\u0627\u0645\u067e\u0648\u0646\u0646\u062a Simple React \u0628\u0631\u0627\u06cc \u0631\u0627\u0628\u0637 \u06a9\u0627\u0631\u0628\u0631\u06cc \u0627\u06cc\u062c\u0627\u062f \u06a9\u0646\u06cc\u062f<\/strong><\/p>\n<p>\u062f\u0631 \u0627\u06cc\u0646\u062c\u0627\u060c \u06cc\u06a9 \u0641\u0631\u0645 \u0633\u0627\u062f\u0647 \u0627\u06cc\u062c\u0627\u062f \u0645\u06cc\u200c\u06a9\u0646\u06cc\u0645 \u06a9\u0647 \u0628\u0647 \u06a9\u0627\u0631\u0628\u0631\u0627\u0646 \u0627\u062c\u0627\u0632\u0647 \u0645\u06cc\u200c\u062f\u0647\u062f \u0645\u0642\u0627\u062f\u06cc\u0631 \u0648\u0631\u0648\u062f\u06cc \u0635\u0641\u062d\u0647 \u0648\u0628 \u0631\u0627 \u062b\u0628\u062a \u06a9\u0646\u0646\u062f. \u0627\u06cc\u0646 \u0641\u0631\u0645 \u062a\u0627\u0628\u0639 \u062a\u0648\u0644\u06cc\u062f \u0631\u0627 \u0628\u0631\u0627\u06cc \u06af\u0631\u0641\u062a\u0646 \u0648 \u062f\u0627\u0646\u0644\u0648\u062f \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u062f\u0631 \u0642\u0627\u0644\u0628 PDF \u0641\u0639\u0627\u0644 \u0645\u06cc \u06a9\u0646\u062f.<\/p>\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>import { useState } from \"react\";\n\nexport default function ScreenCaptureComponent() {\n  const [isProcessing, setProcessing] = useState(false);\n  const [width, setWidth] = useState<string>(\"1920\");\n  const [height, setHeight] = useState<string>(\"1000\");\n  const [delay, setDelay] = useState<string>(\"6000\");\n\n  \/\/ Function to clone HTML and prepare for capture\n  function takeScreenshot() {\n    const clonedElement = document.body.cloneNode(true) as HTMLElement;\n    const blob = new Blob([clonedElement.outerHTML], { type: \"text\/html\" });\n    return blob;\n  }\n\n  \/\/ Function to capture screenshot by sending cloned HTML to API\n  async function generateCapture() {\n    setProcessing(true);\n\n    const htmlBlob = takeScreenshot();\n\n    if (!htmlBlob) {\n      setProcessing(false);\n      return;\n    }\n\n    try {\n      const formData = new FormData();\n      formData.append(\"file\", htmlBlob);\n      formData.append(\"width\", width);\n      formData.append(\"height\", height);\n      formData.append(\"delay\", delay);\n      const response = await fetch(\"\/api\/generate-png\", {\n        method: \"POST\",\n        body: formData,\n      });\n\n      if (!response.ok) throw new Error(\"Capture failed\");\n\n      const blob = await response.blob();\n      const downloadUrl = URL.createObjectURL(blob);\n      const link = document.createElement(\"a\");\n      link.href = downloadUrl;\n      link.download = \"capture.png\";\n      link.click();\n      URL.revokeObjectURL(downloadUrl);\n    } catch (error) {\n      console.error(\"Failed to capture screenshot\", error);\n    } finally {\n      setProcessing(false);\n    }\n  }\n\n  return (\n    <div style=\"{{\" maxwidth:=\"\" margin:=\"\" auto=\"\" padding:=\"\" backgroundcolor:=\"\" borderradius:=\"\" width:=\"\" boxshadow:=\"\" rgba=\"\">\n      <div id=\"ez-toc-container\" class=\"ez-toc-v2_0_81 counter-hierarchy ez-toc-counter-rtl ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">\u0641\u0647\u0631\u0633\u062a \u0645\u0637\u0627\u0644\u0628<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/nabfollower.com\/blog\/how-to-capture-web-page-screenshots-with-nextjs-and-puppeteer-o7p\/#Webpage_Screenshot_Capture\" >Webpage Screenshot Capture<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/nabfollower.com\/blog\/how-to-capture-web-page-screenshots-with-nextjs-and-puppeteer-o7p\/#Content_to_Capture\" >Content to Capture<\/a><\/li><\/ul><\/li><\/ul><\/nav><\/div>\n<h2 style=\"{{\" fontsize:=\"\" fontweight:=\"\" textalign:=\"\" marginbottom:=\"\"><span class=\"ez-toc-section\" id=\"Webpage_Screenshot_Capture\"><\/span>\n        Webpage Screenshot Capture\n      <span class=\"ez-toc-section-end\"><\/span><\/h2>\n      <form onsubmit=\"{(e)\"> {\n          e.preventDefault();\n          generateCapture();\n        }}\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          marginBottom: \"16px\",\n        }}\n      &gt;\n        <label style=\"{{\" marginbottom:=\"\" fontweight:=\"\" htmlfor=\"width\">\n          Width (px)\n        <\/label>\n        <select id=\"width\" value=\"{width}\" onchange=\"{(e)\"> setWidth(e.target.value)}\n          style={{\n            width: \"100%\",\n            padding: \"8px\",\n            marginBottom: \"16px\",\n            borderRadius: \"4px\",\n            border: \"1px solid #ccc\",\n            outline: \"none\",\n          }}\n        &gt;\n          <option value=\"1920\">1920 (Full HD)<\/option>\n          <option value=\"1366\">1366 (Laptop)<\/option>\n          <option value=\"1280\">1280 (Desktop)<\/option>\n          <option value=\"1024\">1024 (Tablet Landscape)<\/option>\n          <option value=\"768\">768 (Tablet Portrait)<\/option>\n          <option value=\"375\">375 (Mobile)<\/option>\n        <\/select>\n\n        <label style=\"{{\" marginbottom:=\"\" fontweight:=\"\" htmlfor=\"height\">\n          Height (px)\n        <\/label>\n        <input type=\"number\" id=\"height\" value=\"{height}\" onchange=\"{(e)\"\/> setHeight(e.target.value)}\n          required\n          style={{\n            width: \"100%\",\n            padding: \"8px\",\n            marginBottom: \"16px\",\n            borderRadius: \"4px\",\n            border: \"1px solid #ccc\",\n            outline: \"none\",\n          }}\n        \/&gt;\n\n        <label style=\"{{\" marginbottom:=\"\" fontweight:=\"\" htmlfor=\"delay\">\n          Delay (ms)\n        <\/label>\n        <input type=\"number\" id=\"delay\" value=\"{delay}\" onchange=\"{(e)\"\/> setDelay(e.target.value)}\n          required\n          style={{\n            width: \"100%\",\n            padding: \"8px\",\n            marginBottom: \"16px\",\n            borderRadius: \"4px\",\n            border: \"1px solid #ccc\",\n            outline: \"none\",\n          }}\n        \/&gt;\n\n        <button type=\"submit\" disabled=\"{isProcessing}\" style=\"{{\" padding:=\"\" color:=\"\" borderradius:=\"\" transition:=\"\" backgroundcolor:=\"\" isprocessing=\"\" :=\"\" cursor:=\"\">\n          {isProcessing ? \"Capturing...\" : \"Capture Screenshot\"}\n        <\/button>\n      <\/form>\n\n      {\/* Example HTML Element to Capture *\/}\n      <div id=\"capture-area\" style=\"{{\" display:=\"\">\n        <h3 style=\"{{\" fontsize:=\"\" fontweight:=\"\"><span class=\"ez-toc-section\" id=\"Content_to_Capture\"><\/span>\n          Content to Capture\n        <span class=\"ez-toc-section-end\"><\/span><\/h3>\n        <p>This is an example of the HTML content that will be captured.<\/p>\n      <\/div>\n    <\/div>\n  );\n}\n<\/string><\/string><\/string><\/code><\/pre>\n<div class=\"highlight__panel js-actions-panel\">\n<div class=\"highlight__panel-action js-fullscreen-code-action\">\n    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-on\"><title>\u0648\u0627\u0631\u062f \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z\"\/>\n<\/svg><\/p>\n<p>    <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20px\" height=\"20px\" viewbox=\"0 0 24 24\" class=\"highlight-action crayons-icon highlight-action--fullscreen-off\"><title>\u0627\u0632 \u062d\u0627\u0644\u062a \u062a\u0645\u0627\u0645 \u0635\u0641\u062d\u0647 \u062e\u0627\u0631\u062c \u0634\u0648\u06cc\u062f<\/title>\n    <path d=\"M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z\"\/>\n<\/svg><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><strong>\u0646\u062a\u06cc\u062c\u0647 \u06af\u06cc\u0631\u06cc<\/strong><\/p>\n<p>\u0627\u06cc\u0646 \u0622\u0645\u0648\u0632\u0634 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc \u06cc\u06a9 \u0627\u0628\u0632\u0627\u0631 \u0636\u0628\u0637 \u0635\u0641\u062d\u0647 \u0648\u0628 \u062f\u0631 Next.js\u060c \u0645\u062f\u06cc\u0631\u06cc\u062a \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0647\u0627 \u0631\u0627 \u067e\u0648\u0634\u0634 \u0645\u06cc \u062f\u0647\u062f <code>Puppeteer<\/code>\u060c \u0648 \u0627\u06cc\u062c\u0627\u062f \u06cc\u06a9 \u0645\u0624\u0644\u0641\u0647 frontend \u062a\u0639\u0627\u0645\u0644\u06cc. \u0628\u0647 \u06cc\u0627\u062f \u062f\u0627\u0634\u062a\u0647 \u0628\u0627\u0634\u06cc\u062f \u0627\u0633\u062a\u0641\u0627\u062f\u0647 \u06a9\u0646\u06cc\u062f <code>puppeteer<\/code> \u0628\u0647 \u0635\u0648\u0631\u062a \u0645\u062d\u0644\u06cc \u0648 \u062a\u063a\u06cc\u06cc\u0631 \u0628\u0647 <code>puppeteer-core<\/code> \u062f\u0631 \u062a\u0648\u0644\u06cc\u062f \u0628\u0631\u0627\u06cc \u06a9\u0627\u0647\u0634 \u0627\u0646\u062f\u0627\u0632\u0647 \u0628\u0627\u0646\u062f\u0644 \u0648 \u0628\u0647\u06cc\u0646\u0647 \u0633\u0627\u0632\u06cc \u0628\u0631\u0627\u06cc \u0645\u062d\u06cc\u0637 \u0647\u0627\u06cc \u0628\u062f\u0648\u0646 \u0633\u0631\u0648\u0631. \u06a9\u062f \u0646\u0648\u06cc\u0633\u06cc \u0645\u0628\u0627\u0631\u06a9!<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Summarize this content to 400 words in Persian Lang \u06af\u0631\u0641\u062a\u0646 \u0627\u0633\u06a9\u0631\u06cc\u0646 \u0634\u0627\u062a \u0627\u0632 \u0635\u0641\u062d\u0627\u062a \u0648\u0628 \u0628\u0647 \u0635\u0648\u0631\u062a \u0628\u0631\u0646\u0627\u0645\u0647 \u0627\u06cc \u0645\u06cc \u062a\u0648\u0627\u0646\u062f \u0628\u0631\u0627\u06cc \u062a\u0648\u0644\u06cc\u062f \u067e\u06cc\u0634 \u0646\u0645\u0627\u06cc\u0634\u060c \u0627\u06cc\u062c\u0627\u062f \u06af\u0632\u0627\u0631\u0634 \u0647\u0627\u06cc \u0645\u0628\u062a\u0646\u06cc \u0628\u0631 \u062a\u0635\u0648\u06cc\u0631 \u0648 \u0645\u0648\u0627\u0631\u062f \u062f\u06cc\u06af\u0631 \u0628\u0633\u06cc\u0627\u0631 \u0645\u0641\u06cc\u062f \u0628\u0627\u0634\u062f. \u062f\u0631 \u0627\u06cc\u0646 \u0631\u0627\u0647\u0646\u0645\u0627\u060c \u0645\u0627 \u06cc\u06a9 \u0645\u0633\u06cc\u0631 API Next.js \u062e\u0648\u0627\u0647\u06cc\u0645 \u0633\u0627\u062e\u062a \u06a9\u0647 \u06cc\u06a9 URL \u0631\u0627 \u0645\u06cc \u06af\u06cc\u0631\u062f \u0648 \u06cc\u06a9 &hellip;<\/p>\n","protected":false},"author":2,"featured_media":82229,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"fifu_image_url":"","fifu_image_alt":"","footnotes":""},"categories":[339],"tags":[],"class_list":["post-82228","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dev"],"_links":{"self":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts\/82228","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/comments?post=82228"}],"version-history":[{"count":0,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/posts\/82228\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/media\/82229"}],"wp:attachment":[{"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/media?parent=82228"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/categories?post=82228"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nabfollower.com\/blog\/wp-json\/wp\/v2\/tags?post=82228"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}