[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

generating MF fonts



Note: This posting is of no use for you if you do not work on DOS or
      UNIX systems.
 
 
Below I attach a perl script which runs MF to produce a set of fonts.
Complete LOGs are put into one file. In addition warnings and errors
are copied to another file. (This allows a quick look on the results.)
 
The script has one disadvantage: It does not allow the specification
of different mode_def's for different fonts because it is mode_def
independent. But for those of you who just want to create a whole
family rather fast it is usable.
 
The script reads a table to discover which fonts are to be created.
An example of such a table looks like this:
 
>> ::HEADER::
>> DIR  /usr/mf/modern/computer     % directory where parameter files are
>> COMMAND cmmf                     % command to use
>> MFINPUTS /usr/mf/modern/program  % env var to set
>>
>> ::FONTS::
>> s0     *.mf                      % all MF files in magstep 0
>> 1.315  cmb10                     % cmb10 scaled 1315 (for texinfo)
 
In the header section information on the run is concentrated, the
fonts section specifies which fonts to create. Wild carding on font
names and specification of magsteps is supported.
 
An example table for the family Computer Modern is enclosed.
 
 
For those of you who don't know perl: It's a prototyping language
which combines features of awk, sed, sh, C, and gives access to the
UNIX system calls. I don't want to recommend it for `real sw
projects,' but as an admin help tool, it's ok.
   You may get it from the usual FTP archives, a nice DOS port is
available from simtel-20. Bitnet folks may fetch it via trickle.
 
 
================ genfam ====================================================
#! /usr/local/bin/perl
# $Id: genfam,v 1.5 1991/04/08 13:22:36 tex Exp tex $
#------------------------------------------------------------
# (c) 1991 by Joachim Schrod <xitijsch@ddathd21.bitnet>.
 
#
#       genfam device family
#
# creates a font set for device as specified in Table/<family>.
# This file has the following line structure:
#
#       <family table> :-
#               preambel with arbitrary text
#               <header>
#               <font sets>
#
#       <header> :-
#               '::HEADER::' new_line+
#               <directory>
#               [ <command> ]
#               [ { <environment variables> } ]
#       <directory> :- 'DIR' not_white_space+ new_line+
#       <command> :- 'COMMAND' rest_of_line new_line+
#       <environment variables> :- env_var_name not_white_space+ new_line+
#
#       <font sets> :-
#               '::FONTS::' new_line+
#               { <font set> }
#       <font set> :- <magnification> file_pattern {new line}+
#       <magnification> :- <mag step> | real_number
#       <mag step> :- 's'real_number
#
# Comments start with `%' and end with the last character on the line.
# Unlike in TeX the new_line does NOT belong to the comment.
 
 
 
# 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 1, 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, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 
 
#
# $Log: genfam,v $
# Revision 1.5  1991/04/08  13:22:36  tex
# included copyright notice for distribution.
#
# Revision 1.4  1991/02/08  20:32:29  tex
# New TFM file that cannot be found is saved in subdirectory tfm.
#
# Revision 1.3  1991/02/08  20:10:06  tex
# Directory with font sources is always included in MFINPUTS path.
# On differences in dpi values our own value is used for the directory
#    name.
# The TFM file is search in the TEXFONTS path and is compared to if
#    a file is found. If the new TFM differs it is not deleted.
#
# Revision 1.2  1991/02/08  12:41:14  tex
# perl is now in /usr/local/bin instead of /software/bin.
# Include dpi in separator of LOG files to distinguish different fonts.
# $#errors is -1 when no errors were found.
# $real_dpi must be computed with round() instead of trunc().
# Check differences between MF dpi values and own computed values.
#
# Revision 1.1  91/02/01  19:33:42  schrod
# Initial revision
#
 
 
# name: table_line
# pre:  TABLE is open
# ret:  next non-empty line of TABLE, comments are deleted
#       leading and trailing white space is discarded
#       empty line if eof
 
sub table_line
{
    if ( eof(TABLE) ) {
        return "";
    }
    while ( <TABLE> ) {
        chop;                       # throw away new_line
        s/%.*$//o;                  # discard comments
        s/^\s*(.*)\s*$/$1/o;        # discard leading and trailing white space
        if ( $_ eq "" ) {           # skip blank lines
            next;
        }
        return $_;                  # We've found a non-empty line!
    }
    return "";                      # end of file reached.
}
 
 
# name: info()
# pre:  TABLE is open and at beginning
# ret:  (dir, command)
#               dir is family directory
#               command is MF command for create_font()
# post: set env vars if necessary
#       ::FONTS:: is already read
# err:  exits if family directory is not given or does not exist
 
sub info
{
    local ($line);          # next non-empty line
    local ($key, $value);   # (key, value) pair from table header
 
    while ( ($line=&table_line()) ne "::HEADER::"  &&  $line ne "" ) {}
    if ( $line eq "" ) {
        print "! There is no header in $table.";
        exit 2;
    }
 
    $line = &table_line();
    ($key, $value) = split(/\s+/o, $line);
    if ( $key eq "DIR" ) {
        $dir = $value;
        if ( ! -d $dir ) {
            print "! Directory $dir does not exist.";
            exit 2;
        }
        if ( $ENV{"MFINPUTS"} ne "" ) {
            $ENV{"MFINPUTS"} .= ":$dir";
        } else {
            $ENV{"MFINPUTS"} .= "$dir";
        }
    } else {
        print "! Source directory must be specified in $table.";
        exit 2;
    }
 
    $line = &table_line();
    ($key, $value) = split(/\s+/o, $line, 2);
    if ( $key eq "COMMAND" ) {
        $command = $value;
        $line = &table_line();
    } else {
        $command = "mf";
    }
 
    while ( $line ne "::FONTS::"  &&  $line ne "" ) {
        ($key, $value) = split(/\s+/o, $line);
        if ( $key eq "MFINPUTS" ) {
            $value .= ":$dir";
        }
        $ENV{$key} = $value;
        $line = &table_line();
    }
}
 
 
# name: resolution(MF_command, mode_def)
# pre:  MF is callable with MF_command
#       mode_def exists with the used base
# ret:  resolution in dpi
 
sub resolution
{
    local ($command, $mode_def) = @_;
    local (@log, $out_markup, $dpi);
 
    # First build a MF call:
    #   MF shall not ask the (non-existent) user if an error has occured,
    #   it shall switch to the mode definition,
    #   and shall output the respective resolution.
    #   stdin is /dev/null
    #       -- this will cause an emergency stop if all else fails.
    $command .= " '\\scrollmode;".
                "mode=$mode_def;$mode_def"."_;".
                "show pixels_per_inch;end;' ".
                "</dev/null";
 
    # Then we execute this call and catch the output in @log.
    # If the command failed we did not have any output.
    @log = `$command`;
    if ( $#log == -1 ) {
        print "! I've had problems in calling METAFONT.";
        exit 2;
    }
 
    # We look for a line with a show output (starting with `>>')
    # in the MF output. The respective
    # line is stored in $out_markup. If no such line is found, $out_markup
    # is "! Error message", simulating a MF error in this way.
    $out_markup = "! Error message";
    foreach ( @log ) {
        $line_no += 1;
        if ( /^>>/o ) {
            $out_markup = $_;
            last;
        }
    }
 
    # We split the found line. Then $out_mark is hopefully ">>", otherwise
    # it's an error.
    ($out_markup, $dpi) = split(/\s+/, $out_markup);
    if ( $out_markup eq "!"  ||  grep(/^!/, @log) > 0 ) {
        print "! There was an error while calling METAFONT.";
        print "  Perhaps the device is no valid mode definition?";
        print "  Let's have a look at the MF output:";
        print "-" x 70;
        print @log;
        unlink "mfput.log";
        exit 2;
    }
 
    # Of course the output should be a real number.
    if ( $dpi !~ /\d+(\.\d*)?/o ) {
        print "! METAFONT did not tell me a resolution for this device.";
        print "  I'm stymied. Perhaps you should take a look at the MF output:";
        print "-" x 70;
        print @log;
        unlink "mfput.log";
        exit 2;
    }
 
    # MF produced mfput.log and mfput.tfm, we will delete them before returning.
    unlink <mfput.*>;
    return($dpi);
}
 
 
# name: create_dir(dir)
# pre:  ---
# post: directory dir exists
 
sub create_dir
{
    local ($dir) = @_;
    if ( -e $dir ) {
        if ( -d _ ) {
            return;
        }
        print "! File $dir exists but is no directory.";
        do finish();
    }
    mkdir($dir, 0777)  ||  die "$0: mkdir $dir: $!.\n";
}
 
 
# name: base_dir(mode_def)
# pre:  ---
# ret:  full path name of base directory (i.e. new current directory)
# post: base directory for mode_def is created if necessary
#       is now current directory
 
sub base_dir
{
    local ($mode_def) = @_;
    do create_dir($mode_def);
    chdir($mode_def);
    chop( $mode_def=`pwd` );
    return $mode_def;
}
 
 
# name: lookup(file, path)
# pre:  $file is a name of a regular file
#       @path is an array with directory names
# ret:  full path name of $file if found in one directory
#       "" if not found
 
sub lookup
{
    local ($file, @path) = @_;
    local ($dir);
 
    foreach $dir ( @path ) {
        next if $dir eq "";
        if ( -f "$dir/$file" ) {
            return "$dir/$file";
        }
    }
    return "";
}
 
 
# name: create_font(mag, file)
# pre:  mag holds the mag string for MF call
#       file is a MF program and is found by $MFINPUTS
#       $command holds the MF call
#       $device holds the mode definition
#       $real_dpi is the dpi value we have computed
#       LOGerror is open for writing
#       LOGwarning is open for writing
# post: PK file is created in subdir dpi<dpi>/.
#       LOG file is analyzed,
#               errors are appended to LOGerror,
#               warnings are appended to LOGwarning,
#               whole LOG is appended to LOGall.
#       no GF file exists.
#       @font_count[0..1] is incremented;
 
sub create_font
{
    local ($mag, $file) = @_;
    local (@log, @errors, $error, $dpi);
    local ($font) = split(/\./, $file);
    local ($font_msg) = $font." at ".$real_dpi." dpi";
    local ($separator)= "=" x 20." ".$font_msg." "."=" x (58-length($font_msg));
 
    $font_count[0] += 1;    # we try the next font
    @log = `$command '\\scrollmode; mode=$device; mag=$mag; input $file`;
 
    {
        @errors = grep(/^!/, @log);
        if ( ($error=$#errors) == -1 ) {
            last;
        }
 
        @errors = grep(!/^! Strange path/, @errors);
        if ( $#errors != $error ) {
            print LOGwarning $separator;
            print LOGwarning $error-$#errors, " strange paths have occured.";
        }
        if ( $#errors == -1 ) {
            last;
        }
 
        print LOGerror $separator;
        $error = 0;
        foreach ( @log ) {
            if ( $error ) {
                chop;  print LOGerror $_;
                if ( $error == 2 ) {
                    $error = 0;
                } elsif ( /^l.\d+/o ) {
                    $error = 2;
                }
            } elsif ( /^!/o  &&  ! /^! Strange/o ) {
                chop;  print LOGerror $_;
                $error = 1;
            }
        }
    }
 
    system "echo '$separator' >>LOGall";
    system "cat $font.log >>LOGall";
 
    $dpi = $log[$#log - 1];
    if ( $dpi !~ /^Output/o ) {
        print LOGerror "!" x 20, " No output!";
        return;
    }
 
    ($dpi) = ( $dpi =~ /$font\.(\d+)gf/ );
    if ( $dpi != $real_dpi ) {
        print "! Dpi-differences between MF ($dpi dpi) and me ($real_dpi dpi).";
    }
    system "gftopk", "$font.${dpi}gf", "dpi$real_dpi/$font"
        ||  print LOGerror "!" x 20," Problems with gftopk on $font.${dpi}gf";
 
    $old_tfm = &lookup("$font.tfm", @tfm_path);
    if ( $old_tfm eq "" ) {
        print "! New TFM file: tfm/$font.tfm";
        rename("$font.tfm", "tfm/$font.tfm");
    } elsif ( system("cmp", "-s", "$font.tfm", $old_tfm) ) {
        print "! Different TFMs for type $font,";
        print "  new one stored as $old_tfm.$device.$real_dpi.";
        system "mv", "$font.tfm", "$old_tfm.$device.$real_dpi";
    }
 
    unlink( grep(!/\.mf/, <$font.*>) );
 
    $font_count[1] += 1;    # well, we succeeded
}
 
 
# name: create_mag(mag, pattern)
# pre:  mag is the magnification of the font set
#       $dpi is the base resolution of the device
#       $cwd is the current directory, i.e., the base directory
#       $dir is the directory where the MF programs reside
#       pattern are files in $dir which shall be created in mag
#       create_font(file) with file from {pattern} is callable
# post: for all files create_font() is called
 
sub create_mag
{
    local ($mag, $pattern) = @_;
    local (@files, $file, $MF_mag, $real_dpi);
 
    chdir $dir;  @files = <${pattern}>;  chdir $cwd;
    if ( $#files == -1 ) {
        return;
    }
 
    $MF_mag = $mag;
    if ( $MF_mag =~ s/^s(\d+(\.\d*)?)/$1/o ) {
        $real_dpi = 1.2 ** $MF_mag;
        $MF_mag = "magstep(".$MF_mag.")";
    } else {
        $real_dpi = $MF_mag;
    }
    if ( $real_dpi == 0 ) {
        print "! Hmm, the magnification $mag is not valid.";
        return;
    }
 
    $real_dpi = int( $real_dpi * $dpi  + 0.5 );     # round does not exist...
    do create_dir("dpi".$real_dpi);
 
    foreach $file ( @files ) {
        do create_font($MF_mag, $file);
    }
}
 
 
# name: finish()
# pre:  $init_phase is set
# post: no return
 
sub finish
{
    local ($log_msg);
 
 
    if ( $init_phase ) {
        exit 2;
    }
 
 
    # We are finished and tell now how long we have run.
 
    $log_msg = "=" x 79;
    print LOGerror $log_msg;
    print LOGwarning $log_msg;
    print LOGall $log_msg;
 
    ($user, $system, $cuser, $csystem) = times;
    $total = sprintf("%.2f", $user + $system + $cuser + $csystem);
    $user = sprintf("%.2f", $user);
    $system = sprintf("%.2f", $system);
    $cuser = sprintf("%.2f", $cuser);
    $csystem = sprintf("%.2f", $csystem);
    chop( $time = &ctime(time) );
 
    $log_msg = <<EOT
 
Well, now it's $time.
 
I've started MF $font_count[0] times and created $font_count[1] fonts.
All in all, we have needed (u:$user, s:$system) seconds to control the run,
MF and associated programs needed (u:$cuser, s:$csystem) seconds,
in total that makes $total seconds.
        Howdy!
EOT
    ;
    print LOGerror $log_msg;
    print LOGwarning $log_msg;
    print LOGall $log_msg;
 
 
    # Send the starter a mail that we are finished
    open(STDOUT, "| /bin/mail $name");
    print <<EOT
Hello,
 
You have started a $0 script for the creation of the $family fonts
for device $device. This script is finished.
 
It's error messages are in $cwd/nohup.out.
You should take a look at LOG* in the same directory (especially at LOGerror).
 
                Virtually, your MF management
EOT
    ;
    close(STDOUT);
 
    exit 0;
}
 
 
# extract basename from $0
# supply new_line at end of line
# and load needed library functions
 
    $0 =~ s:^.*/([^/]+):$1:o;
    $\ = "\n";
    require "ctime.pl";
 
 
    $init_phase = 1;
 
 
    if ( ! defined $ENV{"LOGNAME"} ) {
        if ( defined $ENV{"USER"} ) {
            $ENV{"LOGNAME"} = $ENV{"USER"};
        } else {
            print "Oops, you do not have a logname!";
            local ($\) = "";    # just for the following print
            while {
                print "Who are you? ";
                ($name=<STDIN>) eq "";
            } {}
            $ENV{"LOGNAME"} = $name;
        }
    }
    $name = $ENV{"LOGNAME"};
 
    do create_dir("tfm");
    @tfm_path = split(/:/, $ENV{"TEXFONTS"});
    push(@tfm_path, ".", "/usr/tex/fonts/tfm", "tfm");
 
 
 
# check arguments
 
    if ( $#ARGV != 1 ) {
        print "usage: ",$0," device family";
        exit 1;
    }
 
# give them names
 
    ($device, $family) = @ARGV;
 
# open table file
 
    $table = "Table/".$family;
    if ( ! -f $table  ||  ! -r _ ) {
        print "! Cannot read $table.";
        exit 2;
    }
    open(TABLE,"<".$table);
 
# read table header, compute base resolution and generate base directory
 
    do info();
    $dpi = &resolution($command, $device);
    $cwd = &base_dir($device);
 
# Now we are in the base directory.
 
 
 
 
# All initializations are done, MF ran already, and we may now assume that
# the rest is computer's work. So we fork of a process, disconnect it from
# our main process and start the whole stuff ::::
 
    print <<EOT
 
OK, I fork myself and get METAFONT running. This will need a while.
You will find my error messages in $device/nohup.out.
EOT
    ;
 
    if ( fork != 0 ) {  # parent:
        sleep 2;        # wait for child process to establish SIG handler
        exit 0;
    }
 
    $init_phase = 0;    # start real work
 
    $SIG{'HUP'} = "IGNORE";     # child: disconnect from parent
    open(STDIN, "</dev/null");
    open(STDOUT, ">nohup.out");  open(STDERR, ">&STDOUT");
 
 
 
 
#   Create files LOGerror and LOGwarning because they will be written
# by this script.
#   But delete LOGall as this will be written by cat. This deletion is not
# done by unlink because this file may be linked to somewhere else due to
# space reasons. So we use an open and a close. But before we write a
# header to the LOG files so that the reader will know what we have done.
 
    $log_msg = "Started creation of family $family on ".&ctime(time).
                "Skript activated by $name\n";  # two NL's !!
 
    open(LOGerror, ">LOGerror");
    open(LOGwarning, ">LOGwarning");
    open(LOGall, ">LOGall");
    print LOGerror $log_msg;
    print LOGwarning $log_msg;
    print LOGall $log_msg;
    close(LOGall);
 
 
# Read each font set from TABLE, split it, setup mag specification
# string for MF, and call create_mag().
 
    @font_count = (0, 0);
    while ( ($_ = &table_line()) ne "" ) {
        ($mag, $pattern) = split;
        do create_mag($mag, $pattern);
    }
 
 
# cleanup
 
    do finish();
================ end of genfam =============================================
================ Table/modern ==============================================
% $Id: modern,v 1.1 1991/03/21 18:29:57 tex Exp tex $
%------------------------------------------------------------
 
%
% table of the used fonts of the family Computer Modern
%
 
 
% $Log: modern,v $
% Revision 1.1  1991/03/21  18:29:57  tex
% Initial revision
%
 
 
 
::HEADER::
 
DIR      /usr/mf/modern/computer
COMMAND  cmmf
MFINPUTS /usr/mf/modern/driver:/usr/mf/modern/program
 
 
 
::FONTS::
 
% first a few reduced fonts as substitutions for missing small fonts
 
0.5     cmbsy10.mf
0.5     cmbxsl10.mf
0.5     cmcsc10.mf
0.5     cmitt10.mf
0.5     cmmib10.mf
0.5     cmsl10.mf
0.5     cmsltt10.mf
0.5     cmss10.mf
0.5     cmssbx10.mf
0.5     cmssi10.mf
0.5     cmti10.mf
0.5     cmtt10.mf
0.5     cmu10.mf
 
0.6     cmbsy10.mf
0.6     cmbxsl10.mf
0.6     cmcsc10.mf
0.6     cmitt10.mf
0.6     cmmib10.mf
0.6     cmsl10.mf
0.6     cmsltt10.mf
0.6     cmss10.mf
0.6     cmssbx10.mf
0.6     cmssi10.mf
0.6     cmti10.mf
0.6     cmtt10.mf
0.6     cmu10.mf
 
0.7     cmbsy10.mf
0.7     cmbxsl10.mf
0.7     cmcsc10.mf
0.7     cmitt10.mf
0.7     cmmib10.mf
0.7     cmsl10.mf
0.7     cmsltt10.mf
0.7     cmss10.mf
0.7     cmssbx10.mf
0.7     cmssi10.mf
0.7     cmtt10.mf
0.7     cmu10.mf
 
0.8     cmbsy10.mf
0.8     cmbxsl10.mf
0.8     cmcsc10.mf
0.8     cmitt10.mf
0.8     cmmib10.mf
0.8     cmsltt10.mf
0.8     cmssbx10.mf
0.8     cmu10.mf
 
0.9     cmbsy10.mf
0.9     cmbxsl10.mf
0.9     cmcsc10.mf
0.9     cmitt10.mf
0.9     cmmib10.mf
0.9     cmsltt10.mf
0.9     cmssbx10.mf
0.9     cmu10.mf
 
% next complete steps
 
s0      *.mf
s0.5    *.mf
s1      *.mf
s2      *.mf
s3      *.mf
s4      *.mf
s5      *.mf
 
% these fonts are needed by SliTeX
 
s6      cmmi8.mf
s6      cmsy8.mf
s6      cmtt8.mf
s7      cmmi8.mf
s7      cmsy8.mf
s7      cmtt8.mf
s8      cmmi8.mf
s8      cmsy8.mf
s8      cmtt8.mf
 
% these by texinfo
 
1.315   cmb10
1.315   cmti10
1.315   cmsl10
1.315   cmtt10
1.315   cmss10
 
% and these by our own macros
 
s6      cmcsc10.mf
s7      cmcsc10.mf
================ end of Table/modern =======================================
 
--
Joachim
 
===========================================================================
Joachim Schrod                          <xitijsch@ddathd21.bitnet>
Computer Science Department
Technical University of Darmstadt, Germany