#!/usr/local/bin/perl
#
# MakeTeXPK.pl version 1.0, Copyright (C) 1993,94 by Norman Walsh.
# NO WARRANTY.  Distribute freely under the GNU GPL.
#
# This script attempts to make a new TeX PK font, because one wasn't
# found.  Parameters are:
#
# name dpi bdpi [[[magnification] mode] subdir]
#
# `name'   is the name of the font, such as `cmr10' (*NOT* cmr10.mf).
# `dpi'    is the resolution the font is needed at.
# `bdpi'   is the base resolution, useful for figuring out the mode to
#          make the font in.
# `magnification' is a string to pass to MF as the magnification.
# `mode'   if supplied, is the mode to use.
#
# This script was designed with two goals in mind: to support recursive
# subdirectory searching for fonts and to provide support for PK files
# built from both MF fonts and PS fonts.  It also supports the Sauter
# and DC fonts which can be built at any design size.
#
# This script was designed and tested with the following directory structure
# in mind: each typeface is stored in its own directory with appropriate
# subdirectories for font sources, metrics, and glyphs.  The script may not
# work exactly right if you use a different directory structure (the font
# installation, in particular, will probably be incorrect).  However,
# several other versions of MakeTeXPK exist which will handle simpler
# directory structures, so you need not feel compelled to use the one
# described here.
#
# For MF fonts: (... is usually something like /usr/local/lib/tex/fonts)
#
# .../typeface/src          holds the sources
#             /tfm          holds the TFM files
#             /glyphs       root for glyphs
#             /glyphs/mode  holds the PK files for "mode".
#
# For PS fonts: (... is usually something like /usr/local/lib/tex/fonts)
#
# .../typeface/afm          holds the AFM files
#             /tfm          holds the TFM files
#             /vf           holds the VF files
#             /vpl          holds the VPL files
#             /glyphs       root for glyphs
#             /glyphs/pk/999dpi  holds the PK files at 999 dpi created by ps2pk
#             /glpyhs/type1 holds the type1 PFA/PFB sources for the fonts
#

require "getopts.pl";
$rc = &Getopts ('v');           # Get options from the user...

$USE_MODE_IN_DEST = 1;          # Does the destination directory name include
                               # the name of the mode?

$VERBOSE = $opt_v || $ENV{"DEBUG_MAKETEXPK"}; # Verbose?

chop($CWD = `pwd`);             # Where are we?
$TEMPDIR = "/tmp/mkPK.$$";      # Where do temp files go?
$MFBASE = "&plain";             # What MF base do we use by default?

# Where are fonts stored?
$TEXFONTS = $ENV{"TEXFONTS"} || ".:/usr/local/lib/fonts//";

# Define modes that should be used for base resolutions...
$DPI_MODES{300}  = "laserwriter";
$DPI_MODES{200}  = "FAX";
$DPI_MODES{360}  = "lqhires";
$DPI_MODES{400}  = "nexthi";
$DPI_MODES{600}  = "QMSmoa";
$DPI_MODES{100}  = "nextscreen";

$DPI_MODES{100}  = "videodisplayi";
$DPI_MODES{110}  = "videodisplayii";
$DPI_MODES{118}  = "videodisplayiii";
$DPI_MODES{120}  = "videodisplayiv";
$DPI_MODES{124}  = "videodisplayv";
$DPI_MODES{130}  = "videodisplayvi";
$DPI_MODES{140}  = "videodisplayvii";
$DPI_MODES{150}  = "videodisplayviii";

$DPI_MODES{72}   = "MacTrueSize";
$DPI_MODES{635}  = "linolo";
$DPI_MODES{1270} = "linohi";
$DPI_MODES{2540} = "linosuper";

# Where are the DC fonts stored and what base names can be used?
$DCR_DIR = '/usr/local/lib/fonts/free/dc/src';
@DCR_GEN = ('dcb','dcbom','dcbx','dcbxsl','dcbxti','dccsc','dcdunh','dcff',
           'dcfi','dcfib','dcitt','dcr','dcsl','dcsltt','dcss','dcssbx',
           'dcssi','dctcsc','dcti','dctt','dcu','dcvtt' );

