On Mon, Apr 30, 2012, Denis M. Wilson wrote:
> 
> On Wed, 25 Apr 2012 13:30:38 -0400
> Peter Schaffter <pe...@schaffter.ca> wrote:
> 
> > This works for me:
> > 
> > - use fontforge to convert the .otf to .t42 and .pfa (which
> >   generates an .afm)
> > - use the .afm, with afmtodit, to generate a groff font
> > - move the .t42 and groff font to your groff font dir (I use
> >   site-font/devps so my fonts survive across groff installations)
> > - add the font to your 'download' file
> > 
> 
> Peter, thanks for that. It works fine, although laborious.

Attached is a bash script, install-font, that automates the process.
I'll eventually be making it available on the mom website, so please
test.

> There are still problems, such as the smallcaps names are a.sc etc,
> and ligatures are given unicode names so afmtodit does not recognise
> them.

Of the various freely-available font conversion utilities, fontforge
seems the least error prone; however, it does occasionally entail
the problems you mention.  Does anyone know of a utility that
circumvents them?
 
> By doing the translation I may be violating the license.

Not much one can do about that, except rely on the honour system.

-- 
Peter Schaffter

Author of The Binbrook Caucus
http://www.schaffter.ca
#!/bin/bash

set -e

ROOT_UID=0
OPTERR=1

loc_or_sysdir=/usr/local/share/groff  # make this default
version=`groff -v | tail -1 | sed -e 's/[[:print:]]* \([0-9.]*\)$/\1/'`

trap interrupt SIGINT

shopt -s expand_aliases 

alias cp='cp -f'                      # -p flag for interactive
alias ln='ln -f'
alias mv='mv -f'

show_usage()
{
printf \
"Usage: install-font [-lscpdD] \
[-C \033[4mdir\033[0m] \
[-F \033[4mfamily\033[0m] \
[-f \033[4mgroff fontname\033[0m] | +\033[4mSTYLE\033[0m] file ...\n\
Longer help: install-font -H (pipe through 'less' or 'more')\n"
}

