3 # unipng2hex - program to turn a .png glyph matrix into a
4 # GNU Unifont hex glyph set of 256 characters
6 # Synopsis: unipng2hex [-i in_file.png] [-o out_file.hex] [-w]
9 # Author: Paul Hardy, unifoundry <at> unifoundry.com, December 2007
11 # Perl conversion: Andrew Miller, August 2013
14 # Copyright (C) 2007-2008 Paul Hardy
16 # This program is free software: you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation, either version 2 of the License, or
19 # (at your option) any later version.
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
26 # You should have received a copy of the GNU General Public License
27 # along with this program. If not, see <http://www.gnu.org/licenses/>.
30 use GD qw (:DEFAULT :cmp);
32 $result = GetOptions (
34 "input|i=s" => \$input,
35 "output|o=s" => \$output,
36 "width|w=i" => \$minwidth
40 print << "END" or die ("Cannot print to stdout.\n");
42 Turn a .png glyph matrix into a GNU Unifont hex glyph set of 256 characters
46 unipng2hex -i <Input_File> [-o <Output_File>] [-w <width>]
48 -i, --input the input PNG file
49 -o, --output the output hex file (write to STDOUT if not
51 -w, --width the minimum width of the output glyphs - valid
52 values are 16, 24 and 32 (default is no minimum
54 -?, --help display this help and exit
59 unipng2hex -i u83.png -o u83.hex
65 die ("No input file specified\n")
69 # die ("No output file specified\n")
72 GD::Image->trueColor (1);
73 $im = new GD::Image ("$input") or die ("Cannot open image.\n");
75 if ($im->isTrueColor ()) {
76 $im->trueColorToPalette ();
79 # Disable transparency if it has been set
80 $im->transparent (-1);
82 $black = $im->colorClosest (0, 0, 0);
83 $white = $im->colorClosest (255, 255, 255);
85 # Exit if black and white are not in the image's palette
86 if ($white == -1 || $black == -1) {
87 die ("Invalid image - colors do not match\n");
89 ($black_red, $black_green, $black_blue) = $im->rgb ($black);
90 ($white_red, $white_green, $white_blue) = $im->rgb ($white);
93 ($imagewidth, $imageheight) = $im->getBounds();
99 $boxsize = ($imagewidth - $gridxoffset) / 16;
101 if ($boxsize == 32) {
102 # try to determine if charheight is 16 or 24 by examining grid
103 $pixel = $im->getPixel ($imagewidth - 1, $imageheight - 5);
105 if ($im->rgb ($pixel) == ($black_red, $black_green, $black_blue)) {
109 } elsif ($im->rgb ($pixel) == ($white_red, $white_green, $white_blue)) {
114 # Use the settings below for a height of 24 pixels in the future;
115 # for now, hard code glyph height to 16 pixels.
120 die ("Cannot determine font height\n")
122 } elsif ($boxsize == 40) {
127 die ("Invalid image - incorrect size\n")
131 if ($charheight == 16 || $charheight == 24) {
132 if (!($minwidth == 16 || $minwidth == 24)) {
133 die ("invalid width\n");
135 } elsif ($charheight == 32) {
136 if (!($minwidth == 16 || $minwidth == 24 || $minwidth == 32)) {
137 die ("Invalid width\n");
144 # Build an array of single digit hex character images
145 for ($count = 0; $count < 16; $count++) {
146 $digit = new GD::Image (8, 16, 0);
147 $black = $digit->colorAllocate ($black_red, $black_green, $black_blue);
148 $white = $digit->colorAllocate ($white_red, $white_green, $white_blue);
150 $digit->fill (0, 0, $white);
152 $digit->string (gdLargeFont, 0, 0, sprintf ('%X', $count), $black);
154 push (@digits, $digit);
160 for ($d = 0; $d < 2; $d++) {
161 $c = new GD::Image (8, 16);
162 $c->copy ($im, 0, 0, 24 + ($d * 8), 9, 8, 16);
164 for ($count = 0; $count < 16; $count++) {
165 if (!($c->compare ($digits[$count]) & GD_CMP_IMAGE)) {
166 $codepoint = ($codepoint << 4) + $count;
172 for ($d = 0; $d < 3; $d++) {
173 $c = new GD::Image (8, 16);
174 $c->copy ($im, 0, 0, (($boxsize - 24) / 2) + $gridxoffset + ($d * 8), 9, 8, 16);
176 for ($count = 0; $count < 16; $count++) {
177 if (!($c->compare ($digits[$count]) & GD_CMP_IMAGE)) {
178 $codepoint = ($codepoint << 4) + $count;
183 # Calculate the first codepoint and it's display width
184 $codepoint = ($codepoint << 4);
185 $display_width = $codepoint > 0xFFFF ? 6 : 4;
188 open (HEXFILE, ">$output") or die ("Cannot save hex file.\n");
194 for ($col = 0; $col < 16; $col++) {
195 for ($row = 0; $row < 16; $row++) {
196 # Calculate glyph width
199 for ($count = 0; $count < $charmaxwidth; $count++) {
200 $c = new GD::Image (8, $charheight, 0);
201 $c->copy ($im, 0, 0, ($col * $boxsize) + ($count * 8) + $gridxoffset + $charxoffset, ($row * $boxsize) + $gridyoffset + $charyoffset, 8, $charheight);
203 if ($c->colorsTotal > 1 || $c->colorExact ($white_red, $white_green, $white_blue) == -1) {
204 $charwidth = $count + 1
208 # Force glyphs to minimum width if minwidth is set
209 if ($charwidth < $minwidth) {
210 $charwidth = $minwidth;
213 if ($charwidth != 0) {
214 $char = sprintf ("%0*X:", $display_width, $codepoint);
216 # Loop through the glyph and build up it's hex representation
217 for ($j = 0; $j < $charheight; $j++) {
220 for ($i = 0; $i < $charwidth * 8; $i++) {
221 $bit = $im->getPixel (($col * $boxsize)+ $i + $gridxoffset + $charxoffset, ($row * $boxsize) + $j + $gridyoffset + $charyoffset) == $black ? 1 : 0;
223 $line = ($line << 1) + $bit;
226 $char = $char . sprintf ("%0*X", $charwidth * 2, $line);
229 print HEXFILE "$char\n" or die ("Cannot print to hex file.\n");
236 # Only close HEXFILE if it isn't mapped to STDOUT.
238 close HEXFILE or die ("Cannot properly close hex file.\n");