DEV Community

Murahashi [Matt] Kenichi
Murahashi [Matt] Kenichi

Posted on

4

Toy Browser Engine by TypeScript

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 -
Enter fullscreen mode Exit fullscreen mode

Rendered result

<html>
  <body>
    <div class="outer">
      <div class="inner"></div>
    </div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode
* { display: block; padding: 12px; }
.outer { background: #800000; }
.inner { background: #0000ff; }
Enter fullscreen mode Exit fullscreen mode

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);
  });

Enter fullscreen mode Exit fullscreen mode

Details

References

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay