#!/usr/bin/env perl
#
# BDF -> CJKOS font converter
#
#    Copyright (c) 2003,2006 TSUMURA,Tomoaki.
#    All rights reserved.
#
#    Redistribution and use in source and binary forms, with or without
#    modification, are permitted provided that the following conditions
#    are met:
#    1. Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#    2. Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#    3. Neither the name of TSUMURA Tomoaki nor the names of its contributors
#       may be used to endorse or promote products derived from this software
#       without specific prior written permission.
#
#    THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
#    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#    ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
#    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
#    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
#    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
#    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
#    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#    SUCH DAMAGE.

$| = 1;
$version = '$Id: bdf2cjkos.pl,v 1.11 2006/01/25 09:27:47 tsumura Exp $';

sub usage{
    print "Usage: \n";
    print "\t$0 bdffile\n\n";
    print "\tEx.) $0 jiskan24.bdf\n\n";
    exit;
}

if( $#ARGV < 0 ){ usage(); }

print<<EoH;
$version
##################################################
#          BDF -> CJKOS font converter           #
#         (c) 2003,2006 TSUMURA,Tomoaki          #
##################################################

EoH

$bdffile  = $ARGV[0];
open(BDF, $bdffile) || die "Can't open BDF file `$bdffile'!\n";

$line = <BDF>;
if( $line !~ /STARTFONT/i ){
    die "File `$bdffile' is not a BDF!\n";
}

while( $line !~ /^CHARS /i ){
    $line = <BDF>;
    $line =~ s/\r\n//g;
    chomp($line);
    if( $line =~ /^PIXEL_SIZE ([0-9]+)$/i ){
	$fsize = $1 + 0;
    }elsif( $line =~ /^FONT (.*)$/i ){
	$fontlongname = $1;
    }elsif( $line =~ /^FOUNDRY (.*)$/i ){
	$foundry = $1;
	$foundry =~ s/\"//g;
	$foundry =~ s/_//g;
    }elsif( $line =~ /^CHARSET_REGISTRY (.*)$/i ){
	$registry = $1;
	$registry =~ s/\"//g;
    }elsif( $line =~ /^FAMILY_NAME (.*)$/i ){
	$familyname = $1;
	$familyname =~ s/\"//g;
	$familyname =~ s/ //g;
    }elsif( $line =~ /^WEIGHT_NAME (.*)$/i ){
	$weight = $1;
	$weight =~ s/\"//g;
	if( $weight =~ /medium/i ){
	    $weight = "";
	}else{
	    $weight = substr($weight, 0, 1);
	    $weight =~ tr/a-z/A-Z/;
	}
	    
    }elsif( $line =~ /^FONTBOUNDINGBOX ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)/i ){
	$fbbx_w = $1 + 0;	$fbbx_h = $2 + 0;
	$fbbx_x = $3 + 0;	$fbbx_y = $4 + 0;
    }
}

print "Font Size check .......... ";
if( !defined($fsize) && ($fbbx_w == $fbbx_h) ){
    $fsize = $fbbx_w;
}
if( $fsize != 10 && $fsize != 12 && $fsize != 16 && $fsize != 24 ){
    die " ($fsize px)\nFont Size must be 10/12/16/24 !!\nBye.\n";
}else{
    printf "O.K. (%d px)\n", $fsize;
}

print "Charset Registry check ... ";
if( !defined($registry) && $fontlongname ){
    $fontlongname =~ /-([^-]+)-[0-9]?$/;
    $registry = $1;
}
if( $registry !~ /^jisx02/i ){
    die " only JIS fonts are supported by this script.\nSorry.\n";
}else{
    printf "O.K. (%s)\n", $registry;
}

if( $foundry eq '' ){ $foundry = 'unknown'; }
$pdbfile = "${foundry}${familyname}_${fsize}${weight}.pdb";

open(PDB, ">$pdbfile");
binmode(PDB);

##################################################
# PDB Header
##################################################

print PDB pack("a32", "CJK Font ${fsize}x${fsize} JapaneseJIS"); # DB name
print PDB pack("H*", '0001');			# Flag
print PDB pack("H*", '0001');			# Version
$time = time() + (66*365+17)*24*60*60;
print PDB pack("L", $time);			# Create Time
print PDB pack("L", $time);			# Modified Time
print PDB pack("L", $time);			# Backup Time
print PDB pack("H*", '00000000');		# Modified Num
print PDB pack("H*", '00000000');		# App.Info Size
print PDB pack("H*", '00000000');		# Sort Info Size
print PDB pack("a4",'dFnt');			# Type
print PDB pack("a4",'dCJK');			# Creator ID
print PDB pack("H*", '00000000');		# Unique ID
print PDB pack("H*", '00000000');		# Next Record List