long_help()
{
  printf <<EOF \
"USAGE:

    install-font [-hHlscp] [-C \033[4mdir\033[0m] [-F \033[4mfamily\033[0m]] \
[-f \033[4mgroff fontname\033[0m]] file1 file2 ...

DESCRIPTION:

    A utility to make TrueType (.ttf), OpenType (.otf), and
    PostScript Type1 (.pfa, .pfb) fonts available for use with
    groff.

    With no options, for every file named on the command line,
    \033[4minstall-font\033[0m prompts for

      - a family name
      - a groff fontname
      - whether to make the font available to gropdf
      - whether to copy the input file(s) to a system-wide location

    \033[4minstall-font\033[0m calls fontforge to convert TrueType and OpenType
    fonts to PostScript Type42 (.t42) and Type1 (.pfa), from which
    a groff font is generated.  The .t42 and groff fonts are moved
    to site-font/devps/ and registered in the 'download' file.  If
    the font is to be available to gropdf, the .pfa is moved to
    font/devpdf/ and registered in the 'download' file.  A symlink
    is made to the groff font in site-font/devps.

    PostScript fonts are converted to .pfa, even those with a .pfa
    extension.  The generated .pfa has a filename derived from the
    font's PostScript name.  This is helpful in the case of fonts with
    cryptic names like 'bchri8a.pfa', which becomes 'CharterBT-Italic.pfa'.
    In all cases, the original file is preserved with its original
    name in the directory from which the font is being installed.
    
    The .pfa and groff fonts are moved to site-font/devps/ and
    registered in the 'download' file.  If the font is to be
    available to gropdf, symlinks are made in font/devpdf/ to both
    the .pfa and the groff font.  The .pfa is registered in the
    'download' file.

OPTIONS

    Options not requiring an argument may be grouped.

    -h  Short help.

    -H  Long help.

    -l  Assume prefix /usr/local/ for all directories. (default)

    -s  Assume prefix /usr/ for all directories.

    -c  Copy file(s) named on the command line to a system-accessible
        location.  If neither -s nor -C is given, copy to the family
        directory (see -F) under /usr/local/share/fonts/<fonttype>/,
        where <fonttype> is one of 'truetype', 'type1', or 'opentype'.
        If -s is given, copy to the family directory located under
        /usr/share/fonts/<fonttype>/.

        If neither -c nor -C is given, a prompt asks whether to copy
        the file(s) named on the command line.

    -d  Make font available to gropdf.

    -D  Do not make font available to gropdf.

    -p  Ask before overwriting or linking files.  Default is to
        overwrite without asking.

    -C  \033[4mdir\033[0m
        Copy file(s) named on the command line to the family directory
        (see -F) in \033[4mdir\033[0m.

    -F  \033[4mfamily\033[0m
        Family to which the font belongs.  Creates a family for the
        groff font, which can then be accessed by calls to .fam or
        .FAMILY (mom macros).  Also creates a family directory for
        fonts being made available system-wide (see -c and -C).

        -F is useful for batch processing fonts that belong to the
        same family.  Use with care, since all files named on the
        command line will be assigned to \033[4mfamily\033[0m.

        If -F is not given, a prompt asks for the family name.

    -f  \033[4mname\033[0m or +\033[4mSTYLE\033[0m 
        Name by which to access the font with calls to .ft or
        .FT (mom macros).  \033[4mname\033[0m may be arbitrary, or a '+' sign
        prefixed to an uppercase fontstyle recognized by groff,
        e.g. +R, +I, +B, +BDI, which respectively stand for regular,
        italic, bold, and bold italic.  The mom macros provide
        additional styles, which may also be used.
        
        +\033[4mSTYLE\033[0m is affixed to the family name to arrive at the
        groff fontname.

            install-font -F Garamond -f +R <file>

        creates font GaramondR, which can be accessed by

            .fam Garamond
            .ft  R

        or

            .ft GaramondR
        
        -f is not compatible with batch processing.  \033[4minstall-font\033[0m
        will abort if the -f option is given and multiple files are
        named on the command line.

        If -f is not given, a prompt asks for the font name.
"
EOF
}

case $1 in
  -h | "")
    show_usage
    exit 0
  ;;
  -H)
    long_help
    exit 0
  ;;
esac

# need write access to /usr/ and /usr/local/

if [ "$UID" -ne "$ROOT_UID" ] ; then
  scriptname=`basename $0`
  printf \
"Superuser priviledges required.\nRerun as \
'\033[33msudo ${scriptname} \033[4margs\033[0m' \
or \
'\033[33msu root -c \"${scriptname} \033[4margs\033[0m\033[33m\"\033[0m'\n"
  exit 126
fi

# if fontforge not installed, no point going any further

type fontforge > /dev/null 2>&1 || { 
  printf "fontforge not installed. Aborting.\n" >&2
  exit 1
}

tmp_dir=/tmp/install-font

[ -d ${tmp_dir} ] || mkdir ${tmp_dir}

clean_tmp()
{
  for i in ${tmp_dir}/*[!pe] ; do rm -f "$i" ; done
}

interrupt()
{
  clean_tmp
  printf "\nAborting.\n"
  exit 130
}

error()
{
  case "$1" in
    no_filename)
      printf \
"[install-font]: Missing filename.  \
Aborting.\n" >&2
      exit 1
    ;;
    no_file)
      printf \
"[install-font]: File \"${file}\" not found.  \
Aborting.\n" >&2
      exit 1
    ;;
    no_arg)
      printf \
"[install-font]: Option \"-${option}\" requires an argument.  \
Aborting.\n" >&2 
      show_usage
      exit 1
    ;;
    filename_arg)
      printf \
"[install-font]: Option \"-${option}\" requires a non-filename argument.  \
Aborting.\n" >&2 
      show_usage
      exit 1
    ;;
    no_dir)
      printf \
"[install-font]: Directory \"${copy_dir}\" not found.  \
Aborting.\n" >&2
      exit 1
    ;;
    incompatible)
      printf \
"[install-font]: Incompatible file type. Must have extension ttf|pfa|pfb.  \
Aborting.\n" >&2
      exit 1
    ;;
    arg_to_f)
      printf \
"[install-font]: Option -${option} incompatible with multiple filenames.  \
Aborting.\n" >&2
      exit 1
  esac
}

generate_t42()
{
cat <<EOF  > ${tmp_dir}/generate-t42.pe
# generate-t42.pe
                                                                                
             
Open(\$1);
Generate(\$fontname + ".pfa");
Generate(\$fontname + ".t42");
EOF
}

generate_pfa()
{
cat <<EOF  > ${tmp_dir}/generate-pfa.pe
# generate-pfa.pe
                                                                                
             
Open(\$1);
Generate(\$fontname + ".pfa");
EOF
}

convert_font()
{
  printf "Running fontforge...\n"

  fontforge -script ${script} ${file}

  printf "Done.\n"
}

make_font()
{
  printf "Creating \033[33m${groff_font}\033[0m...\n"

  afmtodit ${font}.afm textmap ${groff_font}
  rm ${font}.afm

  printf "Done.\n"
}

install_to_devps()
{
  printf "Installing \033[33m${groff_font}\033[0m in ${site_font}/... "

  mv ${font}.${ext} ${site_font}/
  mv ${groff_font} ${site_font}/

  # update the download file in site-font/devps
  [ -e ${site_font}/download ] || {
    touch ${site_font}/download
  }

  [ $argc -eq 1 ] && {
    cp ${site_font}/download ${site_font}/download.bak
  }

  printf "${font}\t${font}.${ext}\n" >> ${site_font}/download

  printf "Done.\n"
}

install_to_devpdf()
{
  if [ ${install_in_devpdf} ] ; then
    gropdf=yes
  else
    printf \
"Make \033[33m${groff_font}\033[0m available to gropdf? (y/n; default = y) "
    read gropdf
  fi

  case $gropdf in
    Y | y | YES | Yes | yes | "")
      printf "Checking for gropdf executable and devpdf directory... "
      have_gropdf=`type gropdf`

      if [ "$have_gropdf" ] ; then
        printf \
"gropdf found.\nInstalling \033[33m${groff_font}\033[0m in ${devpdf}/... "

        if [ -d ${devpdf} ] ; then
          case ${ext} in
            t42)
              mv ${font}.pfa ${devpdf}/
            ;;
            pfa)
              ln -s ${site_font}/${font}.pfa ${devpdf}/${font}.pfa
            ;;
          esac

          ln -s ${site_font}/${groff_font} ${devpdf}/${groff_font}

          [ -e ${devpdf}/download.orig ] || {
            cp ${devpdf}/download ${devpdf}/download.orig
          }

          [ $argc -eq 1 ] && {
            cp -f ${devpdf}/download ${devpdf}/download.bak
          }

          printf "\t${font}\t${font}.pfa\n" >> ${devpdf}/download
          
          printf "Done.\n"
        else
          printf \
"You do not have a ${devpdf} directory.\nNo fonts will be install in devpdf.\n"
        fi
      else
        printf \
"gropdf is not present on the system. No fonts will be installed in devpdf.\n"
      fi ;;
  esac
}

get_fontname()
{
  printf \
"Enter +STYLE (eg +R, +I, +BD, +BDI), or a unique groff name for \
\033[36m${font}\033[0m.\n"
  printf \
"Leave blank to set name to '\033[36m${font}\033[0m': "
}

convert_install()
{
  cd ${tmp_dir}
  convert_font                    # generate t42/pfa and afm files

  # get postscript family and font name from afm
  family_name=`grep "^FamilyName" *afm | \
    sed -e 's/^FamilyName *\([[:graph:]]\{1,\}\) *[[:print:]]*$/\1/'`
  font=`grep "^FontName" ${font}*afm | \
    sed -e 's/^FontName *\([[:graph:]]\{1,\}\) *[[:print:]]*$/\1/'`

  if [ ! ${family} ] ; then       # get user's name for family
    printf "Family name (default = ${family_name}): "
    read family
    [ ${family} ] || family=${family_name}
  fi

  printf \
"  =>\033[36m${font}\033[0m (${file}) assigned to family \
'\033[33m${family}\033[0m'.\n"

  if [ ! ${groff_font} ] ; then   # get groff fontname
    if [ ! ${append_style} ] ; then
      get_fontname
      read groff_font
      [ ${groff_font} ] || groff_font=${font}
      append_style=${groff_font:0:1}
      [ "$append_style" = "+" ] && {
        append_style=${groff_font##*+}
        groff_font=${family}${append_style}
      }
    else
      groff_font=${family}${append_style}
    fi
  fi

  printf \
"  =>\033[36m${font}\033[0m assigned groff fontname \
'\033[33m${groff_font}\033[0m'.\n"

  if [ ! -e textmap ] ; then
    [ -e ${devps}/generate/textmap ] && ln -s ${devps}/generate/textmap . || {
      printf "'textmap' not found.  Aborting.\n" >&2 ; exit 1
    }
  fi

  make_font
  install_to_devps

  [ ${skip_devpdf} ] || install_to_devpdf

  rm -f ${font}*
  cd - &>/dev/null
}

copy_file()
{
  [ -d ${copy_dir} ] || { 
    echo "${copy_dir} not found; creating." 
    mkdir ${copy_dir} ; 
  }
  [ -d ${copy_dir}/${family} ] || {
    mkdir ${copy_dir}/${family}
  }
  printf "Installing ${file} in ${copy_dir}/${family}/... "
  cp ${file} ${copy_dir}/${family}/
  printf "Done.\n"
}

print_sep()
{
  printf \
"======================================================================\n"
}

# getopts doesn't check if OPTARG is a flag, or a filename when some
# other value is expected

check_optarg()
{
  [ "${OPTARG:0:1}" = "-" ] && error no_arg

  eval "has_ext=`echo ${OPTARG} | sed -e 's/^[[:graph:]]*\.[[:graph:]]*$/./'`"

  [ "${has_ext}" = "." ] && error filename_arg
  :
}

while getopts ":C:cdDF:f:hHlps" opt
do
  case "$opt" in
    c)
      copy_orig=yes
    ;;
    C)
      option=C
      check_optarg
      copy_orig=yes
      copy_dir=${OPTARG}
      [ -d ${copy_dir} ] || error no_dir
    ;;
    d)
      install_in_devpdf=yes 
    ;;
    D)
      skip_devpdf=yes
    ;;
    F)
      option=F
      check_optarg
      family=${OPTARG}
    ;;
    f)
      option=f
      opt_f=true
      check_optarg
      append_style=${OPTARG:0:1}
      case ${append_style} in
        "+")
          append_style=${OPTARG##*+}
        ;;
        *)
          groff_font=${OPTARG}
        ;;
      esac
    ;;
    h)
      show_usage
      exit 0
    ;;
    H)
      long_help
      exit 0
    ;;
    l)
      loc_or_sysdir=/usr/local/share/groff
    ;;
    p)
      alias mv='mv -i'
      alias ln='ln -i'
      alias cp='cp -i'
    ;;
    s)
      loc_or_sysdir=/usr/share/groff
    ;;
    \?)
      printf "Option \"-${OPTARG}\" not valid. Aborting.\n" >&2 
      show_usage
      exit 1
    ;;
    :)
      printf "Option \"-${OPTARG}\" requires an argument. Aborting.\n" >&2
      show_usage
      exit 1
    ;;
  esac
done

shift $((OPTIND-1))

[ "$#" -eq 0 ] && error no_filename

[ $# -gt 1 -a "${opt_f}" ] && {
  error arg_to_f
  exit 1
}

devps=${loc_or_sysdir}/${version}/font/devps
devpdf=${loc_or_sysdir}/${version}/font/devpdf
site_font=${loc_or_sysdir}/site-font/devps

# create site-font and site-font/devps if not present

[ -d ${loc_or_sysdir}/site-font ] || { 
  printf "${loc_or_sysdir}/site-font not found; creating.\n" 
  mkdir ${loc_or_sysdir}/site-font 
}

[ -d ${site_font} ] || { 
  printf "${site_font} not found; creating.\n" 
  mkdir ${site_font} 
}

# process files

for i in "$@" ; do

  file="$i"
  argc=$((argc+1))

  [ -f ${file} ] || error no_file

  case ${file##*.} in
    TTF | ttf | PFB | pfb | PFA | pfa | OTF | otf)
      font_type=${file##*.}
    ;;
    *)
      error incompatible
    ;;
  esac

  ln -s $PWD/${file} ${tmp_dir}/${file}      # make a link in tmp dir to file

  [ $argc -gt 1 ] && print_sep

  printf "Processing \033[36m$file\033[0m...\n"

  case ${font_type} in
    TTF | ttf | OTF | otf)
      script=generate-t42.pe
      ext=t42

      [ -e /tmp/$script ] || generate_t42   # create if it doesn't exist

      convert_install

      if [ "${font_type}" = "TTF" -o "${font_type}" = "ttf" ] ; then
        [ ${copy_dir} ] || copy_dir=/usr/local/share/fonts/truetype
      else
        [ ${copy_dir} ] || copy_dir=/usr/local/share/fonts/opentype
      fi
    ;;
    PFB | pfb | PFA | pfa)
      script=generate-pfa.pe
      ext=pfa

      [ -e /tmp/$script ] || generate_pfa   # create if it doesn't exist

      convert_install

      [ ${copy_dir} ] || copy_dir=/usr/local/share/fonts/type1
    ;;
  esac

# copy input file to a system or named directory

  if [ ! "$copy_orig" ] ; then
    printf \
"Copy \033[36m${file}\033[0m to ${copy_dir}/${family}/?\n  (y/n; default = n) "
    read copy_file

    case "$copy_file" in
      Y | y | YES | Yes | yes)
        copy_file
      ;;
      * )
        printf \
"Install \033[36m${file}\033[0m manually to make it available system-wide.\n"
      ;;
    esac
  else
    copy_file
  fi

  unset append_style
  unset ext
  unset font
  unset groff_font

done

clean_tmp

exit 0

Reply via email to