# Where are the Sauter fonts stored and what base names can be used?
$SAUTER_DIR = '/usr/local/lib/fonts/free/sauter/src';
@SAUTER_GEN = ('cmb','cmbizx','cmbozx','cmbsy','cmbszx','cmbx','cmbxsl',
              'cmbxti', 'cmbz', 'cmbzx', 'cmcsc', 'cmdszc', 'cmdunh',
              'cmex', 'cmff', 'cmfi', 'cmfib', 'cminch', 'cmitt', 'cmmi',
              'cmmib', 'cmr', 'cmrcz', 'cmrisz', 'cmritz', 'cmriz',
              'cmrotz', 'cmroz', 'cmrsz', 'cmrtz', 'cmruz', 'cmrz',
              'cmsl', 'cmsltt', 'cmss', 'cmssbx', 'cmssdc', 'cmssi',
              'cmssq', 'cmssqi', 'cmsy', 'cmtcsc', 'cmtex', 'cmti',
              'cmtt', 'cmu', 'cmvtt', 'czinch', 'czssq', 'czssqi',
              'lasy', 'lasyb');

$SAUTER_ROUNDING{11} = '10.954451';
$SAUTER_ROUNDING{14} = '14.4';
$SAUTER_ROUNDING{17} = '17.28';
$SAUTER_ROUNDING{20} = '20.736';
$SAUTER_ROUNDING{25} = '24.8832';
$SAUTER_ROUNDING{30} = '29.8685984';

# Get the command line arguments...
($NAME, $DPI, $BDPI, $MAG, $MODE, $FORCEDEST, $EXTRA) = @ARGV;

open (TTY, ">/dev/tty");              # Open the TTY (so we can print messages
select (TTY); $| = 1; select(STDOUT); # even if STDERR and STDOUT are both
                                     # redirected)

if ($VERBOSE) {
   print TTY "$0: font name: $NAME\n";
   print TTY "$0: dpi: $DPI\n";
   print TTY "$0: base dpi: $BDPI\n";
   print TTY "$0: magnification: $MAG\n" if $MAG;
   print TTY "$0: mode: $MODE\n" if $MODE;
   print TTY "$0: force destination directory: $FORCEDEST\n" if $FORCEDEST;
   print TTY "$0: extra: $EXTRA\n" if $EXTRA;
}

# Make sure we got enough arguments, but not too many...
die "$0: Invalid arguments.\n" if ($BDPI eq "" || $EXTRA ne "");

# Calculate the magnification from the requested resolutions if no
# magnification string was provided.
if (!$MAG) {
   $MAG = "$DPI/$BDPI";
   print TTY "$0: magnification: $MAG\n" if $VERBOSE;
}

# Calculate the mode if the mode was not given.  Die if we don't know
# what mode to use for the requested base resolution.
if ($MODE eq "") {
   $MODE = $DPI_MODES{$BDPI};
   die "$0: No mode for ${BDPI}dpi base resolution.\n" if $MODE eq "";
   print TTY "$0: mode: $MODE\n" if $VERBOSE;
}

########################################################################

# Really start the work...
print TTY "Attempting to build PK file for: $NAME at ${DPI}dpi.\n";

$mfFile = $NAME;
$mfFile =~ /^(.*[^0-9])(\d+)$/;
$mfBase = $1;
$mfSize = $2;

# Presumably, we got here because the PK file doesn't exist.  Let's look
# for the MF file or the PFA or PFB file...

#   ... it's more complicated than that...

# If the font is from a PFA/B file, it may have the name "rxxx" or
# "xxx0" because virtual fonts extract glyphs from the "raw" font.
# We need to find the PFA/B file and install the font with the right name.
# I'm not sure what the best solution would really be, but this will work.
# Luckily, it gets installed with the right name 'cause we already
# figured that out...
#
# A better solution on Unix machines might be to make "xxx0.pfa" or
# "rxxx.pfa" a symbolic link to "xxx.pfa".  But that won't work for other
# architectures...

$t1source = "";
$t1source = $1 if $mfFile =~ /^r(.*)$/;
$t1source = $1 if $mfFile =~ /^(.*)0$/ && ($t1source eq "");

if ($t1source) {
   $fontSource = &find_fonts($TEXFONTS,
                             ("$mfFile.mf", "$mfFile.pfa", "$mfFile.pfb",
                              "$t1source.pfa", "$t1source.pfb"));
} else {
   $fontSource = &find_fonts($TEXFONTS,
                             ("$mfFile.mf", "$mfFile.pfa", "$mfFile.pfb"));
}

