font-finder
Advanced tools
Comparing version 1.0.3 to 1.0.4
@@ -0,1 +1,8 @@ | ||
## [1.0.4](https://github.com/princjef/font-finder/compare/v1.0.3...v1.0.4) (2018-08-11) | ||
### Bug Fixes | ||
* **extract:** extract metadata from fonts with no OS/2 table ([7f98cca](https://github.com/princjef/font-finder/commit/7f98cca)) | ||
## [1.0.3](https://github.com/princjef/font-finder/compare/v1.0.2...v1.0.3) (2018-08-11) | ||
@@ -2,0 +9,0 @@ |
@@ -1,3 +0,2 @@ | ||
import { NameTable } from './tables/name'; | ||
import { OS2Table } from './tables/os2'; | ||
import { FontData } from './parse'; | ||
export declare enum Type { | ||
@@ -8,3 +7,3 @@ Serif = "serif", | ||
Cursive = "cursive", | ||
Unknown = "unknown", | ||
Unknown = "unknown" | ||
} | ||
@@ -18,6 +17,7 @@ export declare enum Style { | ||
BoldOblique = "boldOblique", | ||
Other = "other", | ||
Other = "other" | ||
} | ||
export declare function name(names: NameTable, language: string): string; | ||
export declare function type(os2: OS2Table): Type; | ||
export declare function style(os2: OS2Table): Style; | ||
export declare function name(fontData: FontData, language: string): string; | ||
export declare function type(fontData: FontData): Type; | ||
export declare function style(fontData: FontData): Style; | ||
export declare function weight(fontData: FontData): number; |
@@ -30,6 +30,6 @@ "use strict"; | ||
]; | ||
function name(names, language) { | ||
const family = names.preferredFamily && names.preferredFamily[language] | ||
? names.preferredFamily[language] | ||
: names.fontFamily[language]; | ||
function name(fontData, language) { | ||
const family = fontData.names.preferredFamily && fontData.names.preferredFamily[language] | ||
? fontData.names.preferredFamily[language] | ||
: fontData.names.fontFamily[language]; | ||
// On Windows, if the full font name doesn't end with one of the standard | ||
@@ -43,5 +43,5 @@ // forms, the full name is needed to identify the font. Notably, this is not | ||
if (os.platform() === 'win32') { | ||
const subfamily = names.preferredSubfamily && names.preferredSubfamily[language] | ||
? names.preferredSubfamily[language] | ||
: names.fontSubfamily[language]; | ||
const subfamily = fontData.names.preferredSubfamily && fontData.names.preferredSubfamily[language] | ||
? fontData.names.preferredSubfamily[language] | ||
: fontData.names.fontSubfamily[language]; | ||
const fullName = `${family} ${subfamily}`; | ||
@@ -64,18 +64,25 @@ let endIndex = -1; | ||
exports.name = name; | ||
function type(os2) { | ||
// Panose specification: https://monotype.github.io/panose/pan1.htm | ||
switch (os2.panose[0]) { | ||
case 2: | ||
// https://monotype.github.io/panose/pan2.htm#_Toc380547256 | ||
if (os2.panose[3] === 9) { | ||
return Type.Monospace; | ||
} | ||
// https://monotype.github.io/panose/pan2.htm#Sec2SerifStyle | ||
if (os2.panose[1] >= 11 && os2.panose[1] <= 15 || os2.panose[1] === 0) { | ||
return Type.SansSerif; | ||
} | ||
return Type.Serif; | ||
case 3: | ||
return Type.Cursive; | ||
function type(fontData) { | ||
if (fontData.os2) { | ||
// Panose specification: https://monotype.github.io/panose/pan1.htm | ||
switch (fontData.os2.panose[0]) { | ||
case 2: | ||
// https://monotype.github.io/panose/pan2.htm#_Toc380547256 | ||
if (fontData.os2.panose[3] === 9) { | ||
return Type.Monospace; | ||
} | ||
// https://monotype.github.io/panose/pan2.htm#Sec2SerifStyle | ||
if (fontData.os2.panose[1] >= 11 && | ||
fontData.os2.panose[1] <= 15 || | ||
fontData.os2.panose[1] === 0) { | ||
return Type.SansSerif; | ||
} | ||
return Type.Serif; | ||
case 3: | ||
return Type.Cursive; | ||
} | ||
} | ||
else if (fontData.post && fontData.post.isFixedPitch) { | ||
return Type.Monospace; | ||
} | ||
// TODO: better classification | ||
@@ -86,7 +93,24 @@ return Type.Unknown; | ||
// https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fsselection | ||
function style(os2) { | ||
const bold = os2.fsSelection & 0x20; // Bit 5 | ||
const italic = os2.fsSelection & 0x01; // Bit 0 | ||
const oblique = os2.fsSelection & 0x200; // Bit 9 | ||
const regular = os2.fsSelection & 0x140; // Bit 6 or 8 (WWS) | ||
function style(fontData) { | ||
// If we don't have an OS/2 or head table, there's no good way to figure out | ||
// what's in the font | ||
if (!fontData.os2 && !fontData.head) { | ||
return Style.Other; | ||
} | ||
const bold = fontData.os2 | ||
? fontData.os2.fsSelection & 0x20 // OS/2: fsSelection bit 5 | ||
: fontData.head.macStyle & 0x01; // head: macStyle bit 0 | ||
const italic = fontData.os2 | ||
? fontData.os2.fsSelection & 0x01 // OS/2: fsSelection bit 0 | ||
: fontData.post | ||
? fontData.post.italicAngle < 0 // post: negative italicAngle | ||
: fontData.head.macStyle & 0x02; // head: macStyle bit 1 | ||
const oblique = fontData.os2 | ||
? fontData.os2.fsSelection & 0x200 // OS/2: fsSelection bit 9 | ||
: fontData.post | ||
? fontData.post.italicAngle > 0 // post: positive italicAngle | ||
: 0; // head: N/A | ||
const regular = fontData.os2 | ||
? fontData.os2.fsSelection & 0x140 // OS/2: fsSelection bit 6 or 8 (WWS) | ||
: 1; // head: N/A (assume yes for fallback) | ||
if (bold) { | ||
@@ -116,2 +140,18 @@ // Oblique has to come before italic for it to get picked up | ||
exports.style = style; | ||
const boldStyles = [Style.Bold, Style.BoldItalic, Style.BoldOblique]; | ||
function weight(fontData) { | ||
if (fontData.os2) { | ||
// Use the OS/2 weight class if available | ||
return fontData.os2.usWeightClass; | ||
} | ||
else if (boldStyles.includes(style(fontData))) { | ||
// Assume 700 if the font is a bold font | ||
return 700; | ||
} | ||
else { | ||
// Assume the standard 400 if all else fails | ||
return 400; | ||
} | ||
} | ||
exports.weight = weight; | ||
//# sourceMappingURL=extract.js.map |
@@ -30,4 +30,4 @@ "use strict"; | ||
try { | ||
const font = await parse_1.default(file); | ||
return getMetadata(file, font.names, font.os2, opts.language); | ||
const fontData = await parse_1.default(file); | ||
return getMetadata(file, fontData, opts.language); | ||
} | ||
@@ -75,4 +75,4 @@ catch (e) { | ||
const opts = Object.assign({ language: 'en' }, options); | ||
const font = await parse_1.default(path); | ||
return getMetadata(path, font.names, font.os2, opts.language); | ||
const fontData = await parse_1.default(path); | ||
return getMetadata(path, fontData, opts.language); | ||
} | ||
@@ -84,13 +84,12 @@ exports.get = get; | ||
* @param path Absolute path to the font file | ||
* @param names Name table for the font | ||
* @param os2 OS/2 table for the font | ||
* @param fontData Table data for the font | ||
* @param language Language to use when resolving names | ||
*/ | ||
function getMetadata(path, names, os2, language) { | ||
function getMetadata(path, fontData, language) { | ||
return { | ||
name: extract.name(names, language), | ||
name: extract.name(fontData, language), | ||
path, | ||
type: extract.type(os2), | ||
weight: os2.usWeightClass, | ||
style: extract.style(os2) | ||
type: extract.type(fontData), | ||
weight: extract.weight(fontData), | ||
style: extract.style(fontData) | ||
}; | ||
@@ -97,0 +96,0 @@ } |
import { NameTable } from './tables/name'; | ||
import { OS2Table } from './tables/os2'; | ||
import { HeadTable } from './tables/head'; | ||
import { PostTable } from './tables/post'; | ||
export declare type FontData = { | ||
names: NameTable; | ||
os2?: OS2Table; | ||
head?: HeadTable; | ||
post?: PostTable; | ||
}; | ||
/** | ||
@@ -9,5 +17,2 @@ * Loads the bare minimum information needed to retrieve the metadata that we | ||
*/ | ||
export default function parseFont(filePath: string): Promise<{ | ||
names: NameTable; | ||
os2: OS2Table; | ||
}>; | ||
export default function parseFont(filePath: string): Promise<FontData>; |
@@ -8,2 +8,4 @@ "use strict"; | ||
const os2_1 = require("./tables/os2"); | ||
const head_1 = require("./tables/head"); | ||
const post_1 = require("./tables/post"); | ||
var SignatureType; | ||
@@ -27,2 +29,10 @@ (function (SignatureType) { | ||
parse: os2_1.default | ||
}, | ||
head: { | ||
tag: Buffer.from('head'), | ||
parse: head_1.default | ||
}, | ||
post: { | ||
tag: Buffer.from('post'), | ||
parse: post_1.default | ||
} | ||
@@ -84,9 +94,8 @@ }; | ||
} | ||
if (!tableData.os2) { | ||
throw new Error(`missing required OpenType table 'OS/2' in font file: ${filePath}`); | ||
} | ||
// Parse and return the tables we need | ||
return { | ||
names: tableInfo.name.parse(tableData.name, ltag), | ||
os2: tableInfo.os2.parse(tableData.os2) | ||
os2: tableData.os2 && tableInfo.os2.parse(tableData.os2), | ||
head: tableData.head && tableInfo.head.parse(tableData.head), | ||
post: tableData.post && tableInfo.post.parse(tableData.post) | ||
}; | ||
@@ -93,0 +102,0 @@ case SignatureType.Woff: |
/// <reference types="node" /> | ||
declare function parseLtagTable(data: Buffer): string[]; | ||
export default parseLtagTable; | ||
export default function parseLtagTable(data: Buffer): string[]; |
@@ -7,3 +7,2 @@ /// <reference types="node" /> | ||
} | ||
declare function parseNameTable(data: Buffer, ltag: string[]): NameTable; | ||
export default parseNameTable; | ||
export default function parseNameTable(data: Buffer, ltag: string[]): NameTable; |
@@ -520,2 +520,3 @@ "use strict"; | ||
} | ||
exports.default = parseNameTable; | ||
// Data for converting old eight-bit Macintosh encodings to Unicode. | ||
@@ -596,3 +597,2 @@ // This representation is optimized for decoding; encoding is slower | ||
} | ||
exports.default = parseNameTable; | ||
//# sourceMappingURL=name.js.map |
@@ -41,3 +41,2 @@ /// <reference types="node" /> | ||
} | ||
declare function parseOS2Table(data: Buffer): OS2Table; | ||
export default parseOS2Table; | ||
export default function parseOS2Table(data: Buffer): OS2Table; |
{ | ||
"name": "font-finder", | ||
"version": "1.0.3", | ||
"version": "1.0.4", | ||
"description": "Quickly find system font names and metadata without native dependencies", | ||
@@ -63,5 +63,5 @@ "homepage": "https://github.com/princjef/font-finder#readme", | ||
"sinon": "^4.5.0", | ||
"tslint": "^5.9.1", | ||
"tslint": "^5.11.0", | ||
"tslint-config-standard": "^7.0.0", | ||
"typescript": "^2.8.3" | ||
"typescript": "^3.0.1" | ||
}, | ||
@@ -68,0 +68,0 @@ "dependencies": { |
import * as os from 'os'; | ||
import { NameTable } from './tables/name'; | ||
import { OS2Table } from './tables/os2'; | ||
import { FontData } from './parse'; | ||
@@ -33,6 +32,6 @@ export enum Type { | ||
export function name(names: NameTable, language: string): string { | ||
const family = names.preferredFamily && names.preferredFamily[language] | ||
? names.preferredFamily[language] | ||
: names.fontFamily[language]; | ||
export function name(fontData: FontData, language: string): string { | ||
const family = fontData.names.preferredFamily && fontData.names.preferredFamily[language] | ||
? fontData.names.preferredFamily[language] | ||
: fontData.names.fontFamily[language]; | ||
@@ -47,5 +46,5 @@ // On Windows, if the full font name doesn't end with one of the standard | ||
if (os.platform() === 'win32') { | ||
const subfamily = names.preferredSubfamily && names.preferredSubfamily[language] | ||
? names.preferredSubfamily[language] | ||
: names.fontSubfamily[language]; | ||
const subfamily = fontData.names.preferredSubfamily && fontData.names.preferredSubfamily[language] | ||
? fontData.names.preferredSubfamily[language] | ||
: fontData.names.fontSubfamily[language]; | ||
const fullName = `${family} ${subfamily}`; | ||
@@ -72,19 +71,27 @@ | ||
export function type(os2: OS2Table): Type { | ||
// Panose specification: https://monotype.github.io/panose/pan1.htm | ||
switch (os2.panose[0]) { | ||
case 2: | ||
// https://monotype.github.io/panose/pan2.htm#_Toc380547256 | ||
if (os2.panose[3] === 9) { | ||
return Type.Monospace; | ||
} | ||
export function type(fontData: FontData): Type { | ||
if (fontData.os2) { | ||
// Panose specification: https://monotype.github.io/panose/pan1.htm | ||
switch (fontData.os2.panose[0]) { | ||
case 2: | ||
// https://monotype.github.io/panose/pan2.htm#_Toc380547256 | ||
if (fontData.os2.panose[3] === 9) { | ||
return Type.Monospace; | ||
} | ||
// https://monotype.github.io/panose/pan2.htm#Sec2SerifStyle | ||
if (os2.panose[1] >= 11 && os2.panose[1] <= 15 || os2.panose[1] === 0) { | ||
return Type.SansSerif; | ||
} | ||
// https://monotype.github.io/panose/pan2.htm#Sec2SerifStyle | ||
if ( | ||
fontData.os2.panose[1] >= 11 && | ||
fontData.os2.panose[1] <= 15 || | ||
fontData.os2.panose[1] === 0 | ||
) { | ||
return Type.SansSerif; | ||
} | ||
return Type.Serif; | ||
case 3: | ||
return Type.Cursive; | ||
return Type.Serif; | ||
case 3: | ||
return Type.Cursive; | ||
} | ||
} else if (fontData.post && fontData.post.isFixedPitch) { | ||
return Type.Monospace; | ||
} | ||
@@ -97,8 +104,26 @@ | ||
// https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fsselection | ||
export function style(os2: OS2Table): Style { | ||
const bold = os2.fsSelection & 0x20; // Bit 5 | ||
const italic = os2.fsSelection & 0x01; // Bit 0 | ||
const oblique = os2.fsSelection & 0x200; // Bit 9 | ||
const regular = os2.fsSelection & 0x140; // Bit 6 or 8 (WWS) | ||
export function style(fontData: FontData): Style { | ||
// If we don't have an OS/2 or head table, there's no good way to figure out | ||
// what's in the font | ||
if (!fontData.os2 && !fontData.head) { | ||
return Style.Other; | ||
} | ||
const bold = fontData.os2 | ||
? fontData.os2.fsSelection & 0x20 // OS/2: fsSelection bit 5 | ||
: fontData.head!.macStyle & 0x01; // head: macStyle bit 0 | ||
const italic = fontData.os2 | ||
? fontData.os2.fsSelection & 0x01 // OS/2: fsSelection bit 0 | ||
: fontData.post | ||
? fontData.post.italicAngle < 0 // post: negative italicAngle | ||
: fontData.head!.macStyle & 0x02; // head: macStyle bit 1 | ||
const oblique = fontData.os2 | ||
? fontData.os2.fsSelection & 0x200 // OS/2: fsSelection bit 9 | ||
: fontData.post | ||
? fontData.post.italicAngle > 0 // post: positive italicAngle | ||
: 0; // head: N/A | ||
const regular = fontData.os2 | ||
? fontData.os2.fsSelection & 0x140 // OS/2: fsSelection bit 6 or 8 (WWS) | ||
: 1; // head: N/A (assume yes for fallback) | ||
if (bold) { | ||
@@ -133,1 +158,16 @@ // Oblique has to come before italic for it to get picked up | ||
} | ||
const boldStyles = [Style.Bold, Style.BoldItalic, Style.BoldOblique]; | ||
export function weight(fontData: FontData): number { | ||
if (fontData.os2) { | ||
// Use the OS/2 weight class if available | ||
return fontData.os2.usWeightClass; | ||
} else if (boldStyles.includes(style(fontData))) { | ||
// Assume 700 if the font is a bold font | ||
return 700; | ||
} else { | ||
// Assume the standard 400 if all else fails | ||
return 400; | ||
} | ||
} |
import getSystemFonts from 'get-system-fonts'; | ||
import parse from './parse'; | ||
import parse, { FontData } from './parse'; | ||
import * as extract from './extract'; | ||
import { NameTable } from './tables/name'; | ||
import { OS2Table } from './tables/os2'; | ||
@@ -98,4 +96,4 @@ export { Type, Style } from './extract'; | ||
try { | ||
const font = await parse(file); | ||
return getMetadata(file, font.names, font.os2, opts.language); | ||
const fontData = await parse(file); | ||
return getMetadata(file, fontData, opts.language); | ||
} catch (e) { | ||
@@ -152,4 +150,4 @@ // Don't swallow language errors | ||
const font = await parse(path); | ||
return getMetadata(path, font.names, font.os2, opts.language); | ||
const fontData = await parse(path); | ||
return getMetadata(path, fontData, opts.language); | ||
} | ||
@@ -161,13 +159,12 @@ | ||
* @param path Absolute path to the font file | ||
* @param names Name table for the font | ||
* @param os2 OS/2 table for the font | ||
* @param fontData Table data for the font | ||
* @param language Language to use when resolving names | ||
*/ | ||
function getMetadata(path: string, names: NameTable, os2: OS2Table, language: string): NamedFont { | ||
function getMetadata(path: string, fontData: FontData, language: string): NamedFont { | ||
return { | ||
name: extract.name(names, language), | ||
name: extract.name(fontData, language), | ||
path, | ||
type: extract.type(os2), | ||
weight: os2.usWeightClass, | ||
style: extract.style(os2) | ||
type: extract.type(fontData), | ||
weight: extract.weight(fontData), | ||
style: extract.style(fontData) | ||
}; | ||
@@ -174,0 +171,0 @@ } |
@@ -7,2 +7,4 @@ import * as fs from 'fs'; | ||
import parseOS2Table, { OS2Table } from './tables/os2'; | ||
import parseHeadTable, { HeadTable } from './tables/head'; | ||
import parsePostTable, { PostTable } from './tables/post'; | ||
@@ -27,5 +29,20 @@ enum SignatureType { | ||
parse: parseOS2Table | ||
}, | ||
head: { | ||
tag: Buffer.from('head'), | ||
parse: parseHeadTable | ||
}, | ||
post: { | ||
tag: Buffer.from('post'), | ||
parse: parsePostTable | ||
} | ||
}; | ||
export type FontData = { | ||
names: NameTable; | ||
os2?: OS2Table; | ||
head?: HeadTable; | ||
post?: PostTable | ||
}; | ||
/** | ||
@@ -37,4 +54,4 @@ * Loads the bare minimum information needed to retrieve the metadata that we | ||
*/ | ||
export default async function parseFont(filePath: string): Promise<{ names: NameTable; os2: OS2Table; }> { | ||
return new Promise<{ names: NameTable; os2: OS2Table; }>((resolve, reject) => { | ||
export default async function parseFont(filePath: string): Promise<FontData> { | ||
return new Promise<FontData>((resolve, reject) => { | ||
(async () => { | ||
@@ -96,11 +113,9 @@ const pStream = promiseStream(); | ||
if (!tableData.os2) { | ||
throw new Error(`missing required OpenType table 'OS/2' in font file: ${filePath}`); | ||
} | ||
// Parse and return the tables we need | ||
return { | ||
names: tableInfo.name.parse(tableData.name, ltag), | ||
os2: tableInfo.os2.parse(tableData.os2) | ||
}; | ||
os2: tableData.os2 && tableInfo.os2.parse(tableData.os2), | ||
head: tableData.head && tableInfo.head.parse(tableData.head), | ||
post: tableData.post && tableInfo.post.parse(tableData.post) | ||
} as FontData; | ||
case SignatureType.Woff: | ||
@@ -107,0 +122,0 @@ default: |
@@ -11,3 +11,3 @@ // This file is modified from opentype.js. All credit for the capabilities | ||
function parseLtagTable(data: Buffer) { | ||
export default function parseLtagTable(data: Buffer) { | ||
const tableVersion = data.readUInt32BE(0); | ||
@@ -35,3 +35,1 @@ if (tableVersion !== 1) { | ||
} | ||
export default parseLtagTable; |
@@ -501,3 +501,3 @@ // This file is modified from opentype.js. All credit for the capabilities | ||
// ltag is the content of the `ltag' table, such as ['en', 'zh-Hans', 'de-CH-1904']. | ||
function parseNameTable(data: Buffer, ltag: string[]): NameTable { | ||
export default function parseNameTable(data: Buffer, ltag: string[]): NameTable { | ||
const name: NameTable = {}; | ||
@@ -625,3 +625,1 @@ // const format = data.readUInt16BE(0); | ||
} | ||
export default parseNameTable; |
@@ -49,3 +49,3 @@ // This file is modified from opentype.js. All credit for the capabilities | ||
// Parse the OS/2 and Windows metrics `OS/2` table | ||
function parseOS2Table(data: Buffer): OS2Table { | ||
export default function parseOS2Table(data: Buffer): OS2Table { | ||
const os2: OS2Table = { | ||
@@ -115,3 +115,1 @@ version: data.readUInt16BE(0), | ||
} | ||
export default parseOS2Table; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
136621
44
2734