#!/usr/bin/perl =head1 NAME bdf2fbcon - Convert Glyph Bitmap Distribution Format fonts to Linux fbcon fonts =cut use strict; use warnings; no indirect; use English qw(-no_match_vars); use Getopt::Long; use constant { BDF_2_1_SECTION_NONE => 0, BDF_2_1_SECTION_PROPERTIES => 1, BDF_2_1_SECTION_CHAR => 2, BDF_2_1_SECTION_BITMAP => 3, }; my $VERSION; $VERSION = '0.2.0'; sub warning { my ($fmt, @args) = @_; STDERR->printf('bdf2fbcon: Warning: ' . $fmt . "\n", @args); return; } sub error { my ($fmt, @args) = @_; STDERR->printf('bdf2fbcon: Error: ' . $fmt . "\n", @args); return; } sub init_font { my (%opts) = @_; my $font; $font = { 'comments' => [], 'id' => undef, 'idx' => undef, 'name' => undef, 'width' => undef, 'height' => undef, 'chars' => [], 'pref' => 0, }; $font->{'id'} = $opts{'n'} if (exists($opts{'n'})); $font->{'width'} = $opts{'w'} if (exists($opts{'w'})); $font->{'height'} = $opts{'h'} if (exists($opts{'h'})); $font->{'pref'} = $opts{'p'} if (exists($opts{'p'})); foreach my $i (0 .. 255) { $font->{'chars'}[$i] = { 'name' => '', 'encoding' => undef, 'bitmap' => [], }; } return $font; } sub parse_bdf_2_1 { my ($input_fh, $font) = @_; my $section; my $found_end; my $line; my $stmt; my $arg; my @argv; my $font_bbox_x; my $font_bbox_y; my $figure_margin; my $char_bbox_w; my $char_bbox_h; my $char_bbox_x; my $char_bbox_y; my $char; my $encoding; my $bytes_w; my $x; my $y; $section = BDF_2_1_SECTION_NONE; $found_end = 0; while ($line = readline($input_fh)) { chomp($line); ($stmt, $arg) = split(/[ \t]+/, $line, 2); $arg =~ s/^"(.*)"$/$1/ if ($arg); ($stmt, @argv) = split(/[ \t]+/, $line); if ($section == BDF_2_1_SECTION_NONE) { if ($stmt eq 'FONTBOUNDINGBOX') { unless ($font->{'width'}) { $font->{'width'} = $argv[0]; } unless ($font->{'height'}) { $font->{'height'} = $argv[1]; } $font_bbox_x = $argv[2]; $font_bbox_y = $argv[3]; } elsif ($stmt eq 'COMMENT') { push(@{$font->{'comments'}}, $arg); } elsif ($stmt eq 'STARTPROPERTIES') { $section = BDF_2_1_SECTION_PROPERTIES; } elsif ($stmt eq 'STARTCHAR') { $char = $argv[0]; $section = BDF_2_1_SECTION_CHAR; } elsif ($stmt eq 'ENDFONT') { $found_end = 1; last; } } elsif ($section == BDF_2_1_SECTION_PROPERTIES) { if ($stmt eq 'FONT_NAME') { $font->{'id'} = $arg unless ($font->{'id'}); } elsif ($stmt eq 'FIGURE_WIDTH') { $figure_margin = int(($font->{'width'} - $arg) / 2); } elsif ($stmt eq 'ENDPROPERTIES') { $figure_margin = 0 unless ($figure_margin); $section = BDF_2_1_SECTION_NONE; } } elsif ($section == BDF_2_1_SECTION_CHAR) { if ($stmt eq 'ENCODING') { $encoding = $argv[0]; $font->{'chars'}[$encoding]->{'name'} = $char; } elsif ($stmt eq 'BBX') { $char_bbox_w = $argv[0]; $char_bbox_h = $argv[1]; $char_bbox_x = $argv[2]; $char_bbox_y = $argv[3]; } elsif ($stmt eq 'BITMAP') { $bytes_w = int(($char_bbox_w + 7) / 8); $x = ($font->{'width'} + $font_bbox_x) - $figure_margin - ($bytes_w * 8 + $char_bbox_x); $y = ($font->{'height'} + $font_bbox_y) - ($char_bbox_h + $char_bbox_y); $section = BDF_2_1_SECTION_BITMAP; } } elsif ($section == BDF_2_1_SECTION_BITMAP) { if ($stmt eq 'ENDCHAR') { $section = BDF_2_1_SECTION_NONE; } else { if ($x ge 0) { $font->{'chars'}[$encoding]->{'bitmap'}[$y++] = hex($line) << $x; } else { $font->{'chars'}[$encoding]->{'bitmap'}[$y++] = hex($line) >> (0 - $x); } } } } warning('Missing end of font') unless ($found_end); return 1; } sub write_fbcon { my ($output, $font) = @_; my $bytes_x; my $output_fh; my $char; my $bitmap; my $shifted_bitmap; $bytes_x = int(($font->{'width'} + 7) / 8); $font->{'idx'} = uc($font->{'id'}); $font->{'idx'} =~ s/[^A-Z0-9_]/_/g; $font->{'name'} = $font->{'idx'}; $font->{'id'} = lc($font->{'id'}); $font->{'id'} =~ s/[^a-z0-9_]/_/g; $font->{'id'} .= '_' . $font->{'width'} . 'x' . $font->{'height'}; $font->{'idx'} .= '_' . $font->{'width'} . 'x' . $font->{'height'}; $font->{'name'} .= '_' . $font->{'width'} . 'x' . $font->{'height'}; if (not defined ($output)) { $output = 'font_' . $font->{'id'} . '.c'; } if ($output eq '-') { $output_fh = *STDOUT; } else { if (not open($output_fh, '>', $output . '~')) { error('%s: %s', $output . '~', $ERRNO); return; } } $output_fh->printf("/*\n"); foreach my $comment (@{$font->{'comments'}}) { $output_fh->printf(" * %s\n", ($comment or '')); } $output_fh->printf(" * Generated by bdf2fbcon\n */\n\n"); $output_fh->printf("#include \n\n"); $output_fh->printf("#include \n\n"); $output_fh->printf("#define FONTDATAMAX %d\n\n", $bytes_x * $font->{'height'} * 256); $output_fh->printf('static const unsigned char' . " fontdata_%s[FONTDATAMAX] = {\n", $font->{'id'}); foreach my $i (0 .. 255) { $char = $font->{'chars'}[$i]; $output_fh->printf("\n\t/* %3d 0x%02x '%s' */\n", $i, $i, $char->{'name'}); foreach my $j (0 .. ($font->{'height'} - 1)) { $bitmap = $char->{'bitmap'}[$j]; $bitmap = 0 unless ($bitmap); $output_fh->printf("\t"); $shifted_bitmap = $bitmap; $shifted_bitmap <<= $bytes_x * 8 - $font->{'width'}; foreach my $k (reverse(0 .. ($bytes_x - 1))) { $output_fh->printf('0x%02x, ', ($shifted_bitmap >> ($k * 8)) & 0xFF); } $output_fh->printf('/* %0' . $font->{'width'} . "b */\n", $bitmap); } } $output_fh->printf("\n};\n\n"); $output_fh->printf("const struct font_desc font_%s = {\n", $font->{'id'}); $output_fh->printf("\t.idx\t= %s_IDX,\n", $font->{'idx'}); $output_fh->printf("\t.name\t= \"%s\",\n", $font->{'idx'}); $output_fh->printf("\t.width\t= %d,\n", $font->{'width'}); $output_fh->printf("\t.height\t= %d,\n", $font->{'height'}); $output_fh->printf("\t.data\t= fontdata_%s,\n", $font->{'id'}); $output_fh->printf("\t.pref\t= %d,\n", $font->{'pref'}); $output_fh->printf("};\nEXPORT_SYMBOL(font_%s);\n", $font->{'id'}); if ($output ne '-') { close($output_fh); if (not rename($output . '~', $output)) { error('%s: %s', $output, $ERRNO); return; } } return 1; } sub convert { my ($input, $output, %opts) = @_; my $input_fh; my $line; my $font; if ($input eq ($output or '') and $input ne '-') { warning('Input and output files are equal'); } if ($input eq '-') { $input_fh = *STDIN; } else { if (not open($input_fh, '<', $input)) { error('%s: %s', $input, $ERRNO); return; } } $font = init_font(%opts); $line = readline($input_fh); if ($line =~ m/^STARTFONT 2.[12]$/) { parse_bdf_2_1($input_fh, $font); } else { error('Unsupported input format'); return; } if ($input ne '-') { close($input_fh); } if (not write_fbcon($output, $font)) { return; } return 1; } sub usage { my ($fh) = @_; $fh->printf("Usage: %s [-o ] ...\n", $PROGRAM_NAME); return; } sub help { my ($fh) = @_; usage($fh); $fh->print("Options:\n"); $fh->print(" -o Place the output into \n"); $fh->print(" -n Set the font name to \n"); $fh->print(" -s x Override the font size\n"); $fh->print(" -p Set the font's preference to \n"); $fh->print(" -h, --help Display this information\n"); $fh->print(" -V, --version Display version information\n"); return; } sub version { my ($fh) = @_; $fh->printf("bdf2fbcon %s\n", $VERSION); $fh->print("Copyright (C) 2013, 2014 Patrick \"P. J.\" McDermott\n"); $fh->print("License GPLv3+: GNU GPL version 3 or later " . ".\n"); $fh->print("This is free software: you are free to change and " . "redistribute it.\n"); $fh->print("There is NO WARRANTY, to the extent permitted by law.\n"); return; } sub main { my %opts; my %convert_opts; Getopt::Long::Configure('no_ignore_case', 'bundling', 'gnu_compat', 'no_getopt_compat'); if (not GetOptions(\%opts, 'o=s', 'n=s', 's=s', 'p=s', 'h|help', 'V|version', )) { usage(*STDERR); return 4; } if (exists($opts{'h'})) { help(*STDOUT); return 0; } if (exists($opts{'V'})) { version(*STDOUT); return 0; } if (exists($opts{'n'})) { $convert_opts{'n'} = $opts{'n'}; } if (exists($opts{'s'})) { $convert_opts{'w'} = $convert_opts{'h'} = $opts{'s'}; $convert_opts{'w'} =~ s/x.*//; $convert_opts{'h'} =~ s/.*x//; } if (exists($opts{'p'})) { $convert_opts{'p'} = $opts{'p'}; } if ($#ARGV lt 0) { error('No input files'); return 4; } if (exists($opts{'o'})) { if ($#ARGV gt 0) { error('Cannot specify -o with multiple files'); return 4; } if (not convert($ARGV[0], $opts{'o'}, %convert_opts)) { return 4; } } else { foreach my $input (@ARGV) { if (not convert($input, undef, %convert_opts)) { return 4; } } } return 0; } exit(main()); __END__ =head1 SYNOPSIS B [B<-o> I] [B<-s> IxI] [B<-p> I] I ... =head1 DESCRIPTION B converts Glyph Bitmap Distribution Format fonts to Linux fbcon fonts. =head1 OPTIONS =over 4 =item B<-o> I Place the output into I. =item B<-n> I Set the font name to I. =item B<-s> IxI Override the font size. =item B<-p> I Set the font's preference to I. =item B<-h>, B<--help> Display help information. =item B<-V>, B<--version> Display version information. =back =head1 COPYRIGHT Copyright (C) 2013, 2014 Patrick "P. J." McDermott 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 3 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 . =cut