linux - 如何在 PhantomJS 2.1+ 或 headless Chrome 中呈现 woff 字体?

标签 linux google-chrome phantomjs webfonts woff

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

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

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

Correct rendering

但是,我在本地使用 PhantomJS 2.1.1

Failed rendering with incorrect colours


首先,我将脚本超时时间增加到 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 ,这就是为什么 4 月 13 日 PhrantomJS 的维护者宣布他是 stepping down 的原因。 .最终我们将切换到 headless Chrome。 headless Chrome 可以处理网络字体吗?

最佳答案

在对 PhantomJS 源代码进行大量试验、调整和逆向工程之后,再加上它不再维护,我从 58 版的 Node.js 驱动程序切换到 headless 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 演示:

Screenshot

浏览器功能测试:

Browser cap

关于linux - 如何在 PhantomJS 2.1+ 或 headless Chrome 中呈现 woff 字体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43313988/

相关文章:

linux - 我应该把我编译的.ko文件放在哪个文件夹里?

java - 需要在 Fedora 13/Linux 上安装和配置 Java JDK 和 Tomcat 的简单方法

r - .export 许多对象到 R foreach

javascript - 在输入字段中选择文件后浏览器卡住

python - Raspberry Pi 电子邮件通知程序出现意外结果

linux - WebDriver异常: no chrome binary at/usr/bin/google-chrome-stable or chrome binary not found

javascript - HTML5-WebRTC如何记录

node.js - 为在 phantomjs 中的 browserify 包上运行(通过 grunt)的 Jasmine 测试生成 Istanbul 尔代码覆盖率报告

java - 无法使用 BrowserMob-Proxy(PhantomJS) 捕获 HAR 请求和响应

javascript - 在 PhantomJS/CasperJS 中设置屏幕大小