#!/usr/bin/perl # unipng2hex - program to turn a .png glyph matrix into a # GNU Unifont hex glyph set of 256 characters # # Synopsis: unipng2hex [-i in_file.png] [-o out_file.hex] [-w] # # # Author: Paul Hardy, unifoundry unifoundry.com, December 2007 # # Perl conversion: Andrew Miller, August 2013 # # # Copyright (C) 2007-2008 Paul Hardy # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . use Getopt::Long; use GD qw (:DEFAULT :cmp); $result = GetOptions ( "help|?", "input|i=s" => \$input, "output|o=s" => \$output, "width|w=i" => \$minwidth ); if ($opt_help) { print << "END" or die ("Cannot print to stdout.\n"); Turn a .png glyph matrix into a GNU Unifont hex glyph set of 256 characters Syntax: unipng2hex -i [-o ] [-w ] -i, --input the input PNG file -o, --output the output hex file (write to STDOUT if not specified) -w, --width the minimum width of the output glyphs - valid values are 16, 24 and 32 (default is no minimum width) -?, --help display this help and exit Example: unipng2hex -i u83.png -o u83.hex END exit () } if (not $input) { die ("No input file specified\n") } #if (not $output) { # die ("No output file specified\n") #} GD::Image->trueColor (1); $im = new GD::Image ("$input") or die ("Cannot open image.\n"); if ($im->isTrueColor ()) { $im->trueColorToPalette (); } # Disable transparency if it has been set $im->transparent (-1); $black = $im->colorClosest (0, 0, 0); $white = $im->colorClosest (255, 255, 255); # Exit if black and white are not in the image's palette if ($white == -1 || $black == -1) { die ("Invalid image - colors do not match\n"); } else { ($black_red, $black_green, $black_blue) = $im->rgb ($black); ($white_red, $white_green, $white_blue) = $im->rgb ($white); } ($imagewidth, $imageheight) = $im->getBounds(); $charxoffset = 4; $gridxoffset = 48; $gridyoffset = 32; $boxsize = ($imagewidth - $gridxoffset) / 16; if ($boxsize == 32) { # try to determine if charheight is 16 or 24 by examining grid $pixel = $im->getPixel ($imagewidth - 1, $imageheight - 5); if ($im->rgb ($pixel) == ($black_red, $black_green, $black_blue)) { $charyoffset = 7; $charheight = 16; $charmaxwidth = 3; } elsif ($im->rgb ($pixel) == ($white_red, $white_green, $white_blue)) { $charyoffset = 7; $charheight = 16; $charmaxwidth = 3; # # Use the settings below for a height of 24 pixels in the future; # for now, hard code glyph height to 16 pixels. # $charyoffset = 4; # $charheight = 24; # $charmaxwidth = 3; } else { die ("Cannot determine font height\n") } } elsif ($boxsize == 40) { $charyoffset = 4; $charheight = 32; $charmaxwidth = 4; } else { die ("Invalid image - incorrect size\n") } if ($minwidth) { if ($charheight == 16 || $charheight == 24) { if (!($minwidth == 16 || $minwidth == 24)) { die ("invalid width\n"); } } elsif ($charheight == 32) { if (!($minwidth == 16 || $minwidth == 24 || $minwidth == 32)) { die ("Invalid width\n"); } } $minwidth /= 8; } # Build an array of single digit hex character images for ($count = 0; $count < 16; $count++) { $digit = new GD::Image (8, 16, 0); $black = $digit->colorAllocate ($black_red, $black_green, $black_blue); $white = $digit->colorAllocate ($white_red, $white_green, $white_blue); $digit->fill (0, 0, $white); $digit->string (gdLargeFont, 0, 0, sprintf ('%X', $count), $black); push (@digits, $digit); } $codepoint = 0; # Get plane for ($d = 0; $d < 2; $d++) { $c = new GD::Image (8, 16); $c->copy ($im, 0, 0, 24 + ($d * 8), 9, 8, 16); for ($count = 0; $count < 16; $count++) { if (!($c->compare ($digits[$count]) & GD_CMP_IMAGE)) { $codepoint = ($codepoint << 4) + $count; } } } # Get page for ($d = 0; $d < 3; $d++) { $c = new GD::Image (8, 16); $c->copy ($im, 0, 0, (($boxsize - 24) / 2) + $gridxoffset + ($d * 8), 9, 8, 16); for ($count = 0; $count < 16; $count++) { if (!($c->compare ($digits[$count]) & GD_CMP_IMAGE)) { $codepoint = ($codepoint << 4) + $count; } } } # Calculate the first codepoint and it's display width $codepoint = ($codepoint << 4); $display_width = $codepoint > 0xFFFF ? 6 : 4; if ($output) { open (HEXFILE, ">$output") or die ("Cannot save hex file.\n"); } else { *HEXFILE = *STDOUT; } binmode HEXFILE; for ($col = 0; $col < 16; $col++) { for ($row = 0; $row < 16; $row++) { # Calculate glyph width $charwidth = 0; for ($count = 0; $count < $charmaxwidth; $count++) { $c = new GD::Image (8, $charheight, 0); $c->copy ($im, 0, 0, ($col * $boxsize) + ($count * 8) + $gridxoffset + $charxoffset, ($row * $boxsize) + $gridyoffset + $charyoffset, 8, $charheight); if ($c->colorsTotal > 1 || $c->colorExact ($white_red, $white_green, $white_blue) == -1) { $charwidth = $count + 1 } } # Force glyphs to minimum width if minwidth is set if ($charwidth < $minwidth) { $charwidth = $minwidth; } if ($charwidth != 0) { $char = sprintf ("%0*X:", $display_width, $codepoint); # Loop through the glyph and build up it's hex representation for ($j = 0; $j < $charheight; $j++) { $line = 0; for ($i = 0; $i < $charwidth * 8; $i++) { $bit = $im->getPixel (($col * $boxsize)+ $i + $gridxoffset + $charxoffset, ($row * $boxsize) + $j + $gridyoffset + $charyoffset) == $black ? 1 : 0; $line = ($line << 1) + $bit; } $char = $char . sprintf ("%0*X", $charwidth * 2, $line); } print HEXFILE "$char\n" or die ("Cannot print to hex file.\n"); } $codepoint += 1; } } # Only close HEXFILE if it isn't mapped to STDOUT. if ($output) { close HEXFILE or die ("Cannot properly close hex file.\n"); }