Skip to content

Instantly share code, notes, and snippets.

@jseppi
Created December 17, 2019 16:40
Show Gist options
  • Save jseppi/129e8d1b9c7cc43f2efdd3e7bded0d4f to your computer and use it in GitHub Desktop.
Save jseppi/129e8d1b9c7cc43f2efdd3e7bded0d4f to your computer and use it in GitHub Desktop.
Render font previews // source https://jsbin.com/lomovel
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Render font previews</title>
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<script src="https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.js"></script>
<link
href="https://api.tiles.mapbox.com/mapbox-gl-js/v1.5.0/mapbox-gl.css"
rel="stylesheet"
/>
<link
href="https://api.mapbox.com/mapbox-assembly/v0.24.0/assembly.min.css"
rel="stylesheet"
/>
<script
async
defer
src="https://api.mapbox.com/mapbox-assembly/v0.24.0/assembly.js"
></script>
<style>
body {
margin: 0;
padding: 0;
background: lightblue;
}
.preview-image {
border: 1px solid black;
height:60px;
display:block;
margin-top: 10px;
}
</style>
</head>
<body>
<div id="render_map_1" style="position:absolute;top:-200px;width:800px; height: 120px; visibility: hidden;" class="border"></div>
<script id="jsbin-javascript">
'use strict';
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }
mapboxgl.accessToken = "pk.eyJ1IjoianNlcHBpbWJ4IiwiYSI6ImNqbGU1ODdtMzBpZjUzcG1pMWJnaHB2aHgifQ.xGVwKUpyJ-S5iyaLq7GFLA";
var owner = 'jseppimbx';
var renderMap1 = new mapboxgl.Map({
container: 'render_map_1',
center: [0.525, 0], // TODO: real math?
zoom: 9,
pitch: 0,
bearing: 0,
fadeDuration: 0,
preserveDrawingBuffer: true,
localIdeographFontFamily: false
});
var renderMap1Busy = false;
function generateFontPreviewStyle(_ref) {
var owner = _ref.owner;
var name = _ref.name;
var text = _ref.text;
var color = _ref.color;
return {
version: 8,
glyphs: 'mapbox://fonts/' + owner + '/{fontstack}/{range}.pbf',
sources: {
font: {
type: "geojson",
data: {
type: "FeatureCollection",
features: [{
type: "Feature",
geometry: {
type: "Point",
coordinates: [0, 0]
},
properties: {}
}]
}
}
},
layers: [{
id: "preview",
source: "font",
type: "symbol",
layout: {
"text-justify": "left",
"text-anchor": "left",
"text-field": text,
"text-font": [name],
"text-max-width": 200,
"text-size": 80,
"text-line-height": 5 // TODO: does this do anything?
},
paint: {
"text-color": color,
"text-halo-color": color,
"text-halo-width": 0.3,
"text-halo-blur": 0
}
}, {
// Show a circle here for easier
// positioning debugging
id: "markers",
source: "font",
type: "circle",
layout: {},
paint: {
"circle-radius": 5,
"circle-color": "yellow",
"circle-stroke-width": 0
}
}]
};
}
// ***** redux-like stuff
var state = {};
state.fontPreviewsToRender = new Set();
state.renderedFontPreviews = {};
function makeFontKey(name, color, text) {
return name + '^' + color + '^' + text;
}
function fontKeyToParams(key) {
var parts = key.split('^');
return {
name: parts[0],
color: parts[1],
text: parts[2]
};
}
function addFontPreviewToRender(name, color, text) {
var key = makeFontKey(name, color, text);
if (!state.fontPreviewsToRender.has(key)) {
state.fontPreviewsToRender.add(key);
}
}
function getFontPreviewsToRender() {
var paramsToRender = [];
state.fontPreviewsToRender.forEach(function (key) {
var params = fontKeyToParams(key);
paramsToRender.push(params);
});
return paramsToRender;
}
function saveRenderedFontPreviewData(name, color, text, dataURL) {
var key = makeFontKey(name, color, text);
state.renderedFontPreviews[key] = dataURL;
if (state.fontPreviewsToRender.has(key)) {
state.fontPreviewsToRender['delete'](key);
}
}
// ***** fake output helpers
function addImage(dataURL) {
var img = document.createElement("img");
img.src = dataURL;
img.classList.add('preview-image');
document.body.appendChild(img);
}
function doneGeneratingPreviews() {
for (key in state.renderedFontPreviews) {
addImage(state.renderedFontPreviews[key]);
}
}
// ***** render component stuff
var demoFontsToRender = ["Komika Hand Bold Italic", "Komika Hand Bold", "Komika Hand Italic", "Komika Hand Regular", "Komika Parch Regular", "Komika Title - Axis Regular", "Komika Title - Kaps Regular", "Komika Title - Paint Regular", "Komika Title - Wide Regular", "Komika Title Regular"];
var color = '#ff00ff';
demoFontsToRender.forEach(function (name) {
addFontPreviewToRender(name, color, name);
});
// get initial list of job params
var initialJobParams = getFontPreviewsToRender();
var i = 0;
var windowMethod = 'requestAnimationFrame'; // requestAnimationFrame
function processRenderJobs(jobParams) {
if (i > 100) {
console.log('emergency brake');
return;
}
if (renderMap1Busy) {
// requeue
window[windowMethod](function () {
return processRenderJobs(jobParams);
});
return;
}
// else
renderMap1Busy = true;
var _jobParams = _toArray(jobParams);
var params = _jobParams[0];
var remainingParams = _jobParams.slice(1);
var style = generateFontPreviewStyle({
owner: owner, name: params.name, text: params.text, color: params.color
});
renderMap1.setStyle(style);
renderMap1.once('idle', function () {
var dataURL = renderMap1.getCanvas().toDataURL();
// Save the rendered image data into state
saveRenderedFontPreviewData(params.name, params.text, params.color, dataURL);
renderMap1Busy = false;
// we have more to process, so queue up the remaining
if (remainingParams.length) {
window[windowMethod](function () {
return processRenderJobs(remainingParams);
});
} else {
doneGeneratingPreviews();
}
});
}
window[windowMethod](function () {
return processRenderJobs(initialJobParams);
});
</script>
<script id="jsbin-source-javascript" type="text/javascript">mapboxgl.accessToken = "pk.eyJ1IjoianNlcHBpbWJ4IiwiYSI6ImNqbGU1ODdtMzBpZjUzcG1pMWJnaHB2aHgifQ.xGVwKUpyJ-S5iyaLq7GFLA";
const owner = 'jseppimbx';
const renderMap1 = new mapboxgl.Map({
container: 'render_map_1',
center: [0.525, 0], // TODO: real math?
zoom: 9,
pitch: 0,
bearing: 0,
fadeDuration: 0,
preserveDrawingBuffer: true,
localIdeographFontFamily: false
});
let renderMap1Busy = false;
function generateFontPreviewStyle({ owner, name, text, color }) {
return {
version: 8,
glyphs: `mapbox://fonts/${owner}/{fontstack}/{range}.pbf`,
sources: {
font: {
type: "geojson",
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [0, 0]
},
properties: {}
}
]
}
}
},
layers: [
{
id: "preview",
source: "font",
type: "symbol",
layout: {
"text-justify": "left",
"text-anchor": "left",
"text-field": text,
"text-font": [name],
"text-max-width": 200,
"text-size": 80,
"text-line-height": 5 // TODO: does this do anything?
},
paint: {
"text-color": color,
"text-halo-color": color,
"text-halo-width": 0.3,
"text-halo-blur": 0
}
},
{
// Show a circle here for easier
// positioning debugging
id: "markers",
source: "font",
type: "circle",
layout: {},
paint: {
"circle-radius": 5,
"circle-color": "yellow",
"circle-stroke-width": 0
}
}
]
};
}
// ***** redux-like stuff
const state = {};
state.fontPreviewsToRender = new Set();
state.renderedFontPreviews = {};
function makeFontKey(name, color, text) {
return `${name}^${color}^${text}`;
}
function fontKeyToParams(key) {
const parts = key.split('^');
return {
name: parts[0],
color: parts[1],
text: parts[2]
};
}
function addFontPreviewToRender(name, color, text) {
const key = makeFontKey(name, color, text);
if (!state.fontPreviewsToRender.has(key)) {
state.fontPreviewsToRender.add(key);
}
}
function getFontPreviewsToRender() {
const paramsToRender = [];
state.fontPreviewsToRender.forEach((key) => {
const params = fontKeyToParams(key);
paramsToRender.push(params);
});
return paramsToRender;
}
function saveRenderedFontPreviewData(name, color, text, dataURL) {
const key = makeFontKey(name, color, text);
state.renderedFontPreviews[key] = dataURL;
if (state.fontPreviewsToRender.has(key)) {
state.fontPreviewsToRender.delete(key);
}
}
// ***** fake output helpers
function addImage(dataURL) {
const img = document.createElement("img");
img.src = dataURL;
img.classList.add('preview-image');
document.body.appendChild(img);
}
function doneGeneratingPreviews() {
for (key in state.renderedFontPreviews) {
addImage(state.renderedFontPreviews[key])
}
}
// ***** render component stuff
const demoFontsToRender = [
"Komika Hand Bold Italic",
"Komika Hand Bold",
"Komika Hand Italic",
"Komika Hand Regular",
"Komika Parch Regular",
"Komika Title - Axis Regular",
"Komika Title - Kaps Regular",
"Komika Title - Paint Regular",
"Komika Title - Wide Regular",
"Komika Title Regular"
];
const color = '#ff00ff';
demoFontsToRender.forEach((name) => {
addFontPreviewToRender(name, color, name);
});
// get initial list of job params
const initialJobParams = getFontPreviewsToRender();
let i = 0;
const windowMethod = 'requestAnimationFrame'; // requestAnimationFrame
function processRenderJobs(jobParams) {
if (i > 100) {
console.log('emergency brake');
return;
}
if (renderMap1Busy) {
// requeue
window[windowMethod](() => processRenderJobs(jobParams));
return;
}
// else
renderMap1Busy = true;
const [params, ...remainingParams] = jobParams;
const style = generateFontPreviewStyle({
owner, name: params.name, text: params.text, color: params.color
});
renderMap1.setStyle(style);
renderMap1.once('idle', () => {
const dataURL = renderMap1.getCanvas().toDataURL();
// Save the rendered image data into state
saveRenderedFontPreviewData(params.name, params.text, params.color, dataURL)
renderMap1Busy = false;
// we have more to process, so queue up the remaining
if (remainingParams.length) {
window[windowMethod](() => processRenderJobs(remainingParams));
} else {
doneGeneratingPreviews();
}
});
}
window[windowMethod](() => processRenderJobs(initialJobParams));
</script></body>
</html>
'use strict';
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }
mapboxgl.accessToken = "pk.eyJ1IjoianNlcHBpbWJ4IiwiYSI6ImNqbGU1ODdtMzBpZjUzcG1pMWJnaHB2aHgifQ.xGVwKUpyJ-S5iyaLq7GFLA";
var owner = 'jseppimbx';
var renderMap1 = new mapboxgl.Map({
container: 'render_map_1',
center: [0.525, 0], // TODO: real math?
zoom: 9,
pitch: 0,
bearing: 0,
fadeDuration: 0,
preserveDrawingBuffer: true,
localIdeographFontFamily: false
});
var renderMap1Busy = false;
function generateFontPreviewStyle(_ref) {
var owner = _ref.owner;
var name = _ref.name;
var text = _ref.text;
var color = _ref.color;
return {
version: 8,
glyphs: 'mapbox://fonts/' + owner + '/{fontstack}/{range}.pbf',
sources: {
font: {
type: "geojson",
data: {
type: "FeatureCollection",
features: [{
type: "Feature",
geometry: {
type: "Point",
coordinates: [0, 0]
},
properties: {}
}]
}
}
},
layers: [{
id: "preview",
source: "font",
type: "symbol",
layout: {
"text-justify": "left",
"text-anchor": "left",
"text-field": text,
"text-font": [name],
"text-max-width": 200,
"text-size": 80,
"text-line-height": 5 // TODO: does this do anything?
},
paint: {
"text-color": color,
"text-halo-color": color,
"text-halo-width": 0.3,
"text-halo-blur": 0
}
}, {
// Show a circle here for easier
// positioning debugging
id: "markers",
source: "font",
type: "circle",
layout: {},
paint: {
"circle-radius": 5,
"circle-color": "yellow",
"circle-stroke-width": 0
}
}]
};
}
// ***** redux-like stuff
var state = {};
state.fontPreviewsToRender = new Set();
state.renderedFontPreviews = {};
function makeFontKey(name, color, text) {
return name + '^' + color + '^' + text;
}
function fontKeyToParams(key) {
var parts = key.split('^');
return {
name: parts[0],
color: parts[1],
text: parts[2]
};
}
function addFontPreviewToRender(name, color, text) {
var key = makeFontKey(name, color, text);
if (!state.fontPreviewsToRender.has(key)) {
state.fontPreviewsToRender.add(key);
}
}
function getFontPreviewsToRender() {
var paramsToRender = [];
state.fontPreviewsToRender.forEach(function (key) {
var params = fontKeyToParams(key);
paramsToRender.push(params);
});
return paramsToRender;
}
function saveRenderedFontPreviewData(name, color, text, dataURL) {
var key = makeFontKey(name, color, text);
state.renderedFontPreviews[key] = dataURL;
if (state.fontPreviewsToRender.has(key)) {
state.fontPreviewsToRender['delete'](key);
}
}
// ***** fake output helpers
function addImage(dataURL) {
var img = document.createElement("img");
img.src = dataURL;
img.classList.add('preview-image');
document.body.appendChild(img);
}
function doneGeneratingPreviews() {
for (key in state.renderedFontPreviews) {
addImage(state.renderedFontPreviews[key]);
}
}
// ***** render component stuff
var demoFontsToRender = ["Komika Hand Bold Italic", "Komika Hand Bold", "Komika Hand Italic", "Komika Hand Regular", "Komika Parch Regular", "Komika Title - Axis Regular", "Komika Title - Kaps Regular", "Komika Title - Paint Regular", "Komika Title - Wide Regular", "Komika Title Regular"];
var color = '#ff00ff';
demoFontsToRender.forEach(function (name) {
addFontPreviewToRender(name, color, name);
});
// get initial list of job params
var initialJobParams = getFontPreviewsToRender();
var i = 0;
var windowMethod = 'requestAnimationFrame'; // requestAnimationFrame
function processRenderJobs(jobParams) {
if (i > 100) {
console.log('emergency brake');
return;
}
if (renderMap1Busy) {
// requeue
window[windowMethod](function () {
return processRenderJobs(jobParams);
});
return;
}
// else
renderMap1Busy = true;
var _jobParams = _toArray(jobParams);
var params = _jobParams[0];
var remainingParams = _jobParams.slice(1);
var style = generateFontPreviewStyle({
owner: owner, name: params.name, text: params.text, color: params.color
});
renderMap1.setStyle(style);
renderMap1.once('idle', function () {
var dataURL = renderMap1.getCanvas().toDataURL();
// Save the rendered image data into state
saveRenderedFontPreviewData(params.name, params.text, params.color, dataURL);
renderMap1Busy = false;
// we have more to process, so queue up the remaining
if (remainingParams.length) {
window[windowMethod](function () {
return processRenderJobs(remainingParams);
});
} else {
doneGeneratingPreviews();
}
});
}
window[windowMethod](function () {
return processRenderJobs(initialJobParams);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment