【问题标题】:Capture HTML Canvas as gif/jpg/png/pdf?将 HTML Canvas 捕获为 gif/jpg/png/pdf?
【发布时间】:2010-10-29 18:17:01
【问题描述】:

是否可以将 html 画布中显示的内容捕获或打印为图像或 pdf?

我想通过画布生成一个图像,并能够从该图像生成一个 png。

【问题讨论】:

标签: javascript html canvas export png


【解决方案1】:

原始答案是针对类似问题的。这已被修改:

var canvas = document.getElementById("mycanvas");
var img    = canvas.toDataURL("image/png");

使用 IMG 中的值,您可以将其写为新图像,如下所示:

document.getElementById("existing-image-id").src = img;

document.write('<img src="'+img+'"/>');

【讨论】:

  • 还有一个问题,如何将我在这个标签中得到的图像保存到服务器。有什么猜测吗??
  • 但是如果我使用 var img = canvas.toDataURL("image/jpeg");我的背景是完全黑色的。如何解决这个问题
  • 哦,来吧。我在 2009 年回答了这个问题。你期待什么?
  • @donohoe 实际上你是在 2010 年 8 月回答的 :)
  • 执行var img = new Image(); img.src = canvas.toDataURL(); document.body.appendChild(img); 之类的操作会使用更少的内存。 document.write 代码正在制作数据 URL,他们制作一个 HTML 字符串,然后将该字符串的副本放入 DOM,然后浏览器必须解析该 HTML 字符串,将另一个副本放在图像元素上,然后再次解析将数据URL转化为图片数据,最后就可以显示图片了。对于需要大量内存/复制/解析的屏幕大小的图像。只是一个建议
【解决方案2】:

upload image 来自&lt;canvas /&gt;

async function canvasToBlob(canvas) {
  if (canvas.toBlob) {
    return new Promise(function (resolve) {
      canvas.toBlob(resolve)
    })
  } else {
    throw new Error('canvas.toBlob Invalid')
  }
}

await canvasToBlob(yourCanvasEl)

