buzzfont
Version:
Your Own Powerful Google Fonts API
230 lines (208 loc) • 9.06 kB
text/typescript
import { NanoSQLInstance } from "nano-sql";
import Axios from "axios";
import { Promise as LPromise } from "lie-ts";
import { IgoogleFont } from "./IgFont";
import * as puppeteer from "puppeteer";
import * as fs from "fs";
export interface IbuzzFont extends IgoogleFont {
id: string;
styleURL: string;
popularity: number;
previewURL: string;
}
export class BuzzFont {
public apiKey: string;
private _nanoSQL: NanoSQLInstance;
private _chromePage: any;
private _baseURL: string;
public nSQL: (table?: string) => NanoSQLInstance;
constructor(args: {
apiKey: string;
ready?: () => void;
refreshCache?: number; // days
baseURL?: string;
previewFontSize?: number;
previewSetWidth?: number;
}) {
this._nanoSQL = new NanoSQLInstance();
this.apiKey = args.apiKey
this._baseURL = args.baseURL;
this.nSQL = (table?: string) => {
return this._nanoSQL.table(table);
};
if (args.refreshCache) {
setInterval(this._requestFontUpdate, args.refreshCache * 24 * 60 * 60 * 1000);
}
let browser: any;
if (!fs.existsSync("./previews")) {
fs.mkdirSync("./previews");
this._setupDB().then(() => {
return new LPromise((res, rej) => {
puppeteer.launch({
timeout: 1000
}).then((br) => {
browser = br;
return br.newPage();
}).then((page) => {
this._chromePage = page;
res();
});
});
}).then(() => {
return this._requestFontUpdate();
}).then(() => {
return browser.close();
}).then(args.ready);
} else {
this._setupDB().then(args.ready);
}
}
private _setupDB(): LPromise<any> {
return this.nSQL("fonts")
.model([
{key: "id", type: "uuid", props: ["pk"]},
{key: "popularity", type: "int"},
{key: "family", type: "string"},
{key: "category", type: "string"},
{key: "styleURL", type: "string"},
{key: "variants", type: "string[]"},
{key: "subsets", type: "string[]"},
{key: "version", type: "string"},
{key: "lastModified", type: "string"},
{key: "files", type: "map"},
{key: "previewURL", type: "string"}
])
.config({
id: "font",
persistent: true,
history: false
})
.connect();
}
public renderFont(args: {
fontFamily: string,
fontVariant: string,
text?: string,
fontSize?: number,
forceWidth?: number,
}): LPromise<any> {
let browser: any;
return new LPromise((res, rej) => {
puppeteer.launch({
timeout: 10000
}).then((br) => {
browser = br;
return br.newPage();
}).then((page) => {
this._chromePage = page;
return this._generateFontPreview({
...args,
returnB64: true
});
}).then((b64) => {
res(b64);
browser.close();
});
});
}
private _generateFontPreview(args: {
fontFamily: string,
fontVariant: string,
text?: string,
fontSize?: number,
returnB64?: boolean;
forceWidth?: number,
}): LPromise<any> {
return new LPromise((res, rej) => {
const rand = NanoSQLInstance.uuid();
args.text = args.text || "The quick brown fox jumps over the lazy dog.";
args.fontSize = args.fontSize || 30;
this._chromePage.setViewport({ width: args.forceWidth || 800, height: 600 }).then(() => {
return this._chromePage.setContent(`<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.28/webfontloader.js"></script>
<script>
WebFont.load({
google: {
families: ["${args.fontFamily}:${args.fontVariant}"]
},
active: () => {
document.body.style.fontFamily = "${args.fontFamily}";
document.body.classList.add("ready");
}
});
</script>
</head>
<body style="margin:0px;font-size:${args.fontSize}px"><div class="text" style="padding:5px;display:inline-block;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">${args.text}<div></body>
</html>`);
}).then(() => {
return this._chromePage.waitForSelector("body.ready");
}).then(() => {
return this._chromePage.evaluate(() => {
return new Promise((res, rej) => {
res({
width: document.querySelector(".text").clientWidth,
height: document.querySelector(".text").clientHeight
})
});
});
}).then((resolution) => {
return this._chromePage.setViewport({
width: args.forceWidth ? args.forceWidth : resolution.width,
height: resolution.height
});
}).then(() => {
return this._chromePage.screenshot({ path: args.returnB64 ? `./previews/${rand}.png` : `./previews/${args.fontFamily.replace(/ /g, "+")}.png` });
}).then(() => {
if (args.returnB64) {
// read binary data
fs.readFile(`./previews/${rand}.png`, (err, data) => {
fs.unlink(`./previews/${rand}.png`, () => {
res(new Buffer(data).toString('base64'));
});
});
} else {
res(`${this._baseURL}/previews/${args.fontFamily.replace(/ /g, "+")}.png`);
}
}).catch((err) => {
res(false);
});
});
}
private _requestFontUpdate(): LPromise<any> {
return new LPromise((res, rej) => {
Axios.get(`https://www.googleapis.com/webfonts/v1/webfonts?key=${this.apiKey}&sort=popularity`).then((response) => {
const gFonts: IgoogleFont[] = response.data.items;
NanoSQLInstance.chain(gFonts.map((gfont, i) => {
return (nextFont) => {
this.nSQL("fonts").query("select").where(["family", "=", gfont.family]).exec().then((bzFonts: IbuzzFont[]) => {
const thisFont = {
...gfont,
styleURL: "https://fonts.googleapis.com/css?family=" + gfont.family.replace(/ /g,"+").trim(),
popularity: gFonts.length - i
};
if (!bzFonts.length) {
console.log(`Generating Font Preview ${i + 1} of ${gFonts.length} (${gfont.family}), ${Math.round(i/gFonts.length * 100)}%`)
this._generateFontPreview({
fontFamily: thisFont.family,
fontVariant: thisFont.variants[0],
forceWidth: 800
}).then((url) => {
if (!url) {
return new Promise((res) => res());
}
return this.nSQL("fonts").query("upsert", {
...thisFont,
previewURL: url
}).exec();
}).then(nextFont);
} else {
this.nSQL("fonts").query("upsert", thisFont).where(["family", "=", gfont.family]).exec().then(nextFont);
}
});
}
}))(res);
});
});
}
}