@Bluepilled Thu 25th Dec 2025
Wed 12th Mar 2025

Business card

Because nothing less will do, we have to produce our business card in <canvas> and JavaScript. There is not a better way.

v1: Text and a Square

Here we were able to get a rectangle going.

v2: Half Over Two

The card has a better placement and the gradient is much bigger than it was.

This is not the gradient as it was necassarily intended. It seems to work here, but the full value of the gradient is not reflected here.

v3: Tilt Rotor

Finally, the font-family is working. This is where we use our monospace font.

v4: Application Development

Using functions like cardboard and stroke_style, we approximated an isometric perspective on the card by 2px, which was enough to achive the intended result, a 3-D effect.

Does it scale? Being a vector, it should. The image is rendered to size in the viewport, where it can be specified in too many places.

The image can be rendered at 50000dpi.

v5

This is resizable!

v6
v7
v8
var card = {

	id: arguments.callee.name,

	dims: {
		heightCanvas: 0.569,
		height: 0.866,
		rotation: -30,
	},

	fonts: {
		default: 'serif',
		serif: 'Georgia, serif', 
	},

	colors: {
		background: ['#ffce00', '#ff8f00'],
		texts: ['#7000e2', '#cccccc', ],
	},

	h1: function(context, div, iso = 0) {

		var h1 = {
			text: ['APPLICATION', 'DEVELOPMENT'], 
			size: div.sizes[1] * 1.5,  
			font: this.fonts.default,
			color: this.colors.texts[0],
			xy: [
				null,
				0
			],
		};

		context.font = `${h1.size}px ${h1.font}`;

		h1.xy[0] = - context.measureText(h1.text[0]).width / 2;

		h1.xy[1] -= div.sizes[1] * .63;

		if (iso) h1 = this.iso(h1, div);

		return h1;
	},

	h2: function(context, div, iso = 0) {

		var h2 = {
			text: ['COMPUTER', 'SOFTWARE'], 
			size: div.sizes[1] * .75,  
			font: this.fonts.default,
			color: this.colors.texts[0],
			xy: [
				- div.width * .28,
				- div.height * .28 - div.sizes[1] * .6
			],
		};

		if (iso) h2 = this.iso(h2, div);

		return h2;
	},

	iso: function(text, div) {

		var dif = (text.size / 10) * .75;

		text.color = 'rgba(234,65,0,.25)';

		text.xy[0] -= dif;
		text.xy[1] += dif;

		return text;
	},

	scale: function(context, div, transform = 1) {

		div.width = div.offsetWidth;

		div.height = div.width * this.dims.heightCanvas;

		div.sizes = [div.width / 10, div.height / 10];

		context.translate(div.width / 2, div.height / 2); 

		if (transform) {

			context.scale(1, this.dims.height);

			context.rotate(this.dims.rotation * Math.PI / 180); 
		}

		return div;
	},

	build: function(div) {

		var context = div.getContext('2d'), content;

		div.style.backgroundColor = 'transparent';//31333d !

		div = this.scale(context, div, 1);

		this.cardboard(context, [div.width, div.height], this.colors.background, [0, 0]);

		this.ink(context, this.h1(context, div, 1));

		this.ink(context, this.h1(context, div, 0));

		this.ink(context, this.h2(context, div, 1));

		this.ink(context, this.h2(context, div, 0));
	},

	ink: function(context, text) {

		context.font = `${text.size}px ${text.font}`;

		context.textAlign = 'left';
		context.textBaseline = 'middle';

		context.fillStyle = text.color;

		for (var r in text.text) context.fillText(
			text.text[r], 
			text.xy[0], 
			text.xy[1] + (text.size * r),
		);
	},

	cardboard: function(context, dims, colors, xy) {

		var background = context.createLinearGradient(0, 0, dims[0], 0);

		background.addColorStop(0, colors[0]);
		background.addColorStop(1, colors[1]);

		context.fillStyle = background;
		context.fillRect(xy[0] - dims[0] / 2, xy[1] - dims[1] / 2, dims[0], dims[1]);

		context.lineWidth = 2;
		context.strokeStyle = this.colors.texts[0],

		context.beginPath();
		context.moveTo(xy[0] - dims[0] / 2, xy[1] - dims[1] / 2);
		context.lineTo(xy[0] - dims[0] / 2, xy[1] + dims[1] / 2);
		context.stroke();

		context.beginPath();
		context.moveTo(xy[0] - dims[0] / 2, xy[1] + dims[1] / 2);
		context.lineTo(xy[0] + dims[0] / 2, xy[1] + dims[1] / 2);
		context.stroke();
	},
}

card.build(document.getElementById(card.id));

Download businesscard.js!

Wed 12th Mar 2025

To Rome and back

When styling page and chapter titles, the answer is 'quite a lot' if we're asking how often we need to convert to Roman numerals.

Roman numerals occur when a 5 becomes a 'V'. 'Normal' numbers can be converted to roman following all the same rules. Fifty is 'L'.

Please note, no representation > 3,999. Too big.

The other rules of roman numerals:

  • 1,000 = M
  • 900 = CM
  • 500 = D
  • 400 = CD
  • 100 = C
  • 90 = XC
  • 50 = L
  • 40 = XL
  • 10 = X
  • 9 = IX
  • 5 = V
  • 4 = IV
  • 1 = I
  • 3,671 = MMMDCLXXI

revert() here isn't going to try for funny inputs like VIII, VVC or X8:

The function for conversion
static function convert($int) {

	$res = '';
	$int = intval($int);
	$max = 3999;

	if ($int > $max) throw new InvalidArgumentException("{$int} > {$max}");

	foreach (self::$chars as $val => $char) {

		while ($int >= $val) {

			$res .= $char;
			$int -= $val;
		}
	}

	return $res;
}
For reversion
static function revert($roman) {

	$res = 0;
	$int = 0;
	$roman = strtoupper($roman);

	foreach (self::$chars as $val => $char) {
	
		while ($char == substr($roman, $int, strlen($char))) {

			$res += $val;
			$int += strlen($char);
		}
	}

	return $res;
}

Please note how no standard for conversion exists, which is why all of it falls apart with bigger ints, starting with 4,000.

Download ConvertRoman.php!

Wed 12th Mar 2025

post 1

The software is provided "as is" and the author disclaims all warranties with regard to this software including all implied warranties of merchantability and fitness. In no event shall the author be liable for any special, direct, indirect, or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software.

This page was loaded from 087558.com. (26.15 ms)