if ($fontSource) {
   if ($fontSource =~ /\.pfa$/ || $fontSource =~ /\.pfb$/) {
       print TTY "Building PK file from PostScript source.\n";
       &make_and_cd_tempdir();
       &make_from_ps($fontSource);
   } elsif ($fontSource =~ /\.mf$/) {
       local($fpath, $fname);
       print TTY "Building PK file from MF source.\n";
       &make_and_cd_tempdir();

       if ($fontSource =~ /^(.*)\/([^\/]*)$/) {
           $fpath = $1;
           $fname = $2;

           $fpath = $CWD if $fpath eq ".";
           $fpath = "$CWD/.." if $fpath eq "..";
       } else {
           $fpath = "";
           $fname = $fontSource;
       }

       &make_from_mf($fpath, $fname);
   } else {
       print TTY "$0: Cannot build PK font for $NAME.\n";
       print TTY " " x length($0), "  Unprepared for $fontSource.\n";
       die "\n";
   }
} else {
   if (grep(/^$mfBase$/, @DCR_GEN)) {

       print TTY "Building PK file from DC source.\n";

       &make_and_cd_tempdir();

       $MFBASE = "&dxbase";
       open (MFFILE, ">$mfFile.mf");
       print MFFILE "gensize:=$mfSize; generate $mfBase;\n";
       close (MFFILE);

       &make_from_mf("$DCR_DIR","$mfFile.mf");

   } elsif (grep(/^$mfBase$/, @SAUTER_GEN)) {

       print TTY "Building PK file from Sauter source.\n";

       &make_and_cd_tempdir();

       if (defined($SAUTER_ROUNDING{$mfSize})) {
           $designSize = $SAUTER_ROUNDING{$mfSize};
       } else {
           $designSize = $mfSize;
       }

       open (MFFILE, ">$mfFile.mf");
       print MFFILE "design_size := $designSize;\n";
       print MFFILE "input b-$mfBase;\n";
       close (MFFILE);

       &make_from_mf("$SAUTER_DIR","$mfFile.mf");

   } else {
       print TTY "$0: Cannot build PK file.  Can't find source.\n";
       die "\n";
   }
}

&cleanup();

exit 0;

########################################################################

sub run {
   local(@cmd) = @_;
   local($rc);

   open  (SAVEOUT, ">&STDOUT");
   open  (SAVEERR, ">&STDERR");
   close (STDOUT);
   open  (STDOUT, ">&TTY");
   close (STDERR);
   open  (STDERR, ">&TTY");

   # Chdir seems to return a funny exit code.  So do it internally...
   # (this is a hack)
   if (@cmd[0] eq "chdir") {
       $rc = chdir(@cmd[1]);
       $rc = !$rc;
   } else {
       $rc = system(@cmd);
   }

   close (STDOUT);
   open  (STDOUT, ">&SAVEOUT");
   close (SAVEOUT);

   close (STDERR);
   open  (STDERR, ">&SAVEERR");
   close (SAVEERR);

   if ($rc) {
       printf TTY "%s\n", "*" x 72;
       print  TTY "$0 error : system return code: $rc\n";
       print  TTY "$0 failed: @cmd\n";
       printf TTY "%s\n", "*" x 72;
   }

   $rc;
}

sub make_and_cd_tempdir {
   &run ("mkdir", "$TEMPDIR");
   &run ("chdir", "$TEMPDIR");
}

sub cleanup {
   &run ("chdir", "$CWD");
   &run ("rm", "-rf", "$TEMPDIR");
}

