Toy Browser Engine by TypeScript
Murahashi [Matt] Kenichi
Posted on August 25, 2019
Goal: Understand how to transform html and css to image.
node_modules/.bin/ts-node example/toy-engine.ts --css=example/color.css --html=example/color.html --width=200 --height=100 | feh -
<html>
<body>
<div class="outer">
<div class="inner"></div>
</div>
</body>
</html>
* { display: block; padding: 12px; }
.outer { background: #800000; }
.inner { background: #0000ff; }
Pipeline
Understand this rendering pipeline https://limpet.net/mbrubeck/images/2014/pipeline.svg .
Input html and css, then output pixels.
How to run
// node_modules/.bin/ts-node example/toy-engine.ts --css=example/color.css --html=example/color.html --width=200 --height=100 | feh -
import * as meow from "meow";
import * as fs from "fs";
import { htmlParse } from "../src/html";
import { cssParse } from "../src/css";
import { styleTree } from "../src/style";
import { Dimensions, EdgeSizes, layoutTree, Rect } from "../src/layout";
import { Canvas, paint } from "../src/painting";
import * as Jimp from "jimp";
const cli = meow(
`
node_modules/.bin/ts-node example/toy-engine.ts --css=example/color.css --html=example/color.html --width=200 --height=100 | feh -
`,
{
flags: {
css: { type: "string" },
html: { type: "string" },
width: { type: "string", default: 200 },
height: { type: "string", default: 100 }
},
inferType: true
}
);
let canvas: Canvas;
Promise.all([
fs.promises.readFile(cli.flags["html"], "utf-8").then(value => {
return htmlParse(value);
}),
fs.promises.readFile(cli.flags["css"], "utf-8").then(value => {
return cssParse(value);
})
])
.then(values => {
const [domNode, stylesheet] = values;
const styleRoot = styleTree(domNode, stylesheet);
const viewport = new Dimensions(
new Rect(0, 0, cli.flags["width"], cli.flags["height"]),
new EdgeSizes(0, 0, 0, 0),
new EdgeSizes(0, 0, 0, 0),
new EdgeSizes(0, 0, 0, 0)
);
const layoutRoot = layoutTree(styleRoot, viewport);
canvas = paint(layoutRoot, viewport.content);
return Jimp.create(canvas.width, canvas.height);
})
.then(value => {
let buffer = value.bitmap.data;
for (let i = 0; i < canvas.pixels.length; i++) {
buffer[i * 4] = canvas.pixels[i].r;
buffer[i * 4 + 1] = canvas.pixels[i].g;
buffer[i * 4 + 2] = canvas.pixels[i].b;
buffer[i * 4 + 3] = canvas.pixels[i].a;
}
return value.getBufferAsync(Jimp.MIME_PNG);
})
.then(value => {
process.stdout.write(value);
})
.catch(error => {
console.error(error);
});
Details
References
💖 💪 🙅 🚩
Murahashi [Matt] Kenichi
Posted on August 25, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
webdev Understanding HTTP, Cookies, Email Protocols, and DNS: A Guide to Key Internet Technologies
November 30, 2024
react Axios NPM Package: A Beginner's Guide to Installing and Making HTTP Requests
November 30, 2024
webdev Guide to Cookies, Local Storage, Session Storage, and Other Web Storage Mechanisms
November 30, 2024