Published
Edited
Jun 17, 2020
Importers
8 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function getHref(family, range) {
return urlTemplate
.replace('{fontstack}', family.split(" ").join("%20"))
.replace('{range}', range)
.replace('{key}', "mrAq6zQEFxOkanukNbGm"); // Get your own key: maptiler.com
}
Insert cell
Insert cell
testHref = getHref("Noto Sans Regular", "0-255")
Insert cell
Insert cell
Insert cell
glyphBuf = new Pbf(await d3.buffer(testHref))
Insert cell
Insert cell
testGlyphs = glyphBuf.readFields(readFontstacks, [])
Insert cell
testGlyphs[200]
Insert cell
Insert cell
testGlyphs['B'.codePointAt(0)]
Insert cell
Insert cell
showBitmap('B')
Insert cell
function showBitmap(char) {
let codePoint = char.codePointAt(0);
let bitmap = testGlyphs[codePoint].bitmap;
return displayAlpha(bitmap);
}
Insert cell
function displayAlpha(bitmap) {
const { width, height, data } = bitmap;
let nPix = width * height;

// Generate 4-channel data from the input 1-channel data
const arr = new Uint8ClampedArray(nPix * 4); // Initialized to 0
for (let i = 0; i < nPix; i++) {
arr[4 * i + 3] = data[i];
//arr[4 * i + 3] = sdfToAlpha(data[i]);
}

const ctx = DOM.context2d(width, height);
let imageData = new ImageData(arr, width);
ctx.putImageData(imageData, 0, 0);

return ctx.canvas;
}
Insert cell
Insert cell
Insert cell
alphaImage = displayAlpha(testAtlas.image)
Insert cell
testAtlas.positions['À'.charCodeAt(0)]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sliceOfI = testGlyphs['I'.codePointAt(0)].bitmap.data.slice(12 * 12, 12 * 13)
Insert cell
Insert cell
Array.from(sliceOfI).map(d => (191 - d) / 32)
Insert cell
Insert cell
function sdfToAlpha(val, scale = 1.0) {
// Scale is the ratio of the displayed pixel size to the native (24px) glyph size,
// Or, the inverse of the change in the texture coordinate per screen pixel
let distance = scale * ((191 - val) / 32);
let clamp = Math.min(Math.max(0, 0.5 - distance), 1);
return clamp * 255;
}
Insert cell
sliceOfI.map(d => sdfToAlpha(d))
Insert cell
Insert cell
Insert cell
testAtlas = {
const padding = 1;

const positions = {};
const bins = [];

testGlyphs.forEach(glyph => {
const { id, bitmap, metrics } = glyph;
const { width, height } = bitmap;
if (width === 0 || height === 0) return;

const bin = { x: 0, y: 0, w: width + 2 * padding, h: height + 2 * padding };
bins.push(bin);
positions[id] = { rect: bin, metrics };
});

const { w, h } = potpack(bins);
const image = new AlphaImage({ width: w || 1, height: h || 1 });

testGlyphs.forEach(glyph => {
const { id, bitmap, metrics } = glyph;
const bin = positions[id].rect;
let srcPt = { x: 0, y: 0 };
let dstPt = { x: bin.x + padding, y: bin.y + padding };
AlphaImage.copy(bitmap, image, srcPt, dstPt, bitmap);
});

return { image, positions };
}
Insert cell
function initdrawChar(ctx, yShift) {
const glyphPadding = 1.0;
const rectBuffer = GLYPH_PBF_BORDER + glyphPadding;

return function(character, xCursor, yBaseline, scale) {
const {
rect: { x, y, w, h },
metrics: { left, top, advance }
} = testAtlas.positions[character.charCodeAt(0)];

let dx = xCursor + (left - rectBuffer) * scale;
let dy = yBaseline - (top + rectBuffer + yShift) * scale;

ctx.drawImage(alphaImage, x, y, w, h, dx, dy, w * scale, h * scale);
// Draw the bounding box of the character itself (not including padding)
ctx.strokeStyle = "yellow";
ctx.setLineDash([1, 2]);
ctx.strokeRect(
dx + rectBuffer * scale,
dy + rectBuffer * scale,
(w - 2 * rectBuffer) * scale,
(h - 2 * rectBuffer) * scale
);

return advance * scale;
};
}
Insert cell
Insert cell
Insert cell
function readFontstacks(tag, glyphs, pbf) {
// Usage: pbf.readFields(readFontStacks, [])
if (tag === 1) pbf.readMessage(readFontstack, glyphs);
}
Insert cell
function readFontstack(tag, glyphs, pbf) {
if (tag !== 3) return;

const { id, bitmap, width, height, left, top, advance } = pbf.readMessage(
readGlyph,
{}
);
const borders = 2 * GLYPH_PBF_BORDER;
const size = { width: width + borders, height: height + borders };
glyphs.push({
id,
bitmap: new AlphaImage(size, bitmap),
metrics: { width, height, left, top, advance }
});
}
Insert cell
function readGlyph(tag, glyph, pbf) {
if (tag === 1) glyph.id = pbf.readVarint();
else if (tag === 2) glyph.bitmap = pbf.readBytes();
else if (tag === 3) glyph.width = pbf.readVarint();
else if (tag === 4) glyph.height = pbf.readVarint();
else if (tag === 5) glyph.left = pbf.readSVarint();
else if (tag === 6) glyph.top = pbf.readSVarint();
else if (tag === 7) glyph.advance = pbf.readVarint();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
class AlphaImage {
constructor(size, data) {
createImage(this, size, 1, data);
}

resize(size) {
resizeImage(this, size, 1);
}

clone() {
return new AlphaImage(
{ width: this.width, height: this.height },
new Uint8Array(this.data)
);
}

static copy(srcImg, dstImg, srcPt, dstPt, size) {
copyImage(srcImg, dstImg, srcPt, dstPt, size, 1);
}
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
potpack = require('potpack')
Insert cell
Pbf = require('pbf')
Insert cell
d3 = require("d3-fetch@1")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more