Talking about geeky ways to do stuff with Jaap Brasser I’ve stumbled upon this gif, where someone creates what I suppose is C code using MsPaint. Pretty awesome stuff, even if the code won’t compile afterwards because of the bmp header. Wondering how some PowerShell code would look like as a bmp we attempted to write a function to automate the process, and here is my take on it:

function ConvertTo-Bitmap ([String]$Source,
[String]$Destination=([IO.Path]::ChangeExtension($Source,'bmp')))
{
	$header = 	66,77,58,0,0,0,0,0,0,0,54,0,0,0,40,0,0,0,1,0,0,0,
				1,0,0,0,1,0,24,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0

	$File = Get-Content $Source -Encoding Byte | Select-Object -Skip 3

	$Reminder = $File.Count % 4
	$File += ,0 * $Reminder

	$size = [Math]::Sqrt($file.Count/4)
	$header[22] = $size #height
	$header[18] = $size #width

	Set-Content -path $Destination -Force -Encoding Byte -Value ([byte[]]($header+$File))
}

Convertto-Bitmap -Source ".\hello.ps1"

Here is an example of “Hello World!” script converted to bmp:

The image is of course zoomed out, here is link to the original.

The logic behind the code is pretty simple. 54 byte long file header and appropriate file extension is what makes a file a bmp file. The header is represented as an array of byte values in the code. The header contains information about the file contents, most importantly its width (offset 18) and height (offset 22). If these values are set too low not all data in the file are visible. If they are too high the file comes out corrupted so I need to calculate the exact value. The header is followed by image data (4byte groups) created from byte values of the letters. All of this is output to a file as byte data and named appropriately.

There are few things I did not take into account, I blindly assume the input values are correct and the input file is utf-8 encoded.

It turned out my logic behing the first version was failed and it in fact truncates some of the data so here is updated version that tries to fit the data to as much square area as it can.

function ConvertTo-Bitmap ([String]$Source,
[String]$Destination=([IO.Path]::ChangeExtension($Source,'bmp')))
{
	$header = 	66,77,58,0,0,0,0,0,0,0,54,0,0,0,40,0,0,0,1,0,0,0,
				1,0,0,0,1,0,24,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,
				0,0,0,0,0,0,0,0

	$File = Get-Content $Source -Encoding Byte | Select-Object -Skip 3

	#file needs to be created of 4 byte groups - pixels
	$groupSize = 4
	#append filler if needed
	$filler = 0
	$fillerCount = 4 - ($File.Count % $groupSize)
	$file += ,$filler * $fillerCount

	#calculate the dimensions of the picture
	$pixelCount = $file.Count / $groupSize

	$dimensions = Get-Dimensions -Number $pixelCount

	$header[18] = $dimensions.Width
	$header[22] =  $dimensions.Height

	Set-Content -path $Destination -Force -Encoding Byte -Value ([byte[]]($header+$File))
}

function Get-Dimensions ([int]$Number) {
	$dividers  = for ($divider= $number ; $divider -gt 0; $divider)
	{
		$result = $number / $divider
		if ($result -eq [int]$result) { $result }
		$divider--
	}

	#make sure I am working with array - important for 1
	$dividers = @($dividers)

	$dividersCount =$dividers.count

	$half = [Math]::Truncate($dividersCount/2)
	if ($dividersCount % 2)
	{
		$height = $dividers[$half]
		$width = $dividers[$half]
	}
	else
	{
		$height = $dividers[$half]
		$width = $dividers[$half-1]
	}

	new-object -TypeName psObject -Property @{Height = $height; Width = $width}
}

Convertto-Bitmap -Source ".\Hello.ps1"