dirty-variables
clone your own copy | download snapshot

Snapshots | iceberg

Inside this repository

inspector.v2.js
application/javascript

Download raw (8.0 KB)

// var parser = new DOMParser(),
//   doc = parser.parseFromString('<TTGlyph name="A" xMin="0" yMin="0" xMax="500" yMax="750"><contour><pt x="100" y="100" on="1"/><pt x="300" y="100" on="1"/><pt x="300" y="300" on="1"/><pt x="100" y="300" on="1"/></contour><instructions/></TTGlyph>', 'text/xml'),
//   deltas = parser.parseFromString('<glyphVariations glyph="A"><tuple><coord axis="wght" value="1.0"/><delta pt="0" x="-100" y="-100"/><delta pt="1" x="100" y="-100"/><delta pt="2" x="100" y="100"/><delta pt="3" x="-100" y="100"/></tuple></glyphVariations>', '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);
});