Commit aa1c95b9 authored by gijs's avatar gijs

Added electron test and included the pictures in the Markdown

parent 25800dd7
......@@ -76,3 +76,13 @@ No, it is a programming framework.
* For which operating systems is this tool available?
Applications made with Electron can be run on Linux, Mac and Windows. It's one of the essential features of the tool.
To test Electron I wrapped the TTX-inspector developed for the ['Dirty Variables' workshop](https://gitlab.constantvzw.org/osp/workshop.dirty-variables/tree/master/inspector) into an application.
![](images/electron-A.png)
![](images/electron-B.png)
![](images/electron-C.png)
![](images/electron-D.png)
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Glyph</title>
<style>
.controls {
position: fixed;
top: 0;
right: 0;
left: 0;
padding: 10px;
background: #efefef;
}
body {
padding-top: 20px;
}
</style>
</head>
<body>
<section class="controls">
<!-- <label>Font file:
<input type="file" id="font-selector" />
</label> -->
<button id="select-font">Select a TTX</button>
<button id="reload-font">Reload font</button>
<button id="export-canvas">Export Canvas</button>
<select id="glyph-selector"></select>
</section>
<canvas id="canvas" width="1000" height="1000"></canvas>
<script src="ttxInspector.js"></script>
</body>
</html>
\ No newline at end of file
const { app, BrowserWindow } = require('electron')
const path = require('path')
const url = require('url')
function createWindow() {
win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
}
app.on('ready', createWindow)
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (win === null) {
createWindow()
}
})
\ No newline at end of file
{
"name": "tutorial",
"version": "0.1.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^1.8.4"
}
}
const fs = require('fs');
const { dialog } = require('electron').remote
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 deriveCurvePoint(p1, p2) {
return {
'x': p1.x + .5 * (p2.x - p1.x),
'y': p1.y + .5 * (p2.y - p1.y),
'on': false,
'derived': true
};
}
function drawGlyph(name, font) {
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
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;
prepareCanvas = (ctx) => {
ctx.transform(1, 0, 0, -1, 0, canvas.height);
ctx.translate((canvas.width - glyphWidth) / 2 - xmin, (canvas.height - glyphHeight) / 2);
};
ctx.save();
prepareCanvas(ctx);
var glyphTTYPoints = [];
font.contours(name).forEach(contour => {
ctx.beginPath();
var contourTTYPoints = [];
var curvepoints = [];
contour.querySelectorAll('pt').forEach((point) => {
var x = parseInt(point.getAttribute('x')),
y = parseInt(point.getAttribute('y')),
on = (point.getAttribute('on') === "1") ? true : false;
contourTTYPoints.push({
'x': x,
'y': y,
'on': on
});
});
var prevPoint = contourTTYPoints[contourTTYPoints.length - 1];
for (var i = 0; i < contourTTYPoints.length; i++) {
var point = contourTTYPoints[i];
if (!point.on) {
if (!prevPoint.on) {
curvepoints.push(deriveCurvePoint(point, prevPoint));
} else if (i === 0) {
curvepoints.push(prevPoint);
}
}
curvepoints.push(point);
prevPoint = point;
}
if (!curvepoints[curvepoints.length - 1].on) {
curvepoints.push(curvepoints[0]);
}
for (var i = 0; i < curvepoints.length; i++) {
var p = curvepoints[i];
if (p.on) {
if (i === 0) {
ctx.moveTo(p.x, p.y);
} else {
ctx.lineTo(p.x, p.y);
}
} else {
var cp = p,
p = curvepoints[i + 1];
ctx.quadraticCurveTo(cp.x, cp.y, p.x, p.y);
i++;
}
}
ctx.closePath();
ctx.stroke();
glyphTTYPoints = glyphTTYPoints.concat(contourTTYPoints);
});
// Mark points
ctx.save();
ctx.strokeStyle = 'darkblue';
ctx.fillStyle = 'darkblue'
glyphTTYPoints.forEach((p, k) => {
ctx.beginPath();
ctx.arc(p.x, p.y, 3, 0, 2 * Math.PI, false);
if (p.on) {
ctx.fill();
} else {
ctx.stroke();
}
ctx.save();
ctx.strokeStyle = 'orange';
ctx.fillStyle = 'orange';
font.delta(name, k).forEach((d) => {
var x = p.x, y = p.y, dx = parseInt(d.getAttribute('x')), dy = parseInt(d.getAttribute('y'));
if (dx !== 0 || dy !== 0) {
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + dx, y + dy);
ctx.stroke();
arrowHead(ctx, x + dx, y + dy, calcAngle(dx, dy), 10);
}
ctx.beginPath();
ctx.arc(x + dx, y + dy, 2, 0, Math.PI * 2, false);
ctx.fill();
});
ctx.restore();
});
ctx.restore();
ctx.restore();
}
function fillGlyphSelector(selected) {
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));
if (selected && option.value === selected) {
option.selected = true;
}
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(path) {
const xml = fs.readFileSync(path);
font = new Font(xml);
const selectedGlyph = localStorage.getItem('TTXInspectoreSelectedGlyph');
fillGlyphSelector(selectedGlyph);
if (selectedGlyph) {
drawGlyph(selectedGlyph, font);
}
}
function loadFont() {
var paths = dialog.showOpenDialog({ properties: ['openFile'], filters: [{ name: 'ttx', extensions: ['ttx'] }] });
if (paths && paths.length > 0) {
const path = paths[0];
localStorage.setItem('TTXInspectorFontPath', path);
localStorage.removeItem('TTXInspectoreSelectedGlyph');
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
loadTTX(path);
}
}
function loadFromStorage() {
const path = localStorage.getItem('TTXInspectorFontPath')
if (path) {
loadTTX(path);
}
}
document.getElementById('select-font').addEventListener('click', loadFont);
document.getElementById('glyph-selector').addEventListener('change', function () {
var value = this.value;
localStorage.setItem('TTXInspectoreSelectedGlyph', value);
drawGlyph(value, font);
});
document.getElementById('export-canvas').addEventListener('click', () => {
const canvas = document.getElementById('canvas'),
path = dialog.showSaveDialog({});
reader = new FileReader();
reader.onload = function () {
fs.writeFileSync(path, Buffer.from(this.result));
}
if (path) {
canvas.toBlob((blob) => {
reader.readAsArrayBuffer(blob);
}, 'image/png');
}
});
document.getElementById('reload-font').addEventListener('click', loadFromStorage);
loadFromStorage();
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment