website
This commit is contained in:
parent
f582d72059
commit
4f790d0ae8
12 changed files with 38821 additions and 323 deletions
235
docs/glyphs/glyphs.css
Normal file
235
docs/glyphs/glyphs.css
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
h1 span.glyph-name {}
|
||||
h1 span.glyph-name:before {
|
||||
content: " / ";
|
||||
color: rgba(0,0,0,0.2);
|
||||
font-weight:400;
|
||||
}
|
||||
|
||||
body.iframe {
|
||||
background-color:#fff;
|
||||
padding:20px 10px;
|
||||
}
|
||||
|
||||
#glyphs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 6px;
|
||||
overflow: hidden;
|
||||
width:200%;
|
||||
box-sizing: border-box;
|
||||
transform-origin: 0 0;
|
||||
transform: scale(0.5);
|
||||
justify-content: center;
|
||||
transition: transform 50ms ease-out;
|
||||
}
|
||||
body.single #glyphs {
|
||||
justify-content: flex-start;
|
||||
max-width:888px;
|
||||
margin:0 auto;
|
||||
padding-left: 0;
|
||||
}
|
||||
.glyph {
|
||||
height: 346px;
|
||||
width: 346px;
|
||||
background-image: none !important; /* since .glyph is an `a` */
|
||||
background-color: white;
|
||||
border-radius: 9px;
|
||||
margin-right: 6px;
|
||||
margin-bottom: 6px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
text-shadow: none;
|
||||
}
|
||||
.glyph .baseline {
|
||||
background: rgba(0,90,230,.6);
|
||||
position: absolute;
|
||||
left:0; right:0; bottom:64px;
|
||||
height: 1px;
|
||||
z-index:0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.glyph .names {
|
||||
position: absolute;
|
||||
left:20px; right:20px; bottom:6px;
|
||||
color:white;
|
||||
text-align: center;
|
||||
font-size:1em;
|
||||
}
|
||||
.glyph:hover .name {
|
||||
color: rgba(0,0,0,0.8);
|
||||
}
|
||||
.glyph .advance {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
width: 1px;
|
||||
}
|
||||
.glyph.zero-width .advance {
|
||||
background: transparent;
|
||||
border-left: 1px dashed transparent;
|
||||
}
|
||||
.glyph:hover .baseline {
|
||||
visibility: visible;
|
||||
}
|
||||
.glyph:hover .advance {
|
||||
background: rgba(0,90,200,0.1);
|
||||
}
|
||||
.glyph.zero-width:hover .advance {
|
||||
border-color: #aaa;
|
||||
}
|
||||
/*#glyphs .glyph .img*/
|
||||
.glyph svg {
|
||||
/*min-width: 346px;*/
|
||||
/*height: 346px;*/
|
||||
background-repeat: no-repeat;
|
||||
/*background-size: contain;*/
|
||||
/*background-color: #f5f5f5;*/
|
||||
position: absolute;
|
||||
top:0; bottom:0;
|
||||
z-index:1;
|
||||
opacity:0.95;
|
||||
}
|
||||
.glyph .advancez {
|
||||
display: block;
|
||||
background: salmon;
|
||||
z-index:0;
|
||||
}
|
||||
|
||||
|
||||
body.single .intro {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
body.single .intro p {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#single-info {
|
||||
max-width: 500px;
|
||||
}
|
||||
#single-info ul {
|
||||
list-style: none;
|
||||
}
|
||||
#single-info ul li {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
#single-info .num {
|
||||
font-feature-settings: 'ss01' 1, 'zero' 1;
|
||||
}
|
||||
#single-info .unicodeName {
|
||||
font-feature-settings: 'case' 1;
|
||||
padding-left:0.3em;
|
||||
}
|
||||
#single-info .colorMark {
|
||||
display:inline-block;
|
||||
vertical-align:baseline;
|
||||
width:20px;
|
||||
height:20px;
|
||||
border-radius:50%;
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
}
|
||||
#single-info .colorMark.none {
|
||||
border: 1px dashed rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
#svgs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.row.kerning {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.single .row.kerning {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.row.kerning .empty {
|
||||
opacity:0.3;
|
||||
}
|
||||
|
||||
#kerning-list {
|
||||
display: flex;
|
||||
/*flex-direction: column;*/
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#kerning-list .divider {
|
||||
clear:both;
|
||||
width:100%;
|
||||
flex: 1 0 100%;
|
||||
height: 1px;
|
||||
background:rgba(0,0,0,0.1);
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.kernpair {
|
||||
/*background: white;*/
|
||||
border-radius:3px;
|
||||
background-image: none !important;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
margin:0 2em 2em 0;
|
||||
text-shadow: none;
|
||||
}
|
||||
.kernpair:hover {
|
||||
background: white;
|
||||
}
|
||||
.kernpair:target, .kernpair.selected {
|
||||
background: rgba(250, 240, 0, 0.9);
|
||||
}
|
||||
.kernpair:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(0,0,0,0.4);
|
||||
}
|
||||
.kernpair .left, .kernpair .right {
|
||||
width:100px;
|
||||
height:100px;
|
||||
/*background:rgba(40,240,90,0.3);*/
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
opacity:0.8;
|
||||
}
|
||||
.kernpair:hover .left, .kernpair:hover .right {
|
||||
opacity:0.9;
|
||||
}
|
||||
.kernpair.selected .left, .kernpair.selected .right {
|
||||
opacity:1;
|
||||
}
|
||||
/*.kernpair .left { background-color:rgba(120,90,140,0.3); }*/
|
||||
.kernpair .kern {
|
||||
position: absolute;
|
||||
/*background-color: blue;*/
|
||||
background-color: rgba(255,0,0,0.1);
|
||||
right:0; top:0; bottom:0;
|
||||
z-index:9;
|
||||
}
|
||||
.kernpair .kern.pos {
|
||||
background-color: rgba(36,207,128,0.3);
|
||||
}
|
||||
.kernpair:hover .kern, .kernpair.selected .kern {
|
||||
background-color: rgba(255,0,0,0.5);
|
||||
}
|
||||
.kernpair:hover .kern.pos, .kernpair.selected .kern.pos {
|
||||
background-color: rgba(36,207,128,0.5);
|
||||
}
|
||||
.kernpair .kern .label {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
opacity:0.9;
|
||||
height: 20px;
|
||||
right:-8px; bottom:-20px;
|
||||
z-index:1;
|
||||
text-align: center;
|
||||
color: rgba(255,0,0,0.8);
|
||||
visibility:hidden;
|
||||
}
|
||||
.kernpair .kern.pos .label {
|
||||
color: rgba(36,207,128,0.8);
|
||||
}
|
||||
.kernpair:hover .kern .label,
|
||||
.kernpair.selected .kern .label {
|
||||
visibility:visible;
|
||||
}
|
||||
|
||||
757
docs/glyphs/glyphs.js
Normal file
757
docs/glyphs/glyphs.js
Normal file
|
|
@ -0,0 +1,757 @@
|
|||
// constants
|
||||
var kSVGScale = 0.1 // how bmuch metrics are scaled in the SVGs
|
||||
var kGlyphSize = 346 // at kSVGScale. In sync with CSS and SVGs
|
||||
var kUPM = 2816
|
||||
|
||||
if (!isMac) {
|
||||
Array.prototype.forEach.call(document.querySelectorAll('kbd'), function(e) {
|
||||
if (e.innerText == '\u2318') {
|
||||
e.innerText = 'Ctrl'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function httpget(url, cb) {
|
||||
var req = new XMLHttpRequest();
|
||||
req.addEventListener("load", function() {
|
||||
cb(this.responseText, this)
|
||||
})
|
||||
req.open("GET", url)
|
||||
req.send()
|
||||
}
|
||||
|
||||
function fetchGlyphInfo(cb) {
|
||||
httpget("../lab/glyphinfo.json", function(res) {
|
||||
cb(JSON.parse(res).glyphs)
|
||||
})
|
||||
}
|
||||
|
||||
function fetchMetrics(cb) {
|
||||
httpget("metrics.json", function(res) {
|
||||
cb(JSON.parse(res))
|
||||
})
|
||||
}
|
||||
|
||||
var glyphInfo = null
|
||||
var glyphMetrics = null
|
||||
|
||||
function initMetrics(data) {
|
||||
// metrics.json uses a compact format with numerical glyph indentifiers
|
||||
// in order to minimize file size.
|
||||
// We expand the glyph IDs to glyph names here.
|
||||
var nameIds = data.nameids
|
||||
// console.log(data)
|
||||
|
||||
var metrics = {}
|
||||
var metrics0 = data.metrics
|
||||
Object.keys(metrics0).forEach(function (id) {
|
||||
var name = nameIds[id]
|
||||
var v = metrics0[id]
|
||||
// v : [width, advance, left, right]
|
||||
metrics[name] = {
|
||||
width: v[0],
|
||||
advance: v[1],
|
||||
left: v[2],
|
||||
right: v[3]
|
||||
}
|
||||
})
|
||||
|
||||
var kerningLeft = {} // { glyphName: {rightGlyphName: kerningValue, ...}, ... }
|
||||
var kerningRight = {}
|
||||
data.kerning.forEach(function (t) {
|
||||
// each entry looks like this:
|
||||
// [leftGlyphId, rightGlyphId, kerningValue]
|
||||
var leftName = nameIds[t[0]]
|
||||
if (!leftName) {
|
||||
console.error('nameIds missing', t[0])
|
||||
}
|
||||
var rightName = nameIds[t[1]]
|
||||
if (!rightName) {
|
||||
console.error('nameIds missing', t[1])
|
||||
}
|
||||
var kerningValue = t[2]
|
||||
|
||||
var lm = kerningLeft[leftName]
|
||||
if (!lm) {
|
||||
kerningLeft[leftName] = lm = {}
|
||||
}
|
||||
lm[rightName] = kerningValue
|
||||
|
||||
var rm = kerningRight[rightName]
|
||||
if (!rm) {
|
||||
kerningRight[rightName] = rm = {}
|
||||
}
|
||||
rm[leftName] = kerningValue
|
||||
})
|
||||
|
||||
glyphMetrics = {
|
||||
metrics: metrics,
|
||||
kerningLeft: kerningLeft,
|
||||
kerningRight: kerningRight,
|
||||
}
|
||||
// console.log('glyphMetrics', glyphMetrics)
|
||||
}
|
||||
|
||||
function fetchAll(cb) {
|
||||
var i = 0
|
||||
var res = {}
|
||||
var decr = function(){
|
||||
if (--i == 0) {
|
||||
cb(res)
|
||||
}
|
||||
}
|
||||
i++
|
||||
fetchGlyphInfo(function(r){ glyphInfo = r; decr() })
|
||||
i++
|
||||
fetchMetrics(function(r){
|
||||
initMetrics(r)
|
||||
decr()
|
||||
})
|
||||
}
|
||||
|
||||
fetchAll(render)
|
||||
|
||||
|
||||
var styleSheet = document.styleSheets[document.styleSheets.length-1]
|
||||
var glyphRule, baselineRule, zeroWidthAdvRule
|
||||
var currentScale = 0
|
||||
var defaultSingleScale = 1
|
||||
var currentSingleScale = 1
|
||||
var defaultGridScale = 0.4
|
||||
var currentGridScale = defaultGridScale
|
||||
var glyphs = document.getElementById('glyphs')
|
||||
|
||||
function updateLayoutAfterChanges() {
|
||||
var height = parseInt(window.getComputedStyle(glyphs).height)
|
||||
var margin = height - (height * currentScale)
|
||||
// console.log('scale:', currentScale, 'height:', height, 'margin:', margin)
|
||||
if (currentScale > 1) {
|
||||
glyphs.style.marginBottom = null
|
||||
} else {
|
||||
glyphs.style.marginBottom = -margin + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
function setScale(scale) {
|
||||
if (queryString.iframe !== undefined) {
|
||||
scale = 0.11
|
||||
} else if (queryString.g) {
|
||||
scale = Math.min(Math.max(1, scale), 3)
|
||||
} else {
|
||||
scale = Math.min(Math.max(0.05, scale), 3)
|
||||
}
|
||||
|
||||
if (currentScale == scale) {
|
||||
return
|
||||
}
|
||||
|
||||
currentScale = scale
|
||||
if (queryString.g) {
|
||||
currentSingleScale = scale
|
||||
} else {
|
||||
currentGridScale = scale
|
||||
}
|
||||
|
||||
var hairline = Math.ceil(1 / window.devicePixelRatio / scale)
|
||||
var spacing = Math.ceil(6 / scale)
|
||||
|
||||
var s = glyphs.style
|
||||
if (queryString.g || queryString.iframe !== undefined) {
|
||||
s.paddingLeft = null
|
||||
} else {
|
||||
s.paddingLeft = spacing + 'px'
|
||||
}
|
||||
s.width = (100 / scale) + '%'
|
||||
s.transform = 'scale(' + scale + ')'
|
||||
|
||||
if (!glyphRule) {
|
||||
glyphRule = styleSheet.cssRules[styleSheet.insertRule('#glyphs .glyph {}', styleSheet.cssRules.length)]
|
||||
baselineRule = styleSheet.cssRules[styleSheet.insertRule('#glyphs .glyph .baseline {}', styleSheet.cssRules.length)]
|
||||
zeroWidthAdvRule = styleSheet.cssRules[styleSheet.insertRule('#glyphs .glyph.zero-width .advance {}', styleSheet.cssRules.length)]
|
||||
}
|
||||
|
||||
if (queryString.g) {
|
||||
glyphRule.style.marginRight = null
|
||||
glyphRule.style.marginBottom = null
|
||||
} else {
|
||||
glyphRule.style.marginRight = Math.ceil(6 / scale) + 'px';
|
||||
glyphRule.style.marginBottom = Math.ceil(6 / scale) + 'px';
|
||||
if (queryString.iframe !== undefined) {
|
||||
glyphRule.style.marginBottom = Math.ceil(16 / scale) + 'px';
|
||||
}
|
||||
}
|
||||
baselineRule.style.height = hairline + 'px'
|
||||
zeroWidthAdvRule.style.borderWidth = (hairline) + 'px'
|
||||
|
||||
updateLayoutAfterChanges()
|
||||
requestAnimationFrame(updateLayoutAfterChanges)
|
||||
}
|
||||
|
||||
|
||||
function encodeQueryString(q) {
|
||||
return Object.keys(q).map(function(k) {
|
||||
if (k) {
|
||||
var v = q[k]
|
||||
return encodeURIComponent(k) + (v ? '=' + encodeURIComponent(v) : '')
|
||||
}
|
||||
}).filter(function(s) { return !!s }).join('&')
|
||||
}
|
||||
|
||||
function parseQueryString(qs) {
|
||||
var q = {}
|
||||
qs.replace(/^\?/,'').split('&').forEach(function(c) {
|
||||
var kv = c.split('=')
|
||||
var k = decodeURIComponent(kv[0])
|
||||
if (k) {
|
||||
q[k] = kv[1] ? decodeURIComponent(kv[1]) : null
|
||||
}
|
||||
})
|
||||
return q
|
||||
}
|
||||
|
||||
|
||||
var queryString = parseQueryString(location.search)
|
||||
var glyphNameEl = null
|
||||
var baseTitle = document.title
|
||||
var flippedLocationHash = false
|
||||
|
||||
var singleInfo = document.querySelector('#single-info')
|
||||
singleInfo.parentElement.removeChild(singleInfo)
|
||||
singleInfo.style.display = 'block'
|
||||
|
||||
function updateLocation() {
|
||||
queryString = parseQueryString(location.search)
|
||||
|
||||
// var glyphs = document.getElementById('glyphs')
|
||||
var h1 = document.querySelector('h1')
|
||||
if (queryString.g) {
|
||||
if (!glyphNameEl) {
|
||||
glyphNameEl = document.createElement('span')
|
||||
glyphNameEl.className = 'glyph-name'
|
||||
}
|
||||
document.title = queryString.g + ' – ' + baseTitle
|
||||
glyphNameEl.innerText = queryString.g
|
||||
h1.appendChild(glyphNameEl)
|
||||
document.body.classList.add('single')
|
||||
setScale(currentSingleScale)
|
||||
} else {
|
||||
document.title = baseTitle
|
||||
if (glyphNameEl) {
|
||||
h1.removeChild(glyphNameEl)
|
||||
}
|
||||
document.body.classList.remove('single')
|
||||
setScale(currentGridScale)
|
||||
}
|
||||
|
||||
document.querySelector('.row.intro').style.display = (
|
||||
queryString.iframe !== undefined ? 'none' : null
|
||||
)
|
||||
if (queryString.iframe !== undefined) {
|
||||
document.body.classList.add('iframe')
|
||||
} else {
|
||||
document.body.classList.remove('iframe')
|
||||
}
|
||||
|
||||
render()
|
||||
}
|
||||
|
||||
window.onpopstate = function(ev) {
|
||||
updateLocation()
|
||||
}
|
||||
|
||||
function navto(url) {
|
||||
history.pushState({}, "Glyphs", url)
|
||||
updateLocation()
|
||||
window.scrollTo(0,0)
|
||||
}
|
||||
|
||||
function wrapIntLink(a) {
|
||||
a.addEventListener('click', function(ev) {
|
||||
if (!ev.metaKey && !ev.ctrlKey && !ev.shiftKey) {
|
||||
ev.preventDefault()
|
||||
navto(a.href)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
wrapIntLink(document.querySelector('h1 > a'))
|
||||
|
||||
|
||||
// keep refs to svgs so we don't have to refcount while using
|
||||
var svgRepository = {}
|
||||
;(function(){
|
||||
var svgs = document.getElementById('svgs'), svg, name
|
||||
for (var i = 0; i < svgs.children.length; ++i) {
|
||||
svg = svgs.children[i]
|
||||
name = svg.id.substr(4) // strip "svg-" prefix
|
||||
svgRepository[name] = svg
|
||||
}
|
||||
})()
|
||||
|
||||
|
||||
// Maps glyphname to glyphInfo. Only links to first found entry for a flyph.
|
||||
var glyphInfoMap = {}
|
||||
var needsUpdateGlyphInfoMap = true
|
||||
|
||||
|
||||
function render() {
|
||||
if (!glyphInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
var rootEl = document.getElementById('glyphs')
|
||||
rootEl.style.display = 'none'
|
||||
rootEl.innerText = ''
|
||||
|
||||
// glyphInfo: {
|
||||
// "glyphs": [
|
||||
// [name :string, unicode? :int|null, unicodeName? :string, color? :string|null],
|
||||
// ["A", 65, "LATIN CAPITAL LETTER A", "#dbeaf7"],
|
||||
// ...
|
||||
// ],
|
||||
// }
|
||||
//
|
||||
// Note: Glyph names might appear multiple times (always adjacent) when a glyph is
|
||||
// represented by multiple Unicode code points. For example:
|
||||
//
|
||||
// ["Delta", 916, "GREEK CAPITAL LETTER DELTA"],
|
||||
// ["Delta", 8710, "INCREMENT"],
|
||||
//
|
||||
|
||||
var glyphs = glyphInfo
|
||||
var singleGlyph = null
|
||||
var lastGlyphEl = null
|
||||
var lastGlyphName = ''
|
||||
|
||||
glyphInfo.forEach(function(g, i) {
|
||||
var name = g[0]
|
||||
|
||||
if (needsUpdateGlyphInfoMap && !glyphInfoMap[name]) {
|
||||
glyphInfoMap[name] = g
|
||||
}
|
||||
|
||||
if (queryString.g && name != queryString.g) {
|
||||
// ignore
|
||||
return
|
||||
}
|
||||
|
||||
var glyph = renderGlyphGraphicG(g, lastGlyphName, lastGlyphEl, singleGlyph)
|
||||
|
||||
if (glyph) {
|
||||
rootEl.appendChild(glyph.element)
|
||||
lastGlyphEl = glyph.element
|
||||
lastGlyphName = name
|
||||
if (queryString.g) {
|
||||
singleGlyph = glyph
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
needsUpdateGlyphInfoMap = false
|
||||
|
||||
if (singleGlyph) {
|
||||
renderSingleInfo(singleGlyph)
|
||||
rootEl.appendChild(singleInfo)
|
||||
}
|
||||
|
||||
rootEl.style.display = null
|
||||
updateLayoutAfterChanges()
|
||||
}
|
||||
|
||||
|
||||
function renderGlyphGraphic(glyphName) {
|
||||
var g = glyphInfoMap[glyphName]
|
||||
return g ? renderGlyphGraphicG(g) : null
|
||||
}
|
||||
|
||||
|
||||
function renderGlyphGraphicG(g, lastGlyphName, lastGlyphEl, singleGlyph) {
|
||||
var name = g[0]
|
||||
var names, glyph
|
||||
var svg = svgRepository[name]
|
||||
|
||||
if (!svg) {
|
||||
// ignore
|
||||
return null
|
||||
}
|
||||
|
||||
var metrics = glyphMetrics.metrics[name]
|
||||
if (!metrics) {
|
||||
console.error('missing metrics for', name)
|
||||
}
|
||||
|
||||
var info = {
|
||||
name: name,
|
||||
unicode: g[1],
|
||||
unicodeName: g[2],
|
||||
color: g[3],
|
||||
|
||||
// These are all in 1:1 UPM (not scaled)
|
||||
advance: metrics.advance,
|
||||
left: metrics.left,
|
||||
right: metrics.right,
|
||||
width: metrics.width,
|
||||
|
||||
element: null,
|
||||
}
|
||||
|
||||
if (name == lastGlyphName) {
|
||||
// additional Unicode code point for same glyph
|
||||
glyph = lastGlyphEl
|
||||
names = glyph.querySelector('.names')
|
||||
names.innerText += ','
|
||||
if (info.unicode) {
|
||||
var ucid = ' U+' + fmthex(info.unicode)
|
||||
names.innerText += ' U+' + fmthex(info.unicode)
|
||||
if (!queryString.g) {
|
||||
glyph.title += ucid
|
||||
}
|
||||
}
|
||||
if (info.unicodeName) {
|
||||
names.innerText += ' ' + info.unicodeName
|
||||
if (!queryString.g) {
|
||||
glyph.title += ' (' + info.unicodeName + ')'
|
||||
}
|
||||
}
|
||||
|
||||
if (queryString.g) {
|
||||
if (singleGlyph) {
|
||||
if (!singleGlyph.alternates) {
|
||||
singleGlyph.alternates = []
|
||||
}
|
||||
singleGlyph.alternates.push(info)
|
||||
} else {
|
||||
throw new Error('alternate glyph UC, but appears first in glyphinfo data')
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// console.log('svg for', name, svg.width.baseVal.value, '->', svg, '\n', info)
|
||||
|
||||
glyph = document.createElement('a')
|
||||
glyph.className = 'glyph'
|
||||
glyph.href = '?g=' + encodeURIComponent(name)
|
||||
wrapIntLink(glyph)
|
||||
|
||||
info.element = glyph
|
||||
|
||||
if (!queryString.g) {
|
||||
glyph.title = name
|
||||
}
|
||||
|
||||
var baseline = document.createElement('div')
|
||||
baseline.className = 'baseline'
|
||||
glyph.appendChild(baseline)
|
||||
|
||||
names = document.createElement('div')
|
||||
names.className = 'names'
|
||||
names.innerText = name
|
||||
if (info.unicode) {
|
||||
var ucid = ' U+' + fmthex(info.unicode)
|
||||
names.innerText += ' U+' + fmthex(info.unicode, 4)
|
||||
if (!queryString.g) {
|
||||
glyph.title += ucid
|
||||
}
|
||||
}
|
||||
if (info.unicodeName) {
|
||||
names.innerText += ' ' + info.unicodeName
|
||||
if (!queryString.g) {
|
||||
glyph.title += ' (' + info.unicodeName + ')'
|
||||
}
|
||||
}
|
||||
glyph.appendChild(names)
|
||||
|
||||
var scaledAdvance = info.advance * kSVGScale
|
||||
|
||||
if (scaledAdvance > kGlyphSize) {
|
||||
glyph.style.width = scaledAdvance.toFixed(2) + 'px'
|
||||
}
|
||||
|
||||
var adv = document.createElement('div')
|
||||
adv.className = 'advance'
|
||||
glyph.appendChild(adv)
|
||||
|
||||
adv.appendChild(svg)
|
||||
svg.style.left = (info.left * kSVGScale).toFixed(2) + 'px'
|
||||
|
||||
if (info.advance <= 0) {
|
||||
glyph.className += ' zero-width'
|
||||
} else {
|
||||
adv.style.width = scaledAdvance.toFixed(2) + 'px'
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
|
||||
function renderSingleInfo(g) {
|
||||
var e = singleInfo
|
||||
e.querySelector('.name').innerText = g.name
|
||||
|
||||
function setv(el, name, textValue) {
|
||||
el.querySelector('.' + name).innerText = textValue
|
||||
}
|
||||
|
||||
var unicode = e.querySelector('.unicode')
|
||||
|
||||
function configureUnicodeView(el, g) {
|
||||
setv(el, 'unicodeCodePoint', g.unicode ? 'U+' + fmthex(g.unicode, 4) : '–')
|
||||
setv(el, 'unicodeName', g.unicodeName || '')
|
||||
}
|
||||
|
||||
// remove any previous alternate unicode list items
|
||||
var rmli = unicode
|
||||
while ((rmli = rmli.nextSibling)) {
|
||||
if (rmli.nodeType == Node.ELEMENT_NODE) {
|
||||
if (!rmli.parentElement || !rmli.classList.contains('unicode')) {
|
||||
break
|
||||
}
|
||||
rmli.parentElement.removeChild(rmli)
|
||||
}
|
||||
}
|
||||
|
||||
configureUnicodeView(unicode, g)
|
||||
|
||||
if (g.alternates) {
|
||||
var refnode = unicode.nextSibling
|
||||
g.alternates.forEach(function(g) {
|
||||
var li = unicode.cloneNode(true)
|
||||
configureUnicodeView(li, g)
|
||||
if (refnode) {
|
||||
unicode.parentElement.insertBefore(li, unicode.nextSibling)
|
||||
} else {
|
||||
unicode.parentElement.appendChild(li)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
e.querySelector('.advanceWidth').innerText = g.advance
|
||||
e.querySelector('.marginLeft').innerText = g.left
|
||||
e.querySelector('.marginRight').innerText = g.right
|
||||
|
||||
var colorMark = e.querySelector('.colorMark')
|
||||
if (g.color) {
|
||||
colorMark.title = g.color.toUpperCase()
|
||||
colorMark.style.background = g.color
|
||||
colorMark.classList.remove('none')
|
||||
} else {
|
||||
colorMark.title = '(None)'
|
||||
colorMark.style.background = null
|
||||
colorMark.classList.add('none')
|
||||
}
|
||||
|
||||
var svg = svgRepository[g.name]
|
||||
var svgFile = e.querySelector('.svgFile')
|
||||
svgFile.download = g.name + '.svg'
|
||||
svgFile.href = getSvgDataURI(svg)
|
||||
|
||||
renderSingleKerning(g)
|
||||
}
|
||||
|
||||
|
||||
var cachedSVGDataURIs = {}
|
||||
|
||||
function getSvgDataURI(svg) {
|
||||
var cached = cachedSVGDataURIs[svg.id]
|
||||
if (!cached) {
|
||||
cached = 'data:image/svg+xml,' + svg.outerHTML.replace(/[\r\n]+/g, '')
|
||||
cachedSVGDataURIs[svg.id] = cached
|
||||
}
|
||||
return cached
|
||||
}
|
||||
|
||||
|
||||
function pxround(n) {
|
||||
return Math.round(n * 2) / 2
|
||||
}
|
||||
|
||||
|
||||
function selectKerningPair(id, directly) {
|
||||
Array.prototype.forEach.call(
|
||||
document.querySelectorAll('.kernpair.selected'),
|
||||
function(e) {
|
||||
e.classList.remove('selected')
|
||||
}
|
||||
)
|
||||
var el = document.getElementById(id)
|
||||
if (el) {
|
||||
el.classList.add('selected')
|
||||
if (!directly) {
|
||||
el.scrollIntoViewIfNeeded()
|
||||
}
|
||||
}
|
||||
history.replaceState({}, '', location.search + '#' + id)
|
||||
}
|
||||
|
||||
|
||||
// return true if some kerning was rendered
|
||||
function renderSingleKerning(g) {
|
||||
var kerningList = document.getElementById('kerning-list')
|
||||
kerningList.style.display = 'none'
|
||||
kerningList.innerText = ''
|
||||
var thisSvg = svgRepository[g.name]
|
||||
var thisSvgURI = getSvgDataURI(thisSvg)
|
||||
|
||||
if (!thisSvg) {
|
||||
kerningList.style.display = null
|
||||
return false
|
||||
}
|
||||
|
||||
var kerningAsLeft = glyphMetrics.kerningLeft[g.name] || {}
|
||||
var kerningAsRight = glyphMetrics.kerningRight[g.name] || {}
|
||||
|
||||
var kerningAsLeftKeys = Object.keys(kerningAsLeft)
|
||||
var kerningAsRightKeys = Object.keys(kerningAsRight)
|
||||
|
||||
if (kerningAsLeftKeys.length == 0 && kerningAsRightKeys.length == 0) {
|
||||
var p = document.createElement('p')
|
||||
p.className = 'empty'
|
||||
p.innerText = 'No kerning'
|
||||
kerningList.appendChild(p)
|
||||
kerningList.style.display = null
|
||||
return false
|
||||
}
|
||||
|
||||
var lilGlyphSize = 128
|
||||
var lilScale = lilGlyphSize / kGlyphSize
|
||||
|
||||
function mkpairGlyph(glyphName, side, kerningValue, svgURI) {
|
||||
var metrics = glyphMetrics.metrics[glyphName]
|
||||
var svgWidth = pxround(metrics.width * kSVGScale * lilScale)
|
||||
var el = document.createElement('div')
|
||||
|
||||
var leftMargin = metrics.left
|
||||
var rightMargin = metrics.right
|
||||
if (side == 'left') {
|
||||
rightMargin += kerningValue
|
||||
} else {
|
||||
leftMargin += kerningValue
|
||||
}
|
||||
leftMargin = leftMargin * kSVGScale * lilScale
|
||||
rightMargin = rightMargin * kSVGScale * lilScale
|
||||
|
||||
el.className = side
|
||||
el.style.backgroundImage = "url('" + svgURI + "')"
|
||||
el.style.backgroundSize = svgWidth + 'px ' + lilGlyphSize + 'px'
|
||||
el.style.width = svgWidth + 'px'
|
||||
el.style.height = lilGlyphSize + 'px'
|
||||
el.style.marginLeft = pxround(leftMargin) + 'px'
|
||||
el.style.marginRight = pxround(rightMargin) + 'px'
|
||||
return el
|
||||
}
|
||||
|
||||
function mkpairs(kerningInfo, selfName, self, side) {
|
||||
var asLeftSide = side == 'left'
|
||||
var idPrefix = asLeftSide ? 'kernl-' : 'kernr-'
|
||||
var keys = Object.keys(kerningInfo)
|
||||
var otherSide = asLeftSide ? 'right' : 'left'
|
||||
|
||||
keys.forEach(function(glyphName) {
|
||||
var kerningValue = kerningInfo[glyphName]
|
||||
var otherSvg = svgRepository[glyphName]
|
||||
|
||||
var pair = document.createElement('a')
|
||||
pair.className = 'kernpair'
|
||||
pair.title = (
|
||||
asLeftSide ? selfName + '/' + glyphName + ' ' + kerningValue :
|
||||
glyphName + '/' + selfName + ' ' + kerningValue
|
||||
)
|
||||
pair.id = idPrefix + glyphName
|
||||
pair.href = '#' + pair.id
|
||||
pair.onclick = function(ev) {
|
||||
selectKerningPair(pair.id)
|
||||
ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
}
|
||||
|
||||
var otherEl = mkpairGlyph(glyphName, otherSide, kerningValue, getSvgDataURI(otherSvg))
|
||||
|
||||
if (asLeftSide) {
|
||||
if (self.parentNode) {
|
||||
pair.appendChild(self.cloneNode(true))
|
||||
} else {
|
||||
pair.appendChild(self)
|
||||
}
|
||||
pair.appendChild(otherEl)
|
||||
} else {
|
||||
pair.appendChild(otherEl)
|
||||
if (self.parentNode) {
|
||||
pair.appendChild(self.cloneNode(true))
|
||||
} else {
|
||||
pair.appendChild(self)
|
||||
}
|
||||
}
|
||||
|
||||
var kern = document.createElement('div')
|
||||
kern.className = 'kern ' + (kerningValue < 0 ? 'neg' : 'pos')
|
||||
|
||||
var absKernVal = Math.abs(kerningValue)
|
||||
var kernWidth = Math.max(1, pxround(absKernVal * kSVGScale * lilScale))
|
||||
kern.style.width = kernWidth + 'px'
|
||||
|
||||
var leftMetrics = glyphMetrics.metrics[asLeftSide ? selfName : glyphName]
|
||||
var leftAdvance = leftMetrics.advance
|
||||
kern.style.left = pxround((leftAdvance - absKernVal) * kSVGScale * lilScale) + 'px'
|
||||
pair.appendChild(kern)
|
||||
|
||||
var label = document.createElement('div')
|
||||
label.className = 'label'
|
||||
label.innerText = kerningValue
|
||||
kern.appendChild(label)
|
||||
|
||||
kerningList.appendChild(pair)
|
||||
})
|
||||
|
||||
return keys.length != 0
|
||||
}
|
||||
|
||||
var selfLeft = mkpairGlyph(g.name, 'left', 0, thisSvgURI)
|
||||
var selfRight = mkpairGlyph(g.name, 'right', 0, thisSvgURI)
|
||||
|
||||
if (mkpairs(kerningAsLeft, g.name, selfLeft, 'left')) {
|
||||
var div = document.createElement('div')
|
||||
div.className = 'divider'
|
||||
kerningList.appendChild(div)
|
||||
}
|
||||
|
||||
mkpairs(kerningAsRight, g.name, selfRight, 'right')
|
||||
|
||||
if (location.hash && location.hash.indexOf('#kern') == 0) {
|
||||
var id = location.hash.substr(1)
|
||||
setTimeout(function(){
|
||||
selectKerningPair(id)
|
||||
},1)
|
||||
}
|
||||
|
||||
kerningList.style.display = null
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
function fmthex(cp, minWidth) {
|
||||
let s = cp.toString(16).toUpperCase()
|
||||
while (s.length < minWidth) {
|
||||
s = '0' + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('keydown', function(ev) {
|
||||
if (!queryString.g && (ev.metaKey || ev.ctrlKey)) {
|
||||
if (ev.keyCode == 187 || ev.key == '+') {
|
||||
setScale(parseFloat((currentScale + 0.1).toFixed(2)))
|
||||
ev.preventDefault()
|
||||
} else if (ev.keyCode == 189 || ev.key == '-') {
|
||||
setScale(parseFloat((currentScale - 0.1).toFixed(2)))
|
||||
ev.preventDefault()
|
||||
} else if (ev.keyCode == 48 || ev.key == '0') {
|
||||
setScale(queryString.g ? defaultSingleScale : defaultGridScale)
|
||||
ev.preventDefault()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
updateLocation()
|
||||
3202
docs/glyphs/index.html
Normal file
3202
docs/glyphs/index.html
Normal file
File diff suppressed because it is too large
Load diff
33977
docs/glyphs/metrics.json
Normal file
33977
docs/glyphs/metrics.json
Normal file
File diff suppressed because it is too large
Load diff
Reference in a new issue