sub install_font {
   local($source_path, $font, $subdir, $mode) = @_;
   local($pkdirs, @paths, $ptarget);
   local($target) = "";

   if ($VERBOSE) {
       print "Install: source_path: $source_path\n";
       print "Install: font       : $font\n";
       print "Install: subdir     : $subdir\n";
       print "Install: mode       : $mode\n";
   }

   $pkdirs = $ENV{"TEXPKS"} || $ENV{"PKFONTS"} || "";
   @paths = split(/:|;/,$pkdirs);

   # Need to find an installable target for the PK files.  Try
   # ../glyphs/$subdir and ../$subdir then give up and use the best $pkdirs
   # path...

   if (!$target) {
       ($ptarget = $source_path) =~ s#/[^/]*$##;
       $target = "$ptarget/glyphs/$subdir"
           if -d "$ptarget/glyphs/$subdir"
               || (-d "$ptarget/glyphs"
                   && -w "$ptarget/glyphs"
                   && ! -f "$ptarget/glyphs/$subdir");
   }

   if (!$target) {
       ($ptarget = $source_path) =~ s#/[^/]*$##;
       $target = "$ptarget/$subdir"
           if -d "$ptarget/$subdir"
               || (-d $ptarget && -w $ptarget && ! -f "$ptarget/$subdir");

       # what a minute, suppose we just made a font in the current
       # directory...let's put the PK file there too...
       if (! -d "$target" && ($source_path eq $CWD)) {
           $target = $source_path;
           $USE_MODE_IN_DEST = 0;
       }
   }

   while (!$target && ($ptarget = shift @paths)) {
       $target = $ptarget if ($ptarget ne "." && $ptarget ne ".."
                              && -d $ptarget && -w $ptarget);
   }

   if ($target) {
       if (! -d $target) {
           &run ("mkdir", "$target");
           &run ("chmod", "777", "$target");
       }

       if ($USE_MODE_IN_DEST) {
           $target .= "/$mode";
           if (! -d $target) {
               &run ("mkdir", "$target");
               &run ("chmod", "777", "$target");
           }
       }

       print TTY "Installing $font in $target.\n";
       &run ("cp", "$font", "$target/fonttmp.$$");
       &run ("chdir", "$target");
       &run ("mv", "fonttmp.$$", "$font");
       &run ("chmod", "a+r", "$font");
       &run ("chdir", "$TEMPDIR");
       print STDOUT "$target/$font\n";
   } else {
       print TTY "$0: Install failed: no where to put $font.\n";
   }
}

sub make_from_mf {
   local ($source_path, $source_file) = @_;
   local ($mfsource, $mfinputs, $cmd);
   local ($gfname, $pkname, $realdpi, $testdpi);
   local ($cmpath);

   print "source_path: $source_path\n" if $VERBOSE;
   print "source_file: $source_file\n" if $VERBOSE;

   &run ("chdir", "$TEMPDIR");

   if (!$source_file) {
       $mfsource = $source_path;
       ($source_path = $mfsource) =~ s#/[^/]*$##;
       ($source_file = $mfsource) =~ s#^.*/([^/]*)$#$1#;
   }

   $mfinputs = $ENV{"MFINPUTS"};
   $mfinputs =~ s/^:*(.*):*$/$1/ if $mfinputs;
   $ENV{"MFINPUTS"} = ".:$source_path";
   $ENV{"MFINPUTS"} .= ":$mfinputs" if $mfinputs;

   print "MFINPUTS: $ENV{MFINPUTS}\n" if $VERBOSE;

   $cmpath = "/usr/local/lib/fonts/free/cm/src";
   if (-d $cmpath && $ENV{"MFINPUTS"} !~ /$cmpath/) {
       $ENV{"MFINPUTS"} .= ":$cmpath";
   }

   $cmd = "$MFBASE \\mode:=$MODE; mag:=$MAG; scrollmode; " .
          "\\input $source_file";
   print TTY "virmf $cmd\n";

   $saveTERM = $ENV{"TERM"};
   $saveDISPLAY = $ENV{"DISPLAY"};
   delete $ENV{"DISPLAY"};
   $ENV{"TERM"} = "vt100";

   $rc = &run ("virmf", "$cmd");

   $ENV{"DISPLAY"} = $saveDISPLAY;
   $ENV{"TERM"} = $saveTERM;

   $realdpi = $DPI;
   $gfname = "./$mfFile.${realdpi}gf";

   for ($testdpi = $realdpi-2; $testdpi < $realdpi+3; $testdpi++) {
       $gfname = "./$mfFile.${testdpi}gf", $realdpi = $testdpi
           if ! -f $gfname && -f "./$mfFile.${testdpi}gf";
   }

   $gfname = "./$mfFile.${realdpi}gf";
   $pkname = "./$mfFile.${realdpi}pk";

   $rc = &run ("gftopk", "$gfname", "$pkname");

   &install_font($source_path, "$mfFile.${realdpi}pk", 'pk', "$MODE");
}

