#!/usr/bin/env perl

$version = '$Id: bdf2gdr.pl,v 2.2 2005/02/07 23:45:50 tsumura Exp $';

#
# 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 2, 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.
#

#
# This program is based on 
#
# - any2ucs, hex2bdf, bdf2hex
#    (c) Copyright 2000-2001 /efont/ The Electronic Font Open Laboratory.
#
# - bdfmerge
#    by 1@2ch (public domain)
#

#
#    Copyright (c) 2005 TSUMURA,Tomoaki.
#

$| = 1;

use FileHandle;
require 5.005;

sub usage {
    print "Usage: \n";
    print "\t$0 (16|12|fontname) jis0208.bdf jis0201.bdf [ascii.bdf]\n\n";
    exit;
}

$noascii = 0;
$replace = 0;

if( $ARGV[0] =~ '^(1[26])$' ){
    $replace = $1 + 0;
}

if( $#ARGV <  2 ){ usage(); }
if( $#ARGV == 2 ){ $noascii = 1; }

$base = $$;

print<<EoH;
$version
##################################################
#         JIS BDF -> GDR preprocessor            #
#         (c) 2005    TSUMURA,Tomoaki            #
##################################################

EoH

if( $replace ){
    $fontname = "JapanPlain";
}else{
    $fontname = $ARGV[0];
}
$jis{'bdf'}  = $ARGV[1];
$rk{'bdf'}  = $ARGV[2];
if( !$noascii ){
    $asc{'bdf'}    = $ARGV[3];
}else{
    $asc{'bdf'}    = 'dummy';
}

open(JIS, $jis{'bdf'}) || die "\n!!! Error !!! Can't open BDF file `$jis{'bdf'}'!\n\n";
open(RK,  $rk{'bdf'}) || die "\n!!! Error !!! Can't open BDF file `$rk{'bdf'}'!\n\n";
if( !$noascii ){
    open(ASC, $asc{'bdf'})   || die "\n!!! Error !!! Can't open BDF file `$asc{'bdf'}'!\n\n";
}

##################################################
# Check input files
##################################################
print "\n##################################################\n";
print " Checking input bdf files ...\n";
print "##################################################\n";

#
# bdf
#
sub bdfp {
    my($bdf) = $_[0];
    my($line);
    open(TMP, $bdf);    $line = <TMP>;    close(TMP);
    if( $line !~ /STARTFONT/i ){ die "\n!!! Error !!! File `$bdf' is not a BDF!\n\n"; }
}

bdfp( $jis{'bdf'} );
bdfp( $rk{'bdf'} );
bdfp( $asc{'bdf'}   ) if( !$noascii );

#
# parse
#
sub bdf_hdr {
    my($bdf) = $_[0];
    my($line) = '';
    my(%prop);
    $prop{'bdf'} = $bdf;
    open(TMP, $bdf);
    while( $line !~ /^CHARS\s/i ){
	$line = <TMP>;
	$line =~ s/\r\n//g;
	chomp($line);
	if( $line =~ /^PIXEL_SIZE ([0-9]+)$/i ){
	    $prop{'size'} = $1 + 0;
	}elsif( $line =~ /^CHARSET_REGISTRY (.*)$/i ){
	    $prop{'cset'} = $1;
	    $prop{'cset'} =~ s/"//g;
	}elsif( $line =~ /^CHARSET_ENCODING (.*)$/i ){
	    $prop{'enc'} = $1;
	    $prop{'enc'} =~ s/"//g;
	}elsif( $line =~ /^FONTBOUNDINGBOX ([0-9]+) ([0-9]+) ([-0-9]+) ([-0-9]+)$/ ){
	    $prop{'fbbx_w'} = $1 + 0;
	    $prop{'fbbx_h'} = $2 + 0;
	    $prop{'fbbx_x'} = $3 + 0;
	    $prop{'fbbx_y'} = $4 + 0;
	}elsif( $line =~ /^FONT_DESCENT ([0-9]+)$/ ){
	    $prop{'descent'} = $1 + 0;
	}elsif( $line =~ /^FONT_ASCENT ([0-9]+)$/ ){
	    $prop{'ascent'} = $1 + 0;
	}
    }
    close(TMP);
    return( %prop );
}

%jis = bdf_hdr( $jis{'bdf'} );
%rk  = bdf_hdr( $rk{'bdf'} );
if( !$noascii ){
    %asc = bdf_hdr( $asc{'bdf'} );
}

#
# charset
#
sub validcode {
    my($bdf, $cset, $enc, $vcset, $venc) = @_;
    print "Checking charset registry of `$bdf'\t...";
    if( $cset !~ /$vcset/i || $enc ne $venc ){
	die "\n!!! Error !!! File `$bdf' does not have valid charset!\nIt should have `$vcset-$venc' charset, but it has $cset-$enc!\n\n";
    }else{
	print "O.K. ($vcset-$venc)\n";
    }
}

validcode( $jis{'bdf'}, $jis{'cset'}, $jis{'enc'}, 'JISX0208', '0');
validcode( $rk{'bdf'}, $rk{'cset'},  $rk{'enc'},  'JISX0201', '0');
if( !$noascii ){
    validcode( $asc{'bdf'}, $asc{'cset'}, $asc{'enc'}, 'ISO8859', '1');
}else{
    $tmp = 1;
    open(TMPI, $rk{'bdf'});
    while(<TMPI>){
	if( /^STARTCHAR ([a-fA-F0-9]+)$/i && $1 eq '41'){ # 0x41 = 'A'
	    print "\n!!! Warning !!! ASCII (ISO8859-1) font will not be merged.\n\n";
	    $tmp = 0; last;
	}
    }
    close(TMPI);
    if( $tmp ){
	die "\n!!! Error !!! ASCII (ISO8859-1) font is not specified, and $rk{'bdf'} does not seem to have ASCII characters!\n\n";
    }
}

#
# fontboudingbox
#
print "Checking heights of fontboundingboxes\t... ";
if(    ($jis{'fbbx_h'} == $rk{'fbbx_h'})
    && ($rk{'fbbx_h'} == $asc{'fbbx_h'}) ){
    print "O.K. ($jis{'fbbx_h'})\n";
}else{
    die "\n!!! Error !!! The heights of FONTBOUNDINGBOXs are different.\n\n";
}

print "\n##################################################\n";
print " Calculating Bounding Box Offsets...\n";
print "##################################################\n";

sub max{
    my($a, $b, $c) = @_;
    my($m);
    $m = $a > $b ? $a : $b;
    $m = $m > $c ? $m : $c;
    return( $m );
}

sub min{
    my($a, $b, $c) = @_;
    my($m);
    $m = $a < $b ? $a : $b;
    $m = $m < $c ? $m : $c;
    return( $m );
}

# height: glyph height
$height = max( $jis{'fbbx_h'}, $rk{'fbbx_h'}, $asc{'fbbx_h'} );
$width  = max( $jis{'fbbx_w'}, $rk{'fbbx_w'}, $asc{'fbbx_w'} );

$size = $height;
$size = $replace if( $replace );	# size: font size

# define offset
$hmargin = $size - $height;
# jis (bbx_y should be 0)
if( $hmargin > 0 ){
    $jis{'voffset'} = ( $hmargin < abs($jis{'fbbx_y'}) ) ?
     	                   $hmargin : abs($jis{'fbbx_y'}) ;
}else{
    $jis{'voffset'} = abs($jis{'fbbx_y'});
}
$jis{'hoffset'} = 0;
# rk (bbx_y should be 0)
if( $hmargin > 0 ){
    $rk{'voffset'} = ( $hmargin < abs($rk{'fbbx_y'}) ) ?
     	                  $hmargin : abs($rk{'fbbx_y'}) ;
}else{
    $rk{'voffset'} = abs($rk{'fbbx_y'});
}
$rk{'hoffset'} = 0;
# ascii (bbx_y should be same with japanese, bbx_x should > 0)
$asc{'voffset'} = $jis{'voffset'};

if( $asc{'fbbx_x'} < $jis{'fbbx_x'} ){
    $asc{'hoffset'} = $jis{'fbbx_x'} - $asc{'fbbx_x'};
}else{
    $asc{'hoffset'} = 0;
}

print "Offset for JIS:\tX[$jis{'hoffset'}], Y[$jis{'voffset'}]\n";
print "Offset for RK: \tX[$rk{'hoffset'}], Y[$rk{'voffset'}]\n";
print "Offset for ASC:\tX[$asc{'hoffset'}], Y[$asc{'voffset'}]\n";

$output_fbbx_y
    = min( ($jis{'fbbx_y'} + $jis{'voffset'}),
	   ($rk{'fbbx_y'}  + $rk{'voffset'}),
  	   ($asc{'fbbx_y'} + $asc{'voffset'}) );
$output_fbbx_h
    = max( ($jis{'fbbx_y'} + $jis{'fbbx_h'} + $jis{'voffset'}),
	   ($rk{'fbbx_y'}  + $rk{'fbbx_h'}  + $rk{'voffset'}),
	   ($asc{'fbbx_y'} + $asc{'fbbx_h'} + $asc{'voffset'}) )
    - $output_fbbx_y;
$output_fbbx_x
    = min( ($jis{'fbbx_x'} + $jis{'hoffset'}),
	   ($rk{'fbbx_x'}  + $rk{'hoffset'}),
  	   ($asc{'fbbx_x'} + $asc{'hoffset'}) );
$output_fbbx_w = $width;

$fontid = "${fontname}${size}";

#
# UID
#
sub genuid {
    my($r) = int( rand( 251658239 ) );
    return ( $r + 16777216 );
}
$uid = genuid();

##################################################
# to Unicode BDF
##################################################
print "\n##################################################\n";
print " Converting bdf into unicode order...\n";
print "##################################################\n";

$jis{'hex'} = "jis_${base}.hex";
$jis{'uhx'} = "jis_${base}.uhx";
$jis{'uni'} = "jis_${base}.uni"; # bdf
$rk{'hex'} = "rk_${base}.hex";
$rk{'uhx'} = "rk_${base}.uhx";
$rk{'uni'} = "rk_${base}.uni"; # bdf
if( !$noascii ){
    $asc{'hex'}   = "asc_${base}.hex";
    $asc{'uhx'}   = "asc_${base}.uhx";
    $asc{'uni'}   = "asc_${base}.uni"; # bdf
}

sub bdf2hex { # stolen from bdf2hex
    my($infile, $outfile) = @_;
    open(TMPI, $infile);
    open(TMPO, ">$outfile");
    while(<TMPI>){
	if (/^ENCODING\s+(\d+)/) { printf TMPO ("%04X:", $1); }
	elsif (/^BITMAP/) { $BITMAP=1; }
	elsif (/^ENDCHAR/) { $BITMAP=0; print TMPO "\n"; }
	elsif (/^FONTBOUNDINGBOX\s+(\d+)\s+(\d+)/) { print TMPO "$1 $2\n"; }
	elsif ($BITMAP) { y/a-f/A-F/; s/\n$//; print TMPO "$_"; }
    }
    close(TMPI);
    close(TMPO);
}

sub hex2bdf { # stolen from hex2bdf
    my($infile, $outfile) = @_;
    my(@chars, %glyph);
    my($character, $charname, $encoding, $len, $aspect, $width, $swidth, $dwidth);
    open(TMPI, $infile);
#     while(<TMPI>){
# 	$glyph{$1} = $2 if /(.{4,}):(.+)\n/;
#     }
    while(<TMPI>){
	if(  /(.{4,}):(.+)\n/  ){
	    $glyph{$1} = $2;
	}
    }

    @chars = sort keys %glyph; $[ = 1;
    
    open(TMPO, ">$outfile");
    print TMPO "STARTFONT 2.1\n";
    print TMPO "FONT dummy\n";
    print TMPO "SIZE $size 75 75\n";
    print TMPO "FONTBOUNDINGBOX $size $size $jis{'fbbx_x'} $jis{'fbbx_y'}\n";
    print TMPO "CHARS $#chars\n";

    foreach $character (@chars){
	$encoding = hex($character); $glyph = $glyph{$character};
	$len = int(($size - 1) / 8 + 1);
	$aspect = length($glyph) < $len * $height * 2 ? .5 : 1;
	$width = length($glyph) / $height / 2;
	$swidth = 1000 * $aspect;
	$dwidth = int( $height * $aspect + 0.5 ); # modified
	$glyph =~ s/((..){$width})/\n$1/g;
	$character = "$character $charname"
	    if $charname = $charname{pack("n",hex($character))};
	print TMPO "STARTCHAR U+$character\n";
	print TMPO "ENCODING $encoding\n";
	print TMPO "SWIDTH $swidth 0\n";
	print TMPO "DWIDTH $dwidth 0\n";
	print TMPO "BBX $dwidth $height $jis{'fbbx_x'} $jis{'fbbx_y'}\n";
	print TMPO "BITMAP $glyph\n";
	print TMPO "ENDCHAR\n";
    }
    close(TMPI);
    close(TMPO);
}

sub any2ucs { # stolen from any2ucs
    my($infile, $table, $outfile) = @_;
    my($handle_table) = FileHandle->new();
    if( !$handle_table->open("$table", 'r')){
	unlink( $jis{'hex'} );
	unlink( $rk{'hex'} );
	die "\n!!! Error !!! Failed to open the table file: $table\n\n";
    }
    my(%table) = ();
    while( $_ = $handle_table->getline ){
	if (/^0x([0-9A-F]{4,})\t+0x([0-9A-F]{4,})/) {
	    $table{uc($1)} = uc($2);
	}
    }
    close( $handle_table );

    open(TMPI, $infile);
    open(TMPO, ">$outfile");
    $_ = <TMPI>;
    if (/^(\d+)\s+(\d+)/) {
	print TMPO "$1 $2\n";
    } else {
	die;
    }
    while (<TMPI>) {
	if (/^(.{4,}):(.+)$/) {
	    if ($table{$1}) {
		print TMPO "$table{$1}:$2\n";
	    }
	}
    }
    close(TMPI);
    close(TMPO);
}

print "Converting `$jis{'bdf'}' \t... ";
bdf2hex( $jis{'bdf'}, $jis{'hex'} );
any2ucs( $jis{'hex'}, "JIS0208.TXT", $jis{'uhx'} );
unlink( $jis{'hex'} );
hex2bdf( $jis{'uhx'}, $jis{'uni'} );
unlink( $jis{'uhx'} );
print "done.\n";

print "Converting `$rk{'bdf'}' \t... ";
bdf2hex( $rk{'bdf'}, $rk{'hex'} );
any2ucs( $rk{'hex'}, "JIS0201.TXT", $rk{'uhx'} );
unlink( $rk{'hex'} );
hex2bdf( $rk{'uhx'}, $rk{'uni'} );
unlink( $rk{'uhx'} );
print "done.\n";

$symbol = 0;
if( -r "SYMBOL${height}.HEX" ){
    print "Symbol table is found. (SYMBOL${height}.HEX)\n";
    $symbol = 1;
    hex2bdf( "SYMBOL${height}.HEX", "symbol.bdf" );
}

##################################################
# Merge BDFs
##################################################
print "\n##################################################\n";
print " Merging bdf files ...\n";
print "##################################################\n";

%chars = ();

sub bdfmerge_sub {
    my($bdf, $hoffset, $voffset) = @_;
    my($char1, $inchar, $inheader, $enc);
    $inchar = 0;
    open(TMPI, $bdf);
    while(<TMPI>){
	if (/^startchar\b/i) {
	    $inchar = 1; 
	    $char1 = '';
	}
	if ($inchar && /^encoding\s+([-0-9]+)\b/i) {
	    $enc = $1;
	}
	if ($inchar && /^(bbx\s+[-0-9]+\s+[-0-9]+)\s+([-0-9]+)\s+([-0-9]+)\b/i ){
	    $_ = sprintf("$1 %d %d\n", $2+$hoffset, $3+$voffset);
	}
	if ($inchar) {
	    $char1 .= $_;
	}
	if (/^endchar/i) {
	    $inchar = 0;
	    $chars{$enc} = $char1 if (0 < $enc);
	}
    }
}

sub bdfmerge {
    my($jis, $rk, $asc, $outfile) = @_;
    my($char1, $i);
    if( $symbol ){
	bdfmerge_sub("symbol.bdf", $jis{'hoffset'}, $jis{'voffset'});
    }
    bdfmerge_sub($rk, $rk{'hoffset'}, $rk{'voffset'});
    if( !$noascii ){
	bdfmerge_sub($asc, $asc{'hoffset'}, $asc{'voffset'});
    }	
    bdfmerge_sub($jis, $rk{'hoffset'}, $rk{'voffset'});
    #
    # emoji
    #
    if( $replace ){
#	for( $i = 0xE001; $i <= 0xE539; $i++ ){ # J-Phone
	for( $i = 0xE001; $i <= 0xE53E; $i++ ){ # Vodafone
	    $char1 = sprintf("STARTCHAR U+%X\nENCODING %d\n", $i, $i);
	    $char1 .= "SWIDTH 1000 0\nDWIDTH ${size} 0\n";
	    $char1 .= "BBX $jis{'fbbx_w'} $jis{'fbbx_h'} $jis{'fbbx_x'} $jis{'fbbx_y'}\nBITMAP\n";
	    for( 1..$jis{'fbbx_h'} ){
		$char1 .= "00\n";
	    }
	    $char1 .= "ENDCHAR\n";
	    $chars{ int($i) } = $char1;
	}
    }
    # end of emoji

    open(TMPO, ">$outfile");
    print TMPO "STARTFONT 2.1\n";
    print TMPO "FONT ${fontid}\n";
    print TMPO "SIZE $size 75 75\n";
    printf TMPO "FONTBOUNDINGBOX %d %d %d %d\n",
#	$output_fbbx_w, $output_fbbx_h, $output_fbbx_x, $output_fbbx_y;
	$output_fbbx_w, $size, $output_fbbx_x, $output_fbbx_y;

    print TMPO "STARTPROPERTIES 4\n";
    print TMPO "UID ${uid}\n";
    print TMPO "MaxNormalCharWidth $width\n";
    print TMPO "Bold 0\n";
    print TMPO "Italic 0\n";
    print TMPO "ENDPROPERTIES\n";

    print TMPO "CHARS ", 0+keys(%chars), "\n";

    foreach $i (sort {$a<=>$b} keys(%chars)) {
	print TMPO $chars{$i};
    }
    print TMPO "ENDFONT\n";
    close(TMPO);
}

$merge_uni = "${base}.bdf";
print "Merging bdfs \t... ";
bdfmerge( $jis{'uni'}, $rk{'uni'}, $asc{'bdf'}, $merge_uni );
unlink( $jis{'uni'} );
unlink( $rk{'uni'} );
unlink( "symbol.bdf" );
print "O.K. ($merge_uni)\n";

##################################################
# GD
##################################################
print "\n##################################################\n";
print " Generating .gd font definition file ...\n";
print "##################################################\n";

$gd = "${base}.gd";

print "Generating GD file \t... ";


open(GD, ">$gd");

print GD <<EoGD;
Typeface ${fontid}
  Name "${fontid}" Proportional
  FontBitmaps
    ${fontid}
  EndFontBitmaps
EndTypeface

FontStoreFile
  CollectionUid ${uid}
  KPixelAspectRatio 1000
  CopyrightInfo
    "Generated by bdf2gdr.pl and fnttran"
  EndCopyrightInfo
  Typefaces
    ${fontid}
  EndTypefaces
EndFontStoreFile

EoGD

print "O.K. ($gd)\n";

##################################################
# End
##################################################
print "\n##################################################\n";
print " End of Preprocess\n";
print "##################################################\n";

print<<EoM;

	You've got unicode bdf `$merge_uni' and gd file `$gd'.
	The UID of the font is [$uid].

	Okey, now run

	-------------------------------------------
	fnttran $merge_uni $gd ${fontid}.gdr
	-------------------------------------------

	and you will get `${fontid}.gdr' font file.  enjoy!
EoM

#