##################################################
# Resource Header
##################################################
if( $fsize == 10 ){
    $resnum = 4;
    $offset = 120;
}elsif( $fsize == 12 ){
    $resnum = 4;
    $offset = 120;
    $chunkchars = 256;	# 256 chars in 1 FBLK
}else{
    $resnum = 16;
    $offset = 240;
    $chunkchars = 64;	#  64 chars in 1 FBLK
}
print PDB pack("H*", sprintf("%04x", $resnum));	# num. of Records

# Resource index
if( $fsize == 10 ){
    $charsize  = 13 * 8;
    $chunksize = 26624; # I don't know why.
}else{
    $charsize  = $fsize * $fsize;
    $chunksize = $charsize * $chunkchars;
}
for( $i = 0; $i < $resnum; $i++ ){
    # Resource index entry
    print PDB pack("a4",'FBLK');
    print PDB pack("H*", sprintf("%04x", $i));
    print PDB pack("H*", sprintf("%08x", ($offset + $i * $chunksize)));
}

# delimiter
print PDB pack("H*", '0000');

##################################################
# Resources
##################################################

$nextchar = 8481; # 0x2121
$line = <BDF>;
while( $line !~ /^ENCODING 8481$/ ){	# search first char (wide space)
    $line = <BDF>;
}

print "Converting .. |-----------|\b\b\b\b\b\b\b\b\b\b\b\b";
for( $ub = 0x21; $ub <= 0x74; $ub++ ){
    printf "%s\b", substr('/-\|', ($ub % 4), 1);
    if( ! ($ub % 0x8) ){ print '*'; }
    for( $lb = 0x21; $lb <= 0x7e; $lb++ ){
	$char = $ub * 256 + $lb;
	if( $nextchar > $char ){		# pad EMPTY character
	    print PDB pack("b${charsize}", 0);
	}else{
	    while( $line && $line !~ /^BBX/i ){	# search BBX
		$line = <BDF>;
		$line =~ s/\r\n//g;
	    }
	    $line =~ /^BBX ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)$/i;
	    $bbx_w = $1 + 0;		$bbx_h = $2 + 0;
	    $bbx_x = $3 + 0;		$bbx_y = $4 + 0;
	    while( $line !~ /^[0-9A-F]+$/i ){	# search BITMAP glyph data
		$line = <BDF>;
		$line =~ s/\r\n//g;
	    }
	    parsechar( $line );
	    while( $line && $line !~ /^ENCODING/i ){ # scan next ENCODING
		$line = <BDF>;
		$line =~ s/\r\n//g;
	    }
	    if( $line ){			# next ENCODING is found
		$line =~ /^ENCODING ([0-9]+)$/i;
		$nextchar = $1 + 0;
	    }else{		# not found (EOF of bdf)
		$nextchar = hex('747f');
	    }
	}
    }
}

sub parsechar {
    my($line) = $_[0];
    my($marginl) = $bbx_x - $fbbx_x;
    my($marginr) = $fsize - $marginl - $bbx_w;
    my($marginb) = $bbx_y - $fbbx_y;
    my($margint) = $fsize - $bbx_h - $marginb;
    my(@bitmap, $i);
    my($dataw, $dec);
    for( $i = 0; $i < $margint; $i++ ){
	$bitmap[$i] = 0;
    }
    while( $line !~ /^ENDCHAR/i ){
	chomp($line);
	$dataw = 4 * length($line);
	$dec = hex($line);
	if( $dataw < $fsize ){
	    $dec <<= ($fsize - $dataw);
	}elsif( $dataw > $fsize ){
	    $dec >>= ($dataw - $fsize);
	}
	if( $marginl > 0 ){
	    $dec >>= $marginl;
	}
	$bitmap[$i] = $dec;
	#
	$line = <BDF>;
	$line =~ s/\r\n//g;
	$i++;
    }
    while( $i < $fsize ){
	$bitmap[$i] = 0;
	$i++;
    }
        if( $fsize == 10 ){ genchar10( @bitmap );
    }else{		    genchar(   @bitmap ); }
}

# 10dot font
sub genchar10 {
    my(@bitmap) = @_;
    my(@L, @R, $i);
    my($data) = '';
    for( $i = 0; $i < $fsize; $i++ ){
	$L[$i] = ( $bitmap[$i] >> 2 );
	$R[$i] = ( $bitmap[$i] %  4 );
    }
    for( $i = 0; $i < $fsize; $i++ ){
	$data .= sprintf("%02x", $L[$i]);
    }
    for( $i = 0; $i < 5; $i++ ){
	$data .= sprintf("%01x", $R[$i*2]*4+$R[$i*2+1]);
    }
    print PDB pack("H*", "${data}0");
}

# other size font
sub genchar {
    my(@bitmap) = @_;
    my($data) = '';
    my($i, $fmt);
    $fmt = sprintf( "%%0%dx", $fsize/4 );
    for( $i = 0; $i < $fsize; $i++ ){
	$data .= sprintf($fmt, $bitmap[$i]);
    }
    print PDB pack("H*", $data);
}

##################################################
# End
##################################################

print "| done.\n";
print "Font file `${pdbfile}' is generated.\n";

close(BDF);
close(PDB);
# End