sub make_from_ps {
   local ($source_path, $source_file) = @_;
   local ($pssource, @cmd);
   local ($basename, $afmFile, $afmtest, $part);

   &run ("chdir", "$TEMPDIR");

   if (!$source_file) {
       $pssource = $source_path;
       ($source_path = $pssource) =~ s#/[^/]*$##;
       ($source_file = $pssource) =~ s#^.*/([^/]*)$#$1#;
   }

   # Need to find the AFM file...
   $afmFile = "";
   ($basename = $source_file) =~ s/\.pf[ab]$//;
   # First, look in ../afm:
   ($afmtest = $source_path) =~ s#/[^/]*$##;
   $afmtest .= "/afm/$basename.afm";
   $afmFile = $afmtest if -r $afmtest;

   # Then, look in ../../afm:
   ($afmtest = $source_path) =~ s#/[^/]*$##;
   $afmtest =~ s#/[^/]*$##;
   $afmtest .= "/afm/$basename.afm";
   $afmFile = $afmtest if !$afmFile && -r $afmtest;

   die "$0: Cannot find AFM file for $source_file.\n" if !$afmFile;

   @cmd = ('ps2pk', "-a$afmFile", "-X$DPI",
           "$source_path/$source_file", "./$mfFile.${DPI}pk");

   foreach $part (@cmd) {
       print TTY "$part ";
   }
   print TTY "\n";

   $rc = &run (@cmd);

   &install_font($source_path, "$mfFile.${DPI}pk", 'pk', "${DPI}dpi");
}

sub find_fonts {
# This subroutine searches for font sources.  It looks in all the directories
# in the path specified.  Recursive searches are preformed on directories
# that end in //, !, or !!.  The emTeX directive "!", which should search
# only one level deep, is treated exactly like "!!".
#
   local($path, @fonts) = @_;
   local(@dirs, $dir, $font);
   local(@matches) = ();
   local(@recursive_matches);

   $path =~ s/!!/\/\//g;
   $path =~ s/!/\/\//g;
   $path =~ s/\\/\//g;

   print TTY "CWD: ", `pwd` if $VERBOSE;
   print TTY "Find: @fonts\n" if $VERBOSE;
   print TTY "Path: $path\n" if $VERBOSE;

   @dirs = split(/:|;/, $path);
   while (@dirs) {
       $dir = shift @dirs;
       next if !$dir;

       if ($dir =~ /\/\//) {
           @recursive_matches = &recursive_search($dir, @fonts);
           push (@matches, @recursive_matches)
               if @recursive_matches;
       } else {
           $dir =~ s/\/*$//;           # remove trailing /, if present
           foreach $font (@fonts) {
               push (@matches, "$dir/$font")
                   if -f "$dir/$font";
           }
       }
   }

   $font = shift @matches;

   if (@matches) {
       print TTY "$0: Found more than one match.\n";
       print TTY " " x length($0), "  Using: $font\n";
   }

   $font;
}

sub recursive_search {
   local($dir, @fonts) = @_;
   local(@matches) = ();
   local(@dirstack, $rootdir, $font, $fontmask);

   $dir =~ /^(.*)\/\/(.*)$/;
   $rootdir = $1;
   $fontmask = $2;

   $rootdir =~ s/\/*$//;               # remove trailing /'s

   # Note: this perl script has to scan them all, the mask is meaningless.
   # Especially since I'm looking for the font *source* not the TFM or
   # PK file...

   $fontmask =~ s/\$MAKETEX_BASE_DPI/$BDPI/g;
   $fontmask =~ s/\$MAKETEX_MAG/$MAG/g;
   $fontmask =~ s/\$MAKETEX_MODE/$MODE/g;

   print TTY "Search root=$rootdir\n" if $VERBOSE;
   print TTY "Search mask=$fontmask (ignored by $0)\n" if $VERBOSE;

   @dirstack = ($rootdir);

   while ($rootdir = shift @dirstack) {
       opendir (SEARCHDIR, "$rootdir");
       while ($dir = scalar(readdir(SEARCHDIR))) {
           if ($dir ne "." && $dir ne ".." && -d "$rootdir/$dir") {
               push(@dirstack, "$rootdir/$dir");
               foreach $font (@fonts) {
                   if (-f "$rootdir/$dir/$font") {
                       print TTY "Matched: $rootdir/$dir/$font\n" if $VERBOSE;
                       push(@matches, "$rootdir/$dir/$font");
                   }
               }
           }
       }
       closedir (SEARCHDIR);
   }

   @matches;
}