#!/usr/bin/perl =head1 NAME bdf2fbcon - Convert Glyph Bitmap Distribution Format fonts to Linux fbcon fonts =cut use strict; use warnings; 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.1.0'; sub warning { my ($fmt, @args) = @_; printf(STDERR "bdf2fbcon: Warning: " . $fmt . "\n", @args); } sub error { my ($status, $fmt, @args) = @_; printf(STDERR "bdf2fbcon: Error: " . $fmt . "\n", @args); exit($status); } sub init_font { my (%opts) = @_; my $font; my $i; $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'})); for ($i = 0; $i < 256; ++$i) { $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); } sub write_fbcon { my ($output, $font) = @_; my $bytes_x; my $output_fh; my $i; my $char; my $j; my $bitmap; my $shifted_bitmap; my $k; $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(4, "%s: %s", $output . "~", $!); } } for (@{$font->{'comments'}}) { printf($output_fh "/* %s */\n", $_); } printf($output_fh "/* Generated by bdf2fbcon */\n\n"); printf($output_fh "#include \n\n"); printf($output_fh "#define FONTDATAMAX %d\n\n", $bytes_x * $font->{'height'} * 256); printf($output_fh "static const unsigned char" . " fontdata_%s[FONTDATAMAX] = {\n", $font->{'id'}); for ($i = 0; $i < 256; ++$i) { $char = $font->{'chars'}[$i]; printf($output_fh "\n\t/* %3d 0x%02x '%s' */\n", $i, $i, $char->{'name'}); for ($j = 0; $j < $font->{'height'}; ++$j) { $bitmap = $char->{'bitmap'}[$j]; $bitmap = 0 unless ($bitmap); printf($output_fh "\t"); $shifted_bitmap = $bitmap; $shifted_bitmap <<= $bytes_x * 8 - $font->{'width'}; for ($k = ($bytes_x - 1) * 8; $k >= 0; $k -= 8) { printf($output_fh "0x%02x, ", ($shifted_bitmap >> $k) & 0xFF); } printf($output_fh "/* %0" . $font->{'width'} . "b */\n", $bitmap); } } printf($output_fh "\n};\n\n"); printf($output_fh "const struct font_desc font_%s = {\n", $font->{'id'}); printf($output_fh "\t.idx\t= %s_IDX,\n", $font->{'idx'}); printf($output_fh "\t.name\t= \"%s\",\n", $font->{'idx'}); printf($output_fh "\t.width\t= %d,\n", $font->{'width'}); printf($output_fh "\t.height\t= %d,\n", $font->{'height'}); printf($output_fh "\t.data\t= fontdata_%s,\n", $font->{'id'}); printf($output_fh "\t.pref\t= %d,\n", $font->{'pref'}); printf($output_fh "};\n"); if ($output ne "-") { close($output_fh); if (not rename($output . "~", $output)) { error(4, "%s: %s", $output, $!); } } } sub convert { my ($input, $output, %opts) = @_; my $input_fh; my $line; my $font; if ($input eq $output and $input ne "-") { warning("Input and output files are equal"); } if ($input eq "-") { $input_fh = *STDIN; } else { if (not open($input_fh, "<", $input)) { error(4, "%s: %s", $input, $!); } } $font = init_font(%opts); $line = readline($input_fh); if ($line =~ m/^STARTFONT 2.[12]$/) { parse_bdf_2_1($input_fh, $font); } else { error(4, "Unsupported input format"); } if ($input ne "-") { close($input_fh); } write_fbcon($output, $font); } sub usage { my ($fh) = @_; printf($fh "Usage: %s [-o ] ...\n", $0); } sub help { my ($fh) = @_; usage($fh); print($fh "Options:\n"); print($fh " -o Place the output into \n"); print($fh " -n Set the font name to \n"); print($fh " -s x Override the font size\n"); print($fh " -p Set the font's preference to \n"); print($fh " -h, --help Display this information\n"); print($fh " -V, --version Display version information\n"); } sub version { my ($fh) = @_; printf($fh "bdf2fbcon %s\n", $VERSION); print($fh "Copyright (C) 2013, 2014 Patrick \"P. J.\" McDermott\n"); print($fh "License GPLv3+: GNU GPL version 3 or later " . ".\n"); print($fh "This is free software: you are free to change and " . "redistribute it.\n"); print($fh "There is NO WARRANTY, to the extent permitted by law.\n"); } sub main { my %opts; my %convert_opts; my $input; 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); exit(4); } if (exists($opts{'h'})) { help(*STDOUT); exit(0); } if (exists($opts{'V'})) { version(*STDOUT); exit(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(4, "No input files\n"); } if (exists($opts{'o'})) { if ($#ARGV gt 0) { error(4, "Cannot specify -o with multiple files\n"); } convert($ARGV[0], $opts{'o'}, %convert_opts); } else { for $input (@ARGV) { convert($input, undef, %convert_opts); } } } 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