[
  {
    "path": ".gitignore",
    "content": ".DS_Store\nbuild/\nnode_modules/"
  },
  {
    "path": ".npmignore",
    "content": ".DS_Store\nbuild/\nnode_modules/\n.travis.yml\nappveyor.yml\ntravis-linux.sh\ntravis-osx.sh\n"
  },
  {
    "path": ".travis.yml",
    "content": "# setup build matrix\nenv:\n  - NODE_VERSION=6\n  - NODE_VERSION=8\n  - NODE_VERSION=10\n  \naddons:\n  apt:\n    sources:\n    - ubuntu-toolchain-r-test\n    packages:\n    - g++-4.8\n\nos:\n  - linux\n  - osx\n  \nbefore_install:\n  # install nvm if it isn't already installed (the mac VMs don't have it)\n  - if ! command -v nvm &> /dev/null; then curl -s https://raw.githubusercontent.com/creationix/nvm/v0.13.1/install.sh | bash; fi\n  - . ~/.nvm/nvm.sh\n  \n  # install node\n  - nvm install $NODE_VERSION\n  - npm config set spin false\n  - node --version\n  - npm --version\n  \ninstall:\n  # on linux, install some dependencies and fonts used for tests\n  - if [ \"${TRAVIS_OS_NAME}\" = \"linux\" ]; then ./travis-linux.sh; fi\n  \n  # Require c++11 support\n  - if [ \"${TRAVIS_OS_NAME}\" = \"linux\" ]; then export CXX=g++-4.8; fi\n  \n  # now, actually install the module\n  - npm install\n  \nscript:\n  - npm test\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2014-present Devon Govett\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "[![Build Status](https://travis-ci.org/devongovett/font-manager.svg)](https://travis-ci.org/devongovett/font-manager)\n\n# font-manager\n\nA C++ module for Node.js providing access to the system font catalog.\n\n## Features\n\n* List all available fonts\n* Find fonts with specified characteristics\n* Font substitution when characters are missing\n\n## Platforms\n\n* Mac OS X 10.5 and later supported via [CoreText](https://developer.apple.com/library/mac/documentation/Carbon/reference/CoreText_Framework_Ref/_index.html)\n* Windows 7 and later supported via [DirectWrite](http://msdn.microsoft.com/en-us/library/windows/desktop/dd368038(v=vs.85).aspx)\n* Linux supported via [fontconfig](http://www.freedesktop.org/software/fontconfig)\n\n## Installation\n\nInstallation of the `font-manager` module is via npm:\n\n    npm install font-manager\n\nOn Linux, you also may need to install the `libfontconfig-dev` package, for example:\n\n    sudo apt-get install libfontconfig-dev\n\n## API\n\nYou load the `font-manager` module using `require` as with all Node modules:\n\n```javascript\nvar fontManager = require('font-manager');\n```\n\nAll of the methods exported by `font-manager` have both synchronous and asynchronous versions available.\nYou should generally prefer the asynchronous version as it will allow your program to continue doing other\nprocessing while a request for fonts is processing in the background, which may be expensive depending on\nthe platform APIs that are available.\n\n* [`getAvailableFonts()`](#getavailablefonts)\n* [`findFonts(fontDescriptor)`](#findfontsfontdescriptor)\n* [`findFont(fontDescriptor)`](#findfontfontdescriptor)\n* [`substituteFont(postscriptName, text)`](#substitutefontpostscriptname-text)\n\n### getAvailableFonts()\n\nReturns an array of all [font descriptors](#font-descriptor) available on the system.\n\n```javascript\n// asynchronous API\nfontManager.getAvailableFonts(function(fonts) { ... });\n\n// synchronous API\nvar fonts = fontManager.getAvailableFontsSync();\n\n// output\n[ { path: '/Library/Fonts/Arial.ttf',\n    postscriptName: 'ArialMT',\n    family: 'Arial',\n    style: 'Regular',\n    weight: 400,\n    width: 5,\n    italic: false,\n    monospace: false },\n  ... ]\n```\n\n### findFonts(fontDescriptor)\n\nReturns an array of [font descriptors](#font-descriptor) matching a query \n[font descriptor](#font-descriptor). \nThe returned array may be empty if no fonts match the font descriptor.\n\n```javascript\n// asynchronous API\nfontManager.findFonts({ family: 'Arial' }, function(fonts) { ... });\n\n// synchronous API\nvar fonts = fontManager.findFontsSync({ family: 'Arial' });\n\n// output\n[ { path: '/Library/Fonts/Arial.ttf',\n    postscriptName: 'ArialMT',\n    family: 'Arial',\n    style: 'Regular',\n    weight: 400,\n    width: 5,\n    italic: false,\n    monospace: false },\n  { path: '/Library/Fonts/Arial Bold.ttf',\n    postscriptName: 'Arial-BoldMT',\n    family: 'Arial',\n    style: 'Bold',\n    weight: 700,\n    width: 5,\n    italic: false,\n    monospace: false } ]\n```\n\n### findFont(fontDescriptor)\n\nReturns a single [font descriptors](#font-descriptor) matching a query\n[font descriptors](#font-descriptor) as well as possible. This method\nalways returns a result (never `null`), so sometimes the output will not \nexactly match the input font descriptor if not all input parameters\ncould be met.\n\n```javascript\n// asynchronous API\nfontManager.findFont({ family: 'Arial', weight: 700 }, function(font) { ... });\n\n// synchronous API\nvar font = fontManager.findFontSync({ family: 'Arial', weight: 700 });\n\n// output\n{ path: '/Library/Fonts/Arial Bold.ttf',\n  postscriptName: 'Arial-BoldMT',\n  family: 'Arial',\n  style: 'Bold',\n  weight: 700,\n  width: 5,\n  italic: false,\n  monospace: false }\n```\n\n### substituteFont(postscriptName, text)\n\nSubstitutes the font with the given `postscriptName` with another font\nthat contains the characters in `text`.  If a font matching `postscriptName`\nis not found, a font containing the given characters is still returned.  If\na font matching `postscriptName` *is* found, its characteristics (bold, italic, etc.)\nare used to find a suitable replacement.  If the font already contains the characters\nin `text`, it is not replaced and the font descriptor for the original font is returned.\n\n```javascript\n// asynchronous API\nfontManager.substituteFont('TimesNewRomanPSMT', '汉字', function(font) { ... });\n\n// synchronous API\nvar font = fontManager.substituteFontSync('TimesNewRomanPSMT', '汉字');\n\n// output\n{ path: '/Library/Fonts/Songti.ttc',\n  postscriptName: 'STSongti-SC-Regular',\n  family: 'Songti SC',\n  style: 'Regular',\n  weight: 400,\n  width: 5,\n  italic: false,\n  monospace: false }\n```\n\n### Font Descriptor\n\nFont descriptors are normal JavaScript objects that describe characteristics of\na font.  They are passed to the `findFonts` and `findFont` methods and returned by\nall of the methods.  Any combination of the fields documented below can be used to \nfind fonts, but all methods return full font descriptors.\n\nName             | Type    | Description\n---------------- | ------- | -----------\n`path`           | string  | The path to the font file in the filesystem. **(not applicable for queries, only for results)**\n`postscriptName` | string  | The PostScript name of the font (e.g `'Arial-BoldMT'`). This uniquely identities a font in most cases.\n`family`         | string  | The font family name (e.g `'Arial'`)\n`style`          | string  | The font style name (e.g. `'Bold'`)\n`weight`         | number  | The font weight (e.g. `400` for normal weight). Should be a multiple of 100, between 100 and 900. See [below](#weights) for weight documentation.\n`width`          | number  | The font width (e.g. `5` for normal width). Should be an integer between 1 and 9. See [below](#widths) for width documentation.\n`italic`         | boolean | Whether the font is italic or not.\n`monospace`      | boolean | Whether the font is monospace or not.\n\n#### Weights\n\nValue | Name\n----- | -------------------------\n100   | Thin\n200   | Ultra Light\n300   | Light\n400   | Normal\n500   | Medium\n600   | Semi Bold\n700   | Bold\n800   | Ultra Bold\n900   | Heavy\n\n#### Widths\n\nValue | Name\n----- | -----------------------------\n1     | Ultra Condensed\n2     | Extra Condensed\n3     | Condensed\n4     | Semi Condensed\n5     | Normal\n6     | Semi Expanded\n7     | Expanded\n8     | Extra Expanded\n9     | Ultra Expanded\n\n## License\n\nMIT\n"
  },
  {
    "path": "appveyor.yml",
    "content": "os: Windows Server 2012\nenvironment:\n  VisualStudioVersion: 11.0\n  matrix:\n    - nodejs_version: \"6\"\n    - nodejs_version: \"8\"\n    - nodejs_version: \"10\"\n\ninstall:\n  - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version)\n  - npm install\n\ntest_script:\n  - node --version\n  - npm --version\n  - ps: \"npm test # PowerShell\" # Pass comment to PS for easier debugging\n  - cmd: npm test\n\nbuild: off\nversion: \"{build}\"\n"
  },
  {
    "path": "binding.gyp",
    "content": "{\n  \"targets\": [\n    {\n      \"target_name\": \"fontmanager\",\n      \"sources\": [ \"src/FontManager.cc\" ],\n      \"include_dirs\" : [\n        \"<!(node -e \\\"require('nan')\\\")\"\n      ],\n      \"conditions\": [\n        ['OS==\"mac\"', {\n          \"sources\": [\"src/FontManagerMac.mm\"],\n          \"link_settings\": {\n            \"libraries\": [\"CoreText.framework\", \"Foundation.framework\"]\n          }\n        }],\n        ['OS==\"win\"', {\n          \"sources\": [\"src/FontManagerWindows.cc\"],\n          \"link_settings\": {\n            \"libraries\": [\"Dwrite.lib\"]\n          }\n        }],\n        ['OS==\"linux\"', {\n          \"sources\": [\"src/FontManagerLinux.cc\"],\n          \"link_settings\": {\n            \"libraries\": [\"-lfontconfig\"]\n          }\n        }]\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "index.d.ts",
    "content": "declare module 'font-manager' {\n    export interface FontDescriptor {\n        readonly path: string;\n        readonly style: string;\n        readonly width: number;\n        readonly family: string;\n        readonly weight: number;\n        readonly italic: boolean;\n        readonly monospace: boolean;\n        readonly postscriptName: string;\n    }\n\n    export interface QueryFontDescriptor {\n        readonly path?: string;\n        readonly style?: string;\n        readonly width?: number;\n        readonly family?: string;\n        readonly weight?: number;\n        readonly italic?: boolean;\n        readonly monospace?: boolean;\n        readonly postscriptName?: string;\n    }\n\n    /**\n     * Fetches fonts in the system\n     * \n     * @example\n     * getAvailableFontsSync();\n     * @returns All fonts descriptors available\n     */\n    export function getAvailableFontsSync(): FontDescriptor[];\n\n    /**\n     * Returns trough a callback all fonts descriptors available on the system\n     * \n     * @param callback Contains the font data\n     * @example\n     * getAvailableFonts((fonts) => { ... });\n     */\n    export function getAvailableFonts(callback: (fonts: FontDescriptor[]) => void): void;\n\n    /**\n     * Queries all the fonts in the system matching the given parameters\n     *\n     * @param fontDescriptor Query parameters\n     * @example\n     * findFontsSync({ family: 'Arial' });\n     * findFontsSync();\n     * @returns All fonts descriptors matching query parameters\n     */\n    export function findFontsSync(fontDescriptor: QueryFontDescriptor | undefined): FontDescriptor[];\n\n    /**\n     * Queries all the fonts in the system matching the given parameters\n     * \n     * @param fontDescriptor Query parameters\n     * @param callback Contains the font data\n     * @example\n     * findFonts({ family: 'Arial' }, (fonts) => { ... });\n     * findFonts((fonts) => { ... });\n     */\n    export function findFonts(fontDescriptor: QueryFontDescriptor | undefined, callback: (fonts: FontDescriptor[]) => void);\n\n    /**\n     * Find only one font matching the given query. This function always returns\n     * a result (never null), so sometimes the output will not exactly match the\n     * input font descriptor if not all input parameters could be met\n     * \n     * @param fontDescriptor Query parameters\n     * @example\n     * findFontSync({ family: 'Arial', weight: 700 });\n     * findFontSync();\n     * @returns Only one font description matching those query parameters\n     */\n    export function findFontSync(fontDescriptor: QueryFontDescriptor): FontDescriptor;\n\n    /**\n     * Find only one font matching the given query. This function always returns\n     * a result (never null), so sometimes the output will not exactly match the\n     * input font descriptor if not all input parameters could be met\n     *\n     * @param fontDescriptor Query parameters\n     * @example\n     * findFont({ family: 'Arial', weight: 700 }, (font) => { ... });\n     * findFont((font) => { ... });\n     * @returns Only one font description matching those query parameters\n     */\n    export function findFont(fontDescriptor: QueryFontDescriptor | undefined, callback: (font: FontDescriptor) => void);\n\n    /**\n     * Substitutes the font with the given post script name with another font\n     * that contains the characters in text. If a font matching post script\n     * name is not found, a fount containing the given characters is still\n     * returned. If a font matching post script name is found, its\n     * characteristics (bold, italic, etc) are used to find a suitable\n     * replacement. If the font already contains the characters in text, it is\n     * not replaced and the font descriptor for the original font is returned\n     * \n     * @param postscriptName Name of the font to be replaced\n     * @param text Characters for matching\n     * @returns Only one font description matching the function description\n     */\n    export function substituteFontSync(postscriptName: string, text: string): FontDescriptor;\n\n    /**\n     * Substitutes the font with the given post script name with another font\n     * that contains the characters in text. If a font matching post script\n     * name is not found, a fount containing the given characters is still\n     * returned. If a font matching post script name is found, its\n     * characteristics (bold, italic, etc) are used to find a suitable\n     * replacement. If the font already contains the characters in text, it is\n     * not replaced and the font descriptor for the original font is returned\n     *\n     * @param postscriptName Name of the font to be replaced\n     * @param text Characters for matching\n     */\n    export function substituteFont(postscriptName: string, text: string, callback: (font: FontDescriptor) => void);\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"font-manager\",\n  \"version\": \"0.3.1\",\n  \"description\": \"Provides access to the system font catalog\",\n  \"main\": \"build/Release/fontmanager\",\n  \"types\": \"index.d.ts\",\n  \"dependencies\": {\n    \"nan\": \">=2.10.0\"\n  },\n  \"devDependencies\": {\n    \"mocha\": \"*\"\n  },\n  \"directories\": {\n    \"test\": \"test\"\n  },\n  \"scripts\": {\n    \"test\": \"mocha\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/devongovett/font-manager.git\"\n  },\n  \"keywords\": [\n    \"font\",\n    \"find\",\n    \"search\",\n    \"substitute\",\n    \"enumerate\"\n  ],\n  \"author\": \"Devon Govett <devongovett@gmail.com>\",\n  \"license\": \"MIT\",\n  \"gypfile\": true,\n  \"bugs\": {\n    \"url\": \"https://github.com/devongovett/font-manager/issues\"\n  },\n  \"homepage\": \"https://github.com/devongovett/font-manager\"\n}\n"
  },
  {
    "path": "src/FontDescriptor.h",
    "content": "#ifndef FONT_DESCRIPTOR_H\n#define FONT_DESCRIPTOR_H\n#include <node.h>\n#include <v8.h>\n#include <nan.h>\n#include <stdlib.h>\n#include <string.h>\n#include <vector>\n\nusing namespace v8;\n\nenum FontWeight {\n  FontWeightUndefined   = 0,\n  FontWeightThin        = 100,\n  FontWeightUltraLight  = 200,\n  FontWeightLight       = 300,\n  FontWeightNormal      = 400,\n  FontWeightMedium      = 500,\n  FontWeightSemiBold    = 600,\n  FontWeightBold        = 700,\n  FontWeightUltraBold   = 800,\n  FontWeightHeavy       = 900\n};\n\nenum FontWidth {\n  FontWidthUndefined      = 0,\n  FontWidthUltraCondensed = 1,\n  FontWidthExtraCondensed = 2,\n  FontWidthCondensed      = 3,\n  FontWidthSemiCondensed  = 4,\n  FontWidthNormal         = 5,\n  FontWidthSemiExpanded   = 6,\n  FontWidthExpanded       = 7,\n  FontWidthExtraExpanded  = 8,\n  FontWidthUltraExpanded  = 9\n};\n\nstruct FontDescriptor {\npublic:\n  const char *path;\n  const char *postscriptName;\n  const char *family;\n  const char *style;\n  FontWeight weight;\n  FontWidth width;\n  bool italic;\n  bool monospace;\n\n  FontDescriptor(Local<Object> obj) {\n    path = NULL;\n    postscriptName = getString(obj, \"postscriptName\");\n    family = getString(obj, \"family\");\n    style = getString(obj, \"style\");\n    weight = (FontWeight) getNumber(obj, \"weight\");\n    width = (FontWidth) getNumber(obj, \"width\");\n    italic = getBool(obj, \"italic\");\n    monospace = getBool(obj, \"monospace\");\n  }\n\n  FontDescriptor() {\n    path = NULL;\n    postscriptName = NULL;\n    family = NULL;\n    style = NULL;\n    weight = FontWeightUndefined;\n    width = FontWidthUndefined;\n    italic = false;\n    monospace = false;\n  }\n\n  FontDescriptor(const char *path, const char *postscriptName, const char *family, const char *style,\n                 FontWeight weight, FontWidth width, bool italic, bool monospace) {\n    this->path = copyString(path);\n    this->postscriptName = copyString(postscriptName);\n    this->family = copyString(family);\n    this->style = copyString(style);\n    this->weight = weight;\n    this->width = width;\n    this->italic = italic;\n    this->monospace = monospace;\n  }\n\n  FontDescriptor(FontDescriptor *desc) {\n    path = copyString(desc->path);\n    postscriptName = copyString(desc->postscriptName);\n    family = copyString(desc->family);\n    style = copyString(desc->style);\n    weight = desc->weight;\n    width = desc->width;\n    italic = desc->italic;\n    monospace = desc->monospace;\n  }\n\n  ~FontDescriptor() {\n    if (path)\n      delete path;\n\n    if (postscriptName)\n      delete postscriptName;\n\n    if (family)\n      delete family;\n\n    if (style)\n      delete style;\n\n    postscriptName = NULL;\n    family = NULL;\n    style = NULL;\n  }\n\n  Local<Object> toJSObject() {\n    Nan::EscapableHandleScope scope;\n    Local<Object> res = Nan::New<Object>();\n\n    if (path) {\n      Nan::Set(res, Nan::New<String>(\"path\").ToLocalChecked(), Nan::New<String>(path).ToLocalChecked());\n    }\n    \n    if (postscriptName) {\n      Nan::Set(res, Nan::New<String>(\"postscriptName\").ToLocalChecked(), Nan::New<String>(postscriptName).ToLocalChecked());\n    }\n    \n    if (family) {\n      Nan::Set(res, Nan::New<String>(\"family\").ToLocalChecked(), Nan::New<String>(family).ToLocalChecked());\n    }\n    \n    if (style) {\n      Nan::Set(res, Nan::New<String>(\"style\").ToLocalChecked(), Nan::New<String>(style).ToLocalChecked());\n    }\n    \n    Nan::Set(res, Nan::New<String>(\"weight\").ToLocalChecked(), Nan::New<Number>(weight));\n    Nan::Set(res, Nan::New<String>(\"width\").ToLocalChecked(), Nan::New<Number>(width));\n    Nan::Set(res, Nan::New<String>(\"italic\").ToLocalChecked(), Nan::New<v8::Boolean>(italic));\n    Nan::Set(res, Nan::New<String>(\"monospace\").ToLocalChecked(), Nan::New<v8::Boolean>(monospace));\n    return scope.Escape(res);\n  }\n\nprivate:\n  char *copyString(const char *input) {\n    if (!input)\n      return NULL;\n\n    char *str = new char[strlen(input) + 1];\n    strcpy(str, input);\n    return str;\n  }\n\n  char *getString(Local<Object> obj, const char *name) {\n    Nan::HandleScope scope;\n    MaybeLocal<Value> value = Nan::Get(obj, Nan::New<String>(name).ToLocalChecked());\n\n    if (!value.IsEmpty() && value.ToLocalChecked()->IsString()) {\n      return copyString(*Nan::Utf8String(value.ToLocalChecked()));\n    }\n\n    return NULL;\n  }\n\n  int getNumber(Local<Object> obj, const char *name) {\n    Nan::HandleScope scope;\n    MaybeLocal<Value> value = Nan::Get(obj, Nan::New<String>(name).ToLocalChecked());\n\n    if (!value.IsEmpty() && value.ToLocalChecked()->IsNumber()) {\n      return value.ToLocalChecked()->Int32Value(Nan::GetCurrentContext()).FromJust();\n    }\n\n    return 0;\n  }\n\n  bool getBool(Local<Object> obj, const char *name) {\n    Nan::HandleScope scope;\n    MaybeLocal<Value> value = Nan::Get(obj, Nan::New<String>(name).ToLocalChecked());\n\n    if (!value.IsEmpty() && value.ToLocalChecked()->IsBoolean()) {\n      return value.ToLocalChecked()->BooleanValue(Nan::GetCurrentContext()).FromJust();\n    }\n\n    return false;\n  }\n};\n\nclass ResultSet : public std::vector<FontDescriptor *> {\npublic:\n  ~ResultSet() {\n    for (ResultSet::iterator it = this->begin(); it != this->end(); it++) {\n      delete *it;\n    }\n  }\n};\n\n#endif\n"
  },
  {
    "path": "src/FontManager.cc",
    "content": "#include <stdlib.h>\n#include <node.h>\n#include <uv.h>\n#include <v8.h>\n#include <nan.h>\n#include \"FontDescriptor.h\"\n\nusing namespace v8;\n\n// these functions are implemented by the platform\nResultSet *getAvailableFonts();\nResultSet *findFonts(FontDescriptor *);\nFontDescriptor *findFont(FontDescriptor *);\nFontDescriptor *substituteFont(char *, char *);\n\n// converts a ResultSet to a JavaScript array\nLocal<Array> collectResults(ResultSet *results) {\n  Nan::EscapableHandleScope scope;\n  Local<Array> res = Nan::New<Array>(results->size());\n\n  int i = 0;\n  for (ResultSet::iterator it = results->begin(); it != results->end(); it++) {\n    Nan::Set(res, i++, (*it)->toJSObject());\n  }\n\n  delete results;\n  return scope.Escape(res);\n}\n\n// converts a FontDescriptor to a JavaScript object\nLocal<Value> wrapResult(FontDescriptor *result) {\n  Nan::EscapableHandleScope scope;\n  if (result == NULL)\n    return scope.Escape(Nan::Null());\n\n  Local<Object> res = result->toJSObject();\n  delete result;\n  return scope.Escape(res);\n}\n\n// holds data about an operation that will be\n// performed on a background thread\nstruct AsyncRequest {\n  uv_work_t work;\n  FontDescriptor *desc;     // used by findFont and findFonts\n  char *postscriptName;     // used by substituteFont\n  char *substitutionString; // ditto\n  FontDescriptor *result;   // for functions with a single result\n  ResultSet *results;       // for functions with multiple results\n  Nan::Callback *callback;  // the actual JS callback to call when we are done\n\n  AsyncRequest(Local<Value> v) {\n    work.data = (void *)this;\n    callback = new Nan::Callback(v.As<Function>());\n    desc = NULL;\n    postscriptName = NULL;\n    substitutionString = NULL;\n    result = NULL;\n    results = NULL;\n  }\n\n  ~AsyncRequest() {\n    delete callback;\n\n    if (desc)\n      delete desc;\n\n    if (postscriptName)\n      delete postscriptName;\n\n    if (substitutionString)\n      delete substitutionString;\n\n    // result/results deleted by wrapResult/collectResults respectively\n  }\n};\n\n// calls the JavaScript callback for a request\nvoid asyncCallback(uv_work_t *work) {\n  Nan::HandleScope scope;\n  AsyncRequest *req = (AsyncRequest *) work->data;\n  Nan::AsyncResource async(\"asyncCallback\");\n  Local<Value> info[1];\n\n  if (req->results) {\n    info[0] = collectResults(req->results);\n  } else if (req->result) {\n    info[0] = wrapResult(req->result);\n  } else {\n    info[0] = Nan::Null();\n  }\n\n  req->callback->Call(1, info, &async);\n  delete req;\n}\n\nvoid getAvailableFontsAsync(uv_work_t *work) {\n  AsyncRequest *req = (AsyncRequest *) work->data;\n  req->results = getAvailableFonts();\n}\n\ntemplate<bool async>\nNAN_METHOD(getAvailableFonts) {\n  if (async) {\n    if (info.Length() < 1 || !info[0]->IsFunction())\n      return Nan::ThrowTypeError(\"Expected a callback\");\n\n    AsyncRequest *req = new AsyncRequest(info[0]);\n    uv_queue_work(uv_default_loop(), &req->work, getAvailableFontsAsync, (uv_after_work_cb) asyncCallback);\n\n    return;\n  } else {\n    info.GetReturnValue().Set(collectResults(getAvailableFonts()));\n  }\n}\n\nvoid findFontsAsync(uv_work_t *work) {\n  AsyncRequest *req = (AsyncRequest *) work->data;\n  req->results = findFonts(req->desc);\n}\n\ntemplate<bool async>\nNAN_METHOD(findFonts) {\n  if (info.Length() < 1 || !info[0]->IsObject() || info[0]->IsFunction())\n    return Nan::ThrowTypeError(\"Expected a font descriptor\");\n\n  Local<Object> desc = info[0].As<Object>();\n  FontDescriptor *descriptor = new FontDescriptor(desc);\n\n  if (async) {\n    if (info.Length() < 2 || !info[1]->IsFunction())\n      return Nan::ThrowTypeError(\"Expected a callback\");\n\n    AsyncRequest *req = new AsyncRequest(info[1]);\n    req->desc = descriptor;\n    uv_queue_work(uv_default_loop(), &req->work, findFontsAsync, (uv_after_work_cb) asyncCallback);\n\n    return;\n  } else {\n    Local<Object> res = collectResults(findFonts(descriptor));\n    delete descriptor;\n    info.GetReturnValue().Set(res);\n  }\n}\n\nvoid findFontAsync(uv_work_t *work) {\n  AsyncRequest *req = (AsyncRequest *) work->data;\n  req->result = findFont(req->desc);\n}\n\ntemplate<bool async>\nNAN_METHOD(findFont) {\n  if (info.Length() < 1 || !info[0]->IsObject() || info[0]->IsFunction())\n    return Nan::ThrowTypeError(\"Expected a font descriptor\");\n\n  Local<Object> desc = info[0].As<Object>();\n  FontDescriptor *descriptor = new FontDescriptor(desc);\n\n  if (async) {\n    if (info.Length() < 2 || !info[1]->IsFunction())\n      return Nan::ThrowTypeError(\"Expected a callback\");\n\n    AsyncRequest *req = new AsyncRequest(info[1]);\n    req->desc = descriptor;\n    uv_queue_work(uv_default_loop(), &req->work, findFontAsync, (uv_after_work_cb) asyncCallback);\n\n    return;\n  } else {\n    Local<Value> res = wrapResult(findFont(descriptor));\n    delete descriptor;\n    info.GetReturnValue().Set(res);\n  }\n}\n\nvoid substituteFontAsync(uv_work_t *work) {\n  AsyncRequest *req = (AsyncRequest *) work->data;\n  req->result = substituteFont(req->postscriptName, req->substitutionString);\n}\n\ntemplate<bool async>\nNAN_METHOD(substituteFont) {\n  if (info.Length() < 1 || !info[0]->IsString())\n    return Nan::ThrowTypeError(\"Expected postscript name\");\n\n  if (info.Length() < 2 || !info[1]->IsString())\n    return Nan::ThrowTypeError(\"Expected substitution string\");\n\n  Nan::Utf8String postscriptName(info[0]);\n  Nan::Utf8String substitutionString(info[1]);\n\n  if (async) {\n    if (info.Length() < 3 || !info[2]->IsFunction())\n      return Nan::ThrowTypeError(\"Expected a callback\");\n\n    // copy the strings since the JS garbage collector might run before the async request is finished\n    char *ps = new char[postscriptName.length() + 1];\n    strcpy(ps, *postscriptName);\n\n    char *sub = new char[substitutionString.length() + 1];\n    strcpy(sub, *substitutionString);\n\n    AsyncRequest *req = new AsyncRequest(info[2]);\n    req->postscriptName = ps;\n    req->substitutionString = sub;\n    uv_queue_work(uv_default_loop(), &req->work, substituteFontAsync, (uv_after_work_cb) asyncCallback);\n\n    return;\n  } else {\n    info.GetReturnValue().Set(wrapResult(substituteFont(*postscriptName, *substitutionString)));\n  }\n}\n\nNAN_MODULE_INIT(Init) {\n  Nan::Export(target, \"getAvailableFonts\", getAvailableFonts<true>);\n  Nan::Export(target, \"getAvailableFontsSync\", getAvailableFonts<false>);\n  Nan::Export(target, \"findFonts\", findFonts<true>);\n  Nan::Export(target, \"findFontsSync\", findFonts<false>);\n  Nan::Export(target, \"findFont\", findFont<true>);\n  Nan::Export(target, \"findFontSync\", findFont<false>);\n  Nan::Export(target, \"substituteFont\", substituteFont<true>);\n  Nan::Export(target, \"substituteFontSync\", substituteFont<false>);\n}\n\nNODE_MODULE(fontmanager, Init)\n"
  },
  {
    "path": "src/FontManagerLinux.cc",
    "content": "#include <fontconfig/fontconfig.h>\n#include \"FontDescriptor.h\"\n\nint convertWeight(FontWeight weight) {\n  switch (weight) {\n    case FontWeightThin:\n      return FC_WEIGHT_THIN;\n    case FontWeightUltraLight:\n      return FC_WEIGHT_ULTRALIGHT;\n    case FontWeightLight:\n      return FC_WEIGHT_LIGHT;\n    case FontWeightNormal:\n      return FC_WEIGHT_REGULAR;\n    case FontWeightMedium:\n      return FC_WEIGHT_MEDIUM;\n    case FontWeightSemiBold:\n      return FC_WEIGHT_SEMIBOLD;\n    case FontWeightBold:\n      return FC_WEIGHT_BOLD;\n    case FontWeightUltraBold:\n      return FC_WEIGHT_EXTRABOLD;\n    case FontWeightHeavy:\n      return FC_WEIGHT_ULTRABLACK;\n    default:\n      return FC_WEIGHT_REGULAR;\n  }\n}\n\nFontWeight convertWeight(int weight) {\n  switch (weight) {\n    case FC_WEIGHT_THIN:\n      return FontWeightThin;\n    case FC_WEIGHT_ULTRALIGHT:\n      return FontWeightUltraLight;\n    case FC_WEIGHT_LIGHT:\n      return FontWeightLight;\n    case FC_WEIGHT_REGULAR:\n      return FontWeightNormal;\n    case FC_WEIGHT_MEDIUM:\n      return FontWeightMedium;\n    case FC_WEIGHT_SEMIBOLD:\n      return FontWeightSemiBold;\n    case FC_WEIGHT_BOLD:\n      return FontWeightBold;\n    case FC_WEIGHT_EXTRABOLD:\n      return FontWeightUltraBold;\n    case FC_WEIGHT_ULTRABLACK:\n      return FontWeightHeavy;\n    default:\n      return FontWeightNormal;\n  }\n}\n\nint convertWidth(FontWidth width) {\n  switch (width) {\n    case FontWidthUltraCondensed:\n      return FC_WIDTH_ULTRACONDENSED;\n    case FontWidthExtraCondensed:\n      return FC_WIDTH_EXTRACONDENSED;\n    case FontWidthCondensed:\n      return FC_WIDTH_CONDENSED;\n    case FontWidthSemiCondensed:\n      return FC_WIDTH_SEMICONDENSED;\n    case FontWidthNormal:\n      return FC_WIDTH_NORMAL;\n    case FontWidthSemiExpanded:\n      return  FC_WIDTH_SEMIEXPANDED;\n    case FontWidthExpanded:\n      return FC_WIDTH_EXPANDED;\n    case FontWidthExtraExpanded:\n      return FC_WIDTH_EXTRAEXPANDED;\n    case FontWidthUltraExpanded:\n      return FC_WIDTH_ULTRAEXPANDED;\n    default:\n      return FC_WIDTH_NORMAL;\n  }\n}\n\nFontWidth convertWidth(int width) {\n  switch (width) {\n    case FC_WIDTH_ULTRACONDENSED:\n      return FontWidthUltraCondensed;\n    case FC_WIDTH_EXTRACONDENSED:\n      return FontWidthExtraCondensed;\n    case FC_WIDTH_CONDENSED:\n      return FontWidthCondensed;\n    case FC_WIDTH_SEMICONDENSED:\n      return FontWidthSemiCondensed;\n    case FC_WIDTH_NORMAL:\n      return FontWidthNormal;\n    case FC_WIDTH_SEMIEXPANDED:\n      return FontWidthSemiExpanded;\n    case FC_WIDTH_EXPANDED:\n      return FontWidthExpanded;\n    case FC_WIDTH_EXTRAEXPANDED:\n      return FontWidthExtraExpanded;\n    case FC_WIDTH_ULTRAEXPANDED:\n      return FontWidthUltraExpanded;\n    default:\n      return FontWidthNormal;\n  }\n}\n\nFontDescriptor *createFontDescriptor(FcPattern *pattern) {\n  FcChar8 *path, *psName, *family, *style;\n  int weight, width, slant, spacing;\n\n  FcPatternGetString(pattern, FC_FILE, 0, &path);\n  FcPatternGetString(pattern, FC_POSTSCRIPT_NAME, 0, &psName);\n  FcPatternGetString(pattern, FC_FAMILY, 0, &family);\n  FcPatternGetString(pattern, FC_STYLE, 0, &style);\n\n  FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight);\n  FcPatternGetInteger(pattern, FC_WIDTH, 0, &width);\n  FcPatternGetInteger(pattern, FC_SLANT, 0, &slant);\n  FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing);\n\n  return new FontDescriptor(\n    (char *) path,\n    (char *) psName,\n    (char *) family,\n    (char *) style,\n    convertWeight(weight),\n    convertWidth(width),\n    slant == FC_SLANT_ITALIC,\n    spacing == FC_MONO\n  );\n}\n\nResultSet *getResultSet(FcFontSet *fs) {\n  ResultSet *res = new ResultSet();\n  if (!fs)\n    return res;\n\n  for (int i = 0; i < fs->nfont; i++) {\n    res->push_back(createFontDescriptor(fs->fonts[i]));\n  }\n\n  return res;\n}\n\nResultSet *getAvailableFonts() {\n  FcInit();\n\n  FcPattern *pattern = FcPatternCreate();\n  FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_POSTSCRIPT_NAME, FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_SPACING, NULL);\n  FcFontSet *fs = FcFontList(NULL, pattern, os);\n  ResultSet *res = getResultSet(fs);\n  \n  FcPatternDestroy(pattern);\n  FcObjectSetDestroy(os);\n  FcFontSetDestroy(fs);\n\n  return res;\n}\n\n\nFcPattern *createPattern(FontDescriptor *desc) {\n  FcInit();\n  FcPattern *pattern = FcPatternCreate();\n\n  if (desc->postscriptName)\n    FcPatternAddString(pattern, FC_POSTSCRIPT_NAME, (FcChar8 *) desc->postscriptName);\n\n  if (desc->family)\n    FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *) desc->family);\n\n  if (desc->style)\n    FcPatternAddString(pattern, FC_STYLE, (FcChar8 *) desc->style);\n\n  if (desc->italic)\n    FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);\n\n  if (desc->weight)\n    FcPatternAddInteger(pattern, FC_WEIGHT, convertWeight(desc->weight));\n\n  if (desc->width)\n    FcPatternAddInteger(pattern, FC_WIDTH, convertWidth(desc->width));\n\n  if (desc->monospace)\n    FcPatternAddInteger(pattern, FC_SPACING, FC_MONO);\n\n  return pattern;\n}\n\nResultSet *findFonts(FontDescriptor *desc) {\n  FcPattern *pattern = createPattern(desc);\n  FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_POSTSCRIPT_NAME, FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_SPACING, NULL);\n  FcFontSet *fs = FcFontList(NULL, pattern, os);\n  ResultSet *res = getResultSet(fs);\n\n  FcFontSetDestroy(fs);\n  FcPatternDestroy(pattern);\n  FcObjectSetDestroy(os);\n\n  return res;\n}\n\nFontDescriptor *findFont(FontDescriptor *desc) {\n  FcPattern *pattern = createPattern(desc);\n  FcConfigSubstitute(NULL, pattern, FcMatchPattern);\n  FcDefaultSubstitute(pattern);\n\n  FcResult result;\n  FcPattern *font = FcFontMatch(NULL, pattern, &result);\n  FontDescriptor *res = createFontDescriptor(font);\n\n  FcPatternDestroy(pattern);\n  FcPatternDestroy(font);\n\n  return res;\n}\n\nFontDescriptor *substituteFont(char *postscriptName, char *string) {\n  FcInit();\n\n  // create a pattern with the postscript name\n  FcPattern* pattern = FcPatternCreate();\n  FcPatternAddString(pattern, FC_POSTSCRIPT_NAME, (FcChar8 *) postscriptName);\n\n  // create a charset with each character in the string\n  FcCharSet* charset = FcCharSetCreate();\n  int len = strlen(string);\n\n  for (int i = 0; i < len;) {\n    FcChar32 c;\n    i += FcUtf8ToUcs4((FcChar8 *)string + i, &c, len - i);\n    FcCharSetAddChar(charset, c);\n  }\n\n  FcPatternAddCharSet(pattern, FC_CHARSET, charset);\n  FcCharSetDestroy(charset);\n\n  FcConfigSubstitute(0, pattern, FcMatchPattern);\n  FcDefaultSubstitute(pattern);\n\n  // find the best match font\n  FcResult result;\n  FcPattern *font = FcFontMatch(NULL, pattern, &result);\n  FontDescriptor *res = createFontDescriptor(font);\n\n  FcPatternDestroy(pattern);\n  FcPatternDestroy(font);\n\n  return res;\n}\n"
  },
  {
    "path": "src/FontManagerMac.mm",
    "content": "#include <Foundation/Foundation.h>\n#include <CoreText/CoreText.h>\n#include \"FontDescriptor.h\"\n\n// converts a CoreText weight (-1 to +1) to a standard weight (100 to 900)\nstatic int convertWeight(float weight) {\n  if (weight <= -0.8f)\n    return 100;\n  else if (weight <= -0.6f)\n    return 200;\n  else if (weight <= -0.4f)\n    return 300;\n  else if (weight <= 0.0f)\n    return 400;\n  else if (weight <= 0.25f)\n    return 500;\n  else if (weight <= 0.35f)\n    return 600;\n  else if (weight <= 0.4f)\n    return 700;\n  else if (weight <= 0.6f)\n    return 800;\n  else\n    return 900;\n}\n\n// converts a CoreText width (-1 to +1) to a standard width (1 to 9)\nstatic int convertWidth(float unit) {\n  if (unit < 0) {\n    return 1 + (1 + unit) * 4;\n  } else {\n    return 5 + unit * 4;\n  }\n}\n\nFontDescriptor *createFontDescriptor(CTFontDescriptorRef descriptor) {\n  NSURL *url = (NSURL *) CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);\n  NSString *psName = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontNameAttribute);  \n  NSString *family = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontFamilyNameAttribute);\n  NSString *style = (NSString *) CTFontDescriptorCopyAttribute(descriptor, kCTFontStyleNameAttribute);\n  \n  NSDictionary *traits = (NSDictionary *) CTFontDescriptorCopyAttribute(descriptor, kCTFontTraitsAttribute);\n  NSNumber *weightVal = traits[(id)kCTFontWeightTrait];\n  FontWeight weight = (FontWeight) convertWeight([weightVal floatValue]);\n  \n  NSNumber *widthVal = traits[(id)kCTFontWidthTrait];\n  FontWidth width = (FontWidth) convertWidth([widthVal floatValue]);\n  \n  NSNumber *symbolicTraitsVal = traits[(id)kCTFontSymbolicTrait];\n  unsigned int symbolicTraits = [symbolicTraitsVal unsignedIntValue];\n    \n  FontDescriptor *res = new FontDescriptor(\n    [[url path] UTF8String],\n    [psName UTF8String],\n    [family UTF8String],\n    [style UTF8String],\n    weight,\n    width,\n    (symbolicTraits & kCTFontItalicTrait) != 0,\n    (symbolicTraits & kCTFontMonoSpaceTrait) != 0\n  );\n    \n  [url release];\n  [psName release];\n  [family release];\n  [style release];\n  [traits release];\n  return res;\n}\n\nResultSet *getAvailableFonts() {\n  // cache font collection for fast use in future calls\n  static CTFontCollectionRef collection = NULL;\n  if (collection == NULL)\n    collection = CTFontCollectionCreateFromAvailableFonts(NULL);\n  \n  NSArray *matches = (NSArray *) CTFontCollectionCreateMatchingFontDescriptors(collection);  \n  ResultSet *results = new ResultSet();\n  \n  for (id m in matches) {\n    CTFontDescriptorRef match = (CTFontDescriptorRef) m;\n    results->push_back(createFontDescriptor(match));\n  }\n  \n  [matches release];\n  return results;\n}\n\n// helper to square a value\nstatic inline int sqr(int value) {\n  return value * value;\n}\n\nCTFontDescriptorRef getFontDescriptor(FontDescriptor *desc) {\n  // build a dictionary of font attributes\n  NSMutableDictionary *attrs = [NSMutableDictionary dictionary];\n  CTFontSymbolicTraits symbolicTraits = 0;\n\n  if (desc->postscriptName) {\n    NSString *postscriptName = [NSString stringWithUTF8String:desc->postscriptName];\n    attrs[(id)kCTFontNameAttribute] = postscriptName;\n  }\n\n  if (desc->family) {\n    NSString *family = [NSString stringWithUTF8String:desc->family];\n    attrs[(id)kCTFontFamilyNameAttribute] = family;\n  }\n\n  if (desc->style) {\n    NSString *style = [NSString stringWithUTF8String:desc->style];\n    attrs[(id)kCTFontStyleNameAttribute] = style;\n  }\n\n  // build symbolic traits\n  if (desc->italic)\n    symbolicTraits |= kCTFontItalicTrait;\n\n  if (desc->weight == FontWeightBold)\n    symbolicTraits |= kCTFontBoldTrait;\n\n  if (desc->monospace)\n    symbolicTraits |= kCTFontMonoSpaceTrait;\n\n  if (desc->width == FontWidthCondensed)\n    symbolicTraits |= kCTFontCondensedTrait;\n\n  if (desc->width == FontWidthExpanded)\n    symbolicTraits |= kCTFontExpandedTrait;\n\n  if (symbolicTraits) {\n    NSDictionary *traits = @{(id)kCTFontSymbolicTrait:[NSNumber numberWithUnsignedInt:symbolicTraits]};\n    attrs[(id)kCTFontTraitsAttribute] = traits;\n  }\n\n  // create a font descriptor and search for matches\n  return CTFontDescriptorCreateWithAttributes((CFDictionaryRef) attrs);  \n}\n\nint metricForMatch(CTFontDescriptorRef match, FontDescriptor *desc) {\n  NSDictionary *dict = (NSDictionary *)CTFontDescriptorCopyAttribute(match, kCTFontTraitsAttribute);\n\n  bool italic = ([dict[(id)kCTFontSymbolicTrait] unsignedIntValue] & kCTFontItalicTrait);\n    \n  // normalize everything to base-900\n  int metric = 0;\n  if (desc->weight)\n    metric += sqr(convertWeight([dict[(id)kCTFontWeightTrait] floatValue]) - desc->weight);\n  \n  if (desc->width)\n    metric += sqr((convertWidth([dict[(id)kCTFontWidthTrait] floatValue]) - desc->width) * 100);\n  \n  metric += sqr((italic != desc->italic) * 900);\n\n  [dict release];\n  return metric;\n}\n\nResultSet *findFonts(FontDescriptor *desc) {\n  CTFontDescriptorRef descriptor = getFontDescriptor(desc);\n  NSArray *matches = (NSArray *) CTFontDescriptorCreateMatchingFontDescriptors(descriptor, NULL);\n  ResultSet *results = new ResultSet();\n  \n  NSArray *sorted = [matches sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {\n    int ma = metricForMatch((CTFontDescriptorRef) a, desc);\n    int mb = metricForMatch((CTFontDescriptorRef) b, desc);\n    return ma < mb ? NSOrderedAscending : ma > mb ? NSOrderedDescending : NSOrderedSame;\n  }];\n  \n  for (id m in sorted) {\n    CTFontDescriptorRef match = (CTFontDescriptorRef) m;\n    int mb = metricForMatch((CTFontDescriptorRef) m, desc);\n    \n    if (mb < 10000) {\n      results->push_back(createFontDescriptor(match));\n    }\n  }\n  \n  CFRelease(descriptor);\n  [matches release];\n  return results;\n}\n\nCTFontDescriptorRef findBest(FontDescriptor *desc, NSArray *matches) {\n  // find the closest match for width and weight attributes\n  CTFontDescriptorRef best = NULL;\n  int bestMetric = INT_MAX;\n\n  for (id m in matches) {\n    int metric = metricForMatch((CTFontDescriptorRef) m, desc);\n\n    if (metric < bestMetric) {\n      bestMetric = metric;\n      best = (CTFontDescriptorRef) m;\n    }\n\n    // break if this is an exact match\n    if (metric == 0)\n      break;\n  }\n  \n  return best;\n}\n\nFontDescriptor *findFont(FontDescriptor *desc) {  \n  FontDescriptor *res = NULL;\n  CTFontDescriptorRef descriptor = getFontDescriptor(desc);\n  NSArray *matches = (NSArray *) CTFontDescriptorCreateMatchingFontDescriptors(descriptor, NULL);\n  \n  // if there was no match, try again but only try to match traits\n  if ([matches count] == 0) {\n    [matches release];\n    NSSet *set = [NSSet setWithObjects:(id)kCTFontTraitsAttribute, nil];\n    matches = (NSArray *) CTFontDescriptorCreateMatchingFontDescriptors(descriptor, (CFSetRef) set);\n  }\n  \n  // find the closest match for width and weight attributes\n  CTFontDescriptorRef best = findBest(desc, matches);\n      \n  // if we found a match, generate and return a URL for it\n  if (best) {    \n    res = createFontDescriptor(best);\n  }\n  \n  [matches release];\n  CFRelease(descriptor);\n  return res;\n}\n\nFontDescriptor *substituteFont(char *postscriptName, char *string) {\n  FontDescriptor *res = NULL;\n  \n  // create a font descriptor to find the font by its postscript name\n  // we don't use CTFontCreateWithName because that supports font\n  // names other than the postscript name but prints warnings.\n  NSString *ps = [NSString stringWithUTF8String:postscriptName];\n  NSDictionary *attrs = @{(id)kCTFontNameAttribute: ps};\n  CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes((CFDictionaryRef) attrs);\n  CTFontRef font = CTFontCreateWithFontDescriptor(descriptor, 12.0, NULL);\n  \n  // find a substitute font that support the given characters\n  NSString *str = [NSString stringWithUTF8String:string];\n  CTFontRef substituteFont = CTFontCreateForString(font, (CFStringRef) str, CFRangeMake(0, [str length]));\n  CTFontDescriptorRef substituteDescriptor = CTFontCopyFontDescriptor(substituteFont);\n  \n  // finally, create and return a result object for this substitute font\n  res = createFontDescriptor(substituteDescriptor);\n  \n  CFRelease(font);\n  CFRelease(substituteFont);\n  CFRelease(substituteDescriptor);\n  \n  return res;\n}\n"
  },
  {
    "path": "src/FontManagerWindows.cc",
    "content": "#define WINVER 0x0600\n#include \"FontDescriptor.h\"\n#include <dwrite.h>\n#include <dwrite_1.h>\n#include <unordered_set>\n\n// throws a JS error when there is some exception in DirectWrite\n#define HR(hr) \\\n  if (FAILED(hr)) throw \"Font loading error\";\n\nWCHAR *utf8ToUtf16(const char *input) {\n  unsigned int len = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);\n  WCHAR *output = new WCHAR[len];\n  MultiByteToWideChar(CP_UTF8, 0, input, -1, output, len);\n  return output;\n}\n\nchar *utf16ToUtf8(const WCHAR *input) {\n  unsigned int len = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL);\n  char *output = new char[len];\n  WideCharToMultiByte(CP_UTF8, 0, input, -1, output, len, NULL, NULL);\n  return output;\n}\n\n// returns the index of the user's locale in the set of localized strings\nunsigned int getLocaleIndex(IDWriteLocalizedStrings *strings) {\n  unsigned int index = 0;\n  BOOL exists = false;\n  wchar_t localeName[LOCALE_NAME_MAX_LENGTH];\n\n  // Get the default locale for this user.\n  int success = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH);\n\n  // If the default locale is returned, find that locale name, otherwise use \"en-us\".\n  if (success) {\n    HR(strings->FindLocaleName(localeName, &index, &exists));\n  }\n\n  // if the above find did not find a match, retry with US English\n  if (!exists) {\n    HR(strings->FindLocaleName(L\"en-us\", &index, &exists));\n  }\n\n  if (!exists)\n    index = 0;\n\n  return index;\n}\n\n// gets a localized string for a font\nchar *getString(IDWriteFont *font, DWRITE_INFORMATIONAL_STRING_ID string_id) {\n  char *res = NULL;\n  IDWriteLocalizedStrings *strings = NULL;\n\n  BOOL exists = false;\n  HR(font->GetInformationalStrings(\n    string_id,\n    &strings,\n    &exists\n  ));\n\n  if (exists) {\n    unsigned int index = getLocaleIndex(strings);\n    unsigned int len = 0;\n    WCHAR *str = NULL;\n\n    HR(strings->GetStringLength(index, &len));\n    str = new WCHAR[len + 1];\n\n    HR(strings->GetString(index, str, len + 1));\n\n    // convert to utf8\n    res = utf16ToUtf8(str);\n    delete str;\n    \n    strings->Release();\n  }\n  \n  if (!res) {\n    res = new char[1];\n    res[0] = '\\0';\n  }\n  \n  return res;\n}\n\nFontDescriptor *resultFromFont(IDWriteFont *font) {\n  FontDescriptor *res = NULL;\n  IDWriteFontFace *face = NULL;\n  unsigned int numFiles = 0;\n\n  HR(font->CreateFontFace(&face));\n\n  // get the font files from this font face\n  IDWriteFontFile *files = NULL;\n  HR(face->GetFiles(&numFiles, NULL));\n  HR(face->GetFiles(&numFiles, &files));\n\n  // return the first one\n  if (numFiles > 0) {\n    IDWriteFontFileLoader *loader = NULL;\n    IDWriteLocalFontFileLoader *fileLoader = NULL;\n    unsigned int nameLength = 0;\n    const void *referenceKey = NULL;\n    unsigned int referenceKeySize = 0;\n    WCHAR *name = NULL;\n\n    HR(files[0].GetLoader(&loader));\n\n    // check if this is a local font file\n    HRESULT hr = loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void **)&fileLoader);\n    if (SUCCEEDED(hr)) {\n      // get the file path\n      HR(files[0].GetReferenceKey(&referenceKey, &referenceKeySize));\n      HR(fileLoader->GetFilePathLengthFromKey(referenceKey, referenceKeySize, &nameLength));\n\n      name = new WCHAR[nameLength + 1];\n      HR(fileLoader->GetFilePathFromKey(referenceKey, referenceKeySize, name, nameLength + 1));\n\n      char *psName = utf16ToUtf8(name);\n      char *postscriptName = getString(font, DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME);\n      char *family = getString(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES);\n      char *style = getString(font, DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES);\n\n      bool monospace = false;\n      // this method requires windows 7, so we need to cast to an IDWriteFontFace1\n\n      IDWriteFontFace1 *face1;\n      HRESULT hr = face->QueryInterface(__uuidof(IDWriteFontFace1), (void **)&face1);\n      if (SUCCEEDED(hr)) {\n        monospace = face1->IsMonospacedFont() == TRUE;\n      }\n\n      res = new FontDescriptor(\n        psName,\n        postscriptName,\n        family,\n        style,\n        (FontWeight) font->GetWeight(),\n        (FontWidth) font->GetStretch(),\n        font->GetStyle() == DWRITE_FONT_STYLE_ITALIC,\n        monospace\n      );\n\n      delete psName;\n      delete name;\n      delete postscriptName;\n      delete family;\n      delete style;\n      fileLoader->Release();\n    }\n\n    loader->Release();\n  }\n\n  face->Release();\n  files->Release();\n\n  return res;\n}\n\nResultSet *getAvailableFonts() {\n  ResultSet *res = new ResultSet();\n  int count = 0;\n\n  IDWriteFactory *factory = NULL;\n  HR(DWriteCreateFactory(\n    DWRITE_FACTORY_TYPE_SHARED,\n    __uuidof(IDWriteFactory),\n    reinterpret_cast<IUnknown**>(&factory)\n  ));\n\n  // Get the system font collection.\n  IDWriteFontCollection *collection = NULL;\n  HR(factory->GetSystemFontCollection(&collection));\n\n  // Get the number of font families in the collection.\n  int familyCount = collection->GetFontFamilyCount();\n\n  // track postscript names we've already added\n  // using a set so we don't get any duplicates.\n  std::unordered_set<std::string> psNames;\n\n  for (int i = 0; i < familyCount; i++) {\n    IDWriteFontFamily *family = NULL;\n\n    // Get the font family.\n    HR(collection->GetFontFamily(i, &family));\n    int fontCount = family->GetFontCount();\n\n    for (int j = 0; j < fontCount; j++) {\n      IDWriteFont *font = NULL;\n      HR(family->GetFont(j, &font));\n\n      FontDescriptor *result = resultFromFont(font);\n      if (psNames.count(result->postscriptName) == 0) {\n        res->push_back(resultFromFont(font));\n        psNames.insert(result->postscriptName);\n      }\n    }\n\n    family->Release();\n  }\n\n  collection->Release();\n  factory->Release();\n\n  return res;\n}\n\nbool resultMatches(FontDescriptor *result, FontDescriptor *desc) {\n  if (desc->postscriptName && strcmp(desc->postscriptName, result->postscriptName) != 0)\n    return false;\n\n  if (desc->family && strcmp(desc->family, result->family) != 0)\n    return false;\n\n  if (desc->style && strcmp(desc->style, result->style) != 0)\n    return false;\n\n  if (desc->weight && desc->weight != result->weight)\n    return false;\n\n  if (desc->width && desc->width != result->width)\n    return false;\n\n  if (desc->italic != result->italic)\n    return false;\n\n  if (desc->monospace != result->monospace)\n    return false;\n\n  return true;\n}\n\nResultSet *findFonts(FontDescriptor *desc) {\n  ResultSet *fonts = getAvailableFonts();\n\n  for (ResultSet::iterator it = fonts->begin(); it != fonts->end();) {\n    if (!resultMatches(*it, desc)) {\n      delete *it;\n      it = fonts->erase(it);\n    } else {\n      it++;\n    }\n  }\n\n  return fonts;\n}\n\nFontDescriptor *findFont(FontDescriptor *desc) {\n  ResultSet *fonts = findFonts(desc);\n\n  // if we didn't find anything, try again with only the font traits, no string names\n  if (fonts->size() == 0) {\n    delete fonts;\n\n    FontDescriptor *fallback = new FontDescriptor(\n      NULL, NULL, NULL, NULL, \n      desc->weight, desc->width, desc->italic, false\n    );\n\n    fonts = findFonts(fallback);\n  }\n\n  // ok, nothing. shouldn't happen often. \n  // just return the first available font\n  if (fonts->size() == 0) {\n    delete fonts;\n    fonts = getAvailableFonts();\n  }\n\n  // hopefully we found something now.\n  // copy and return the first result\n  if (fonts->size() > 0) {\n    FontDescriptor *res = new FontDescriptor(fonts->front());\n    delete fonts;\n    return res;\n  }\n\n  // whoa, weird. no fonts installed or something went wrong.\n  delete fonts;\n  return NULL;\n}\n\n// custom text renderer used to determine the fallback font for a given char\nclass FontFallbackRenderer : public IDWriteTextRenderer {\npublic:\n  IDWriteFontCollection *systemFonts;\n  IDWriteFont *font;\n  unsigned long refCount;\n\n  FontFallbackRenderer(IDWriteFontCollection *collection) {\n    refCount = 0;\n    collection->AddRef();\n    systemFonts = collection;\n    font = NULL;\n  }\n\n  ~FontFallbackRenderer() {\n    if (systemFonts)\n      systemFonts->Release();\n\n    if (font)\n      font->Release();\n  }\n\n  // IDWriteTextRenderer methods\n  IFACEMETHOD(DrawGlyphRun)(\n      void *clientDrawingContext,\n      FLOAT baselineOriginX,\n      FLOAT baselineOriginY,\n      DWRITE_MEASURING_MODE measuringMode,\n      DWRITE_GLYPH_RUN const *glyphRun,\n      DWRITE_GLYPH_RUN_DESCRIPTION const *glyphRunDescription,\n      IUnknown *clientDrawingEffect) {\n\n    // save the font that was actually rendered\n    return systemFonts->GetFontFromFontFace(glyphRun->fontFace, &font);\n  }\n\n  IFACEMETHOD(DrawUnderline)(\n      void *clientDrawingContext,\n      FLOAT baselineOriginX,\n      FLOAT baselineOriginY,\n      DWRITE_UNDERLINE const *underline,\n      IUnknown *clientDrawingEffect) {\n    return E_NOTIMPL;\n  }\n\n\n  IFACEMETHOD(DrawStrikethrough)(\n      void *clientDrawingContext,\n      FLOAT baselineOriginX,\n      FLOAT baselineOriginY,\n      DWRITE_STRIKETHROUGH const *strikethrough,\n      IUnknown *clientDrawingEffect) {\n    return E_NOTIMPL;\n  }\n\n\n  IFACEMETHOD(DrawInlineObject)(\n      void *clientDrawingContext,\n      FLOAT originX,\n      FLOAT originY,\n      IDWriteInlineObject *inlineObject,\n      BOOL isSideways,\n      BOOL isRightToLeft,\n      IUnknown *clientDrawingEffect) {\n    return E_NOTIMPL;\n  }\n\n  // IDWritePixelSnapping methods\n  IFACEMETHOD(IsPixelSnappingDisabled)(void *clientDrawingContext, BOOL *isDisabled) {\n    *isDisabled = FALSE;\n    return S_OK;\n  }\n\n  IFACEMETHOD(GetCurrentTransform)(void *clientDrawingContext, DWRITE_MATRIX *transform) {\n    const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};\n    *transform = ident;\n    return S_OK;\n  }\n\n  IFACEMETHOD(GetPixelsPerDip)(void *clientDrawingContext, FLOAT *pixelsPerDip) {\n    *pixelsPerDip = 1.0f;\n    return S_OK;\n  }\n\n  // IUnknown methods\n  IFACEMETHOD_(unsigned long, AddRef)() {\n    return InterlockedIncrement(&refCount);\n  }\n\n  IFACEMETHOD_(unsigned long,  Release)() {\n    unsigned long newCount = InterlockedDecrement(&refCount);\n    if (newCount == 0) {\n      delete this;\n      return 0;\n    }\n\n    return newCount;\n  }\n\n  IFACEMETHOD(QueryInterface)(IID const& riid, void **ppvObject) {\n    if (__uuidof(IDWriteTextRenderer) == riid) {\n      *ppvObject = this;\n    } else if (__uuidof(IDWritePixelSnapping) == riid) {\n      *ppvObject = this;\n    } else if (__uuidof(IUnknown) == riid) {\n      *ppvObject = this;\n    } else {\n      *ppvObject = nullptr;\n      return E_FAIL;\n    }\n\n    this->AddRef();\n    return S_OK;\n  }\n};\n\nFontDescriptor *substituteFont(char *postscriptName, char *string) {\n  FontDescriptor *res = NULL;\n\n  IDWriteFactory *factory = NULL;\n  HR(DWriteCreateFactory(\n    DWRITE_FACTORY_TYPE_SHARED,\n    __uuidof(IDWriteFactory),\n    reinterpret_cast<IUnknown**>(&factory)\n  ));\n\n  // Get the system font collection.\n  IDWriteFontCollection *collection = NULL;\n  HR(factory->GetSystemFontCollection(&collection));\n\n  // find the font for the given postscript name\n  FontDescriptor *desc = new FontDescriptor();\n  desc->postscriptName = postscriptName;\n  FontDescriptor *font = findFont(desc);\n\n  // create a text format object for this font\n  IDWriteTextFormat *format = NULL;\n  if (font) {\n    WCHAR *familyName = utf8ToUtf16(font->family);\n\n    // create a text format\n    HR(factory->CreateTextFormat(\n      familyName,\n      collection,\n      (DWRITE_FONT_WEIGHT) font->weight,\n      font->italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL,\n      (DWRITE_FONT_STRETCH) font->width,\n      12.0,\n      L\"en-us\",\n      &format\n    ));\n\n    delete familyName;\n    delete font;\n  } else {\n    // this should never happen, but just in case, let the system\n    // decide the default font in case findFont returned nothing.\n    HR(factory->CreateTextFormat(\n      L\"\",\n      collection,\n      DWRITE_FONT_WEIGHT_REGULAR,\n      DWRITE_FONT_STYLE_NORMAL,\n      DWRITE_FONT_STRETCH_NORMAL,\n      12.0,\n      L\"en-us\",\n      &format\n    ));\n  }\n\n  // convert utf8 string for substitution to utf16\n  WCHAR *str = utf8ToUtf16(string);\n\n  // create a text layout for the substitution string\n  IDWriteTextLayout *layout = NULL;\n  HR(factory->CreateTextLayout(\n    str,\n    wcslen(str),\n    format,\n    100.0,\n    100.0,\n    &layout\n  ));\n\n  // render it using a custom renderer that saves the physical font being used\n  FontFallbackRenderer *renderer = new FontFallbackRenderer(collection);\n  HR(layout->Draw(NULL, renderer, 100.0, 100.0));\n\n  // if we found something, create a result object\n  if (renderer->font) {\n    res = resultFromFont(renderer->font);\n  }\n\n  // free all the things\n  delete renderer;\n  layout->Release();\n  format->Release();\n\n  desc->postscriptName = NULL;\n  delete desc;\n  delete str;\n  collection->Release();\n  factory->Release();\n\n  return res;\n}\n"
  },
  {
    "path": "test/index.js",
    "content": "var fontManager = require('../');\nvar assert = require('assert');\n\n// some standard fonts that are likely to be installed on the platform the tests are running on\nvar standardFont = process.platform === 'linux' ? 'Liberation Sans' : 'Arial';\nvar postscriptName = process.platform === 'linux' ? 'LiberationSans' : 'ArialMT';\n\ndescribe('font-manager', function() {\n  it('should export some functions', function() {\n    assert.equal(typeof fontManager.getAvailableFonts, 'function');\n    assert.equal(typeof fontManager.getAvailableFontsSync, 'function');\n    assert.equal(typeof fontManager.findFonts, 'function');\n    assert.equal(typeof fontManager.findFontsSync, 'function');\n    assert.equal(typeof fontManager.findFont, 'function');\n    assert.equal(typeof fontManager.findFontSync, 'function');\n    assert.equal(typeof fontManager.substituteFont, 'function');\n    assert.equal(typeof fontManager.substituteFontSync, 'function');\n  });\n  \n  function assertFontDescriptor(font) {\n    assert.equal(typeof font, 'object');\n    assert.equal(typeof font.path, 'string');\n    assert.equal(typeof font.postscriptName, 'string');\n    assert.equal(typeof font.family, 'string');\n    assert.equal(typeof font.style, 'string');\n    assert.equal(typeof font.weight, 'number');\n    assert.equal(typeof font.width, 'number');\n    assert.equal(typeof font.italic, 'boolean');\n    assert.equal(typeof font.monospace, 'boolean');\n  }\n  \n  describe('getAvailableFonts', function() {\n    it('should throw if no callback is provided', function() {\n      assert.throws(function() {\n        fontManager.getAvailableFonts();\n      }, /Expected a callback/);\n    });\n    \n    it('should throw if callback is not a function', function() {\n      assert.throws(function() {\n        fontManager.getAvailableFonts(2);\n      }, /Expected a callback/);\n    });\n    \n    it('should getAvailableFonts asynchronously', function(done) {\n      var async = false;\n\n      fontManager.getAvailableFonts(function(fonts) {\n        assert(async); \n        assert(Array.isArray(fonts));\n        assert(fonts.length > 0);\n        fonts.forEach(assertFontDescriptor);\n        done();\n      });\n\n      async = true;\n    });\n  });\n\n  describe('getAvailableFontsSync', function() {\n    it('should getAvailableFonts synchronously', function() {\n      var fonts = fontManager.getAvailableFontsSync();\n      assert(Array.isArray(fonts));\n      assert(fonts.length > 0);\n      fonts.forEach(assertFontDescriptor);\n    });\n  });\n  \n  describe('findFonts', function() {\n    it('should throw if no font descriptor is provided', function() {\n      assert.throws(function() {\n        fontManager.findFonts(function(fonts) {});\n      }, /Expected a font descriptor/);\n    });\n    \n    it('should throw if font descriptor is not an object', function() {\n      assert.throws(function() {\n        fontManager.findFonts(2, function(fonts) {});\n      }, /Expected a font descriptor/);\n    });\n    \n    it('should throw if no callback is provided', function() {\n      assert.throws(function() {\n        fontManager.findFonts({ family: standardFont });\n      }, /Expected a callback/);\n    });\n    \n    it('should throw if callback is not a function', function() {\n      assert.throws(function() {\n        fontManager.findFonts({ family: standardFont }, 2);\n      }, /Expected a callback/);\n    });\n    \n    it('should findFonts asynchronously', function(done) {\n      var async = false;\n    \n      fontManager.findFonts({ family: standardFont }, function(fonts) {\n        assert(async); \n        assert(Array.isArray(fonts));\n        assert(fonts.length > 0);\n        fonts.forEach(assertFontDescriptor);\n        done();\n      });\n    \n      async = true;\n    });\n    \n    it('should find fonts by postscriptName', function(done) {\n      fontManager.findFonts({ postscriptName: postscriptName }, function(fonts) {\n        assert(Array.isArray(fonts));\n        assert.equal(fonts.length, 1);\n        fonts.forEach(assertFontDescriptor);\n        assert.equal(fonts[0].postscriptName, postscriptName);\n        assert.equal(fonts[0].family, standardFont);\n        done();\n      });\n    });\n    \n    it('should find fonts by family and style', function(done) {\n      fontManager.findFonts({ family: standardFont, style: 'Bold' }, function(fonts) {\n        assert(Array.isArray(fonts));\n        assert.equal(fonts.length, 1);\n        fonts.forEach(assertFontDescriptor);\n        assert.equal(fonts[0].family, standardFont);\n        assert.equal(fonts[0].style, 'Bold');\n        assert.equal(fonts[0].weight, 700);\n        done();\n      });\n    });\n    \n    it('should find fonts by weight', function(done) {\n      fontManager.findFonts({ family: standardFont, weight: 700 }, function(fonts) {\n        assert(Array.isArray(fonts));\n        assert(fonts.length > 0);\n        fonts.forEach(assertFontDescriptor);\n        fonts.forEach(function(font) {\n          assert.equal(font.weight, 700);\n        });\n        done();\n      });\n    });\n    \n    it('should find italic fonts', function(done) {\n      fontManager.findFonts({ family: standardFont, italic: true }, function(fonts) {\n        assert(Array.isArray(fonts));\n        assert(fonts.length > 0);\n        fonts.forEach(assertFontDescriptor);\n        fonts.forEach(function(font) {\n          assert.equal(font.italic, true);\n        });\n        done();\n      });\n    });\n    \n    it('should find italic and bold fonts', function(done) {\n      fontManager.findFonts({ family: standardFont, italic: true, weight: 700 }, function(fonts) {\n        assert(Array.isArray(fonts));\n        assert(fonts.length > 0);\n        fonts.forEach(assertFontDescriptor);\n        fonts.forEach(function(font) {\n          assert.equal(font.italic, true);\n          assert.equal(font.weight, 700);\n        });\n        done();\n      });\n    });\n    \n    it('should return an empty array for nonexistent family', function(done) {\n      fontManager.findFonts({ family: '' + Date.now() }, function(fonts) {\n        assert(Array.isArray(fonts));\n        assert.equal(fonts.length, 0);\n        done();\n      });\n    });\n    \n    it('should return an empty array for nonexistent postscriptName', function(done) {\n      fontManager.findFonts({ postscriptName: '' + Date.now() }, function(fonts) {\n        assert(Array.isArray(fonts));\n        assert.equal(fonts.length, 0);\n        done();\n      });\n    });\n    \n    it('should return many fonts for empty font descriptor', function(done) {\n      fontManager.findFonts({}, function(fonts) {\n        assert(Array.isArray(fonts));\n        assert(fonts.length > 0);\n        fonts.forEach(assertFontDescriptor);\n        done();\n      });\n    });\n  });\n  \n  describe('findFontsSync', function() {\n    it('should throw if no font descriptor is provided', function() {\n      assert.throws(function() {\n        fontManager.findFontsSync();\n      }, /Expected a font descriptor/);\n    });\n    \n    it('should throw if font descriptor is not an object', function() {\n      assert.throws(function() {\n        fontManager.findFontsSync(2);\n      }, /Expected a font descriptor/);\n    });\n    \n    it('should findFonts synchronously', function() {\n      var fonts = fontManager.findFontsSync({ family: standardFont });\n      assert(Array.isArray(fonts));\n      assert(fonts.length > 0);\n      fonts.forEach(assertFontDescriptor);\n    });\n    \n    it('should find fonts by postscriptName', function() {\n      var fonts = fontManager.findFontsSync({ postscriptName: postscriptName });\n      assert(Array.isArray(fonts));\n      assert.equal(fonts.length, 1);\n      fonts.forEach(assertFontDescriptor);\n      assert.equal(fonts[0].postscriptName, postscriptName);\n      assert.equal(fonts[0].family, standardFont);\n    });\n    \n    it('should find fonts by family and style', function() {\n      var fonts = fontManager.findFontsSync({ family: standardFont, style: 'Bold' });\n      assert(Array.isArray(fonts));\n      assert.equal(fonts.length, 1);\n      fonts.forEach(assertFontDescriptor);\n      assert.equal(fonts[0].family, standardFont);\n      assert.equal(fonts[0].style, 'Bold');\n      assert.equal(fonts[0].weight, 700);\n    });\n    \n    it('should find fonts by weight', function() {\n      var fonts = fontManager.findFontsSync({ family: standardFont, weight: 700 });\n      assert(Array.isArray(fonts));\n      assert(fonts.length > 0);\n      fonts.forEach(assertFontDescriptor);\n      assert.equal(fonts[0].weight, 700);\n    });\n    \n    it('should find italic fonts', function() {\n      var fonts = fontManager.findFontsSync({ family: standardFont, italic: true });\n      assert(Array.isArray(fonts));\n      assert(fonts.length > 0);\n      fonts.forEach(assertFontDescriptor);\n      assert.equal(fonts[0].italic, true);\n    });\n    \n    it('should find italic and bold fonts', function() {\n      var fonts = fontManager.findFontsSync({ family: standardFont, italic: true, weight: 700 });\n      assert(Array.isArray(fonts));\n      assert(fonts.length > 0);\n      fonts.forEach(assertFontDescriptor);\n      assert.equal(fonts[0].italic, true);\n      assert.equal(fonts[0].weight, 700);\n    });\n    \n    it('should return an empty array for nonexistent family', function() {\n      var fonts = fontManager.findFontsSync({ family: '' + Date.now() });\n      assert(Array.isArray(fonts));\n      assert.equal(fonts.length, 0);\n    });\n    \n    it('should return an empty array for nonexistent postscriptName', function() {\n      var fonts = fontManager.findFontsSync({ postscriptName: '' + Date.now() });\n      assert(Array.isArray(fonts));\n      assert.equal(fonts.length, 0);\n    });\n    \n    it('should return many fonts for empty font descriptor', function() {\n      var fonts = fontManager.findFontsSync({});\n      assert(Array.isArray(fonts));\n      assert(fonts.length > 0);\n      fonts.forEach(assertFontDescriptor);\n    });\n  });\n  \n  describe('findFont', function() {\n    it('should throw if no font descriptor is provided', function() {\n      assert.throws(function() {\n        fontManager.findFont(function(fonts) {});\n      }, /Expected a font descriptor/);\n    });\n    \n    it('should throw if font descriptor is not an object', function() {\n      assert.throws(function() {\n        fontManager.findFont(2, function(fonts) {});\n      }, /Expected a font descriptor/);\n    });\n    \n    it('should throw if no callback is provided', function() {\n      assert.throws(function() {\n        fontManager.findFont({ family: standardFont });\n      }, /Expected a callback/);\n    });\n    \n    it('should throw if callback is not a function', function() {\n      assert.throws(function() {\n        fontManager.findFont({ family: standardFont }, 2);\n      }, /Expected a callback/);\n    });\n    \n    it('should findFont asynchronously', function(done) {\n      var async = false;\n    \n      fontManager.findFont({ family: standardFont }, function(font) {\n        assert(async); \n        assert.equal(typeof font, 'object');\n        assert(!Array.isArray(font));\n        assertFontDescriptor(font);\n        assert.equal(font.family, standardFont);\n        done();\n      });\n    \n      async = true;\n    });\n    \n    it('should find font by postscriptName', function(done) {\n      fontManager.findFont({ postscriptName: postscriptName }, function(font) {\n        assertFontDescriptor(font);\n        assert.equal(font.postscriptName, postscriptName);\n        assert.equal(font.family, standardFont);\n        done();\n      });\n    });\n    \n    it('should find font by family and style', function(done) {\n      fontManager.findFont({ family: standardFont, style: 'Bold' }, function(font) {\n        assertFontDescriptor(font);\n        assert.equal(font.family, standardFont);\n        assert.equal(font.style, 'Bold');\n        assert.equal(font.weight, 700);\n        done();\n      });\n    });\n    \n    it('should find font by weight', function(done) {\n      fontManager.findFont({ family: standardFont, weight: 700 }, function(font) {\n        assertFontDescriptor(font);\n        assert.equal(font.weight, 700);\n        done();\n      });\n    });\n    \n    it('should find italic font', function(done) {\n      fontManager.findFont({ family: standardFont, italic: true }, function(font) {\n        assertFontDescriptor(font);\n        assert.equal(font.italic, true);\n        done();\n      });\n    });\n    \n    it('should find bold italic font', function(done) {\n      fontManager.findFont({ family: standardFont, italic: true, weight: 700 }, function(font) {\n        assertFontDescriptor(font);\n        assert.equal(font.italic, true);\n        assert.equal(font.weight, 700);\n        done();\n      });\n    });\n    \n    it('should return a fallback font for nonexistent family', function(done) {\n      fontManager.findFont({ family: '' + Date.now() }, function(font) {\n        assertFontDescriptor(font);\n        done();\n      });\n    });\n    \n    it('should return a fallback font for nonexistent postscriptName', function(done) {\n      fontManager.findFont({ postscriptName: '' + Date.now() }, function(font) {\n        assertFontDescriptor(font);\n        done();\n      });\n    });\n    \n    it('should return a fallback font matching traits as best as possible', function(done) {\n      fontManager.findFont({ family: '' + Date.now(), weight: 700 }, function(font) {\n        assertFontDescriptor(font);\n        assert.equal(font.weight, 700);\n        done();\n      });\n    });\n    \n    it('should return a font for empty font descriptor', function(done) {\n      fontManager.findFont({}, function(font) {\n        assertFontDescriptor(font);\n        done();\n      });\n    });\n  });\n  \n  describe('findFontSync', function() {\n    it('should throw if no font descriptor is provided', function() {\n      assert.throws(function() {\n        fontManager.findFontSync();\n      }, /Expected a font descriptor/);\n    });\n    \n    it('should throw if font descriptor is not an object', function() {\n      assert.throws(function() {\n        fontManager.findFontSync(2);\n      }, /Expected a font descriptor/);\n    });\n    \n    it('should findFonts synchronously', function() {\n      var font = fontManager.findFontSync({ family: standardFont });\n      assert.equal(typeof font, 'object');\n      assert(!Array.isArray(font));\n      assertFontDescriptor(font);\n    });\n    \n    it('should find font by postscriptName', function() {\n      var font = fontManager.findFontSync({ postscriptName: postscriptName });\n      assertFontDescriptor(font);\n      assert.equal(font.postscriptName, postscriptName);\n      assert.equal(font.family, standardFont);\n    });\n    \n    it('should find font by family and style', function() {\n      var font = fontManager.findFontSync({ family: standardFont, style: 'Bold' });\n      assertFontDescriptor(font);\n      assert.equal(font.family, standardFont);\n      assert.equal(font.style, 'Bold');\n      assert.equal(font.weight, 700);\n    });\n    \n    it('should find font by weight', function() {\n      var font = fontManager.findFontSync({ family: standardFont, weight: 700 });\n      assertFontDescriptor(font);\n      assert.equal(font.weight, 700);\n    });\n    \n    it('should find italic font', function() {\n      var font = fontManager.findFontSync({ family: standardFont, italic: true });\n      assertFontDescriptor(font);\n      assert.equal(font.italic, true);\n    });\n    \n    it('should find bold italic font', function() {\n      var font = fontManager.findFontSync({ family: standardFont, italic: true, weight: 700 });\n      assertFontDescriptor(font);\n      assert.equal(font.italic, true);\n      assert.equal(font.weight, 700);\n    });\n    \n    it('should return a fallback font for nonexistent family', function() {\n      var font = fontManager.findFontSync({ family: '' + Date.now() });\n      assertFontDescriptor(font);\n    });\n    \n    it('should return a fallback font for nonexistent postscriptName', function() {\n      var font = fontManager.findFontSync({ postscriptName: '' + Date.now() });\n      assertFontDescriptor(font);\n    });\n    \n    it('should return a fallback font matching traits as best as possible', function() {\n      var font = fontManager.findFontSync({ family: '' + Date.now(), weight: 700 });\n      assertFontDescriptor(font);\n      assert.equal(font.weight, 700);\n    });\n    \n    it('should return a font for empty font descriptor', function() {\n      var font = fontManager.findFontSync({});\n      assertFontDescriptor(font);\n    });\n  });\n  \n  describe('substituteFont', function() {\n    it('should throw if no postscript name is provided', function() {\n      assert.throws(function() {\n        fontManager.substituteFont(function(font) {});\n      }, /Expected postscript name/);\n    });\n    \n    it('should throw if postscript name is not a string', function() {\n      assert.throws(function() {\n        fontManager.substituteFont(2, 'hi', function(font) {});\n      }, /Expected postscript name/);\n    });\n    \n    it('should throw if no substitution string is provided', function() {\n      assert.throws(function() {\n        fontManager.substituteFont(postscriptName, function(font) {});\n      }, /Expected substitution string/);\n    });\n    \n    it('should throw if substitution string is not a string', function() {\n      assert.throws(function() {\n        fontManager.substituteFont(postscriptName, 2, function(font) {});\n      }, /Expected substitution string/);\n    });\n    \n    it('should throw if no callback is provided', function() {\n      assert.throws(function() {\n        fontManager.substituteFont(postscriptName, '汉字');\n      }, /Expected a callback/);\n    });\n    \n    it('should throw if callback is not a function', function() {\n      assert.throws(function() {\n        fontManager.substituteFont(postscriptName, '汉字', 52);\n      }, /Expected a callback/);\n    });\n    \n    it('should substituteFont asynchronously', function(done) {\n      var async = false;\n    \n      fontManager.substituteFont(postscriptName, '汉字', function(font) {\n        assert(async); \n        assert.equal(typeof font, 'object');\n        assert(!Array.isArray(font));\n        assertFontDescriptor(font);\n        assert.notEqual(font.postscriptName, postscriptName);\n        done();\n      });\n    \n      async = true;\n    });\n    \n    it('should return the same font if it already contains the requested characters', function(done) {\n      fontManager.substituteFont(postscriptName, 'hi', function(font) {\n        assertFontDescriptor(font);\n        assert.equal(font.postscriptName, postscriptName);\n        done();\n      });\n    });\n    \n    it('should return a default font if no font exists for the given postscriptName', function(done) {\n      fontManager.substituteFont('' + Date.now(), '汉字', function(font) {\n        assertFontDescriptor(font);\n        done();\n      });\n    });\n  });\n  \n  describe('substituteFontSync', function() {\n    it('should throw if no postscript name is provided', function() {\n      assert.throws(function() {\n        fontManager.substituteFontSync();\n      }, /Expected postscript name/);\n    });\n    \n    it('should throw if postscript name is not a string', function() {\n      assert.throws(function() {\n        fontManager.substituteFontSync(2, 'hi');\n      }, /Expected postscript name/);\n    });\n    \n    it('should throw if no substitution string is provided', function() {\n      assert.throws(function() {\n        fontManager.substituteFontSync(postscriptName);\n      }, /Expected substitution string/);\n    });\n    \n    it('should throw if substitution string is not a string', function() {\n      assert.throws(function() {\n        fontManager.substituteFontSync(postscriptName, 2);\n      }, /Expected substitution string/);\n    });\n    \n    it('should substituteFont synchronously', function() {\n      var font = fontManager.substituteFontSync(postscriptName, '汉字');\n      assert.equal(typeof font, 'object');\n      assert(!Array.isArray(font));\n      assertFontDescriptor(font);\n      assert.notEqual(font.postscriptName, postscriptName);\n    });\n    \n    it('should return the same font if it already contains the requested characters', function() {\n      var font = fontManager.substituteFontSync(postscriptName, 'hi');\n      assertFontDescriptor(font);\n      assert.equal(font.postscriptName, postscriptName);\n    });\n    \n    it('should return a default font if no font exists for the given postscriptName', function() {\n      var font = fontManager.substituteFontSync('' + Date.now(), '汉字');\n      assertFontDescriptor(font);\n    });\n  });\n});\n"
  },
  {
    "path": "test/mocha.opts",
    "content": "--reporter spec"
  },
  {
    "path": "travis-linux.sh",
    "content": "#!/bin/bash\n\n# make sure we install the new version of libfontconfig1-dev\nsudo apt-add-repository 'deb http://archive.ubuntu.com/ubuntu trusty main restricted universe multiverse'\nsudo apt-get -yqq update\nsudo apt-get install -y -t trusty libfontconfig1-dev\n\n# install some fonts needed for the tests\nsudo apt-get install -y fonts-droid fonts-liberation\n"
  }
]