I'm trying to download a .ttf file like this one using Node.
I'm using mikeal/request to handle HTTP requests. The actual function looks something like this:
request.get(url, function(err, res, body) {
if(err) throw err;
fs.writeFileSync(path, body);
});
After a quick glance, it behaved exactly as expected; everything ended up in the right place and it looked more or less right.
However, when I tried to load the font into a browser using the following @font-face
snippet, the rule was applied, but the font wasn't. No errors, nothing failing to load or being overwritten.
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans'), local('OpenSans'), url(cJZKeOuBrn4kERxqtaUH3aCWcynf_cDxXwCLxiixG1c.ttf) format('truetype');
}
For a preliminary check I tried to open it with KFontView, but was met with a "Could not read font" error.
I verified this error by wgeting a fresh copy of the file and doing the same. It loaded into the preview fine.
This jogged my memory regarding an optional argument that could be passed to writeFile
, so I checked out the encodings of the wget version and the node version.
$ enca wget.ttf -L none
> Unrecognized Encoding
$ enca node.ttf -L none
> Universal transformation format 8 bits; UTF-8
> Mixed line terminators
> Surrounded by/intermixed with non-text data
So, fairly obvious. wget is saving it as a binary whereas node was writing it to file as UTF-8. Quicky confirmed with a file
$ file -i wget.ttf
> wget.ttf: application/x-font-ttf; charset=binary
Quick scan of the Node docs, turns out I can do this
fs.writeFileSync(path, body, 'binary');
Everything looked good, the node version was now showing up as the correct encoding. However, still having the exact same problem in the browser.
Made sure that my version of Chrome supported .ttf font-faces (Version 37.0.2062.94 (64-bit). It does.)
As far as I could see, the file were now the same. Diff was particularly unhelpful.
$ diff wget.ttf node.ttf
> Binary files wget.ttf and node.ttf differ.
There might be a more sensible way to use diff with binary files. Afraid, I don't know enough about it. I decided to go for a primitive manual diff.
I fired up vim, got both files on screen and knocked them into hex mode to have a look. Apple, Microsoft and Adobe all seem to have different specifications for TTF files and I'm not sure which spec this sticks to, but I would guess that first row of bytes is part of a generic header.
After the top row of bytes, the rest of the file is different.
What on earth is happening here? How does Node end up with a different file to wget?
I'm hoping I've missed something obvious. Otherwise, any suggestions would be welcome.
You need to specify that you want binary encoding by setting encoding
to null
.
request.get({
url: url,
encoding: null,
}, function (err, res, body) {
Otherwise, it will default to UTF-8 encoding. Documentation: https://github.com/mikeal/request
Really though, you should be letting it stream to a file for you, for efficiency.
request('http://example.com/font.ttf').pipe(fs.createWriteStream('font.ttf'));