【问题标题】:How to render woff fonts in PhantomJS 2.1+ or headless Chrome?如何在 PhantomJS 2.1+ 或无头 Chrome 中渲染 woff 字体?
【发布时间】:2017-04-10 02:01:33
【问题描述】:

我在 CentOS 上使用 PhantomJS 2.1.1 和示例脚本 rasterize.js 只是为了重现截图问题,即截取一个简单的网络字体演示站点的截图:

http://castellsonclaret.com/public/external/georgiapro/demo.htm

它应该像这样呈现(使用 SaaS PhantomJSCloud 服务):

但是,我在本地使用 PhantomJS 2.1.1


首先,我将脚本超时时间增加到 10 秒,以确保这不是问题所在。

接下来我认为 css 或字体以某种方式被阻止下载。当我在运行phantomjs 脚本之前使用tcpflow(就像wireshark)时,我可以看到上面的网页正在下载.woff 字体。但是,它们并没有在我截取的屏幕截图中呈现。

当我在 phantomjs 脚本之前运行以下命令时

tcpflow -p -c -i eth0 port 80 | grep -oE '(GET|POST|HEAD) .* HTTP/1.[01]'

我可以看到正在下载字体。真正的控制台输出:

GET /public/external/georgiapro/demo.htm HTTP/1.1

GET /t/1.css?apiType=css&projectid=4a82c0c9-a48a-4ef5-97ae-de0d7e62c8d0 HTTP/1.1

GET /public/external/georgiapro/Fonts/a5d15255-f5b4-4cca-808f-211ec0f25ac8.woff HTTP/1.1

GET /public/external/georgiapro/Fonts/3859825b-bdc4-47f3-af3d-a2ef42d58cfb.woff HTTP/1.1

... [snip] ...

GET /public/external/georgiapro/Fonts/ab79a7ac-4aaf-4393-896b-feb6610c9528.woff HTTP/1.1

然后我认为 PhantomJS 2.x 仍然不支持 woff,但是 1)它应该被支持(参见 here),以及 2)SaaS PhantomJSCloud 服务可以很好地渲染它们。渲染网络字体还需要做些什么吗?


更新:我已经确认安装了zlib,并从源代码编译了PhantomJS 2.1.1,但结果还是和上面一样。


更新:Chrome 有 headless support,这就是为什么 PhrantomJS 的维护者在 4 月 13 日宣布他是 stepping down 的原因。最终我们将切换到无头 Chrome。无头 Chrome 可以处理网络字体吗?

【问题讨论】:

    标签: linux google-chrome phantomjs webfonts woff


    【解决方案1】:

    在对 PhantomJS 源代码进行大量试验、调整和逆向工程之后,加上它不再维护的事实,我从 58 版切换到带有 Node.js 驱动程序的无头 Chrome。它可以正确截取使用 WOFF 字体的网站的屏幕截图。

    这是我为感兴趣的人准备的设置。

    安装 Node.js 和 NPM

    yum install epel-release
    yum install nodejs
    node --version # to confirm successful install
    yum install npm
    # OR, for v8
    # curl -sL https://rpm.nodesource.com/setup_8.x | bash -
    

    安装 Node.js 模块

    npm install chrome-remote-interface --no-bin-links --save
    npm install minimist --no-bin-links --save
    

    在 CentOS 上安装 Chrome

    cd /tmp
    wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
    yum -y localinstall google-chrome-*
    google-chrome --version # to confirm successful install
    

    Node.js 截图驱动脚本

    将此脚本另存为screenshot.js。该脚本的来源最初来自here。我已经修改了我的版本以使其更灵活,但为了感谢作者schnerd,我将以其原始形式复制它:

    const CDP = require('chrome-remote-interface');
    const argv = require('minimist')(process.argv.slice(2));
    const file = require('fs');
    
    // CLI Args
    const url = argv.url || 'https://www.google.com';
    const format = argv.format === 'jpeg' ? 'jpeg' : 'png';
    const viewportWidth = argv.viewportWidth || 1440;
    const viewportHeight = argv.viewportHeight || 900;
    const delay = argv.delay || 0;
    const userAgent = argv.userAgent;
    const fullPage = argv.full;
    
    // Start the Chrome Debugging Protocol
    CDP(async function(client) {
      // Extract used DevTools domains.
      const {DOM, Emulation, Network, Page, Runtime} = client;
    
      // Enable events on domains we are interested in.
      await Page.enable();
      await DOM.enable();
      await Network.enable();
    
      // If user agent override was specified, pass to Network domain
      if (userAgent) {
        await Network.setUserAgentOverride({userAgent});
      }
    
      // Set up viewport resolution, etc.
      const deviceMetrics = {
        width: viewportWidth,
        height: viewportHeight,
        deviceScaleFactor: 0,
        mobile: false,
        fitWindow: false,
      };
      await Emulation.setDeviceMetricsOverride(deviceMetrics);
      await Emulation.setVisibleSize({width: viewportWidth, height: viewportHeight});
    
      // Navigate to target page
      await Page.navigate({url});
    
      // Wait for page load event to take screenshot
      Page.loadEventFired(async () => {
        // If the `full` CLI option was passed, we need to measure the height of
        // the rendered page and use Emulation.setVisibleSize
        if (fullPage) {
          const {root: {nodeId: documentNodeId}} = await DOM.getDocument();
          const {nodeId: bodyNodeId} = await DOM.querySelector({
            selector: 'body',
            nodeId: documentNodeId,
          });
          const {model: {height}} = await DOM.getBoxModel({nodeId: bodyNodeId});
    
          await Emulation.setVisibleSize({width: viewportWidth, height: height});
          // This forceViewport call ensures that content outside the viewport is
          // rendered, otherwise it shows up as grey. Possibly a bug?
          await Emulation.forceViewport({x: 0, y: 0, scale: 1});
        }
    
        setTimeout(async function() {
          const screenshot = await Page.captureScreenshot({format});
          const buffer = new Buffer(screenshot.data, 'base64');
          file.writeFile('output.png', buffer, 'base64', function(err) {
            if (err) {
              console.error(err);
            } else {
              console.log('Screenshot saved');
            }
            client.close();
          });
        }, delay);
      });
    }).on('error', err => {
      console.error('Cannot connect to browser:', err);
    });
    

    将 Chrome 作为后台进程运行

    nohup google-chrome --headless --hide-scrollbars --remote-debugging-port=9222 --disable-gpu &
    

    注意:--disable-gpu 目前是必需的,请参阅here

    截图

    node screenshot.js --url="http://castellsonclaret.com/public/external/georgiapro/demo.htm" --outFile="screenshot.png" --format="jpeg" --viewportWidth=1440 --viewportHeight=900 --delay=1000
    

    结果

    WOFF 演示:

    浏览器功能测试:

    【讨论】:

      【解决方案2】:

      您应该尝试使用抽象,这样您就不必处理来自 CDP 的噪音。

      我已经开始了一个项目,就是这样做(以及其他一些事情)https://github.com/joelgriffith/navalia。我非常乐意修复您发现的任何缺失(只是提出一个问题!)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-09
        • 1970-01-01
        • 1970-01-01
        • 2012-05-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-18
        相关资源
        最近更新 更多