// var parser = new DOMParser(), // doc = parser.parseFromString('', 'text/xml'), // deltas = parser.parseFromString('', 'text/xml'), // canvas = document.getElementById('canvas'), // ctx = canvas.getContext('2d'), // points = []; // var glyf = font.querySelector('glyf'), // gvar = font.querySelector('gvar'), // glyphs = glyf.querySelectorAll('TTGlyph'), // variatonss = gvar.querySelectorAll('glyphVariations'); // glyphs.forEach((glyph) => { console.log(glyph) }); // doc.childNodes.forEach((glyph) => { // if (glyph.nodeName.toLowerCase() == 'ttglyph') { // glyph.childNodes.forEach((contour) => { // if (contour.nodeName.toLowerCase() == 'contour') { // ctx.save(); // ctx.transform(1, 0, 0, -1, 200, 800); // ctx.beginPath(); // contour.childNodes.forEach((pt, k) => { // if (pt.nodeName.toLowerCase() == 'pt') { // var x = parseInt(pt.attributes.x.value), // y = parseInt(pt.attributes.y.value); // if (k > 0) { // ctx.lineTo(x, y); // } else { // ctx.moveTo(x, y); // } // points.push([x, y]); // } // }); // ctx.closePath(); // ctx.stroke(); // ctx.restore(); // } // }); // } // }); // console.log(deltas) // deltas.querySelectorAll('glyphVariations').forEach((glyphVariations) => { // glyphVariations.querySelectorAll('tuple').forEach((tuple) => { // tuple.querySelectorAll('delta').forEach((delta) => { // var pt = delta.attributes.pt.value, // x = parseInt(delta.attributes.x.value), // y = parseInt(delta.attributes.y.value); // ctx.save(); // ctx.transform(1, 0, 0, -1, 200, 800); // ctx.beginPath(); // ctx.moveTo(points[pt][0], points[pt][1]); // ctx.lineTo(points[pt][0] + x, points[pt][1] + y); // ctx.stroke(); // ctx.beginPath(); // ctx.arc(points[pt][0] + x, points[pt][1] + y, 2, 0, 2 * Math.PI, false); // ctx.fill(); // ctx.restore(); // }); // }); // }); var font; class Font { constructor(xml) { this.xml = xml; let parser = new DOMParser(); this.dom = parser.parseFromString(xml, 'text/xml'); } table(name) { return this.dom.querySelector(name) }; glyphs() { return this.table('glyf').querySelectorAll('TTGlyph'); }; glyph(name) { return this.table('glyf').querySelector(`TTGlyph[name="${name}"]`); }; variations() { return this.table('fvar'); }; glyphVariations() { return this.table('gvar').querySelectorAll('glyphVariations'); }; glyphVariation(name) { return this.table('gvar').querySelector(`glyphVariations[glyph="${name}"]`); }; delta(name, point) { return this.glyphVariation(name).querySelectorAll(`delta[pt="${point}"]`); }; contours(name) { return this.glyph(name).querySelectorAll('contour'); }; } function drawGlyph(name, font) { var canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'), // deltaCtx = canvas.getContext('2d'), points = []; ctx.clearRect(0, 0, canvas.width, canvas.height); var glyph = font.glyph(name); var xmin = glyph.getAttribute('xMin'), xmax = glyph.getAttribute('xMax'), ymin = glyph.getAttribute('yMin'), ymax = glyph.getAttribute('yMax'), glyphWidth = xmax - xmin, glyphHeight = ymax - ymin; console.log(glyphWidth, glyphHeight, xmin, xmax, ymin, ymax); prepareCanvas = (ctx) => { ctx.transform(1, 0, 0, -1, 0, canvas.height); ctx.translate((canvas.width - glyphWidth) / 2 - xmin, (canvas.height - glyphHeight) / 2); }; // console.log(glyph.getAttribute('xMin'), glyglyph.getAttribute('xMax') - glyph.getAttribute('xMin');ph.getAttribute('xMax')); font.contours(name).forEach(contour => { ctx.save(); prepareCanvas(ctx); // deltaCtx.save(); // prepareCanvas(deltaCtx); var start = []; var curveBuffer = []; var sections = []; contour.querySelectorAll('pt').forEach((point, k) => { var x = parseInt(point.getAttribute('x')), y = parseInt(point.getAttribute('y')), onPath = (point.getAttribute('on') === "1") ? true : false; ctx.beginPath(); ctx.arc(x, y, 3, 0, 2 * Math.PI, false); if (onPath) { ctx.fill(); } else { ctx.stroke(); } ctx.closePath(); ctx.font = "14px monospace"; ctx.fillStyle = "red"; ctx.fillText(k, 5 + x, 5 + y); if (!onPath) { if (curveBuffer.length > 0) { curveBuffer.push(curveBuffer[0] + ((x - curveBuffer[0]) / 2), curveBuffer[1] + ((y - curveBuffer[1]) / 2)); sections.push(curveBuffer); curveBuffer = []; } curveBuffer.push(x, y); } else if (curveBuffer.length > 0) { curveBuffer.push(x, y); sections.push(curveBuffer); curveBuffer = []; } else { sections.push([x, y]); } points.push([x, y]); }); ctx.beginPath(); sections.forEach((p, k) => { if (k === 0) { if (p.length == 2) { ctx.moveTo(p[0], p[1]); } else { var last = sections[sections.length - 1]; console.log('LAST', last) if (last.length == 2) { ctx.moveTo(last[0], last[1]); } else { ctx.moveTo(p[2], p[3]); } } } else { if (p.length == 4) { ctx.quadraticCurveTo(p[0], p[1], p[2], p[3]); } else { ctx.lineTo(p[0], p[1]); } } }); ctx.closePath(); ctx.stroke(); ctx.restore(); }); ctx.save(); prepareCanvas(ctx); ctx.strokeStyle = 'red'; points.forEach((p, k) => { font.delta(name, k).forEach((d) => { var x = p[0], y = p[1], dx = parseInt(d.getAttribute('x')), dy = parseInt(d.getAttribute('y')); ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + dx, y + dy); ctx.stroke(); arrowHead(ctx, x + dx, y + dy, calcAngle(dx, dy), 10); console.log(calcAngle(dx, dy)); // ctx.beginPath(); // ctx.arc(x + parseInt(d.getAttribute('x')), y + parseInt(d.getAttribute('y')), 2, 0, 2 * Math.PI, false); // ctx.fill(); }); }); ctx.restore(); } function fillGlyphSelector() { var glyphSelector = document.getElementById('glyph-selector'); while (glyphSelector.firstChild) { glyphSelector.removeChild(glyphSelector.firstChild); } font.glyphs().forEach((glyph) => { var option = document.createElement('option'); option.value = glyph.getAttribute('name'); option.appendChild(document.createTextNode(option.value)); glyphSelector.appendChild(option); }); } function arrowHead(ctx, x, y, angle, size) { ctx.save(); ctx.translate(x, y); // ctx.arc(0, 0, 10, 0, 2 * Math.PI, false); // // ctx.stroke(); ctx.rotate(angle + Math.PI * .75); ctx.scale(size / 10, size / 10); ctx.beginPath(); ctx.moveTo(3, 10); ctx.lineTo(0, 0); ctx.lineTo(10, 3); // ctx.endPath(); ctx.stroke(); ctx.restore(); } function calcAngle(dx, dy) { return Math.atan(dy / dx) + ((dx < 0) ? Math.PI : 0); } function loadTTX() { var fontSelector = document.getElementById('font-selector'); if (fontSelector.files.length > 0) { var reader = new FileReader(); reader.onload = function (event) { font = new Font(event.target.result); fillGlyphSelector(); }; reader.readAsText(document.getElementById('font-selector').files[0]); } } document.getElementById('font-selector').addEventListener('change', loadTTX); loadTTX(); document.getElementById('glyph-selector').addEventListener('change', function () { var value = this.value; drawGlyph(value, font); });