Last active
June 28, 2020 16:58
-
-
Save be5invis/3b7cb80680af4c7488b1 to your computer and use it in GitHub Desktop.
Automatic gridfit generator for Han characters.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var upm = 1000; | |
function Point(x, y, on, interpolated){ | |
this.xori = x; | |
this.yori = y; | |
this.xtouch = x; | |
this.ytouch = y; | |
this.touched = false; | |
this.donttouch = false; | |
this.on = on; | |
this.interpolated = interpolated; | |
} | |
function Contour(){ | |
this.points = [] | |
this.ccw = false | |
} | |
Contour.prototype.stat = function(){ | |
var xoris = this.points.map(function(p){ return p.xori }) | |
var yoris = this.points.map(function(p){ return p.yori }) | |
this.xmax = Math.max.apply(Math, xoris) | |
this.ymax = Math.max.apply(Math, yoris) | |
this.xmin = Math.min.apply(Math, xoris) | |
this.ymin = Math.min.apply(Math, yoris) | |
this.orient() | |
} | |
Contour.prototype.orient = function() { | |
// Findout PYmin | |
var jm = 0, ym = this.points[0].yori | |
for(var j = 0; j < this.points.length - 1; j++) if(this.points[j].yori < ym){ | |
jm = j; ym = this.points[j].yori; | |
} | |
var p0 = this.points[(jm ? jm - 1 : this.points.length - 2)], p1 = this.points[jm], p2 = this.points[jm + 1]; | |
var x = ((p0.xori - p1.xori) * (p2.yori - p1.yori) - (p0.yori - p1.yori) * (p2.xori - p1.xori)) | |
if(x < 0) this.ccw = true; | |
else if(x === 0) this.ccw = p2.xori > p1.xori | |
} | |
var inPoly = function (point, vs) { | |
// ray-casting algorithm based on | |
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html | |
var x = point.xori, y = point.yori; | |
var inside = false; | |
for (var i = 0, j = vs.length - 2; i < vs.length - 1; j = i++) { | |
var xi = vs[i].xori, yi = vs[i].yori; | |
var xj = vs[j].xori, yj = vs[j].yori; | |
var intersect = ((yi > y) != (yj > y)) | |
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi); | |
if (intersect) inside = !inside; | |
} | |
return inside; | |
}; | |
Contour.prototype.includes = function(that){ | |
for(var j = 0; j < that.points.length - 1; j++){ | |
if(!inPoly(that.points[j], this.points)) return false | |
} | |
return true; | |
} | |
function tx(x){ return x * 0.75 } | |
function ty(y){ return (0.95 * upm - y) * 0.75 } | |
function drawOutline(hDC, contours, xselector, yselector, controlcolor, strokecolor, vertexcolor, touchcolor) { | |
var nPoints = 0; | |
// Untouched | |
for(var j = 0; j < contours.length; j++){ | |
var contour = contours[j]; | |
// Layer 1 : Control outline | |
hDC.strokeStyle = controlcolor; | |
hDC.beginPath(); | |
hDC.moveTo(tx(contour.points[0][xselector]), ty(contour.points[0][yselector])) | |
for(var k = 1; k < contour.points.length; k++){ | |
hDC.lineTo(tx(contour.points[k][xselector]), ty(contour.points[k][yselector])) | |
} | |
hDC.closePath(); | |
hDC.stroke() | |
// Layer 2 : Character outline | |
hDC.strokeStyle = strokecolor; | |
hDC.beginPath(); | |
hDC.moveTo(tx(contour.points[0][xselector]), ty(contour.points[0][yselector])) | |
for(var k = 1; k < contour.points.length; k++){ | |
if(contour.points[k].on) hDC.lineTo(tx(contour.points[k][xselector]), ty(contour.points[k][yselector])) | |
else { | |
hDC.quadraticCurveTo( | |
tx(contour.points[k][xselector]), | |
ty(contour.points[k][yselector]), | |
tx(contour.points[k + 1][xselector]), | |
ty(contour.points[k + 1][yselector])) | |
k += 1; | |
} | |
} | |
hDC.closePath(); | |
hDC.stroke() | |
hDC.font = "10px 'Sayo UV DL'" | |
for(var k = 0; k < contour.points.length - 1; k++){ | |
var point = contour.points[k] | |
if(!contour.points[k].interpolated) { | |
hDC.strokeStyle = hDC.fillStyle = point.touched ? touchcolor || vertexcolor : vertexcolor | |
hDC.fillText('' + (nPoints++), tx(point[xselector]), ty(point[yselector] + 1)) | |
if(point.on) { | |
hDC.beginPath() | |
hDC.arc(tx(point[xselector]), ty(point[yselector]), 2, 0, Math.PI*2); | |
hDC.fill() | |
} else { | |
hDC.beginPath() | |
hDC.moveTo(tx(point[xselector] - 3), ty(point[yselector] - 3)) | |
hDC.lineTo(tx(point[xselector] + 3), ty(point[yselector] + 3)) | |
hDC.stroke() | |
hDC.beginPath() | |
hDC.moveTo(tx(point[xselector] - 3), ty(point[yselector] + 3)) | |
hDC.lineTo(tx(point[xselector] + 3), ty(point[yselector] - 3)) | |
hDC.stroke() | |
} | |
} | |
} | |
} | |
} | |
function drawContours(hDC, contours){ | |
// Fill | |
hDC.fillStyle = 'rgba(0, 0, 0, 0.1)' | |
hDC.beginPath(); | |
for(var j = 0; j < contours.length; j++){ | |
var contour = contours[j]; | |
// Layer 1 : Control outline | |
hDC.moveTo(tx(contour.points[0].xtouch), ty(contour.points[0].ytouch)) | |
for(var k = 1; k < contour.points.length; k++){ | |
if(contour.points[k].on) hDC.lineTo(tx(contour.points[k].xtouch), ty(contour.points[k].ytouch)) | |
else { | |
hDC.quadraticCurveTo(tx(contour.points[k].xtouch), ty(contour.points[k].ytouch), tx(contour.points[k + 1].xtouch), ty(contour.points[k + 1].ytouch)) | |
k += 1; | |
} | |
} | |
hDC.closePath(); | |
} | |
hDC.fill('nonzero'); | |
// Outlines | |
drawOutline(hDC, contours, 'xori', 'yori', 'rgba(0, 0, 0, 0.1)', 'rgba(0, 0, 0, 0.3)', '#aaa') | |
drawOutline(hDC, contours, 'xtouch', 'ytouch', 'rgba(0, 0, 255, 0.3)', 'black', 'green', 'red') | |
} | |
function drawArrowhead(hDC, x, y, angle, color){ | |
hDC.save(); | |
hDC.translate(x, y); | |
hDC.rotate(angle); | |
hDC.fillStyle = color; | |
hDC.beginPath(); | |
hDC.moveTo(0, 0); | |
hDC.lineTo(-12, 6); | |
hDC.lineTo(-12, -6); | |
hDC.fill(); | |
hDC.restore(); | |
} | |
function drawLabel(hDC, x, y, text, color){ | |
var textMetric = hDC.measureText(text); | |
hDC.fillStyle = "white"; | |
hDC.strokeStyle = color; | |
hDC.fillRect(x - textMetric.width / 2 - 4, y - 8, textMetric.width + 8, 16); | |
hDC.strokeRect(x - textMetric.width / 2 - 4, y - 8, textMetric.width + 8, 16); | |
hDC.fillStyle = color; | |
hDC.fillText(text, x - textMetric.width / 2, y + 5) | |
} | |
function drawInstructions(hDC, instrs){ | |
instrs = instrs.blueZoneAlignments.concat(instrs.roundings, instrs.stemTopAlignments, instrs.stemBottomAlignments, instrs.interpolations, instrs.instemAlignments) | |
var roundColor = "blue"; | |
var ipColor = "green"; | |
var alignColor = "black"; | |
var labels = []; | |
for(var j = 0; j < instrs.length; j++) { | |
var instr = instrs[j]; | |
switch(instr[0]) { | |
case "ROUNDUP": case "ROUNDDOWN": case "ROUNDUP2": { | |
var p = instr[1], align = instr[2]; | |
var direction = (instr[0] === "ROUNDDOWN" ? -1 : 1); | |
hDC.fillStyle = "white"; | |
hDC.strokeStyle = roundColor; | |
hDC.beginPath(); | |
hDC.moveTo(tx(p.xtouch), ty(p.ytouch)); | |
hDC.lineTo(tx(p.xtouch) - 6, ty(p.ytouch) + direction * 12); | |
hDC.lineTo(tx(p.xtouch) + 6, ty(p.ytouch) + direction * 12); | |
hDC.closePath(); | |
hDC.fill(); | |
hDC.stroke(); | |
if(align) labels.push([hDC, tx(p.xtouch), ty(p.ytouch) + direction * 24, "blue:" + align.toFixed(0), roundColor]); | |
else if(instr[0] === "ROUNDUP2") labels.push([hDC, tx(p.xtouch), ty(p.ytouch) + direction * 24, "SHPIX y:1", roundColor]) | |
break; | |
} | |
case "ALIGN0": case "ALIGNW": { | |
var from = instr[1], to = instr[2], width = instr[3]; | |
hDC.strokeStyle = alignColor; | |
hDC.beginPath(); | |
var mx = (tx(from.xtouch) + tx(to.xtouch)) / 2; | |
var my = (ty(from.ytouch) + ty(to.ytouch)) / 2 | |
hDC.moveTo(tx(from.xtouch), ty(from.ytouch)); | |
hDC.quadraticCurveTo(mx, my - 20, tx(to.xtouch), ty(to.ytouch)) | |
hDC.stroke(); | |
var angle = Math.atan((my - 20 - ty(to.ytouch)) / (mx - tx(to.xtouch))); | |
if(angle < 0) angle += Math.PI | |
drawArrowhead(hDC, tx(to.xtouch), ty(to.ytouch), angle, alignColor); | |
if(width) labels.push([hDC, mx, my - 12, "y:" + width.toFixed(0), alignColor]) | |
break; | |
} | |
case "IP": { | |
var a = instr[1], b = instr[2], c = instr[3]; | |
hDC.strokeStyle = ipColor; | |
hDC.beginPath(); | |
hDC.moveTo(tx(a.xtouch), ty(a.ytouch)); | |
var mx = (tx(a.xtouch) + tx(c.xtouch)) / 2; | |
var my = (ty(a.ytouch) + ty(c.ytouch)) / 2; | |
hDC.quadraticCurveTo(mx + 20, my, tx(c.xtouch), ty(c.ytouch)) | |
var mx = (tx(b.xtouch) + tx(c.xtouch)) / 2; | |
var my = (ty(b.ytouch) + ty(c.ytouch)) / 2; | |
hDC.quadraticCurveTo(mx + 20, my, tx(b.xtouch), ty(b.ytouch)) | |
hDC.stroke(); | |
labels.push([hDC, tx(c.xtouch), ty(c.ytouch) + 15, "IP", ipColor]) | |
} | |
} | |
} | |
for(var j = 0; j < labels.length; j++){ | |
drawLabel.apply(null, labels[j]) | |
} | |
} | |
function drawPreview(hDC, contours, ppem, dx, dy, SUPERSAMPLING){ | |
function txp(x){ return (x / upm * ppem + dx) * 3 * SUPERSAMPLING} | |
function typ(y){ return ((- y) / upm * ppem + dy) * 3} | |
// Fill | |
hDC.fillStyle = 'black' | |
hDC.beginPath(); | |
for(var j = 0; j < contours.length; j++){ | |
var contour = contours[j]; | |
// Layer 1 : Control outline | |
hDC.moveTo(txp(contour.points[0].xtouch), typ(contour.points[0].ytouch)) | |
for(var k = 1; k < contour.points.length; k++){ | |
if(contour.points[k].on) hDC.lineTo(txp(contour.points[k].xtouch), typ(contour.points[k].ytouch)) | |
else { | |
hDC.quadraticCurveTo(txp(contour.points[k].xtouch), typ(contour.points[k].ytouch), txp(contour.points[k + 1].xtouch), typ(contour.points[k + 1].ytouch)) | |
k += 1; | |
} | |
} | |
hDC.closePath(); | |
} | |
hDC.fill('nonzero'); | |
} | |
function drawPixelGrid(hDC, ppem){ | |
var uppx = upm / ppem | |
// Vertical lines | |
for(var j = 0; j <= ppem; j++){ | |
hDC.strokeStyle = '#ddd' | |
hDC.beginPath(); | |
hDC.moveTo(tx(j * uppx), ty(upm)) | |
hDC.lineTo(tx(j * uppx), ty(-upm)) | |
hDC.stroke() | |
} | |
// Horizontal lines | |
for(var j = -ppem; j <= ppem; j++){ | |
hDC.strokeStyle = j ? '#ddd' : 'black' | |
hDC.beginPath(); | |
hDC.moveTo(tx(0), ty(j * uppx)) | |
hDC.lineTo(tx(upm), ty(j * uppx)) | |
hDC.stroke() | |
} | |
// Crosses | |
for(var j = -ppem + 0.5; j <= ppem; j++) { | |
for(var k = 0.5; k <= ppem; k++) { | |
hDC.strokeStyle = '#ddd' | |
hDC.beginPath(); | |
hDC.moveTo(tx(k * uppx) - 2, ty(j * uppx) - 2) | |
hDC.lineTo(tx(k * uppx) + 2, ty(j * uppx) + 2) | |
hDC.stroke() | |
hDC.beginPath(); | |
hDC.moveTo(tx(k * uppx) + 2, ty(j * uppx) - 2) | |
hDC.lineTo(tx(k * uppx) - 2, ty(j * uppx) + 2) | |
hDC.stroke() | |
} | |
} | |
} | |
function parseSFD(input){ | |
var contours = [], currentContour = null | |
input = input.trim().split('\n'); | |
for(var j = 0; j < input.length; j++){ | |
var line = input[j].trim().split(/ +/); | |
if(line[2] === 'm'){ | |
// Moveto | |
if(currentContour) contours.push(currentContour); | |
currentContour = new Contour(); | |
currentContour.points.push(new Point(line[0] - 0, line[1] - 0, true)) | |
} else if(line[2] === 'l' && currentContour){ | |
// Lineto | |
currentContour.points.push(new Point(line[0] - 0, line[1] - 0, true)) | |
} else if(line[6] === 'c' && currentContour){ | |
// curveTo | |
currentContour.points.push(new Point(line[0] - 0, line[1] - 0, false)) | |
currentContour.points.push(new Point(line[4] - 0, line[5] - 0, true, /^128,/.test(line[7]))) | |
} | |
} | |
if(currentContour) contours.push(currentContour); | |
contours.forEach(function(c){ c.stat() }) | |
return contours; | |
} | |
function overlapRatio(a, b){ | |
var events = [] | |
for(var j = 0; j < a.length; j++){ | |
var low = Math.min(a[j][0].xori, a[j][a[j].length - 1].xori) | |
var high = Math.max(a[j][0].xori, a[j][a[j].length - 1].xori) | |
events.push({at: low, on: true, a: true}) | |
events.push({at: high, on: false, a: true}) | |
} | |
var probeb = new Array(upm); | |
for(var j = 0; j < b.length; j++){ | |
var low = Math.min(b[j][0].xori, b[j][b[j].length - 1].xori) | |
var high = Math.max(b[j][0].xori, b[j][b[j].length - 1].xori) | |
events.push({at: low, on: true, a: false}) | |
events.push({at: high, on: false, a: false}) | |
} | |
events.sort(function(p, q){ return p.at - q.at }) | |
var len = 0, la = 0, lb = 0; | |
var st = 0, sa = 0, sb = 0; | |
var ac = 0; | |
var bc = 0; | |
for(var j = 0; j < events.length; j++){ | |
var e = events[j] | |
var intersectBefore = ac * bc; | |
var ab = ac, bb = bc; | |
if(e.a) { if(e.on) ac += 1; else ac -= 1 } | |
else { if(e.on) bc += 1; else bc -= 1 } | |
if(ac * bc && !intersectBefore) st = e.at; | |
if(!(ac * bc) && intersectBefore) len += e.at - st; | |
if(ac && !ab) sa = e.at; | |
if(!ac && ab) la += e.at - sa; | |
if(bc && !bb) sb += e.at; | |
if(!bc && bb) lb += e.at - sb; | |
} | |
return len / Math.max(la, lb) | |
} | |
function enoughOverlapBetweenSegments(a, b, ratio){ | |
return overlapRatio(a, b) >= ratio | |
} | |
function enoughOverlapBetweenStems(a, b){ | |
return enoughOverlapBetweenSegments(a.low, b.low, MIN_STEM_OVERLAP_RATIO) | |
|| enoughOverlapBetweenSegments(a.high, b.high, MIN_STEM_OVERLAP_RATIO) | |
|| enoughOverlapBetweenSegments(a.low, b.high, MIN_STEM_OVERLAP_RATIO) | |
|| enoughOverlapBetweenSegments(a.high, b.low, MIN_STEM_OVERLAP_RATIO) | |
} | |
var blueFuzz = 15 | |
var MIN_OVERLAP_RATIO = 0.3; | |
var MIN_STEM_OVERLAP_RATIO = 0.2; | |
var Y_FUZZ = 3 | |
var SLOPE_FUZZ = 0.02 | |
function findStems(contours, MIN_STEM_WIDTH, MAX_STEM_WIDTH) { | |
function statGlyph(contours){ | |
var points = [] | |
points = points.concat.apply(points, contours.map(function(c){ return c.points })); | |
var ys = points.map(function(p){ return p.yori }) | |
var xs = points.map(function(p){ return p.xori }) | |
return { | |
xmax: Math.max.apply(Math, xs), | |
ymax: Math.max.apply(Math, ys), | |
xmin: Math.min.apply(Math, xs), | |
ymin: Math.min.apply(Math, ys) | |
} | |
} | |
function rootof(radical){ | |
if(radical.root === radical) return radical; | |
else { | |
// Path compression | |
var r = rootof(radical.root); | |
radical.root = r; | |
return r; | |
} | |
} | |
function findRadicals(contours){ | |
var radicals = [] | |
for(var j = 0; j < contours.length; j++){ | |
radicals[j] = { | |
contour: contours[j], | |
parts: [] | |
} | |
radicals[j].root = radicals[j]; | |
} | |
// Merge disjoint sets | |
for(var j = 0; j < radicals.length; j++){ | |
for(var k = 0; k < radicals.length; k++){ | |
if(rootof(radicals[j]).contour.includes(radicals[k].contour)) { | |
radicals[k].root = radicals[j].root; | |
} | |
} | |
}; | |
for(var j = 0; j < radicals.length; j++){ | |
rootof(radicals[j]).parts.push(radicals[j].contour) | |
}; | |
return radicals.filter(function(r){ return r.parts.length }).map(function(r){ return { parts: r.parts, outline: r.contour } }) | |
} | |
function findHorizontalSegments(radicals){ | |
var segments = [] | |
for(var r = 0; r < radicals.length; r++) { | |
radicals[r].mergedSegments = [] | |
for(var j = 0; j < radicals[r].parts.length; j++){ | |
var contour = radicals[r].parts[j]; | |
var lastPoint = contour.points[0] | |
var segment = [lastPoint]; | |
segment.radical = r; | |
for(var k = 1; k < contour.points.length - 1; k++) if(!contour.points[k].interpolated) { | |
if(Math.abs((contour.points[k].yori - lastPoint.yori) / (contour.points[k].xori - lastPoint.xori)) <= SLOPE_FUZZ) { | |
segment.push(contour.points[k]) | |
} else { | |
if(segment.length > 1) segments.push(segment) | |
lastPoint = contour.points[k]; | |
segment = [lastPoint] | |
segment.radical = r; | |
} | |
}; | |
if(Math.abs((contour.points[0].yori - lastPoint.yori) / (contour.points[0].xori - lastPoint.xori)) <= SLOPE_FUZZ) { | |
segment.push(contour.points[0]) | |
segment.push(contour.points[contour.points.length - 1]) | |
} | |
if(segment.length > 1) segments.push(segment) | |
} | |
} | |
segments = segments.sort(function(p, q){ return p.xori - q.xori }) | |
for(var j = 0; j < segments.length; j++) if(segments[j]){ | |
var pivot = [segments[j]]; | |
var pivotRadical = segments[j].radical; | |
var orientation = pivot[0][1].xori > pivot[0][0].xori | |
segments[j] = null; | |
for(var k = j + 1; k < segments.length; k++) if(segments[k] && Math.abs(segments[k][0].yori - pivot[0][0].yori) <= Y_FUZZ && segments[k].radical === pivotRadical && orientation === (segments[k][1].xori > segments[k][0].xori)){ | |
var r = pivot.radical; | |
pivot.push(segments[k]) | |
segments[k] = null; | |
} | |
radicals[pivotRadical].mergedSegments.push(pivot.sort(function(s1, s2){ | |
return orientation ? s1[0].xori - s2[0].xori : s2[0].xori - s1[0].xori})) | |
} | |
} | |
function stemAtRadicalTop(stem, radical){ | |
var a0 = stem.low[0][0].xori, az = stem.low[stem.low.length - 1][stem.low[stem.low.length - 1].length - 1].xori | |
var b0 = stem.high[0][0].xori, bz = stem.high[stem.high.length - 1][stem.high[stem.high.length - 1].length - 1].xori | |
var xmin = Math.min(a0, b0, az, bz), xmax = Math.max(a0, b0, az, bz); | |
for(var j = 0; j < radical.parts.length; j++) for(var k = 0; k < radical.parts[j].points.length; k++) { | |
var point = radical.parts[j].points[k]; | |
if(point.yori > stem.yori + blueFuzz && point.xori <= xmax && point.xori >= xmin) return false; | |
} | |
//return stem.yori >= radical.outline.ymax - MAX_STEM_WIDTH; | |
return true | |
} | |
function stemSegments(radicals){ | |
var stems = []; | |
for(var r = 0; r < radicals.length; r++) { | |
var radicalStems = []; | |
var segs = radicals[r].mergedSegments.sort(function(a, b){ return a[0][0].yori - b[0][0].yori}); | |
var ori = radicals[r].outline.ccw; | |
// We stem segments bottom-up. | |
for(var j = 0; j < segs.length; j++) if(segs[j] && ori === (segs[j][0][0].xori < segs[j][0][segs[j][0].length - 1].xori)) { | |
var stem = {low: segs[j]}; | |
for(var k = j + 1; k < segs.length; k++) if(segs[k] && overlapRatio(segs[j], segs[k]) >= MIN_OVERLAP_RATIO) { | |
if(ori !== (segs[k][0][0].xori < segs[k][0][segs[k][0].length - 1].xori) | |
&& segs[k][0][0].yori - segs[j][0][0].yori <= MAX_STEM_WIDTH | |
&& segs[k][0][0].yori - segs[j][0][0].yori >= MIN_STEM_WIDTH) { | |
// A stem is found | |
stem.high = segs[k] | |
stem.yori = stem.high[0][0].yori | |
stem.width = Math.abs(segs[k][0][0].yori - segs[j][0][0].yori) | |
stem.atRadicalTop = stemAtRadicalTop(stem, radicals[r]) | |
stem.atGlyphTop = stem.high[0][0].yori >= stats.ymax; | |
stem.belongRadical = radicals[r]; | |
segs[j] = segs[k] = null; | |
radicalStems.push(stem); | |
} | |
break; | |
} | |
}; | |
for(var k = 0; k < radicalStems.length; k++) { | |
for(var j = 0; j < radicalStems.length; j++) { | |
if(enoughOverlapBetweenStems(radicalStems[j], radicalStems[k]) && radicalStems[j].yori > radicalStems[k].yori) | |
radicalStems[k].atRadicalTop = radicalStems[k].atGlyphTop = false; | |
} | |
} | |
stems = stems.concat(radicalStems) | |
} | |
return stems; | |
} | |
var radicals = findRadicals(contours); | |
var stats = statGlyph(contours); | |
findHorizontalSegments(radicals); | |
var stems = stemSegments(radicals); | |
return stems; | |
} | |
function autohint(contours, stems, ppem){ | |
var uppx = upm / ppem; | |
var glyfBottom = -round(0.075 * upm) | |
var glyfTop = round(0.84 * upm) | |
function round(xori){ return Math.round(xori / upm * ppem) / ppem * upm } | |
function roundDown(xori){ return Math.floor(xori / upm * ppem) / ppem * upm } | |
function roundUp(xori){ return Math.ceil(xori / upm * ppem) / ppem * upm } | |
function roundDownStem(stem){ | |
stem.roundMethod = -1; // Positive for round up, negative for round down | |
stem.ytouch = roundDown(stem.yori); | |
stem.deltaY = 0 | |
} | |
function roundUpStem(stem){ | |
stem.roundMethod = 1; | |
stem.ytouch = roundUp(stem.yori); | |
stem.deltaY = 0 | |
} | |
function alignStem(stem, that){ | |
while(that.alignTo) that = that.alignTo; | |
stem.roundMethod = 0; | |
stem.alignTo = that; | |
stem.ytouch = that.ytouch; | |
} | |
var WIDTH_FACTOR_X = 2 | |
var MIN_ADJUST_PPEM = 12 | |
var MAX_ADJUST_PPEM = 32 | |
function clamp(x){ return Math.min(1, Math.max(0, x)) } | |
function calculateWidth(w){ | |
if(ppem < 20) return uppx; | |
if(w < uppx) return uppx; | |
else if (w < 2 * uppx) return uppx * WIDTH_FACTOR_X | |
* (w / uppx / WIDTH_FACTOR_X + clamp((ppem - MIN_ADJUST_PPEM) / (MAX_ADJUST_PPEM - MIN_ADJUST_PPEM)) * (1 - w / uppx / WIDTH_FACTOR_X)); | |
else return w; | |
} | |
function initStemTouches(stems){ | |
for(var j = 0; j < stems.length; j++) { | |
var w = calculateWidth(stems[j].width); | |
if(w < 1.9 * uppx) w = uppx | |
// stems[j].touchwidth = w; | |
stems[j].touchwidth = uppx; | |
stems[j].alignTo = null; | |
roundDownStem(stems[j]) | |
if(stems[j].ytouch - roundUp(w) < glyfBottom){ | |
roundUpStem(stems[j]) | |
} | |
} | |
} | |
var COLLISION_FUZZ = 1.04; | |
var HIGHLY_COLLISION_FUZZ = 0.3; | |
function collideWith(stems, transitions, j, k){ | |
return transitions[j][k] && (stems[j].ytouch > stems[k].ytouch | |
? stems[j].ytouch - stems[k].ytouch <= stems[j].touchwidth * COLLISION_FUZZ | |
: stems[k].ytouch - stems[j].ytouch <= stems[k].touchwidth * COLLISION_FUZZ) | |
} | |
function highlyCollideWith(stems, transitions, j, k){ | |
return transitions[j][k] && (stems[j].ytouch > stems[k].ytouch | |
? stems[j].ytouch - stems[k].ytouch <= stems[j].touchwidth * HIGHLY_COLLISION_FUZZ | |
: stems[k].ytouch - stems[j].ytouch <= stems[k].touchwidth * HIGHLY_COLLISION_FUZZ) | |
} | |
function spaceBelow(stems, transitions, k, bottom){ | |
var space = stems[k].ytouch - stems[k].touchwidth + bottom; | |
for(var j = k - 1; j >= 0; j--){ | |
if(transitions[j][k] && Math.abs(stems[k].ytouch - stems[j].ytouch) - stems[k].touchwidth < space) | |
space = stems[k].ytouch - stems[j].ytouch - stems[k].touchwidth | |
} | |
return space; | |
} | |
function spaceAbove(stems, transitions, k, top){ | |
var space = top - stems[k].ytouch; | |
for(var j = k + 1; j < stems.length; j++){ | |
if(transitions[k][j] && Math.abs(stems[j].ytouch - stems[k].ytouch) - stems[j].touchwidth < space) | |
space = stems[j].ytouch - stems[k].ytouch - stems[j].touchwidth | |
} | |
return space; | |
} | |
function canBeAdjustedUp(stems, transitions, k, distance){ | |
for(var j = k + 1; j < stems.length; j++){ | |
if(transitions[j][k] && Math.abs(stems[j].ytouch - stems[k].ytouch) - stems[j].touchwidth <= distance) | |
return false | |
} | |
return true; | |
} | |
function adjustDownward(stems, transitions, k, bottom){ | |
var s = spaceBelow(stems, transitions, k, bottom); | |
if(s >= 2 * uppx || s < 0.3 * uppx) { | |
// There is enough space below stem k, just bring it downward | |
if(stems[k].roundMethod === 1 && stems[k].ytouch > bottom) { | |
roundDownStem(stems[k]); | |
return true; | |
} | |
} | |
for(var j = 0; j < k; j++){ | |
if(!adjustDownward(stems, transitions, j, bottom)) return false; | |
} | |
return false; | |
} | |
// Collision resolving | |
function uncollide(stems){ | |
// In this procedure we move some segment stems to resolve collisions between them. | |
// A "collision" means that two stems meet togther after gridfitting. | |
// We will merge some of these stems to preserve the outfit of glyph while leaving | |
// space between strokes; | |
if(!stems.length) return; | |
stems = stems.sort(function(a, b){ return a.yori - b.yori }); | |
var transitions = []; | |
for(var j = 0; j < stems.length; j++){ | |
transitions[j] = [] | |
for(var k = 0; k < stems.length; k++){ | |
transitions[j][k] = enoughOverlapBetweenStems(stems[j], stems[k]) | |
} | |
} | |
// Step 0 : Adjust top and bottom stems | |
var ytouchmin0 = stems[0].ytouch; | |
var ytouchmin = ytouchmin0; | |
for(var j = 0; j < stems.length; j++) { | |
if(stems[j].roundMethod === -1 && stems[j].ytouch === ytouchmin0 && stems[j].ytouch - stems[j].touchwidth >= -1 | |
&& stems[j].yori - stems[j].ytouch >= 0.5 * uppx) { | |
ytouchmin = ytouchmin0 + uppx; | |
roundUpStem(stems[j]) | |
} | |
} | |
// Avoid stem merging at the bottom | |
for(var j = 0; j < stems.length; j++) if(stems[j].ytouch === ytouchmin) for(var k = 0; k < j; k++) { | |
if(transitions[j][k] && stems[j].roundMethod === -1) roundUpStem(stems[j]); | |
} | |
var ytouchmax0 = stems[stems.length - 1].ytouch; | |
var ytouchmax = ytouchmax0; | |
var hasStemAtGlyphTop = stems[stems.length - 1].atGlyphTop | |
for(var j = stems.length - 1; j >= 0; j--) { | |
var stem = stems[j]; | |
if(stem.ytouch === ytouchmax0) { | |
var canAdjustUpToGlyphTop = stem.ytouch < glyfTop - blueFuzz && stem.ytouch >= glyfTop - uppx - 1 | |
if(stem.roundMethod === -1 && stem.atRadicalTop | |
? stem.ytouch < glyfTop - blueFuzz && stem.yori - stem.ytouch >= 0.47 * uppx | |
: stem.ytouch + uppx < glyfTop - blueFuzz && stem.yori - stem.ytouch >= 0.47 * uppx) { | |
ytouchmax = ytouchmax0 + uppx; | |
roundUpStem(stem); | |
}; | |
var canAdjustUpToGlyphTop = stem.ytouch < glyfTop - blueFuzz && stem.ytouch >= glyfTop - uppx - 1 | |
if(hasStemAtGlyphTop && stem.atRadicalTop && canAdjustUpToGlyphTop && stem.yori - stem.ytouch >= 0.2 * uppx) { | |
if(stem.roundMethod === -1) { | |
ytouchmax = ytouchmax0 + uppx; | |
roundUpStem(stem); | |
} | |
}; | |
if(stem.atRadicalTop && canAdjustUpToGlyphTop) { | |
stem.allowMoveUpward = true | |
} | |
} | |
} | |
// Step 1: Uncollide | |
// We will perform stem movement using greedy method | |
// Not always works but okay for most characters | |
for(var j = 0; j < stems.length; j++){ | |
debugger; | |
if(stems[j].ytouch <= ytouchmin) { | |
// Stems[j] is a bottom stem | |
// DON'T MOVE IT | |
} else if(stems[j].ytouch >= ytouchmax) { | |
// Stems[j] is a top stem | |
// It should not be moved, but we can uncollide stems below it. | |
for(var k = j - 1; k >= 0; k--) if(collideWith(stems, transitions, j, k)) { | |
if(highlyCollideWith(stems, transitions, j, k)) { | |
alignStem(stems[k], stems[j]) | |
continue | |
} | |
var r = adjustDownward(stems, transitions, k, ytouchmin) | |
if(r) continue; | |
if(stems[j].roundMethod === -1 && stems[j].allowMoveUpward) { | |
roundUpStem(stems[j]); | |
break; | |
} | |
} | |
} else { | |
// Stems[j] is a middle stem | |
for(var k = j - 1; k >= 0; k--) if(collideWith(stems, transitions, j, k)) { | |
if(highlyCollideWith(stems, transitions, j, k)) { | |
alignStem(stems[j], stems[k]) | |
break; | |
} | |
var r = adjustDownward(stems, transitions, k, ytouchmin); | |
if(r) continue; | |
if(stems[j].roundMethod === -1) { | |
roundUpStem(stems[j]); | |
break; | |
} | |
} | |
} | |
}; | |
// Stem 2 : Alignment reduction | |
// In this step we will move aligned stems two pixels upward there is enough space. | |
for(var j = stems.length - 1; j >= 0; j--) if(stems[j].ytouch <= Math.min(ytouchmax - 1, glyfTop - 2 * uppx) && stems[j].ytouch > ytouchmin) { | |
if(canBeAdjustedUp(stems, transitions, j, 2.8 * uppx) && stems[j].roundMethod === 0) { | |
stems[j].alignTo = null; | |
roundUpStem(stems[j]); | |
stems[j].roundMethod = 2; | |
stems[j].ytouch += uppx; | |
} | |
} | |
// Step 3 : In-radical alignment reduction | |
// Once we find a in-radical alignment...... | |
for(var j = stems.length - 1; j >= 0; j--) if(stems[j].atRadicalTop && stems[j].roundMethod === 0 && stems[j].belongRadical === stems[j].alignTo.belongRadical) { | |
// Find a proper stem above it...... | |
for(var k = j; k < stems.length; k++) if(stems[k].ytouch === stems[j].ytouch + 2 * uppx && stems[k].belongRadical !== stems[j].belongRadical) { | |
// And align them. | |
alignStem(stems[j], stems[k]); | |
break; | |
} | |
} | |
// Step 4 : Position Rebalance | |
// Stems are rounded down by default, may cause improper movements | |
// Therefore we bring them upward one pixel when there is enough space | |
// above. | |
for(var j = stems.length - 1; j >= 0; j--) if(stems[j].ytouch < ytouchmax && stems[j].ytouch > ytouchmin) { | |
if(canBeAdjustedUp(stems, transitions, j, 1.8 * uppx) && stems[j].yori - stems[j].ytouch > 0.5 * uppx) { | |
if(stems[j].roundMethod === -1) { roundUpStem(stems[j]) } | |
} | |
} | |
// Stem 5 : Stem Width Allocation | |
// In this step we will adjust stem width when there is enough space below the stem. | |
for(var j = stems.length - 1; j >= 0; j--) { | |
debugger; | |
var sb = spaceBelow(stems, transitions, j, ytouchmin + uppx * 3); | |
var sa = spaceAbove(stems, transitions, j, ytouchmax + uppx * 3); | |
var w = Math.round(Math.min(stems[j].touchwidth + sa + sb - 2 * uppx, calculateWidth(stems[j].width)) / uppx) * uppx; | |
if(w <= uppx) continue; | |
if(sb >= 1.75 * uppx && stems[j].ytouch - w >= glyfBottom - 1) { | |
stems[j].touchwidth = w; | |
} else if (sa > 1.6 * uppx && stems[j].roundMethod === -1 && stems[j].ytouch - w + uppx >= glyfBottom - 1) { | |
roundUpStem(stems[j]); | |
stems[j].touchwidth = w | |
} | |
} | |
} | |
var instructions = { | |
roundings : [], | |
stemTopAlignments: [], | |
stemBottomAlignments: [], | |
blueZoneAlignments: [], | |
instemAlignments: [], | |
interpolations: [] | |
}; | |
// Touching procedure | |
function touchStemPoints(stems){ | |
for(var j = 0; j < stems.length; j++){ | |
var stem = stems[j], w = stem.touchwidth; | |
// Top edge of a stem | |
for(var k = 0; k < stem.high.length; k++) for(var p = 0; p < stem.high[k].length; p++) { | |
if(p === 0) { | |
stem.high[k][p].ytouch = stem.ytouch | |
stem.high[k][p].touched = true; | |
stem.high[k][p].keypoint = true; | |
if(k === 0) { | |
if(stem.roundMethod === 1) instructions.roundings.push(['ROUNDUP', stem.high[0][0]]) | |
else if(stem.roundMethod === -1) instructions.roundings.push(['ROUNDDOWN', stem.high[0][0]]) | |
else if(stem.roundMethod === 2) instructions.roundings.push(['ROUNDUP2', stem.high[0][0]]) | |
else if(stem.alignTo) instructions.stemTopAlignments.push(['ALIGN0', stem.alignTo.high[0][0], stem.high[0][0]]) | |
} else { | |
instructions.instemAlignments.push(['ALIGN0', stem.high[0][0], stem.high[k][0]]) | |
} | |
} else { | |
stem.high[k][p].donttouch = true; | |
} | |
} | |
for(var k = 0; k < stem.low.length; k++) for(var p = 0; p < stem.low[k].length; p++) { | |
if(p === 0) { | |
stem.low[k][p].ytouch = stem.ytouch - w; | |
stem.low[k][p].touched = true; | |
stem.low[k][p].keypoint = true; | |
if(k === 0) { | |
instructions.stemBottomAlignments.push(['ALIGNW', stem.high[0][0], stem.low[0][0], stem.touchwidth]) | |
} else { | |
instructions.instemAlignments.push(['ALIGN0', stem.low[0][0], stem.low[k][0]]) | |
} | |
} else { | |
stem.low[k][p].donttouch = true; | |
} | |
} | |
} | |
} | |
function touchBlueZonePoints(contours) { | |
function flushBottom(seq){ | |
var mink = 0; | |
for(var s = 0; s < seq.length; s++) if(seq[s].yori < seq[mink].yori) mink = s; | |
seq[mink].touched = true; | |
seq[mink].ytouch = glyfBottom; | |
seq[mink].keypoint = true; | |
instructions.blueZoneAlignments.push([seq[mink].yori > seq[mink].ytouch ? "ROUNDDOWN" : "ROUNDUP", seq[mink], glyfBottom]) | |
} | |
function flushTop(seq){ | |
var mink = 0; | |
for(var s = 0; s < seq.length; s++) if(seq[s].yori > seq[mink].yori) mink = s; | |
seq[mink].touched = true; | |
seq[mink].ytouch = glyfTop; | |
seq[mink].keypoint = true; | |
instructions.blueZoneAlignments.push([seq[mink].yori > seq[mink].ytouch ? "ROUNDDOWN" : "ROUNDUP", seq[mink], glyfTop]) | |
} | |
for(var j = 0; j < contours.length; j++) { | |
var seq = [] | |
for(var k = 0; k < contours[j].points.length; k++){ | |
var point = contours[j].points[k]; | |
if(point.yori <= -65){ | |
if(!point.touched && !point.donttouch) seq.push(point); | |
} else if(seq.length){ | |
flushBottom(seq); seq = []; | |
} | |
} | |
if(seq.length){ | |
flushBottom(seq); seq = []; | |
} | |
var seq = [] | |
for(var k = 0; k < contours[j].points.length; k++){ | |
var point = contours[j].points[k]; | |
if(point.yori >= 825){ | |
if(!point.touched && !point.donttouch) seq.push(point); | |
} else if(seq.length){ | |
flushTop(seq); seq = []; | |
} | |
} | |
if(seq.length){ | |
flushTop(seq); seq = []; | |
} | |
} | |
} | |
function interpolate(a, b, c, touch){ | |
c.touched = touch; | |
if(c.yori <= a.yori) c.ytouch = c.yori - a.yori + a.ytouch; | |
else if(c.yori >= b.yori) c.ytouch = c.yori - b.yori + b.ytouch; | |
else c.ytouch = (c.yori - a.yori) / (b.yori - a.yori) * (b.ytouch - a.ytouch) + a.ytouch; | |
if(touch) { | |
instructions.interpolations.push(['IP', a, b, c]) | |
} | |
} | |
function interpolatedUntouchedTopBottomPoints(contours){ | |
var touchedPoints = []; | |
for(var j = 0; j < contours.length; j++) for(var k = 0; k < contours[j].points.length; k++) if(contours[j].points[k].touched && contours[j].points.keypoint) { | |
touchedPoints.push(contours[j].points[k]); | |
} | |
touchedPoints = touchedPoints.sort(function(p, q){ return p.yori - q.yori }) | |
out: for(var j = 0; j < contours.length; j++) { | |
var localtopp = null, localbottomp = null; | |
for(var k = 0; k < contours[j].points.length; k++) { | |
var point = contours[j].points[k]; | |
if(!localtopp || point.yori > localtopp.yori) localtopp = point; | |
if(!localbottomp || point.yori < localbottomp.yori) localbottomp = point; | |
} | |
if(!localtopp.touched && !localtopp.donttouch) for(var k = 1; k < touchedPoints.length; k++) { | |
if(touchedPoints[k].yori > localtopp.yori && touchedPoints[k - 1].yori <= localtopp.yori) { | |
interpolate(touchedPoints[k], touchedPoints[k - 1], localtopp, true); | |
break; | |
} | |
} | |
if(!localbottomp.touched && !localbottomp.donttouch) for(var k = 1; k < touchedPoints.length; k++) { | |
if(touchedPoints[k].yori > localbottomp.yori && touchedPoints[k - 1].yori <= localbottomp.yori) { | |
interpolate(touchedPoints[k], touchedPoints[k - 1], localbottomp, true); | |
break; | |
} | |
} | |
} | |
} | |
// IUPy interpolates untouched points just like TT instructions. | |
function IUPy(contours){ | |
for(var j = 0; j < contours.length; j++){ | |
var contour = contours[j]; | |
var k = 0; | |
while(k < contour.points.length && !contour.points[k].touched) k++; | |
if(contour.points[k]) { | |
// Found a touched point in contour | |
var kleft = k, k0 = k; | |
var untoucheds = [] | |
for(var k = 0; k <= contour.points.length; k++){ | |
var ki = (k + k0) % contour.points.length; | |
if(contour.points[ki].touched){ | |
var pleft = contour.points[kleft]; | |
var pright = contour.points[ki]; | |
var lower = pleft.yori < pright.yori ? pleft : pright | |
var higher = pleft.yori < pright.yori ? pright : pleft | |
for(var w = 0; w < untoucheds.length; w++) interpolate(lower, higher, untoucheds[w]) | |
untoucheds = [] | |
kleft = ki; | |
} else { | |
untoucheds.push(contour.points[ki]) | |
} | |
} | |
} | |
} | |
} | |
function untouchAll(contours) { | |
for(var j = 0; j < contours.length; j++) for(var k = 0; k < contours[j].points.length; k++) { | |
contours[j].points[k].touched = false; | |
contours[j].points[k].donttouch = false; | |
contours[j].points[k].ytouch = contours[j].points[k].yori; | |
} | |
} | |
untouchAll(contours); | |
initStemTouches(stems); | |
uncollide(stems); | |
touchStemPoints(stems); | |
touchBlueZonePoints(contours); | |
interpolatedUntouchedTopBottomPoints(contours); | |
IUPy(contours); | |
return { | |
contours: contours, | |
instructions: instructions | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<div id="panel"><textarea id="input"> | |
307 716 m 1,0,-1 | |
263 661 l 1,1,2 | |
237 687 237 687 187.5 719.5 c 128,-1,3 | |
138 752 138 752 92 774 c 1,4,-1 | |
136 827 l 1,5,6 | |
251 775 251 775 307 721 c 1,7,-1 | |
307 750 l 1,8,-1 | |
467 750 l 1,9,-1 | |
467 836 l 1,10,-1 | |
535 836 l 1,11,-1 | |
535 750 l 1,12,-1 | |
700 750 l 1,13,-1 | |
700 836 l 1,14,-1 | |
770 836 l 1,15,-1 | |
770 750 l 1,16,-1 | |
943 750 l 1,17,-1 | |
943 687 l 1,18,-1 | |
770 687 l 1,19,-1 | |
770 616 l 1,20,-1 | |
700 616 l 1,21,-1 | |
700 687 l 1,22,-1 | |
535 687 l 1,23,-1 | |
535 616 l 1,24,-1 | |
467 616 l 1,25,-1 | |
467 687 l 1,26,-1 | |
307 687 l 1,27,-1 | |
307 716 l 1,0,-1 | |
259 462 m 1,28,-1 | |
215 401 l 1,29,30 | |
156 456 156 456 42 508 c 1,31,-1 | |
85 562 l 1,32,33 | |
199 515 199 515 259 462 c 1,28,-1 | |
232 304 m 1,34,-1 | |
289 256 l 1,35,36 | |
212 76 212 76 133 -65 c 1,37,-1 | |
69 -16 l 1,38,39 | |
147 111 147 111 232 304 c 1,34,-1 | |
428 431 m 1,40,-1 | |
582 431 l 1,41,-1 | |
582 538 l 1,42,-1 | |
428 538 l 1,43,-1 | |
428 431 l 1,40,-1 | |
815 431 m 1,44,-1 | |
815 538 l 1,45,-1 | |
655 538 l 1,46,-1 | |
655 431 l 1,47,-1 | |
815 431 l 1,44,-1 | |
950 124 m 1,48,-1 | |
693 124 l 1,49,50 | |
777 13 777 13 962 -18 c 1,51,52 | |
932 -46 932 -46 915 -82 c 1,53,54 | |
702 -35 702 -35 616 124 c 1,55,-1 | |
615 124 l 1,56,57 | |
541 -10 541 -10 322 -75 c 1,58,59 | |
302 -41 302 -41 277 -18 c 1,60,61 | |
461 31 461 31 531 124 c 1,62,-1 | |
296 124 l 1,63,-1 | |
296 184 l 1,64,-1 | |
563 184 l 1,65,66 | |
576 215 576 215 579 252 c 1,67,-1 | |
338 252 l 1,68,-1 | |
338 312 l 1,69,-1 | |
582 312 l 1,70,-1 | |
582 376 l 1,71,-1 | |
362 376 l 1,72,-1 | |
362 594 l 1,73,-1 | |
884 594 l 1,74,-1 | |
884 376 l 1,75,-1 | |
655 376 l 1,76,-1 | |
655 312 l 1,77,-1 | |
908 312 l 1,78,-1 | |
908 252 l 1,79,-1 | |
652 252 l 1,80,81 | |
649 217 649 217 639 184 c 1,82,-1 | |
950 184 l 1,83,-1 | |
950 124 l 1,48,-1 | |
// | |
372 800 m 1,0,-1 | |
372 740 l 1,1,-1 | |
97 740 l 1,2,-1 | |
97 800 l 1,3,-1 | |
372 800 l 1,0,-1 | |
313 197 m 1,4,-1 | |
155 197 l 1,5,-1 | |
155 29 l 1,6,-1 | |
313 29 l 1,7,-1 | |
313 197 l 1,4,-1 | |
377 258 m 1,8,-1 | |
377 -33 l 1,9,-1 | |
155 -33 l 1,10,-1 | |
155 -77 l 1,11,-1 | |
90 -77 l 1,12,-1 | |
90 258 l 1,13,-1 | |
377 258 l 1,8,-1 | |
91 334 m 1,14,-1 | |
91 394 l 1,15,-1 | |
375 394 l 1,16,-1 | |
375 334 l 1,17,-1 | |
91 334 l 1,14,-1 | |
375 530 m 1,18,-1 | |
375 471 l 1,19,-1 | |
91 471 l 1,20,-1 | |
91 530 l 1,21,-1 | |
375 530 l 1,18,-1 | |
45 606 m 1,22,-1 | |
45 667 l 1,23,-1 | |
408 667 l 1,24,-1 | |
408 606 l 1,25,-1 | |
45 606 l 1,22,-1 | |
827 213 m 1,26,-1 | |
549 213 l 1,27,-1 | |
549 36 l 1,28,-1 | |
827 36 l 1,29,-1 | |
827 213 l 1,26,-1 | |
479 -80 m 1,30,-1 | |
479 280 l 1,31,-1 | |
900 280 l 1,32,-1 | |
900 -76 l 1,33,-1 | |
827 -76 l 1,34,-1 | |
827 -31 l 1,35,-1 | |
549 -31 l 1,36,-1 | |
549 -80 l 1,37,-1 | |
479 -80 l 1,30,-1 | |
805 567 m 1,38,-1 | |
636 567 l 1,39,40 | |
624 498 624 498 608 422 c 1,41,-1 | |
805 422 l 1,42,-1 | |
805 567 l 1,38,-1 | |
876 422 m 1,43,-1 | |
961 422 l 1,44,-1 | |
961 355 l 1,45,-1 | |
409 355 l 1,46,-1 | |
409 422 l 1,47,-1 | |
536 422 l 1,48,49 | |
546 468 546 468 563 567 c 1,50,-1 | |
453 567 l 1,51,-1 | |
453 631 l 1,52,-1 | |
574 631 l 1,53,54 | |
575 635 575 635 590 733 c 1,55,-1 | |
438 733 l 1,56,-1 | |
438 798 l 1,57,-1 | |
928 798 l 1,58,-1 | |
928 733 l 1,59,-1 | |
664 733 l 1,60,61 | |
660 709 660 709 647 631 c 1,62,-1 | |
876 631 l 1,63,-1 | |
876 422 l 1,43,-1 | |
// | |
919 644 m 1,0,-1 | |
859 601 l 1,1,2 | |
806 681 806 681 702 771 c 1,3,-1 | |
759 810 l 1,4,5 | |
866 720 866 720 919 644 c 1,0,-1 | |
829 426 m 1,6,-1 | |
895 399 l 1,7,8 | |
828 273 828 273 721 164 c 1,9,10 | |
782 -5 782 -5 854 -5 c 0,11,12 | |
874 -5 874 -5 884 36 c 128,-1,13 | |
894 77 894 77 899 174 c 1,14,15 | |
928 146 928 146 962 134 c 1,16,17 | |
952 10 952 10 927.5 -34.5 c 128,-1,18 | |
903 -79 903 -79 847 -79 c 0,19,20 | |
736 -79 736 -79 662 108 c 1,21,22 | |
562 19 562 19 451 -39 c 1,23,24 | |
431 -6 431 -6 400 20 c 1,25,26 | |
532 84 532 84 637 182 c 1,27,28 | |
601 307 601 307 581 472 c 1,29,-1 | |
346 472 l 1,30,-1 | |
346 312 l 1,31,32 | |
376 318 376 318 436 331 c 128,-1,33 | |
496 344 496 344 525 351 c 1,34,-1 | |
530 284 l 1,35,36 | |
483 272 483 272 346 241 c 1,37,-1 | |
346 19 l 2,38,39 | |
346 -17 346 -17 336 -35.5 c 128,-1,40 | |
326 -54 326 -54 303 -63 c 0,41,42 | |
267 -77 267 -77 137 -77 c 1,43,44 | |
130 -42 130 -42 109 -2 c 1,45,46 | |
196 -6 196 -6 249 -3 c 0,47,48 | |
263 -3 263 -3 268 2 c 128,-1,49 | |
273 7 273 7 273 20 c 2,50,-1 | |
273 223 l 1,51,52 | |
232 214 232 214 161.5 198 c 128,-1,53 | |
91 182 91 182 66 177 c 1,54,-1 | |
45 252 l 1,55,56 | |
99 261 99 261 273 297 c 1,57,-1 | |
273 472 l 1,58,-1 | |
59 472 l 1,59,-1 | |
59 543 l 1,60,-1 | |
273 543 l 1,61,-1 | |
273 702 l 1,62,63 | |
186 685 186 685 89 672 c 1,64,65 | |
82 703 82 703 65 734 c 1,66,67 | |
322 773 322 773 460 824 c 1,68,-1 | |
513 763 l 1,69,70 | |
439 737 439 737 346 717 c 1,71,-1 | |
346 543 l 1,72,-1 | |
575 543 l 1,73,74 | |
563 668 563 668 561 828 c 1,75,-1 | |
637 828 l 1,76,77 | |
637 676 637 676 649 543 c 1,78,-1 | |
943 543 l 1,79,-1 | |
943 472 l 1,80,-1 | |
657 472 l 1,81,82 | |
672 345 672 345 698 244 c 1,83,84 | |
778 330 778 330 829 426 c 1,6,-1 | |
// | |
838 397 m 2,0,-1 | |
672 397 l 2,1,2 | |
600 397 600 397 575 418.5 c 128,-1,3 | |
550 440 550 440 550 503 c 2,4,-1 | |
550 836 l 1,5,-1 | |
623 836 l 1,6,-1 | |
623 668 l 1,7,8 | |
776 720 776 720 854 764 c 1,9,-1 | |
905 710 l 1,10,11 | |
803 661 803 661 623 606 c 1,12,-1 | |
623 503 l 2,13,14 | |
623 480 623 480 633 473 c 128,-1,15 | |
643 466 643 466 678 466 c 2,16,-1 | |
832 466 l 2,17,18 | |
861 466 861 466 870 486.5 c 128,-1,19 | |
879 507 879 507 882 581 c 1,20,21 | |
911 561 911 561 951 552 c 1,22,23 | |
944 459 944 459 921.5 428 c 128,-1,24 | |
899 397 899 397 838 397 c 2,0,-1 | |
173 187 m 1,25,-1 | |
383 187 l 1,26,-1 | |
383 277 l 1,27,-1 | |
173 277 l 1,28,-1 | |
173 187 l 1,25,-1 | |
383 422 m 1,29,-1 | |
173 422 l 1,30,-1 | |
173 336 l 1,31,-1 | |
383 336 l 1,32,-1 | |
383 422 l 1,29,-1 | |
457 484 m 1,33,-1 | |
457 13 l 2,34,35 | |
457 -44 457 -44 422 -61 c 0,36,37 | |
390 -73 390 -73 288 -73 c 1,38,39 | |
283 -36 283 -36 263 -1 c 1,40,41 | |
337 -4 337 -4 367 -2 c 0,42,43 | |
383 -2 383 -2 383 14 c 2,44,-1 | |
383 129 l 1,45,-1 | |
173 129 l 1,46,-1 | |
173 -71 l 1,47,-1 | |
103 -71 l 1,48,-1 | |
103 484 l 1,49,-1 | |
457 484 l 1,33,-1 | |
333 746 m 1,50,-1 | |
391 774 l 1,51,52 | |
489 648 489 648 524 550 c 1,53,-1 | |
462 518 l 1,54,55 | |
452 545 452 545 437 576 c 1,56,57 | |
366 572 366 572 234 565 c 128,-1,58 | |
102 558 102 558 51 555 c 1,59,-1 | |
43 626 l 1,60,-1 | |
115 628 l 1,61,62 | |
171 729 171 729 210 837 c 1,63,-1 | |
283 815 l 1,64,65 | |
228 696 228 696 192 632 c 1,66,67 | |
230 633 230 633 305.5 636.5 c 128,-1,68 | |
381 640 381 640 402 641 c 1,69,70 | |
359 711 359 711 333 746 c 1,50,-1 | |
680 -3 m 2,71,-1 | |
840 -3 l 2,72,73 | |
871 -3 871 -3 880.5 21 c 128,-1,74 | |
890 45 890 45 894 130 c 1,75,76 | |
921 111 921 111 962 101 c 1,77,78 | |
955 -1 955 -1 932 -35 c 128,-1,79 | |
909 -69 909 -69 845 -69 c 2,80,-1 | |
674 -69 l 2,81,82 | |
600 -69 600 -69 575 -47.5 c 128,-1,83 | |
550 -26 550 -26 550 36 c 2,84,-1 | |
550 375 l 1,85,-1 | |
623 375 l 1,86,-1 | |
623 217 l 1,87,88 | |
788 272 788 272 866 323 c 1,89,-1 | |
915 268 l 1,90,91 | |
819 213 819 213 623 155 c 1,92,-1 | |
623 36 l 2,93,94 | |
623 11 623 11 633 4 c 128,-1,95 | |
643 -3 643 -3 680 -3 c 2,71,-1 | |
// | |
287 316 m 1,0,-1 | |
719 316 l 1,1,2 | |
628 393 628 393 577 486 c 1,3,-1 | |
419 486 l 1,4,5 | |
364 391 364 391 287 316 c 1,0,-1 | |
284 38 m 1,6,-1 | |
717 38 l 1,7,-1 | |
717 247 l 1,8,-1 | |
284 247 l 1,9,-1 | |
284 38 l 1,6,-1 | |
946 486 m 1,10,-1 | |
656 486 l 1,11,12 | |
708 410 708 410 789 351 c 128,-1,13 | |
870 292 870 292 965 261 c 1,14,15 | |
944 230 944 230 934 186 c 1,16,17 | |
860.5 214.5 860.5 214.5 796 258 c 1,18,-1 | |
796 -74 l 1,19,-1 | |
717 -74 l 1,20,-1 | |
717 -32 l 1,21,-1 | |
284 -32 l 1,22,-1 | |
284 -76 l 1,23,-1 | |
209 -76 l 1,24,-1 | |
209 249 l 1,25,26 | |
148 205 148 205 80 172 c 1,27,28 | |
61 211 61 211 37 236 c 1,29,30 | |
221 318 221 318 331 486 c 1,31,-1 | |
55 486 l 1,32,-1 | |
55 555 l 1,33,-1 | |
372 555 l 1,34,35 | |
412 633 412 633 434 712 c 1,36,-1 | |
114 712 l 1,37,-1 | |
114 781 l 1,38,-1 | |
890 781 l 1,39,-1 | |
890 712 l 1,40,-1 | |
518 712 l 1,41,42 | |
495 638 495 638 457 555 c 1,43,-1 | |
946 555 l 1,44,-1 | |
946 486 l 1,10,-1 | |
// | |
942 688 m 1,0,-1 | |
520 688 l 1,1,-1 | |
520 512 l 1,2,-1 | |
533 527 l 1,3,4 | |
627 480 627 480 730 420 c 128,-1,5 | |
833 360 833 360 889 317 c 1,6,-1 | |
837 248 l 1,7,8 | |
729 338 729 338 520 451 c 1,9,-1 | |
520 -71 l 1,10,-1 | |
441 -71 l 1,11,-1 | |
441 688 l 1,12,-1 | |
59 688 l 1,13,-1 | |
59 763 l 1,14,-1 | |
942 763 l 1,15,-1 | |
942 688 l 1,0,-1 | |
// | |
343 214 m 1,0,-1 | |
355 147 l 1,1,2 | |
191 81 191 81 58 31 c 1,3,-1 | |
42 101 l 1,4,5 | |
64 108 64 108 172 148 c 1,6,-1 | |
172 413 l 1,7,-1 | |
60 413 l 1,8,-1 | |
60 482 l 1,9,-1 | |
172 482 l 1,10,-1 | |
172 699 l 1,11,-1 | |
50 699 l 1,12,-1 | |
50 769 l 1,13,-1 | |
353 769 l 1,14,-1 | |
353 699 l 1,15,-1 | |
241 699 l 1,16,-1 | |
241 482 l 1,17,-1 | |
332 482 l 1,18,-1 | |
332 413 l 1,19,-1 | |
241 413 l 1,20,-1 | |
241 174 l 1,21,-1 | |
343 214 l 1,0,-1 | |
813 381 m 1,22,-1 | |
543 381 l 1,23,24 | |
591 250 591 250 681 157 c 1,25,26 | |
768 250 768 250 813 381 c 1,22,-1 | |
620 620 m 1,27,-1 | |
464 620 l 1,28,-1 | |
464 447 l 1,29,-1 | |
620 447 l 1,30,-1 | |
620 620 l 1,27,-1 | |
854 450 m 1,31,-1 | |
900 432 l 1,32,33 | |
851 239 851 239 733 110 c 1,34,35 | |
836 26 836 26 963 -8 c 1,36,37 | |
936 -34 936 -34 915 -73 c 1,38,39 | |
780 -28 780 -28 681 59 c 1,40,41 | |
585 -25 585 -25 453 -77 c 1,42,43 | |
436 -42 436 -42 412 -17 c 1,44,45 | |
537 26 537 26 630 108 c 1,46,47 | |
529 220 529 220 473 381 c 1,48,-1 | |
463 381 l 1,49,50 | |
455 81 455 81 340 -69 c 1,51,52 | |
324 -45 324 -45 286 -22 c 1,53,54 | |
394 125 394 125 394 429 c 2,55,-1 | |
394 690 l 1,56,-1 | |
620 690 l 1,57,-1 | |
620 836 l 1,58,-1 | |
691 836 l 1,59,-1 | |
691 690 l 1,60,-1 | |
881 690 l 1,61,-1 | |
893 693 l 1,62,-1 | |
946 679 l 1,63,64 | |
906 538 906 538 881 479 c 1,65,-1 | |
816 494 l 1,66,67 | |
833 536 833 536 855 620 c 1,68,-1 | |
691 620 l 1,69,-1 | |
691 447 l 1,70,-1 | |
841 447 l 1,71,-1 | |
854 450 l 1,31,-1 | |
// | |
230 175 m 1,0,-1 | |
358 210 l 1,1,-1 | |
369 144 l 1,2,3 | |
315 128 315 128 208.5 97.5 c 128,-1,4 | |
102 67 102 67 58 54 c 1,5,-1 | |
35 125 l 1,6,-1 | |
163 157 l 1,7,-1 | |
163 402 l 1,8,-1 | |
59 402 l 1,9,-1 | |
59 469 l 1,10,-1 | |
163 469 l 1,11,-1 | |
163 687 l 1,12,-1 | |
46 687 l 1,13,-1 | |
46 754 l 1,14,-1 | |
351 754 l 1,15,-1 | |
351 687 l 1,16,-1 | |
230 687 l 1,17,-1 | |
230 469 l 1,18,-1 | |
337 469 l 1,19,-1 | |
337 402 l 1,20,-1 | |
230 402 l 1,21,-1 | |
230 175 l 1,0,-1 | |
895 298 m 1,22,-1 | |
895 297 l 1,23,-1 | |
929 297 l 1,24,-1 | |
929 -3 l 2,25,26 | |
929 -54 929 -54 897 -68 c 0,27,28 | |
870 -79 870 -79 772 -79 c 1,29,30 | |
766 -44 766 -44 750 -15 c 1,31,32 | |
793 -16 793 -16 844 -16 c 0,33,34 | |
854 -15 854 -15 857 -12.5 c 128,-1,35 | |
860 -10 860 -10 860 -2 c 2,36,-1 | |
860 233 l 1,37,-1 | |
632 233 l 1,38,39 | |
601 139 601 139 592 113 c 1,40,41 | |
706 121 706 121 736 123 c 1,42,43 | |
722 153 722 153 702 187 c 1,44,-1 | |
746 206 l 1,45,46 | |
808 106 808 106 825 37 c 1,47,-1 | |
778 16 l 1,48,49 | |
772 37 772 37 761 68 c 1,50,51 | |
540 48 540 48 484 44 c 1,52,-1 | |
477 106 l 1,53,-1 | |
532 109 l 1,54,55 | |
544 145 544 145 568 233 c 1,56,-1 | |
454 233 l 1,57,-1 | |
454 -77 l 1,58,-1 | |
384 -77 l 1,59,-1 | |
384 298 l 1,60,-1 | |
584 298 l 1,61,62 | |
593 332 593 332 599 366 c 1,63,-1 | |
424 366 l 1,64,-1 | |
424 646 l 1,65,-1 | |
489 646 l 1,66,-1 | |
489 425 l 1,67,-1 | |
825 425 l 1,68,-1 | |
825 646 l 1,69,-1 | |
893 646 l 1,70,-1 | |
893 366 l 1,71,-1 | |
671 366 l 1,72,-1 | |
651 298 l 1,73,-1 | |
895 298 l 1,22,-1 | |
793 483 m 1,74,-1 | |
758 447 l 1,75,76 | |
725 477 725 477 663 518 c 1,77,78 | |
602 465 602 465 540 431 c 1,79,80 | |
522 460 522 460 504 473 c 1,81,82 | |
568 505 568 505 619 546 c 1,83,84 | |
571 574 571 574 524 598 c 1,85,-1 | |
557 630 l 1,86,87 | |
604 608 604 608 656 578 c 1,88,89 | |
696 617 696 617 721 659 c 1,90,-1 | |
771 644 l 1,91,92 | |
745 598 745 598 699 551 c 1,93,94 | |
760 513 760 513 793 483 c 1,74,-1 | |
689 745 m 1,95,-1 | |
950 745 l 1,96,-1 | |
950 680 l 1,97,-1 | |
363 680 l 1,98,-1 | |
363 745 l 1,99,-1 | |
616 745 l 1,100,-1 | |
616 832 l 1,101,-1 | |
689 832 l 1,102,-1 | |
689 745 l 1,95,-1 | |
// | |
947 710 m 1,0,-1 | |
533 710 l 1,1,2 | |
514 643 514 643 487 565 c 1,3,-1 | |
896 565 l 1,4,-1 | |
896 17 l 2,5,6 | |
896 -16 896 -16 887.5 -32.5 c 128,-1,7 | |
879 -49 879 -49 858 -58 c 0,8,9 | |
826 -71 826 -71 706 -71 c 1,10,11 | |
699 -34 699 -34 680 -2 c 1,12,13 | |
760 -5 760 -5 802 -2 c 0,14,15 | |
813 -1 813 -1 817.5 3 c 128,-1,16 | |
822 7 822 7 822 18 c 2,17,-1 | |
822 495 l 1,18,-1 | |
652 495 l 1,19,-1 | |
652 -44 l 1,20,-1 | |
579 -44 l 1,21,-1 | |
579 495 l 1,22,-1 | |
414 495 l 1,23,-1 | |
414 -44 l 1,24,-1 | |
341 -44 l 1,25,-1 | |
341 495 l 1,26,-1 | |
183 495 l 1,27,-1 | |
183 -77 l 1,28,-1 | |
108 -77 l 1,29,-1 | |
108 565 l 1,30,-1 | |
409 565 l 1,31,32 | |
430 635 430 635 444 710 c 1,33,-1 | |
57 710 l 1,34,-1 | |
57 784 l 1,35,-1 | |
947 784 l 1,36,-1 | |
947 710 l 1,0,-1 | |
// | |
927 692 m 1,0,-1 | |
610 692 l 1,1,2 | |
586 651 586 651 539 582 c 1,3,-1 | |
539 -74 l 1,4,-1 | |
460 -74 l 1,5,-1 | |
460 482 l 1,6,7 | |
296 295 296 295 98 191 c 1,8,9 | |
84 215 84 215 48 256 c 1,10,11 | |
193 329 193 329 316 445 c 128,-1,12 | |
439 561 439 561 515 692 c 1,13,-1 | |
73 692 l 1,14,-1 | |
73 766 l 1,15,-1 | |
927 766 l 1,16,-1 | |
927 692 l 1,0,-1 | |
559 477 m 1,17,-1 | |
614 525 l 1,18,19 | |
847 374 847 374 955 262 c 1,20,-1 | |
896 205 l 1,21,22 | |
844 261 844 261 746.5 339 c 128,-1,23 | |
649 417 649 417 559 477 c 1,17,-1 | |
// | |
486 491 m 1,0,-1 | |
486 432 l 1,1,-1 | |
797 432 l 1,2,-1 | |
797 491 l 1,3,-1 | |
486 491 l 1,0,-1 | |
486 593 m 1,4,-1 | |
486 535 l 1,5,-1 | |
797 535 l 1,6,-1 | |
797 593 l 1,7,-1 | |
486 593 l 1,4,-1 | |
418 385 m 1,8,-1 | |
418 634 l 1,9,10 | |
382 580 382 580 343 538 c 1,11,12 | |
320 561 320 561 286 581 c 1,13,14 | |
389 681 389 681 448 836 c 1,15,-1 | |
515 818 l 1,16,17 | |
501 781 501 781 485 750 c 1,18,-1 | |
939 750 l 1,19,-1 | |
939 692 l 1,20,-1 | |
454 692 l 1,21,22 | |
444 674 444 674 422 640 c 1,23,-1 | |
867 640 l 1,24,-1 | |
867 385 l 1,25,-1 | |
418 385 l 1,8,-1 | |
265 832 m 1,26,-1 | |
335 812 l 1,27,28 | |
291 697 291 697 232 597 c 1,29,-1 | |
232 -71 l 1,30,-1 | |
162 -71 l 1,31,-1 | |
162 488 l 1,32,33 | |
114 421 114 421 62 367 c 1,34,35 | |
42 408 42 408 16 435 c 1,36,37 | |
91 507 91 507 157 612.5 c 128,-1,38 | |
223 718 223 718 265 832 c 1,26,-1 | |
959 279 m 1,39,-1 | |
531 279 l 1,40,41 | |
518 259 518 259 488 221 c 1,42,-1 | |
930 221 l 1,43,44 | |
929 201 929 201 927 191 c 0,45,46 | |
905 -16 905 -16 866 -55 c 0,47,48 | |
844 -74 844 -74 807 -77 c 0,49,50 | |
763 -79 763 -79 714 -75 c 1,51,52 | |
712 -45 712 -45 696 -18 c 1,53,54 | |
742 -23 742 -23 782 -23 c 0,55,56 | |
804 -23 804 -23 813 -15 c 0,57,58 | |
840 10 840 10 859 166 c 1,59,-1 | |
774 166 l 1,60,61 | |
707 7 707 7 580 -89 c 1,62,63 | |
560 -65 560 -65 532 -46 c 1,64,65 | |
647 34 647 34 706 166 c 1,66,-1 | |
603 166 l 1,67,68 | |
520 22 520 22 380 -69 c 1,69,70 | |
359 -43 359 -43 334 -25 c 1,71,72 | |
460 49 460 49 534 166 c 1,73,-1 | |
438 166 l 1,74,75 | |
374 104 374 104 316 67 c 1,76,77 | |
296 90 296 90 269 114 c 1,78,79 | |
384 179 384 179 458 279 c 1,80,-1 | |
292 279 l 1,81,-1 | |
292 337 l 1,82,-1 | |
959 337 l 1,83,-1 | |
959 279 l 1,39,-1 | |
// | |
288 374 m 1,0,-1 | |
288 272 l 1,1,2 | |
319 274 319 274 664 295 c 1,3,4 | |
670 300 670 300 681 310 c 128,-1,5 | |
692 320 692 320 698 325 c 1,6,-1 | |
698 374 l 1,7,-1 | |
288 374 l 1,0,-1 | |
698 672 m 1,8,-1 | |
288 672 l 1,9,-1 | |
288 580 l 1,10,-1 | |
698 580 l 1,11,-1 | |
698 672 l 1,8,-1 | |
698 431 m 1,12,-1 | |
698 522 l 1,13,-1 | |
288 522 l 1,14,-1 | |
288 431 l 1,15,-1 | |
698 431 l 1,12,-1 | |
867 518 m 1,16,-1 | |
937 481 l 1,17,18 | |
871 384 871 384 773 291 c 1,19,-1 | |
773 29 l 2,20,21 | |
773 -11 773 -11 762 -31.5 c 128,-1,22 | |
751 -52 751 -52 724 -62 c 0,23,24 | |
685 -75 685 -75 524 -75 c 1,25,26 | |
516 -32 516 -32 495 3 c 1,27,28 | |
546 1 546 1 670 1 c 0,29,30 | |
686 2 686 2 692 8 c 128,-1,31 | |
698 14 698 14 698 29 c 2,32,-1 | |
698 227 l 1,33,34 | |
458 37 458 37 97 -68 c 1,35,36 | |
73 -26 73 -26 50 1 c 1,37,38 | |
347 77 347 77 565 223 c 1,39,40 | |
257 198 257 198 70 185 c 1,41,-1 | |
58 259 l 1,42,43 | |
67 259 67 259 214 267 c 1,44,-1 | |
214 738 l 1,45,-1 | |
419 738 l 1,46,47 | |
444 792 444 792 459 839 c 1,48,-1 | |
542 816 l 1,49,50 | |
518 770 518 770 498 738 c 1,51,-1 | |
773 738 l 1,52,-1 | |
773 398 l 1,53,54 | |
824 453 824 453 867 518 c 1,16,-1 | |
// | |
859 624 m 1,0,-1 | |
859 692 l 1,1,-1 | |
784 692 l 1,2,-1 | |
784 624 l 1,3,-1 | |
859 624 l 1,0,-1 | |
859 506 m 1,4,-1 | |
859 574 l 1,5,-1 | |
784 574 l 1,6,-1 | |
784 506 l 1,7,-1 | |
859 506 l 1,4,-1 | |
552 574 m 1,8,-1 | |
552 506 l 1,9,-1 | |
620 506 l 1,10,-1 | |
620 574 l 1,11,-1 | |
552 574 l 1,8,-1 | |
552 692 m 1,12,-1 | |
552 624 l 1,13,-1 | |
620 624 l 1,14,-1 | |
620 692 l 1,15,-1 | |
552 692 l 1,12,-1 | |
737 574 m 1,16,-1 | |
667 574 l 1,17,-1 | |
667 506 l 1,18,-1 | |
737 506 l 1,19,-1 | |
737 574 l 1,16,-1 | |
667 624 m 1,20,-1 | |
737 624 l 1,21,-1 | |
737 692 l 1,22,-1 | |
667 692 l 1,23,-1 | |
667 624 l 1,20,-1 | |
918 744 m 1,24,-1 | |
918 454 l 1,25,-1 | |
494 454 l 1,26,-1 | |
494 744 l 1,27,-1 | |
612 744 l 1,28,-1 | |
612 835 l 1,29,-1 | |
668 835 l 1,30,-1 | |
668 744 l 1,31,-1 | |
738 744 l 1,32,-1 | |
738 835 l 1,33,-1 | |
795 835 l 1,34,-1 | |
795 744 l 1,35,-1 | |
918 744 l 1,24,-1 | |
335 349 m 1,36,-1 | |
185 349 l 1,37,-1 | |
185 278 l 1,38,-1 | |
335 278 l 1,39,-1 | |
335 349 l 1,36,-1 | |
185 152 m 1,40,-1 | |
335 152 l 1,41,-1 | |
335 225 l 1,42,-1 | |
185 225 l 1,43,-1 | |
185 152 l 1,40,-1 | |
180 742 m 1,44,-1 | |
180 523 l 1,45,-1 | |
230 523 l 1,46,-1 | |
230 678 l 1,47,-1 | |
343 678 l 1,48,-1 | |
343 742 l 1,49,-1 | |
180 742 l 1,44,-1 | |
343 523 m 1,50,-1 | |
343 630 l 1,51,-1 | |
278 630 l 1,52,-1 | |
278 523 l 1,53,-1 | |
343 523 l 1,50,-1 | |
463 379 m 1,54,-1 | |
404 379 l 1,55,-1 | |
404 470 l 1,56,-1 | |
123 470 l 1,57,-1 | |
123 403 l 1,58,-1 | |
400 403 l 1,59,-1 | |
400 -12 l 2,60,61 | |
400 -59 400 -59 372 -70 c 0,62,63 | |
349 -81 349 -81 267 -81 c 1,64,65 | |
262 -53 262 -53 245 -23 c 1,66,67 | |
279 -24 279 -24 322 -24 c 0,68,69 | |
335 -23 335 -23 335 -12 c 2,70,-1 | |
335 98 l 1,71,-1 | |
185 98 l 1,72,-1 | |
185 -82 l 1,73,-1 | |
122 -82 l 1,74,-1 | |
122 379 l 1,75,-1 | |
65 379 l 1,76,-1 | |
65 523 l 1,77,-1 | |
122 523 l 1,78,-1 | |
122 799 l 1,79,-1 | |
403 799 l 1,80,-1 | |
403 523 l 1,81,-1 | |
464 523 l 1,82,-1 | |
464 405 l 1,83,-1 | |
953 405 l 1,84,-1 | |
953 351 l 1,85,-1 | |
463 351 l 1,86,-1 | |
463 379 l 1,54,-1 | |
789 114 m 1,87,-1 | |
615 114 l 1,88,89 | |
648 60 648 60 659 1 c 1,90,-1 | |
650 -2 l 1,91,-1 | |
741 -2 l 1,92,93 | |
771 55 771 55 789 114 c 1,87,-1 | |
575 253 m 1,94,-1 | |
575 164 l 1,95,-1 | |
837 164 l 1,96,-1 | |
837 253 l 1,97,-1 | |
575 253 l 1,94,-1 | |
798 -2 m 1,98,-1 | |
959 -2 l 1,99,-1 | |
959 -59 l 1,100,-1 | |
446 -59 l 1,101,-1 | |
446 -2 l 1,102,-1 | |
598 -2 l 1,103,104 | |
587 48 587 48 560 98 c 1,105,-1 | |
608 114 l 1,106,-1 | |
513 114 l 1,107,-1 | |
513 303 l 1,108,-1 | |
902 303 l 1,109,-1 | |
902 114 l 1,110,-1 | |
798 114 l 1,111,-1 | |
850 95 l 1,112,113 | |
845 86 845 86 826.5 50 c 128,-1,114 | |
808 14 808 14 798 -2 c 1,98,-1 | |
// | |
424 592 m 1,0,-1 | |
424 571 l 1,1,-1 | |
580 571 l 1,2,-1 | |
580 772 l 1,3,-1 | |
424 772 l 1,4,-1 | |
424 748 l 1,5,-1 | |
540 748 l 1,6,-1 | |
540 712 l 1,7,-1 | |
424 712 l 1,8,-1 | |
424 690 l 1,9,-1 | |
534 690 l 1,10,-1 | |
534 592 l 1,11,-1 | |
424 592 l 1,0,-1 | |
424 657 m 1,12,-1 | |
424 625 l 1,13,-1 | |
500 625 l 1,14,-1 | |
500 657 l 1,15,-1 | |
424 657 l 1,12,-1 | |
148 522 m 1,16,-1 | |
148 426 l 1,17,-1 | |
81 426 l 1,18,-1 | |
81 571 l 1,19,-1 | |
171 571 l 1,20,-1 | |
159 807 l 1,21,-1 | |
213 794 l 1,22,23 | |
278 813 278 813 324 836 c 1,24,-1 | |
367 796 l 1,25,26 | |
321 776 321 776 226 750 c 1,27,-1 | |
227 722 l 1,28,-1 | |
343 722 l 1,29,-1 | |
343 683 l 1,30,-1 | |
229 683 l 1,31,-1 | |
230 648 l 1,32,-1 | |
344 648 l 1,33,-1 | |
344 609 l 1,34,-1 | |
232 609 l 1,35,-1 | |
234 571 l 1,36,-1 | |
372 571 l 1,37,-1 | |
372 812 l 1,38,-1 | |
633 812 l 1,39,-1 | |
633 571 l 1,40,-1 | |
766 571 l 1,41,-1 | |
769 609 l 1,42,-1 | |
666 609 l 1,43,-1 | |
666 648 l 1,44,-1 | |
772 648 l 1,45,-1 | |
774 685 l 1,46,-1 | |
666 685 l 1,47,-1 | |
666 724 l 1,48,-1 | |
776 724 l 1,49,-1 | |
778 760 l 1,50,-1 | |
665 760 l 1,51,-1 | |
665 805 l 1,52,-1 | |
779 805 l 1,53,-1 | |
779 806 l 1,54,-1 | |
843 806 l 1,55,56 | |
838 677 838 677 830 571 c 1,57,-1 | |
916 571 l 1,58,-1 | |
916 426 l 1,59,-1 | |
845 426 l 1,60,-1 | |
845 522 l 1,61,-1 | |
148 522 l 1,16,-1 | |
541 449 m 1,62,-1 | |
451 449 l 1,63,64 | |
450 443 450 443 448.5 433 c 128,-1,65 | |
447 423 447 423 447 418 c 1,66,-1 | |
541 418 l 1,67,-1 | |
541 449 l 1,62,-1 | |
769 351 m 1,68,-1 | |
769 380 l 1,69,-1 | |
597 380 l 1,70,-1 | |
597 369 l 2,71,72 | |
597 356 597 356 603.5 352.5 c 128,-1,73 | |
610 349 610 349 632 349 c 2,74,-1 | |
746 349 l 2,75,76 | |
767 349 767 349 769 351 c 1,68,-1 | |
228 229 m 1,77,-1 | |
769 229 l 1,78,-1 | |
769 258 l 1,79,-1 | |
228 258 l 1,80,-1 | |
228 229 l 1,77,-1 | |
755 307 m 1,81,-1 | |
628 307 l 2,82,83 | |
577 307 577 307 559 319.5 c 128,-1,84 | |
541 332 541 332 541 369 c 2,85,-1 | |
541 380 l 1,86,-1 | |
436 380 l 1,87,88 | |
410 317 410 317 309 293 c 1,89,-1 | |
769 293 l 1,90,-1 | |
769 307 l 1,91,-1 | |
755 307 l 1,81,-1 | |
375 380 m 1,92,-1 | |
228 380 l 1,93,-1 | |
228 293 l 1,94,-1 | |
270 293 l 1,95,96 | |
259 314 259 314 246 326 c 1,97,98 | |
348 340 348 340 375 380 c 1,92,-1 | |
164 418 m 1,99,-1 | |
391 418 l 1,100,101 | |
395 435 395 435 396 449 c 1,102,-1 | |
213 449 l 1,103,-1 | |
213 490 l 1,104,-1 | |
773 490 l 1,105,-1 | |
773 449 l 1,106,-1 | |
597 449 l 1,107,-1 | |
597 418 l 1,108,-1 | |
837 418 l 1,109,-1 | |
837 191 l 1,110,-1 | |
164 191 l 1,111,-1 | |
164 418 l 1,99,-1 | |
269 162 m 1,112,-1 | |
336 148 l 1,113,114 | |
259 75 259 75 88 25 c 1,115,116 | |
72 51 72 51 44 69 c 1,117,118 | |
200 102 200 102 269 162 c 1,112,-1 | |
631 166 m 1,119,-1 | |
699 166 l 1,120,121 | |
738 134 738 134 811 108 c 128,-1,122 | |
884 82 884 82 960 69 c 1,123,124 | |
934 43 934 43 919 15 c 1,125,126 | |
841 35 841 35 774 62 c 1,127,128 | |
774 61 774 61 773.5 59 c 128,-1,129 | |
773 57 773 57 772.5 55.5 c 128,-1,130 | |
772 54 772 54 771.5 52.5 c 128,-1,131 | |
771 51 771 51 771 50 c 0,132,133 | |
744 -44 744 -44 709 -67 c 0,134,135 | |
686 -81 686 -81 644 -83 c 0,136,137 | |
573 -83 573 -83 525 -79 c 1,138,139 | |
523 -54 523 -54 510 -29 c 1,140,141 | |
557 -34 557 -34 624 -34 c 0,142,143 | |
649 -34 649 -34 660 -28 c 0,144,145 | |
681 -12 681 -12 697 35 c 1,146,-1 | |
479 35 l 1,147,148 | |
441 -16 441 -16 355.5 -41.5 c 128,-1,149 | |
270 -67 270 -67 147 -82 c 1,150,151 | |
138 -59 138 -59 108 -26 c 1,152,153 | |
331 -16 331 -16 397 35 c 1,154,-1 | |
236 35 l 1,155,-1 | |
236 78 l 1,156,-1 | |
709 78 l 1,157,-1 | |
709 81 l 1,158,-1 | |
731 81 l 1,159,160 | |
692 100 692 100 663 121 c 1,161,-1 | |
371 121 l 1,162,-1 | |
371 163 l 1,163,-1 | |
631 163 l 1,164,-1 | |
631 166 l 1,119,-1 | |
// | |
533 388 m 1,0,-1 | |
533 322 l 1,1,-1 | |
767 322 l 1,2,-1 | |
767 388 l 1,3,-1 | |
533 388 l 1,0,-1 | |
533 505 m 1,4,-1 | |
533 446 l 1,5,-1 | |
767 446 l 1,6,-1 | |
767 505 l 1,7,-1 | |
533 505 l 1,4,-1 | |
842 266 m 1,8,-1 | |
533 266 l 1,9,-1 | |
533 209 l 1,10,-1 | |
867 209 l 1,11,-1 | |
867 151 l 1,12,-1 | |
533 151 l 1,13,-1 | |
533 89 l 1,14,-1 | |
930 89 l 1,15,-1 | |
930 30 l 1,16,-1 | |
533 30 l 1,17,-1 | |
533 -73 l 1,18,-1 | |
459 -73 l 1,19,-1 | |
459 30 l 1,20,-1 | |
70 30 l 1,21,-1 | |
70 89 l 1,22,-1 | |
459 89 l 1,23,-1 | |
459 151 l 1,24,-1 | |
133 151 l 1,25,-1 | |
133 209 l 1,26,-1 | |
459 209 l 1,27,-1 | |
459 266 l 1,28,-1 | |
166 266 l 1,29,-1 | |
166 322 l 1,30,-1 | |
459 322 l 1,31,-1 | |
459 388 l 1,32,-1 | |
53 388 l 1,33,-1 | |
53 446 l 1,34,-1 | |
459 446 l 1,35,-1 | |
459 505 l 1,36,-1 | |
172 505 l 1,37,-1 | |
172 561 l 1,38,-1 | |
459 561 l 1,39,-1 | |
459 586 l 1,40,-1 | |
456 584 l 1,41,42 | |
426 612 426 612 397 627 c 1,43,44 | |
441 657 441 657 477 693 c 1,45,-1 | |
318 693 l 1,46,47 | |
349 647 349 647 364 611 c 1,48,-1 | |
301 586 l 1,49,50 | |
283 633 283 633 242 693 c 1,51,-1 | |
203 693 l 1,52,53 | |
151 615 151 615 101 568 c 1,54,55 | |
74 593 74 593 43 611 c 1,56,57 | |
144 701 144 701 202 837 c 1,58,-1 | |
271 818 l 1,59,60 | |
253 779 253 779 238 751 c 1,61,-1 | |
491 751 l 1,62,-1 | |
491 708 l 1,63,64 | |
545 768 545 768 575 837 c 1,65,-1 | |
644 819 l 1,66,67 | |
632 792 632 792 608 751 c 1,68,-1 | |
942 751 l 1,69,-1 | |
942 693 l 1,70,-1 | |
713 693 l 1,71,72 | |
748 652 748 652 765 619 c 1,73,-1 | |
704 587 l 1,74,75 | |
681 634 681 634 631 693 c 1,76,-1 | |
567 693 l 1,77,78 | |
535 653 535 653 494 615 c 1,79,-1 | |
533 615 l 1,80,-1 | |
533 561 l 1,81,-1 | |
842 561 l 1,82,-1 | |
842 446 l 1,83,-1 | |
945 446 l 1,84,-1 | |
945 388 l 1,85,-1 | |
842 388 l 1,86,-1 | |
842 266 l 1,8,-1 | |
// | |
940 705 m 1,0,-1 | |
848 640 l 1,1,2 | |
814 700 814 700 727 785 c 1,3,-1 | |
812 844 l 1,4,5 | |
906 758 906 758 940 705 c 1,0,-1 | |
549 87 m 1,6,-1 | |
549 73 l 1,7,8 | |
588 105 588 105 634 155 c 1,9,10 | |
595 302 595 302 582 539 c 1,11,-1 | |
363 539 l 1,12,-1 | |
363 494 l 1,13,-1 | |
550 494 l 1,14,-1 | |
550 410 l 1,15,-1 | |
363 410 l 1,16,-1 | |
363 372 l 1,17,-1 | |
533 372 l 1,18,-1 | |
533 125 l 1,19,-1 | |
361 125 l 1,20,-1 | |
361 87 l 1,21,-1 | |
549 87 l 1,6,-1 | |
178 221 m 1,22,-1 | |
178 183 l 1,23,-1 | |
266 183 l 1,24,-1 | |
266 221 l 1,25,-1 | |
178 221 l 1,22,-1 | |
178 314 m 1,26,-1 | |
178 277 l 1,27,-1 | |
266 277 l 1,28,-1 | |
266 314 l 1,29,-1 | |
178 314 l 1,26,-1 | |
353 277 m 1,30,-1 | |
446 277 l 1,31,-1 | |
446 314 l 1,32,-1 | |
353 314 l 1,33,-1 | |
353 277 l 1,30,-1 | |
353 183 m 1,34,-1 | |
446 183 l 1,35,-1 | |
446 221 l 1,36,-1 | |
353 221 l 1,37,-1 | |
353 183 l 1,34,-1 | |
816 494 m 1,38,-1 | |
921 462 l 1,39,40 | |
860 270 860 270 759 133 c 1,41,42 | |
794 28 794 28 836 28 c 0,43,44 | |
864 28 864 28 871 191 c 1,45,46 | |
912 149 912 149 966 128 c 1,47,48 | |
954 1 954 1 923.5 -43.5 c 128,-1,49 | |
893 -88 893 -88 826 -88 c 0,50,51 | |
735 -88 735 -88 676 34 c 1,52,53 | |
610 -33.5 610 -33.5 529 -86 c 1,54,55 | |
500 -41 500 -41 453 0 c 1,56,-1 | |
361 0 l 1,57,-1 | |
361 -91 l 1,58,-1 | |
258 -91 l 1,59,-1 | |
258 0 l 1,60,-1 | |
62 0 l 1,61,-1 | |
62 87 l 1,62,-1 | |
258 87 l 1,63,-1 | |
258 125 l 1,64,-1 | |
94 125 l 1,65,-1 | |
94 372 l 1,66,-1 | |
256 372 l 1,67,-1 | |
256 410 l 1,68,-1 | |
76 410 l 1,69,-1 | |
76 494 l 1,70,-1 | |
256 494 l 1,71,-1 | |
256 539 l 1,72,-1 | |
49 539 l 1,73,-1 | |
49 636 l 1,74,-1 | |
254 636 l 1,75,-1 | |
254 688 l 1,76,-1 | |
93 688 l 1,77,-1 | |
93 779 l 1,78,-1 | |
254 779 l 1,79,-1 | |
254 846 l 1,80,-1 | |
367 846 l 1,81,-1 | |
367 779 l 1,82,-1 | |
531 779 l 1,83,-1 | |
531 688 l 1,84,-1 | |
367 688 l 1,85,-1 | |
367 636 l 1,86,-1 | |
577 636 l 1,87,88 | |
573 727 573 727 573 845 c 1,89,-1 | |
692 845 l 1,90,91 | |
689 755 689 755 693 636 c 1,92,-1 | |
953 636 l 1,93,-1 | |
953 539 l 1,94,-1 | |
697 539 l 1,95,96 | |
707 388 707 388 725 283 c 1,97,98 | |
779 376 779 376 816 494 c 1,38,-1 | |
</textarea></div> | |
<canvas id="output" width="750" height="800"></canvas> | |
<canvas id="preview" width="750" height="800"></canvas> | |
<script src="autohint.js"></script> | |
<script src="main.js"></script> | |
<style> | |
canvas { border: 1px solid #ddd } | |
#output { | |
position: absolute; | |
top: 0; | |
left: 300px; | |
} | |
#preview { | |
position: absolute; | |
top: 0; | |
left: 1050px; | |
} | |
#panel { | |
display: inline-block; | |
width: 300px; | |
height: 800px; | |
position: absolute; | |
top: 0; | |
left: 0; | |
} | |
#panel textarea { | |
display: block; | |
position: absolute; | |
top: 3em; left: 0; right: 0; bottom: 0; | |
width: 300px; height: auto; | |
box-sizing: border-box; | |
-webkit-box-sizing: border-box; | |
} | |
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var hOutput = document.getElementById('output').getContext('2d'); | |
var hPreview = document.getElementById('preview').getContext('2d'); | |
var minsw = 20; | |
var maxsw = 140; | |
var input = document.getElementById('input').value.split(/^\/\//m).map(parseSFD); | |
var inputStems = input.map(function(contours){ return findStems(contours, minsw, maxsw) }); | |
var ppem = 14; | |
var cs = autohint(input[13], inputStems[13], ppem); | |
drawPixelGrid(hOutput, ppem) | |
drawContours(hOutput, cs.contours); | |
drawInstructions(hOutput, cs.instructions); | |
debugger; | |
var CANVASWIDTH = 750; | |
var CANVASHEIGHT = 800; | |
var SUPERSAMPLING = 8; | |
var eTemp = document.createElement('canvas') | |
eTemp.width = CANVASWIDTH * 3 * SUPERSAMPLING; | |
eTemp.height = CANVASHEIGHT * 3; | |
var hTemp = eTemp.getContext('2d') | |
hTemp.fillStyle = "white"; | |
hTemp.fillRect(0, 0, CANVASWIDTH * 3 * SUPERSAMPLING, CANVASHEIGHT * 3); | |
var dy = 40 | |
for(var ppem = 12; ppem <= 30; ppem++) { | |
var dx = 10; | |
for(var k = 0; k < input.length; k++) if(input[k].length){ | |
var c = autohint(input[k], inputStems[k], ppem) | |
drawPreview(hTemp, c.contours, ppem, dx, dy, SUPERSAMPLING); | |
dx += ppem; | |
} | |
dy += 40; | |
} | |
setTimeout(function(){ | |
var GAMMA = 2.2; | |
var ori = hTemp.getImageData(0, 0, CANVASWIDTH * 3 * SUPERSAMPLING, CANVASHEIGHT * 3); | |
var aa = hPreview.createImageData(CANVASWIDTH, CANVASHEIGHT); | |
var w = 4 * 3 * SUPERSAMPLING * CANVASWIDTH; | |
var h = []; for(var j = 0; j < 3 * SUPERSAMPLING; j++) h[j] = 1; | |
var jSample = 0; | |
var a = 3 * SUPERSAMPLING; | |
for(var j = 0; j < CANVASHEIGHT; j++) { | |
for(var k = 0; k < CANVASWIDTH; k++) { | |
for(var component = 0; component < 3; component++) { | |
for(var ss = 0; ss < SUPERSAMPLING; ss++) { | |
var d = ori.data[w] < 128 ? 0 : 1; | |
a += d | |
a -= h[jSample] | |
h[jSample] = d; | |
w += 4; | |
jSample += 1; | |
if(jSample >= 3 * SUPERSAMPLING) jSample = 0; | |
} | |
var alpha = a / (3 * SUPERSAMPLING); | |
aa.data[(j * CANVASWIDTH + k) * 4 + component] = 255 * Math.pow(alpha, 1 / 1.8) | |
} | |
aa.data[(j * CANVASWIDTH + k) * 4 + 3] = 255 | |
} | |
w += 4 * 2 * 3 * SUPERSAMPLING * CANVASWIDTH | |
} | |
hPreview.putImageData(aa, 0, 0) | |
}, 10) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment