1 | #!/usr/bin/perl
|
---|
2 | use warnings;
|
---|
3 | use strict;
|
---|
4 | $| = 1;
|
---|
5 |
|
---|
6 | my ($input, $outimage, $outstats) = @ARGV;
|
---|
7 | die "need an input file" unless $input;
|
---|
8 | die "need an output image file" unless $outimage;
|
---|
9 | die "need an output stats file" unless $outstats;
|
---|
10 |
|
---|
11 | die "output image file must have extension .png" unless $outimage =~ /\.png$/;
|
---|
12 |
|
---|
13 | my $skipped = 0;
|
---|
14 |
|
---|
15 | my %font_map;
|
---|
16 | my $font_width;
|
---|
17 | my $font_height;
|
---|
18 |
|
---|
19 | sub parse_char {
|
---|
20 | my ($char, $line) = @_;
|
---|
21 | my @lines = grep { defined and length } split /\n/, $char;
|
---|
22 |
|
---|
23 | my $header = shift @lines;
|
---|
24 |
|
---|
25 | my $unicode_value;
|
---|
26 |
|
---|
27 | if ( $header =~ /^U\+([a-fA-F0-9]{4})$/ ) {
|
---|
28 | $unicode_value = unpack "n", pack "H*", $1;
|
---|
29 | } elsif ( length $header == 1 ) {
|
---|
30 | $unicode_value = ord $header;
|
---|
31 | } elsif ( $header =~ /^"(.)"$/ ) {
|
---|
32 | $unicode_value = ord $1;
|
---|
33 | } elsif ( $header =~ /^'(.)'$/ ) {
|
---|
34 | $unicode_value = ord $1;
|
---|
35 | } elsif ( $header eq "TODO" or $header eq "SKIP" ) {
|
---|
36 | $skipped++;
|
---|
37 | return;
|
---|
38 | } else {
|
---|
39 | die "can't handle a header of \"$header\" (line $line)";
|
---|
40 | }
|
---|
41 |
|
---|
42 | my $height = @lines;
|
---|
43 | my $width = length $lines[0];
|
---|
44 |
|
---|
45 | length $lines[$_] == $width or die "Non-rectangular character \"$header\" (line $line)"
|
---|
46 | for 0 .. $#lines;
|
---|
47 |
|
---|
48 | $font_width = $width unless defined $font_width;
|
---|
49 | $font_height = $height unless defined $font_height;
|
---|
50 |
|
---|
51 | die "Character dimensions (${width}x${height}) for \"$header\" do not match font dimensions (${font_width}x${font_height}) (line $line)"
|
---|
52 | if $font_width != $width or $font_height != $height;
|
---|
53 |
|
---|
54 | tr/[., ]/0/, tr/[1X@MOSW]/1/ for @lines;
|
---|
55 |
|
---|
56 | ($_ ne '1' and $_ ne '0') and die "Bad character value '$_' for \"$header\" (line $line)"
|
---|
57 | for map split(//, $_), @lines;
|
---|
58 |
|
---|
59 | $font_map{$unicode_value} = \@lines;
|
---|
60 | }
|
---|
61 |
|
---|
62 | open my $lf, "<", $ARGV[0] or die;
|
---|
63 | my $char_data = '';
|
---|
64 | my $char_line_no;
|
---|
65 | my $line_no = 0;
|
---|
66 | while ( <$lf> ) {
|
---|
67 | chomp;
|
---|
68 | s/\s*#.*$//;
|
---|
69 | if ( length ) {
|
---|
70 | $char_line_no = $line_no unless defined $char_line_no;
|
---|
71 | $char_data .= "$_\n";
|
---|
72 | } elsif ( length $char_data ) {
|
---|
73 | parse_char($char_data, $char_line_no);
|
---|
74 | $char_data = '';
|
---|
75 | undef $char_line_no;
|
---|
76 | }
|
---|
77 | }
|
---|
78 | parse_char($char_data) if length $char_data;
|
---|
79 | close $lf;
|
---|
80 |
|
---|
81 | print STDERR "Characters: parsed ".scalar(keys %font_map).", skipped $skipped\n";
|
---|
82 | print STDERR "Font size: ${font_width}x${font_height}\n";
|
---|
83 |
|
---|
84 | my @codepoints = sort { $a <=> $b } keys %font_map;
|
---|
85 |
|
---|
86 | my $width = int sqrt int scalar @codepoints;
|
---|
87 | $width = 8 if $width < 8;
|
---|
88 |
|
---|
89 | print STDERR "Building image and stats...\n";
|
---|
90 | open my $sf, ">", $outstats or die;
|
---|
91 |
|
---|
92 | my $this_ch_x = 0;
|
---|
93 | my $this_ch_y = 0;
|
---|
94 | my $image_data = '';
|
---|
95 | my @row_bits = map +("0" x ($width*$font_width)), 1..$font_height;
|
---|
96 | my $last_cp = 0;
|
---|
97 | my $cp_run = 0;
|
---|
98 | sub flush_run {
|
---|
99 | if ( $cp_run ) {
|
---|
100 | print $sf "r$cp_run\n";
|
---|
101 | $cp_run = 0;
|
---|
102 | }
|
---|
103 | }
|
---|
104 | for my $idx ( 0 .. $#codepoints ) {
|
---|
105 | my $cp = $codepoints[$idx];
|
---|
106 |
|
---|
107 | # flush this row to the pbm buffer if neccessary
|
---|
108 | if ( $this_ch_x == $width ) {
|
---|
109 | $this_ch_y++;
|
---|
110 | $this_ch_x = 0;
|
---|
111 | for my $row ( @row_bits ) {
|
---|
112 | $row .= "0" while length($row) % 8 != 0;
|
---|
113 | $image_data .= pack "B*", $row;
|
---|
114 | $row = "0" x ($width*$font_width);
|
---|
115 | }
|
---|
116 | print "image data length: ".length($image_data)."\n";
|
---|
117 | flush_run();
|
---|
118 | print $sf "y\n";
|
---|
119 | }
|
---|
120 |
|
---|
121 |
|
---|
122 | # add the character to this image row
|
---|
123 | if ( $last_cp + 1 == $cp ) {
|
---|
124 | $cp_run++;
|
---|
125 | $last_cp = $cp;
|
---|
126 | } else {
|
---|
127 | flush_run();
|
---|
128 | print $sf "$cp\n";
|
---|
129 | $last_cp = $cp;
|
---|
130 | }
|
---|
131 |
|
---|
132 | for my $row ( 0 .. $font_height-1 ) {
|
---|
133 | substr($row_bits[$row], $this_ch_x*$font_width, $font_width, $font_map{$cp}[$row]);
|
---|
134 | }
|
---|
135 |
|
---|
136 | $this_ch_x++;
|
---|
137 | }
|
---|
138 |
|
---|
139 | flush_run();
|
---|
140 |
|
---|
141 | if ( $this_ch_x ) {
|
---|
142 | # we have put a character on the last line
|
---|
143 | for my $row ( @row_bits ) {
|
---|
144 | $row .= "0" while length($row) % 8 != 0;
|
---|
145 | $image_data .= pack "B*", $row;
|
---|
146 | }
|
---|
147 | }
|
---|
148 |
|
---|
149 | close $sf;
|
---|
150 |
|
---|
151 | print STDERR "Converting image to png...\n";
|
---|
152 |
|
---|
153 | open $sf, "|-", "convert", "pbm:-", "png:$outimage" or die;
|
---|
154 | print $sf "P4 ".($font_width * $width)." ".(($this_ch_y+1) * $font_height)." ".$image_data;
|
---|
155 | close $sf;
|
---|
156 |
|
---|
157 | print STDERR "Done.\n";
|
---|
158 |
|
---|