Skip to content

Commit

Permalink
feat(fonts): add fonts funtion to list fonts
Browse files Browse the repository at this point in the history
  • Loading branch information
Robin Joseph committed May 16, 2016
1 parent cc8a4a8 commit 14493ef
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 19 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ pids
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
# Coverage directory and file used by tools like istanbul and lcov
coverage*

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
Expand Down
12 changes: 5 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
language: node_js
sudo: false
sudo: required
dist: trusty
addons:
apt:
sources: ['ubuntu-toolchain-r-test']
packages:
- libpoppler-cpp-dev
- libpoppler-dev
- libfontconfig1-dev
- libpng12-dev
- libjpeg-turbo8-dev
- libtiff4-dev
- g++-4.9
- lcov
- libpoppler-cpp-dev
env: CXX=g++-4.9
node_js:
- '0.10'
Expand All @@ -23,3 +20,4 @@ before_install:
script:
- npm test
- npm run lint
- npm run coveralls
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# pdffonts

[![Build Status](https://travis-ci.org/lob/pdffonts.svg?branch=master)](https://travis-ci.org/lob/pdffonts)
[![Coverage Status](https://coveralls.io/repos/github/lob/pdffonts/badge.svg?branch=master)](https://coveralls.io/github/lob/pdffonts?branch=master)

Node bindings for Poppler's `pdffonts` CLI.

## Dependencies
Expand All @@ -12,20 +15,77 @@ To install Poppler on Mac OS X using Homebrew:
brew install poppler
```

To install Poppler on Ubuntu:
To install Poppler on Ubuntu 14.04:

```
apt-get install pkg-config
apt-get install libpoppler-cpp-dev
```

**Note: The version of Poppler on `apt-get` on Ubuntu 12.04 doesn't contain all the functions necessary for this package. If you're using 12.04, you need to build the latest version Poppler from source.**

## Usage

#### `PDFFonts.fonts()`

```
Returns an array of font objects.
@param {String} path - path to the PDF
@returns {Array<Object>} array of font objects
```

Here's an example font object:

```js
{
name: 'LDJWDV+DejaVuSerif-Bold',
type: 'CID TrueType',
encoding: 'Identity-H',
embedded: true,
subset: true,
unicode: true,
object: {
number: 8,
generation: 0
}
}
```

## Testing

```bash
$ npm i
$ npm test
```

## Coverage

The coverage report is generated using [`lcov`](http://ltp.sourceforge.net/coverage/lcov.php), so you need to make sure you have it installed:

To install `lcov` on Mac OS X using Homebrew:

```bash
$ brew install lcov
```

To install `lcov` on Ubuntu 14.04:

```bash
$ apt-get install lcov
```

Once it's installed, you can generate a `coverage.info` file by running:

```bash
$ npm run cover
```

If you want to view it as an HTML file to see which lines haven't been covered, you can run the following to generate a `coverage/index.html`:

```bash
$ npm run cover:html
```

## Linting

```bash
Expand Down
34 changes: 33 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,41 @@
"sources": [
"src/pdffonts.cc"
],
"libraries": [
"<!@(pkg-config --libs poppler)"
],
"cflags": [
"<!@(pkg-config --cflags poppler)"
],
"xcode_settings": {
"OTHER_CFLAGS": [
"<!@(pkg-config --cflags poppler)"
],
"OTHER_LDFLAGS": [
"-liconv"
]
},
"include_dirs": [
"<!(node -e \"require('nan')\")"
]
],
"configurations": {
"Debug": {
"cflags": [
"--coverage"
],
"ldflags": [
"--coverage"
],
"xcode_settings": {
"OTHER_CFLAGS": [
"--coverage"
],
"OTHER_LDFLAGS": [
"--coverage"
]
}
}
}
}
]
}
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
"description": "Node bindings for Poppler's pdffonts CLI",
"main": "./lib/pdffonts.js",
"scripts": {
"build": "node-gyp rebuild",
"cover": "lcov -c -d build/Debug/obj.target/pdffonts/src -o coverage.info && lcov -e coverage.info '*pdffonts.cc' -o coverage.info",
"cover:html": "npm run cover && genhtml coverage.info -o coverage",
"coveralls": "npm run cover && cat coverage.info | coveralls",
"lint": "eslint .",
"release:major": "changelog -M && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version major && git push origin && git push origin --tags",
"release:minor": "changelog -m && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version minor && git push origin && git push origin --tags",
"release:patch": "changelog -p && git add CHANGELOG.md && git commit -m 'updated CHANGELOG.md' && npm version patch && git push origin && git push origin --tags",
"test": "node-gyp rebuild && mocha test --require test/setup.js --recursive --timeout 30000"
"test": "node-gyp --debug rebuild && mocha test --require test/setup.js --recursive --timeout 30000"
},
"repository": {
"type": "git",
Expand All @@ -32,6 +36,7 @@
},
"devDependencies": {
"chai": "^3.5.0",
"coveralls": "^2.11.9",
"eslint": "^1.10.3",
"eslint-config-lob": "^1.0.1",
"generate-changelog": "^1.0.1",
Expand Down
84 changes: 81 additions & 3 deletions src/pdffonts.cc
Original file line number Diff line number Diff line change
@@ -1,13 +1,91 @@
#include <FontInfo.h>
#include <GlobalParams.h>
#include <PDFDoc.h>
#include <PDFDocFactory.h>
#include <goo/GooString.h>
#include <nan.h>

using namespace v8;

NAN_METHOD(Hello) {
info.GetReturnValue().Set(Nan::New("world").ToLocalChecked());
static const char *fontTypeNames[] = {
"unknown",
"Type 1",
"Type 1C",
"Type 1C (OT)",
"Type 3",
"TrueType",
"TrueType (OT)",
"CID Type 0",
"CID Type 0C",
"CID Type 0C (OT)",
"CID TrueType",
"CID TrueType (OT)"
};

NAN_METHOD(Fonts) {
// ensure a file name is passed in
if (info.Length() < 1) {
Nan::ThrowError("file name is required");
return;
}

// convert the file name into a string poppler can understand
String::Utf8Value cmd(info[0]);
std::string s = std::string(*cmd);
GooString *filename = new GooString(s.c_str());

// read config file
globalParams = new GlobalParams();

// open PDF
PDFDoc *doc = PDFDocFactory().createPDFDoc(*filename, NULL, NULL);

// make sure it's a valid PDF
if (!doc->isOk()) {
Nan::ThrowError("file is not a valid PDF");
return;
}

// load fonts
FontInfoScanner scanner(doc, 0);
GooList *fonts = scanner.scan(doc->getNumPages());

// construct Node array of object with font information
Local<v8::Array> fontArray = Nan::New<v8::Array>(fonts->getLength());

for (int i = 0; i < fonts->getLength(); ++i) {
FontInfo *font = (FontInfo *)fonts->get(i);
const Ref fontRef = font->getRef();

Local<v8::Object> fontObj = Nan::New<v8::Object>();
Local<v8::Object> objectObj = Nan::New<v8::Object>();

fontObj->Set(Nan::New("name").ToLocalChecked(), Nan::New(font->getName()->getCString()).ToLocalChecked());
fontObj->Set(Nan::New("type").ToLocalChecked(), Nan::New(fontTypeNames[font->getType()]).ToLocalChecked());
fontObj->Set(Nan::New("encoding").ToLocalChecked(), Nan::New(font->getEncoding()->getCString()).ToLocalChecked());
fontObj->Set(Nan::New("embedded").ToLocalChecked(), Nan::New(font->getEmbedded()));
fontObj->Set(Nan::New("subset").ToLocalChecked(), Nan::New(font->getSubset()));
fontObj->Set(Nan::New("unicode").ToLocalChecked(), Nan::New(font->getToUnicode()));

objectObj->Set(Nan::New("number").ToLocalChecked(), Nan::New(fontRef.num));
objectObj->Set(Nan::New("generation").ToLocalChecked(), Nan::New(fontRef.gen));
fontObj->Set(Nan::New("object").ToLocalChecked(), objectObj);

fontArray->Set(i, fontObj);
delete font;
}

// memory cleanup
delete filename;
delete globalParams;
delete doc;
delete fonts;

info.GetReturnValue().Set(fontArray);
}

NAN_MODULE_INIT(Init) {
Nan::Set(target, Nan::New("hello").ToLocalChecked(), Nan::GetFunction(Nan::New<FunctionTemplate>(Hello)).ToLocalChecked());
Nan::Set(target, Nan::New("fonts").ToLocalChecked(), Nan::GetFunction(Nan::New<FunctionTemplate>(Fonts)).ToLocalChecked());
}

NODE_MODULE(pdffonts, Init)
Binary file added test/assets/embedded-fonts.pdf
Binary file not shown.
Binary file added test/assets/no-fonts.pdf
Binary file not shown.
Binary file added test/assets/non-pdf.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/assets/nonembedded-fonts.pdf
Binary file not shown.
70 changes: 66 additions & 4 deletions test/pdffonts.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,75 @@
'use strict';

var Pdffonts = require('../lib/pdffonts');
var Path = require('path');

var PDFFonts = require('../lib/pdffonts');

var EMBEDDED_FONTS_PATH = Path.resolve(__dirname, 'assets/embedded-fonts.pdf');
var NONEMBEDDED_FONTS_PATH = Path.resolve(__dirname, 'assets/nonembedded-fonts.pdf');
var NO_FONTS_PATH = Path.resolve(__dirname, 'assets/no-fonts.pdf');
var NONEXISTENT_PATH = Path.resolve(__dirname, 'assets/nonexistent.pdf');
var NON_PDF_PATH = Path.resolve(__dirname, 'assets/non-pdf.png');

describe('pdffonts', function () {

describe('hello', function () {
describe('fonts', function () {

it('returns an array of font objects', function () {
var fonts = PDFFonts.fonts(EMBEDDED_FONTS_PATH);
expect(fonts).to.eql([{
name: 'LDJWDV+DejaVuSerif-Bold',
type: 'CID TrueType',
encoding: 'Identity-H',
embedded: true,
subset: true,
unicode: true,
object: {
number: 8,
generation: 0
}
}]);
});

it('detects nonembedded fonts', function () {
var fonts = PDFFonts.fonts(NONEMBEDDED_FONTS_PATH);
fonts.forEach(function (font) {
expect(font.embedded).to.be.false;
});
});

it('returns an empty array if there are no fonts', function () {
var fonts = PDFFonts.fonts(NO_FONTS_PATH);
expect(fonts).to.be.empty;
});

it('throws an error if a file name is not passed in', function (done) {
try {
PDFFonts.fonts();
} catch (err) {
expect(err).to.be.an.instanceof(Error);
expect(err.message).to.eql('file name is required');
done();
}
});

it('throws an error if a file does not exist', function (done) {
try {
PDFFonts.fonts(NONEXISTENT_PATH);
} catch (err) {
expect(err).to.be.an.instanceof(Error);
expect(err.message).to.eql('file is not a valid PDF');
done();
}
});

it('returns "world"', function () {
expect(Pdffonts.hello()).to.eql('world');
it('throws an error if the file is not a PDF', function (done) {
try {
PDFFonts.fonts(NON_PDF_PATH);
} catch (err) {
expect(err).to.be.an.instanceof(Error);
expect(err.message).to.eql('file is not a valid PDF');
done();
}
});

});
Expand Down

0 comments on commit 14493ef

Please sign in to comment.