unifont-7.0.01.tar.gz
[unifont.git] / src / unipng2hex
1 #!/usr/bin/perl
2
3 #  unipng2hex - program to turn a .png glyph matrix into a
4 #               GNU Unifont hex glyph set of 256 characters
5 #
6 #  Synopsis: unipng2hex [-i in_file.png] [-o out_file.hex] [-w]
7 #
8 #
9 #  Author: Paul Hardy, unifoundry <at> unifoundry.com, December 2007
10 #
11 #  Perl conversion: Andrew Miller, August 2013
12 #
13 #
14 #   Copyright (C) 2007-2008 Paul Hardy
15 #
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.
20 #
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.
25 #
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/>.
28
29 use Getopt::Long;
30 use GD qw (:DEFAULT :cmp);
31
32 $result = GetOptions (
33         "help|?",
34         "input|i=s" => \$input,
35         "output|o=s" => \$output,
36         "width|w=i" => \$minwidth
37 );
38
39 if ($opt_help) {
40         print << "END";
41
42 Turn a .png glyph matrix into a GNU Unifont hex glyph set of 256 characters
43
44 Syntax:
45
46    unipng2hex -i <Input_File> [-o <Output_File>] [-w <width>]
47
48    -i, --input               the input PNG file
49    -o, --output              the output hex file (write to STDOUT if not
50                              specified)
51    -w, --width               the minimum width of the output glyphs - valid
52                              values are 16, 24 and 32 (default is no minimum
53                              width)
54    -?, --help                display this help and exit
55
56
57 Example:
58
59    unipng2hex -i u83.png -o u83.hex
60 END
61         exit ()
62 }
63
64 if (not $input) {
65         die ("No input file specified\n")
66 }
67
68 #if (not $output) {
69 #       die ("No output file specified\n")
70 #}
71
72 GD::Image->trueColor (1);
73 $im = new GD::Image ("$input") or die ("Cannot open image\n");
74
75 if ($im->isTrueColor ()) {
76         $im->trueColorToPalette ();
77 }
78
79 # Disable transparency if it has been set
80 $im->transparent (-1);
81
82 $black = $im->colorClosest (0, 0, 0);
83 $white = $im->colorClosest (255, 255, 255);
84
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");
88 } else {
89         ($black_red, $black_green, $black_blue) = $im->rgb ($black);
90         ($white_red, $white_green, $white_blue) = $im->rgb ($white);
91 }
92
93 ($imagewidth, $imageheight) = $im->getBounds();
94
95 $charxoffset = 4;
96 $gridxoffset = 48;
97 $gridyoffset = 32;
98
99 $boxsize = ($imagewidth - $gridxoffset) / 16;
100
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);
104
105         if ($im->rgb ($pixel) == ($black_red, $black_green, $black_blue)) {
106                 $charyoffset = 7;
107                 $charheight = 16;
108                 $charmaxwidth = 3;
109         } elsif ($im->rgb ($pixel) == ($white_red, $white_green, $white_blue)) {
110                 $charyoffset = 7;
111                 $charheight = 16;
112                 $charmaxwidth = 3;
113 #
114 #               Use the settings below for a height of 24 pixels in the future;
115 #               for now, hard code glyph height to 16 pixels.
116 #               $charyoffset = 4;
117 #               $charheight = 24;
118 #               $charmaxwidth = 3;
119         } else {
120                 die ("Cannot determine font height\n")
121         }
122 } elsif ($boxsize == 40) {
123         $charyoffset = 4;
124         $charheight = 32;
125         $charmaxwidth = 4;
126 } else {
127         die ("Invalid image - incorrect size\n")
128 }
129
130 if ($minwidth) {
131         if ($charheight == 16 || $charheight == 24) {
132                 if (!($minwidth == 16 || $minwidth == 24)) {
133                         die ("invalid width\n");
134                 }
135         } elsif ($charheight == 32) {
136                 if (!($minwidth == 16 || $minwidth == 24 || $minwidth == 32)) {
137                         die ("Invalid width\n");
138                 }
139         }
140
141         $minwidth /= 8;
142 }
143
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);
149
150         $digit->fill (0, 0, $white);
151
152         $digit->string (gdLargeFont, 0, 0, sprintf ('%X', $count), $black);
153
154         push (@digits, $digit);
155 }
156
157 $codepoint = 0;
158
159 # Get plane
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);
163
164         for ($count = 0; $count < 16; $count++) {
165                 if (!($c->compare ($digits[$count]) & GD_CMP_IMAGE)) {
166                         $codepoint = ($codepoint << 4) + $count;
167                 }
168         }
169 }
170
171 # Get page
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);
175
176         for ($count = 0; $count < 16; $count++) {
177                 if (!($c->compare ($digits[$count]) & GD_CMP_IMAGE)) {
178                         $codepoint = ($codepoint << 4) + $count;
179                 }
180         }
181 }
182
183 # Calculate the first codepoint and it's display width
184 $codepoint = ($codepoint << 4);
185 $display_width = $codepoint > 0xFFFF ? 6 : 4;
186
187 if ($output) {
188         open (HEXFILE, ">$output") or die ("Cannot save file\n");
189 } else {
190         *HEXFILE = *STDOUT;
191 }
192 binmode HEXFILE;
193
194 for ($col = 0; $col < 16; $col++) {
195         for ($row = 0; $row < 16; $row++) {
196                 # Calculate glyph width
197                 $charwidth = 0;
198
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);
202
203                         if ($c->colorsTotal > 1 || $c->colorExact ($white_red, $white_green, $white_blue) == -1) {
204                                 $charwidth = $count + 1
205                         }
206                 }
207
208                 # Force glyphs to minimum width if minwidth is set
209                 if ($charwidth < $minwidth) {
210                         $charwidth = $minwidth;
211                 }
212
213                 if ($charwidth != 0) {
214                         $char = sprintf ("%0*X:", $display_width, $codepoint);
215
216                         # Loop through the glyph and build up it's hex representation
217                         for ($j = 0; $j < $charheight; $j++) {
218                                 $line = 0;
219
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;
222
223                                         $line = ($line << 1) + $bit;
224                                 }
225
226                                 $char = $char . sprintf ("%0*X", $charwidth * 2, $line);
227                         }
228
229                         print HEXFILE "$char\n";
230                 }
231
232                 $codepoint += 1;
233         }
234 }
235
236 # Only close HEXFILE if it isn't mapped to STDOUT.
237 if ($output) {
238         close HEXFILE;
239 }