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
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 die ("Cannot determine font height\n")
116 } elsif ($boxsize == 40) {
121 die ("Invalid image - incorrect size\n")
125 if ($charheight == 16 || $charheight == 24) {
126 if (!($minwidth == 16 || $minwidth == 24)) {
127 die ("invalid width\n");
129 } elsif ($charheight == 32) {
130 if (!($minwidth == 16 || $minwidth == 24 || $minwidth == 32)) {
131 die ("Invalid width\n");
138 # Build an array of single digit hex character images
139 for ($count = 0; $count < 16; $count++) {
140 $digit = new GD::Image (8, 16, 0);
141 $black = $digit->colorAllocate ($black_red, $black_green, $black_blue);
142 $white = $digit->colorAllocate ($white_red, $white_green, $white_blue);
144 $digit->fill (0, 0, $white);
146 $digit->string (gdLargeFont, 0, 0, sprintf ('%X', $count), $black);
148 push (@digits, $digit);
154 for ($d = 0; $d < 2; $d++) {
155 $c = new GD::Image (8, 16);
156 $c->copy ($im, 0, 0, 24 + ($d * 8), 9, 8, 16);
158 for ($count = 0; $count < 16; $count++) {
159 if (!($c->compare ($digits[$count]) & GD_CMP_IMAGE)) {
160 $codepoint = ($codepoint << 4) + $count;
166 for ($d = 0; $d < 3; $d++) {
167 $c = new GD::Image (8, 16);
168 $c->copy ($im, 0, 0, (($boxsize - 24) / 2) + $gridxoffset + ($d * 8), 9, 8, 16);
170 for ($count = 0; $count < 16; $count++) {
171 if (!($c->compare ($digits[$count]) & GD_CMP_IMAGE)) {
172 $codepoint = ($codepoint << 4) + $count;
177 # Calculate the first codepoint and it's display width
178 $codepoint = ($codepoint << 4);
179 $display_width = $codepoint > 0xFFFF ? 6 : 4;
182 open (HEXFILE, ">$output") or die ("Cannot save file\n");
188 for ($col = 0; $col < 16; $col++) {
189 for ($row = 0; $row < 16; $row++) {
190 # Calculate glyph width
193 for ($count = 0; $count < $charmaxwidth; $count++) {
194 $c = new GD::Image (8, $charheight, 0);
195 $c->copy ($im, 0, 0, ($col * $boxsize) + ($count * 8) + $gridxoffset + $charxoffset, ($row * $boxsize) + $gridyoffset + $charyoffset, 8, $charheight);
197 if ($c->colorsTotal > 1 || $c->colorExact ($white_red, $white_green, $white_blue) == -1) {
198 $charwidth = $count + 1
202 # Force glyphs to minimum width if minwidth is set
203 if ($charwidth < $minwidth) {
204 $charwidth = $minwidth;
207 if ($charwidth != 0) {
208 $char = sprintf ("%0*X:", $display_width, $codepoint);
210 # Loop through the glyph and build up it's hex representation
211 for ($j = 0; $j < $charheight; $j++) {
214 for ($i = 0; $i < $charwidth * 8; $i++) {
215 $bit = $im->getPixel (($col * $boxsize)+ $i + $gridxoffset + $charxoffset, ($row * $boxsize) + $j + $gridyoffset + $charyoffset) == $black ? 1 : 0;
217 $line = ($line << 1) + $bit;
220 $char = $char . sprintf ("%0*X", $charwidth * 2, $line);
223 print HEXFILE "$char\n";
230 # Only close HEXFILE if it isn't mapped to STDOUT.