How to render woff fonts in PhantomJS 2.1+ or headless Chrome?

时间:2017-04-10 02:01:34

标签: linux google-chrome phantomjs webfonts woff

I'm using PhantomJS 2.1.1 on CentOS with the example script rasterize.js just to reproduce the screenshot problem, which is to take a screenshot of a simple web-font demo site:

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

It should render like so (as taken using the SaaS PhantomJSCloud service):

Correct rendering

However, with PhantomJS 2.1.1 locally I get

Failed rendering with incorrect colours


First I increased the script timeout to 10s just to be sure that isn't the issue.

Next I thought the css or fonts were blocked somehow from downloading. When I use tcpflow (it's like wireshark) before running phantomjs scripts I can see that the above web page is downloading the .woff fonts. However, they are not being rendered in the screenshot I'm taking.

When I run the following before the phantomjs script

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

I can see the fonts are being downloaded. Real console output:

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

I then thought that PhantomJS 2.x still doesn't support woff, but 1) it is supposed to be supported (see here), and 2) the SaaS PhantomJSCloud service can render them fine. Is there something more that is needed to be done to render web fonts?


Update: I've confirmed zlib is installed, and compiled PhantomJS 2.1.1 from source, but the results are still the same as above.


Update: Chrome has headless support, and that is the reason why on April 13 the maintainer of PhrantomJS has announced he is stepping down. Eventually we will switch to headless Chrome. Can headless Chrome handle web fonts?

2 个答案:

答案 0 :(得分:0)

经过大量的PhantomJS源代码的实验,调整,逆向工程以及不再维护的事实,我从带有Node.js驱动程序的58版切换到无头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

答案 1 :(得分:0)

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

我已经开始了一个项目(也是其他一些事情)https://github.com/joelgriffith/navalia。我很乐意解决你看不到的任何问题(只是提出一个问题!)