【讨论】:

    【解决方案3】:

    重点是

    canvas.toDataURL(type, quality)


    我想为像我这样想要将 SVG 保存为 PNG 的人提供一个示例(如果您愿意,也可以添加一些文本),它可能来自在线资源或字体真棒图标等。

    示例

    100% javascript,没有其​​他 3-rd 库。

    <script>
      (() => {
        window.onload = () => {
          // Test 1: SVG from Online
          const canvas = new Canvas(650, 500)
          // canvas.DrawGrid() // If you want to show grid, you can use it.
          const svg2img = new SVG2IMG(canvas.canvas, "https://upload.wikimedia.org/wikipedia/commons/b/bd/Test.svg")
          svg2img.AddText("Hello", 100, 250, {mode: "fill", color: "yellow", alpha: 0.8})
          svg2img.AddText("world", 200, 250, {mode: "stroke", color: "red"})
          svg2img.AddText("!", 280, 250, {color: "#f700ff", size: "72px"})
          svg2img.Build("Test.png")
    
          // Test 2: URI.data
          const canvas2 = new Canvas(180, 180)
          const uriData = "data:image/svg+xml;base64,PHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc21pbGUtd2luayBmYS13LTE2IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZm9jdXNhYmxlPSJmYWxzZSIgZGF0YS1wcmVmaXg9ImZhciIgZGF0YS1pY29uPSJzbWlsZS13aW5rIiByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDQ5NiA1MTIiIGRhdGEtZmEtaTJzdmc9IiI+PHBhdGggZmlsbD0iY3VycmVudENvbG9yIiBkPSJNMjQ4IDhDMTExIDggMCAxMTkgMCAyNTZzMTExIDI0OCAyNDggMjQ4IDI0OC0xMTEgMjQ4LTI0OFMzODUgOCAyNDggOHptMCA0NDhjLTExMC4zIDAtMjAwLTg5LjctMjAwLTIwMFMxMzcuNyA1NiAyNDggNTZzMjAwIDg5LjcgMjAwIDIwMC04OS43IDIwMC0yMDAgMjAwem0xMTcuOC0xNDYuNGMtMTAuMi04LjUtMjUuMy03LjEtMzMuOCAzLjEtMjAuOCAyNS01MS41IDM5LjQtODQgMzkuNHMtNjMuMi0xNC4zLTg0LTM5LjRjLTguNS0xMC4yLTIzLjctMTEuNS0zMy44LTMuMS0xMC4yIDguNS0xMS41IDIzLjYtMy4xIDMzLjggMzAgMzYgNzQuMSA1Ni42IDEyMC45IDU2LjZzOTAuOS0yMC42IDEyMC45LTU2LjZjOC41LTEwLjIgNy4xLTI1LjMtMy4xLTMzLjh6TTE2OCAyNDBjMTcuNyAwIDMyLTE0LjMgMzItMzJzLTE0LjMtMzItMzItMzItMzIgMTQuMy0zMiAzMiAxNC4zIDMyIDMyIDMyem0xNjAtNjBjLTI1LjcgMC01NS45IDE2LjktNTkuOSA0Mi4xLTEuNyAxMS4yIDExLjUgMTguMiAxOS44IDEwLjhsOS41LTguNWMxNC44LTEzLjIgNDYuMi0xMy4yIDYxIDBsOS41IDguNWM4LjUgNy40IDIxLjYuMyAxOS44LTEwLjgtMy44LTI1LjItMzQtNDIuMS01OS43LTQyLjF6Ij48L3BhdGg+PC9zdmc+"
          const svg2img2 = new SVG2IMG(canvas2.canvas, uriData)
          svg2img2.Build("SmileWink.png")
    
          // Test 3: Exists SVG
          ImportFontAwesome()
          const range = document.createRange()
          const fragSmile = range.createContextualFragment(`<i class="far fa-smile" style="background-color:black;color:yellow"></i>`)
          document.querySelector(`body`).append(fragSmile)
    
          // use MutationObserver wait the fontawesome convert ``<i class="far fa-smile"></i>`` to SVG. If you write the element in the HTML, then you can skip this hassle way.
          const observer = new MutationObserver((mutationRecordList, observer) => {
            for (const mutation of mutationRecordList) {
              switch (mutation.type) {
                case "childList":
                  const targetSVG = mutation.target.querySelector(`svg`)
                  if (targetSVG !== null) {
                    const canvas3 = new Canvas(64, 64) // ? Focus here. The part of the observer is not important.
                    const svg2img3 = new SVG2IMG(canvas3.canvas, SVG2IMG.Convert2URIData(targetSVG))
                    svg2img3.Build("Smile.png")
                    targetSVG.remove() // This SVG is created by font-awesome, and it's an extra element. I don't want to see it.
                    observer.disconnect()
                    return
                  }
              }
            }
          })
          observer.observe(document.querySelector(`body`), {childList: true})
        }
      })()
    
      class SVG2IMG {
        /**
         * @param {HTMLCanvasElement} canvas
         * @param {string} src "http://.../xxx.svg"  or "data:image/svg+xml;base64,${base64}"
         * */
        constructor(canvas, src) {
          this.canvas = canvas;
          this.context = this.canvas.getContext("2d")
          this.src = src
          this.addTextList = []
        }
    
        /**
         * @param {HTMLElement} node
         * @param {string} mediaType: https://en.wikipedia.org/wiki/Media_type#Common_examples_%5B10%5D
         * @see https://en.wikipedia.org/wiki/List_of_URI_schemes
         * */
        static Convert2URIData(node, mediaType = 'data:image/svg+xml') {
          const base64 = btoa(node.outerHTML)
          return `${mediaType};base64,${base64}`
        }
    
        /**
         * @param {string} text
         * @param {int} x
         * @param {int} y
         * @param {"stroke"|"fill"} mode
         * @param {string} size, "30px"
         * @param {string} font, example: "Arial"
         * @param {string} color, example: "#3ae016" or "yellow"
         * @param {int} alpha, 0.0 (fully transparent) to 1.0 (fully opaque) // https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors#transparency
         * */
        AddText(text, x, y, {mode = "fill", size = "32px", font = "Arial", color = "black", alpha = 1.0}) {
          const drawFunc = (text, x, y, mode, font) => {
            return () => {
              // https://www.w3schools.com/graphics/canvas_text.asp
              // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillText
              const context = this.context
              const originAlpha = context.globalAlpha
              context.globalAlpha = alpha
              context.font = `${size} ${font}`
    
              switch (mode) {
                case "fill":
                  context.fillStyle = color
                  context.fillText(text, x, y)
                  break
                case "stroke":
                  context.strokeStyle = color
                  context.strokeText(text, x, y)
                  break
                default:
                  throw Error(`Unknown mode:${mode}`)
              }
              context.globalAlpha = originAlpha
            }
          }
          this.addTextList.push(drawFunc(text, x, y, mode, font))
        }
    
        /**
         * @description When the build is finished, you can click the filename to download the PNG or mouse enters to copy PNG to the clipboard.
         * */
        Build(filename = "download.png") {
          const img = new Image()
          img.src = this.src
          img.crossOrigin = "anonymous" // Fixes: Tainted canvases may not be exported
    
          img.onload = (event) => {
            this.context.drawImage(event.target, 0, 0)
            for (const drawTextFunc of this.addTextList) {
              drawTextFunc()
            }
    
            // create a "a" node for download
            const a = document.createElement('a')
            document.querySelector('body').append(a)
            a.innerText = filename
            a.download = filename
    
            const quality = 1.0
            // a.target = "_blank"
            a.href = this.canvas.toDataURL("image/png", quality)
            a.append(this.canvas)
          }
    
          this.canvas.onmouseenter = (event) => {
            // set background to white. Otherwise, background-color is black.
            this.context.globalCompositeOperation = "destination-over" // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation // https://www.w3schools.com/tags/canvas_globalcompositeoperation.asp
            this.context.fillStyle = "rgb(255,255,255)"
            this.context.fillRect(0, 0, this.canvas.width, this.canvas.height)
            this.canvas.toBlob(blob => navigator.clipboard.write([new ClipboardItem({'image/png': blob})])) // copy to clipboard
          }
        }
      }
    
      class Canvas {
    
        /**
         * @description for do something like that: ``<canvas width="" height=""></>canvas>``
         **/
        constructor(w, h) {
          const canvas = document.createElement("canvas")
          document.querySelector(`body`).append(canvas)
          this.canvas = canvas;
          [this.canvas.width, this.canvas.height] = [w, h]
        }
    
        /**
         * @description If your SVG is large, you may want to know which part is what you wanted.
         * */
        DrawGrid(step = 100) {
          const ctx = this.canvas.getContext('2d')
          const w = this.canvas.width
          const h = this.canvas.height
    
          // Draw the vertical line.
          ctx.beginPath();
          for (let x = 0; x <= w; x += step) {
            ctx.moveTo(x, 0);
            ctx.lineTo(x, h);
          }
          // set the color of the line
          ctx.strokeStyle = 'rgba(255,0,0, 0.5)'
          ctx.lineWidth = 1
          ctx.stroke();
    
          // Draw the horizontal line.
          ctx.beginPath();
          for (let y = 0; y <= h; y += step) {
            ctx.moveTo(0, y)
            ctx.lineTo(w, y)
          }
          ctx.strokeStyle = 'rgba(128, 128, 128, 0.5)'
          ctx.lineWidth = 5
          ctx.stroke()
        }
      }
    
      function ImportFontAwesome() {
        const range = document.createRange()
        const frag = range.createContextualFragment(`
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" integrity="sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w==" crossorigin="anonymous" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/js/all.min.js" integrity="sha512-UwcC/iaz5ziHX7V6LjSKaXgCuRRqbTp1QHpbOJ4l1nw2/boCfZ2KlFIqBUA/uRVF0onbREnY9do8rM/uT/ilqw==" crossorigin="anonymous"/>
    `)
        document.querySelector("head").append(frag)
      }
    </script>

    如果你想在*上运行并在图片上移动鼠标可能会出错

    DOMException:剪贴板 API 已被阻止,因为当前文档应用了权限策略

    你可以把代码复制到本地机器上再运行一次就可以了。

    【讨论】:

      【解决方案4】:

      如果你想嵌入画布,你可以使用这个 sn-p

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Document</title>
      </head>
      <body>
          <canvas id=canvas width=200 height=200></canvas>
          <iframe id='img' width=200 height=200></iframe>
          <script>
              window.onload = function() {
                  var canvas = document.getElementById("canvas");
                  var context = canvas.getContext("2d");
                  context.fillStyle = "green";
                  context.fillRect(50, 50, 100, 100);
                  document.getElementById('img').src = canvas.toDataURL("image/jpeg");
                  console.log(canvas.toDataURL("image/jpeg"));
              }
          </script>
      </body>
      </html>
      

      【讨论】:

        【解决方案5】:

        这是另一种方式,没有字符串,虽然我真的不知道它是否更快。而不是 toDataURL (正如这里提出的所有问题)。在我的情况下,我想阻止 dataUrl/base64,因为我需要一个数组缓冲区或视图。所以 HTMLCanvasElement 中的另一个方法是toBlob。 (TypeScript 函数):

            export function canvasToArrayBuffer(canvas: HTMLCanvasElement, mime: string): Promise<ArrayBuffer> {
          return new Promise((resolve, reject) => canvas.toBlob(async (d) => {
            if (d) {
              const r = new FileReader();
              r.addEventListener('loadend', e => {
                const ab = r.result;
                if (ab) {
                  resolve(ab as ArrayBuffer);
                }
                else {
                   reject(new Error('Expected FileReader result'));
                }
              }); r.addEventListener('error', e => {
                reject(e)
              });
              r.readAsArrayBuffer(d);
            }
            else {
              reject(new Error('Expected toBlob() to be defined'));
            }
          }, mime));
        }
        

        blob 的另一个优点是您可以创建 ObjectUrls 来将数据表示为文件,类似于 HTMLInputFile 的“文件”成员。更多信息:

        https://developer.mozilla.org/en/docs/Web/API/HTMLCanvasElement/toBlob

        【讨论】:

        • 这个答案必须被标记为正确的并且需要被投票。谢谢!
        【解决方案6】:

        简单的答案就是获取它的 blob 并将 img src 设置为该 blob 的新对象 URL,然后使用某些库将该图像添加到 PDF 中,例如

        var ok = document.createElement("canvas")
        ok.width = 400
        ok.height = 140
        var ctx = ok.getContext("2d");
        for(let k = 0; k < ok.height; k++) 
          (
            k 
            % 
            Math.floor(
              (
                Math.random()
              ) *
              10
            )
            == 
            0
          ) && (y => {
            for(var i = 0; i < ok.width; i++) {
              if(i % 25 == 0) {
                ctx.globalAlpha = Math.random()
                ctx.fillStyle = (
                  "rgb(" + 
                  Math.random() * 255 + "," +
                  Math.random() * 255 + "," +
                  Math.random() * 255 + ")"
                );
        
                (wdth =>
                  ctx.fillRect(
                    Math.sin(
                      i * Math.PI / 180
                    ) * 
                      Math.random() *
                      ok.width,
                    Math.cos(
                      i * Math.PI / 180,
                    ) * wdth + y,
                    wdth,
                    wdth
                  )
                )(15)
              }
            }
          })(k)
        
        ok.toBlob(blob => {
          k.src = URL.createObjectURL(blob)
        })
        &lt;img id=k&gt;

        或者,如果您想使用低级字节数据,您可以获取画布的原始字节,然后根据文件规范,将原始图像数据写入数据的必要字节。你只需要调用ctx.getImageData(0, 0, ctx.canvas.widht, ctx.canvas.height)获取原始图像数据,然后根据文件规范,将其写入该文件

        【讨论】:

          【解决方案7】:

          您可以使用 jspdf 将画布捕获为图像或 pdf,如下所示:

          var imgData = canvas.toDataURL('image/png');              
          var doc = new jsPDF('p', 'mm');
          doc.addImage(imgData, 'PNG', 10, 10);
          doc.save('sample-file.pdf');
          

          更多信息:https://github.com/MrRio/jsPDF

          【讨论】:

            【解决方案8】:

            我会使用“wkhtmltopdf”。它工作得很好。它使用webkit引擎(用于Chrome、Safari等),非常好用:

            wkhtmltopdf *.com/questions/923885/ this_question.pdf
            

            就是这样!

            Try it

            【讨论】:

            • 我也在 wkhtmltopdf 阵营中。我们一直在使用它进行归档,它非常棒。
            • 如何在需要登录信息的页面中使用 WKHTMLtoPDF ?我正在使用 Jsreport 转换为 PDF,但我使用 HTML2Canvas 捕获我的内容,我在将 Canvas 作为参数发送到 JSreport 时遇到问题
            • @khaledDehia 检查:*.com/questions/10287386/…
            【解决方案9】:

            HTML5 提供 Canvas.toDataURL(mimetype),它在 Opera、Firefox 和 Safari 4 beta 中实现。但是,存在许多安全限制(主要是与将内容从另一个来源绘制到画布上有关)。

            因此您不需要额外的库。

            例如

             <canvas id=canvas width=200 height=200></canvas>
             <script>
                  window.onload = function() {
                      var canvas = document.getElementById("canvas");
                      var context = canvas.getContext("2d");
                      context.fillStyle = "green";
                      context.fillRect(50, 50, 100, 100);
                      // no argument defaults to image/png; image/jpeg, etc also work on some
                      // implementations -- image/png is the only one that must be supported per spec.
                      window.location = canvas.toDataURL("image/png");
                  }
             </script>
            

            理论上,这应该会创建并导航到中间有一个绿色方块的图像,但我还没有测试过。

            【讨论】:

            • 图像的保存位置。在我当地的位置?
            • 图像将在您的浏览器中显示为图像。然后,您可以将其保存到磁盘或其他任何东西。这是一个快速而肮脏的通用“Canvas2PNG”书签,它将页面中的第一个画布转换为 PNG,并在新窗口的浏览器中显示:javascript:void(window.open().location = document.getElementsByTagName("canvas")[0].toDataURL("image/png"))
            • 如果图片大小只有几 MB,请准备让您的浏览器崩溃(我在 FireFox 中做过一段时间)。
            • 如何修改它来渲染多个图像?
            • 数据 URI 具有最大长度,因此您可以放入数据 url 的图像大小有上限。
            【解决方案10】:

            如果您通过服务器进行下载,这里有一些帮助(这样您可以命名/转换/后处理/等您的文件):

            -使用toDataURL发布数据

            -设置标题

            $filename = "test.jpg"; //or png
            header('Content-Description: File Transfer');
            if($msie = !strstr($_SERVER["HTTP_USER_AGENT"],"MSIE")==false)      
              header("Content-type: application/force-download");else       
              header("Content-type: application/octet-stream"); 
            header("Content-Disposition: attachment; filename=\"$filename\"");   
            header("Content-Transfer-Encoding: binary"); 
            header("Expires: 0"); header("Cache-Control: must-revalidate"); 
            header("Pragma: public");
            

            -创建图像

            $data = $_POST['data'];
            $img = imagecreatefromstring(base64_decode(substr($data,strpos($data,',')+1)));
            

            -导出图片as JPEG

            $width = imagesx($img);
            $height = imagesy($img);
            $output = imagecreatetruecolor($width, $height);
            $white = imagecolorallocate($output,  255, 255, 255);
            imagefilledrectangle($output, 0, 0, $width, $height, $white);
            imagecopy($output, $img, 0, 0, 0, 0, $width, $height);
            imagejpeg($output);
            exit();
            

            -或as transparent PNG

            imagesavealpha($img, true);
            imagepng($img);
            die($img);
            

            【讨论】:

              【解决方案11】:

              如果您使用的是 jQuery(很多人都这样做),那么您可以像这样实现公认的答案:

              var canvas = $("#mycanvas")[0];
              var img = canvas.toDataURL("image/png");
              
              $("#elememt-to-write-to").html('<img src="'+img+'"/>');
              

              【讨论】:

              • 请注意这里 only 使用 jQuery 是选择画布。 .toDataURL 是原生 JS。
              • 我有保存问题,有人可以帮我看看这个链接:*.com/questions/25131763/…
              • 纯 (100%) jQuery 解决方案如下:$('&lt;img&gt;').attr('src',$('#mycanvas')[0].toDataURL('image/png')).appendTo($('#element-to-write-to').empty()); 正好一行。
              【解决方案12】:

              在某些版本的 Chrome 上,您可以:

              1. 使用绘图功能ctx.drawImage(image1, 0, 0, w, h);
              2. 在画布上单击鼠标右键

              【讨论】:

                【解决方案13】:
                function exportCanvasAsPNG(id, fileName) {
                
                    var canvasElement = document.getElementById(id);
                
                    var MIME_TYPE = "image/png";
                
                    var imgURL = canvasElement.toDataURL(MIME_TYPE);
                
                    var dlLink = document.createElement('a');
                    dlLink.download = fileName;
                    dlLink.href = imgURL;
                    dlLink.dataset.downloadurl = [MIME_TYPE, dlLink.download, dlLink.href].join(':');
                
                    document.body.appendChild(dlLink);
                    dlLink.click();
                    document.body.removeChild(dlLink);
                }
                

                【讨论】:

                • 保存的文件保持为 .svg 格式。如何保存为png?
                • 这是一个不错的解决方案,但它可能不适用于 IE。收到错误“传递给系统调用的数据区域太小”
                • 在 Chrome 中它显示“网络错误”,而在 Firefox 中它运行良好。 (在 Linux 上)
                【解决方案14】:

                我想我会稍微扩展一下这个问题的范围,并提供一些有用的花絮。

                为了将画布作为图像获取,您应该执行以下操作:

                var canvas = document.getElementById("mycanvas");
                var image = canvas.toDataURL("image/png");
                

                您可以使用它来将图像写入页面:

                document.write('<img src="'+image+'"/>');
                

                其中“image/png”是一种 mime 类型(png 是唯一必须支持的类型)。如果您想要一个受支持类型的数组,您可以按照以下方式执行操作:

                var imageMimes = ['image/png', 'image/bmp', 'image/gif', 'image/jpeg', 'image/tiff']; //Extend as necessary 
                var acceptedMimes = new Array();
                for(i = 0; i < imageMimes.length; i++) {
                    if(canvas.toDataURL(imageMimes[i]).search(imageMimes[i])>=0) {
                        acceptedMimes[acceptedMimes.length] = imageMimes[i];
                    }
                }
                

                您只需要在每个页面运行一次 - 它永远不会在页面的生命周期中改变。

                如果您希望用户下载保存的文件,您可以执行以下操作:

                var canvas = document.getElementById("mycanvas");
                var image = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); //Convert image to 'octet-stream' (Just a download, really)
                window.location.href = image;
                

                如果您将它与不同的 mime 类型一起使用,请务必更改 image/png 的两个实例,但不要更改 image/octet-stream。 另外值得一提的是,如果你在渲染画布时使用了任何跨域资源,在尝试使用 toDataUrl 方法时会遇到安全错误。

                【讨论】:

                【解决方案15】:

                另一个有趣的解决方案是PhantomJS。 它是一个可使用 JavaScript 或 CoffeeScript 编写脚本的无头 WebKit。

                其中一个用例是屏幕捕获:您可以通过编程方式捕获 Web 内容,包括 SVG 和 Canvas 和/或创建带有缩略图预览的网站屏幕截图。

                最好的入口点是screen capture wiki 页面。

                这是极坐标时钟的一个很好的例子(来自 RaphaelJS):

                >phantomjs rasterize.js http://raphaeljs.com/polar-clock.html clock.png
                

                您想将页面呈现为 PDF 吗?

                > phantomjs rasterize.js 'http://en.wikipedia.org/w/index.php?title=Jakarta&printable=yes' jakarta.pdf
                

                【讨论】:

                • +1:PhantomJS 是一个简单、有据可查且经过深思熟虑的系统,非常适合这项工作。它允许的不仅仅是抓取画布 - 例如,您可以在抓取之前修改页面或其中的一部分(通过 JS),使其看起来像您想要的那样。完美!
                • PhantomJs 现已过时
                • PhantomJS 不是“过时的”——它没有维护......它仍然支持大多数 ES6 的东西,并且仍然是唯一一个在 x86 上工作且不需要编译的现代/体面的无头浏览器。因此,它仍然是唯一适用于各种嵌入式或轻量级发行版/系统的 [正确] 无头浏览器。
                最近更新 更多