/********************************************************************
 *                                                                  *
 *  Title:  pfm2afm - Convert Windows .pfm files to .afm files      *
 *                                                                  *
 *  Author: Ken Borgendale   10/9/91  Version 1.0                   *
 *                                                                  *
 *  Function:                                                       *
 *      Convert a Windows .pfm (Printer Font Metrics) file to a     *
 *      .afm (Adobe Font Metrics) file.  The purpose of this is     *
 *      to allow fonts put out for Windows to be used with OS/2.    *
 *                                                                  *
 *  Syntax:                                                         *
 *      pfm2afm  infile  [outfile] -a                               *
 *                                                                  *
 *  Copyright:                                                      *
 *      pfm2afm - Copyright (C) IBM Corp., 1991                     *
 *                                                                  *
 *      This code is released for public use as long as the         *
 *      copyright remains intact.  This code is provided asis       *
 *      without any warrenties, express or implied.                 *
 *                                                                  *
 *  Notes:                                                          *
 *      1. Much of the information in the original .afm file is     *
 *         lost when the .pfm file is created, and thus cannot be   *
 *         reconstructed by this utility.  This is especially true  *
 *         of data for characters not in the Windows character set. *
 *                                                                  *
 *      2. This module is coded to be compiled by the MSC 6.0.      *
 *         For other compilers, be careful of the packing of the    *
 *         PFM structure.                                           *
 *                                                                  *
 ********************************************************************/
#ifndef ATARI
#include <libriaries/dos.h>  /* For the return code #defines */
#else
#include <portab.h>
#define VOID void
#define RETURN_OK	0
#define RETURN_WARN	1
#define RETURN_ERROR	2
#define REGISTER
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "pfm2afm.h"

#define BUFSIZE 4096

/*
 *  Function Prototypes
 */
VOID    help        (VOID);
VOID    parseargs   (int, UBYTE **);
VOID    openpfm     (VOID);
VOID    openafm     (VOID);
VOID    putheader   (VOID);
VOID    putchartab  (VOID);
VOID    outchar     (int, UWORD, UBYTE *);
VOID    putkerntab  (KERN *, UWORD);
VOID    puttrailer  (VOID);
VOID    outval      (int);
VOID    revbyt      (UBYTE *, int);

/*
 *  GLOBAL VARIABLES
 */
FILE   *inf;                 /* INPUT FILE        */
FILE   *outf;                /* OUTPUT FILE       */
UBYTE   mmsg[256];           /* BUFFER FOR MSGS   */
UBYTE   infname[272];        /* INPUT FILE NAME   */
UBYTE   outfname[272];       /* OUTPUT FILE NAME  */
UBYTE   buffer[BUFSIZE];     /* .PFM READ BUFFER  */
PFM     pfm;                 /* .PFM HEADER       */
PSX     psx;                 /* METRICS EXTENSION */

UBYTE   debugflag;           /* DEBUG INFORMATION FLAG */
UBYTE   allflag;
UBYTE   isMono;              /* Font is mono-spaced */

/*
 *  DO THE FUNCTION
 */
int main(int argc,UBYTE *argv[])
{
    UWORD pairs;

    /* PARSE ARGUMENTS */
    parseargs(argc, argv);

    /* OPEN AND CHECK INPUT FILE */
    openpfm();

    /* MAKE OUTPUT FILE NAME AND OPEN */
    openafm();

    /* PUT OUT HEADER INFORMATION */
    putheader();

    /* PUT OUT CHARACTER TABLE */
    putchartab();

    /* PUT OUT KERNING TABLE */
    if (pfm.kernpairs)
    {   memcpy(&pairs, buffer+pfm.kernpairs, sizeof(pairs));
        revbyt((UBYTE *)&pairs, sizeof(pairs));
        putkerntab((KERN *)(buffer+pfm.kernpairs+2), pairs);
    }

    /* PUT OUT TRAILER LINE */
    puttrailer();

    return(RETURN_OK);
}

/*
 *  PUT OUT NORMAL HELP
 */
VOID  help (VOID)
{
    puts("\nPFM2AFM - Convert Windows .PFM to .AFM - Version 1.0\n");
    puts("This utility converts Windows .PFM files for Adobe type 1 fonts");
    puts("to .AFM files for use on OS/2.  This allows fonts created for");
    puts("Windows, and shipped without the .AFM file to be used on OS/2.\n");
    puts("PFM2AFM  infile  [outfile]  -opts");
    puts("    The extension .PFM is added to the infile if it has none.");
    puts("    The outfile is defaulted from the input file name.");
    puts("    -a = All codepoints in range\n");
    puts("Note that .PFM files are missing some of the data necessary to");
    puts("construct .AFM files, so the conversion may not be perfect.\n");
    puts("Ken Borgendale  -  kwb@betasvm2.vnet.ibm.com\n");
    puts("Ported to Amiga by Kevin Ross  -  GEnie: K.ROSS12  BIX: kross\n");
    exit (RETURN_WARN);
}


/*
 *  Parse arguments.  This is the full arg treatment, which is sort of
 *  overkill for one option, but it allows more to be added later.
 */
VOID  parseargs (argc, argv)
int              argc;
UBYTE                 *argv[];
{
    UBYTE swchar, *argp;
    int argcnt, filecnt;

    argcnt = 1;
    filecnt = 0;

    /* READ THE ARGUMENTS AND DECIDE WHAT WE ARE DOING */
    while (argcnt<argc)
    {   argp = argv[argcnt];

        switch(argp[0])
        {
            /* PROCESS SWITCHES.  FILES MAY NOT START WITH '-' */
            case OPTSEP:
                swchar = (UBYTE)tolower(argp[1]);
                argp += 2;
                switch (swchar)
                {
                    /* ALL CODEPOINTS */
                    case 'a':
                        allflag = 0;
                        break;

                    /* DEBUG OPTION */
                    case 'd':
                        debugflag = 1;
                        break;

                    default:
                        fputs("Unknown option: ", stderr);
                        fputs(argp-2, stderr);
                        fputc('\n', stderr);
                }
                break;

            case '?':
                help();      /* DOES NOT RETURN */

            default:
                switch(++filecnt)
                {   case 1:
                        strcpy(infname, argp);
                        break;
                    case 2:
                        strcpy(outfname, argp);
                        break;
                    default:
                        fputs("Extra parameter ignored: ", stderr);
                        fputs(argp, stderr);
                        fputc('\n', stderr);
                }
        }
        argcnt++;
    }

    /* WE REQUIRE THE INPUT FILE NAME */
    if (!filecnt)
        help();
}


/*
 *  OPEN THE .PFM FILE AND CHECK IT
 */
VOID  openpfm(VOID)
{
    UBYTE *cp;
    int len;

    /* CHECK FOR A FILE EXTENSION */
    cp = infname + strlen(infname) - 1;
    while (cp >= infname && *cp != '.' && *cp != '/' && *cp != ':')
        cp--;
    if (*cp != '.')
        strcat(infname, ".pfm");

    /* OPEN THE FILE */
    inf = fopen(infname, "rb");
    if (!inf)
    {   strcpy(mmsg, "Unable to open input file \"");
        strcat(mmsg, infname);
        strcat(mmsg, "\"");
        perror(mmsg);
        exit(RETURN_ERROR);
    }

    /* READ THE FILE */
    len = fread(buffer, 1, BUFSIZE, inf);
    if (len < 256 || len == BUFSIZE)
    {   strcpy(mmsg, "Input file \"");
        strcat(mmsg, infname);
        strcat(mmsg, "\" read error");
        perror(mmsg);
        exit(RETURN_ERROR);
    }

    /* UNPACK RECORD INTO PFM STRUCT */
    cp = buffer;
    memcpy(&pfm.vers,      cp, sizeof(pfm.vers));      cp += sizeof(pfm.vers);
    memcpy(&pfm.len,       cp, sizeof(pfm.len));       cp += sizeof(pfm.len);
    memcpy(&pfm.copyright, cp, sizeof(pfm.copyright)); cp += sizeof(pfm.copyright);
    memcpy(&pfm.type,      cp, sizeof(pfm.type));      cp += sizeof(pfm.type);
    memcpy(&pfm.points,    cp, sizeof(pfm.points));    cp += sizeof(pfm.points);
    memcpy(&pfm.verres,    cp, sizeof(pfm.verres));    cp += sizeof(pfm.verres);
    memcpy(&pfm.horres,    cp, sizeof(pfm.horres));    cp += sizeof(pfm.horres);
    memcpy(&pfm.ascent,    cp, sizeof(pfm.ascent));    cp += sizeof(pfm.ascent);
    memcpy(&pfm.intleading,cp, sizeof(pfm.intleading));cp += sizeof(pfm.intleading);
    memcpy(&pfm.extleading,cp, sizeof(pfm.extleading));cp += sizeof(pfm.extleading);
    memcpy(&pfm.italic,    cp, sizeof(pfm.italic));    cp += sizeof(pfm.italic);
    memcpy(&pfm.uline,     cp, sizeof(pfm.uline));     cp += sizeof(pfm.uline);
    memcpy(&pfm.overs,     cp, sizeof(pfm.overs));     cp += sizeof(pfm.overs);
    memcpy(&pfm.weight,    cp, sizeof(pfm.weight));    cp += sizeof(pfm.weight);
    memcpy(&pfm.charset,   cp, sizeof(pfm.charset));   cp += sizeof(pfm.charset);
    memcpy(&pfm.pixwidth,  cp, sizeof(pfm.pixwidth));  cp += sizeof(pfm.pixwidth);
    memcpy(&pfm.pixheight, cp, sizeof(pfm.pixheight)); cp += sizeof(pfm.pixheight);
    memcpy(&pfm.kind,      cp, sizeof(pfm.kind));      cp += sizeof(pfm.kind);
    memcpy(&pfm.avgwidth,  cp, sizeof(pfm.avgwidth));  cp += sizeof(pfm.avgwidth);
    memcpy(&pfm.maxwidth,  cp, sizeof(pfm.maxwidth));  cp += sizeof(pfm.maxwidth);
    memcpy(&pfm.firstchar, cp, sizeof(pfm.firstchar)); cp += sizeof(pfm.firstchar);
    memcpy(&pfm.lastchar,  cp, sizeof(pfm.lastchar));  cp += sizeof(pfm.lastchar);
    memcpy(&pfm.defchar,   cp, sizeof(pfm.defchar));   cp += sizeof(pfm.defchar);
    memcpy(&pfm.brkchar,   cp, sizeof(pfm.brkchar));   cp += sizeof(pfm.brkchar);
    memcpy(&pfm.widthby,   cp, sizeof(pfm.widthby));   cp += sizeof(pfm.widthby);
    memcpy(&pfm.device,    cp, sizeof(pfm.device));    cp += sizeof(pfm.device);
    memcpy(&pfm.face,      cp, sizeof(pfm.face));      cp += sizeof(pfm.face);
    memcpy(&pfm.bits,      cp, sizeof(pfm.bits));      cp += sizeof(pfm.bits);
    memcpy(&pfm.bitoff,    cp, sizeof(pfm.bitoff));    cp += sizeof(pfm.bitoff);
    memcpy(&pfm.extlen,    cp, sizeof(pfm.extlen));    cp += sizeof(pfm.extlen);
    memcpy(&pfm.psext,     cp, sizeof(pfm.psext));     cp += sizeof(pfm.psext);
    memcpy(&pfm.chartab,   cp, sizeof(pfm.chartab));   cp += sizeof(pfm.chartab);
    cp += sizeof(pfm.res1);
    memcpy(&pfm.kernpairs, cp, sizeof(pfm.kernpairs)); cp += sizeof(pfm.kernpairs);
    cp += sizeof(pfm.res2);
    memcpy(&pfm.fontname,  cp, sizeof(pfm.fontname));  cp += sizeof(pfm.fontname);

    /*
     *  Adjust ordering of bytes in binary values
     *  68000 orders values MSB to LSB  --  8086 orders values LSB to MSB
     */
    revbyt((UBYTE *)&pfm.vers,       sizeof(pfm.vers));
    revbyt((UBYTE *)&pfm.len,        sizeof(pfm.len));
    revbyt((UBYTE *)&pfm.type,       sizeof(pfm.type));
    revbyt((UBYTE *)&pfm.points,     sizeof(pfm.points));
    revbyt((UBYTE *)&pfm.verres,     sizeof(pfm.verres));
    revbyt((UBYTE *)&pfm.horres,     sizeof(pfm.horres));
    revbyt((UBYTE *)&pfm.ascent,     sizeof(pfm.ascent));
    revbyt((UBYTE *)&pfm.intleading, sizeof(pfm.intleading));
    revbyt((UBYTE *)&pfm.extleading, sizeof(pfm.extleading));
    revbyt((UBYTE *)&pfm.weight,     sizeof(pfm.weight));
    revbyt((UBYTE *)&pfm.pixwidth,   sizeof(pfm.pixwidth));
    revbyt((UBYTE *)&pfm.pixheight,  sizeof(pfm.pixheight));
    revbyt((UBYTE *)&pfm.avgwidth,   sizeof(pfm.avgwidth));
    revbyt((UBYTE *)&pfm.maxwidth,   sizeof(pfm.maxwidth));
    revbyt((UBYTE *)&pfm.widthby,    sizeof(pfm.widthby));
    revbyt((UBYTE *)&pfm.device,     sizeof(pfm.device));
    revbyt((UBYTE *)&pfm.face,       sizeof(pfm.face));
    revbyt((UBYTE *)&pfm.bits,       sizeof(pfm.bits));
    revbyt((UBYTE *)&pfm.bitoff,     sizeof(pfm.bitoff));
    revbyt((UBYTE *)&pfm.extlen,     sizeof(pfm.extlen));
    revbyt((UBYTE *)&pfm.psext,      sizeof(pfm.psext));
    revbyt((UBYTE *)&pfm.chartab,    sizeof(pfm.chartab));
    revbyt((UBYTE *)&pfm.kernpairs,  sizeof(pfm.kernpairs));
    revbyt((UBYTE *)&pfm.fontname,   sizeof(pfm.fontname));

    /* DO CONSISTENCY CHECK */
    if (len != (int)pfm.len    /* CHECK LENGTH FIELD MATCHES FILE LENGTH */
    && pfm.extlen != 30        /* CHECK LENGTH OF PostScript EXTENSION   */
    && pfm.fontname > 75 && pfm.fontname < 512)   /* FONT NAME SPECIFIED */
    {   fputs("Not a valid Windows type 1 .PFM file - ", stderr);
        fputs(infname, stderr);
        fputc('\n', stderr);
        exit(RETURN_ERROR);
    }
}

/*
 *  CREATE THE .AFM FILE
 */
VOID  openafm(VOID)
{
    UBYTE *cp;

    /* ADD .AFM IF THERE IS NONE */
    if (!*outfname)
    {   strcpy(outfname, infname);
        cp = outfname+strlen(outfname)-1;
        while (cp >= outfname && *cp != '.' && *cp != '/' && *cp != ':')
           cp--;
        if (*cp == '.')
            *cp = 0;
        strcat(outfname, ".afm");
    }

    /* OPEN THE FILE */
    outf = fopen(outfname, "w");
    if (!outf)
    {   strcpy(mmsg, "Unable to open output file \"");
        strcat(mmsg, outfname);
        strcat(mmsg, "\"");
        perror(mmsg);
        exit(RETURN_ERROR);
    }
}

/*
 *  PUT OUT THE HEADER OF THE .AFM FILE
 */
VOID  putheader(VOID)
{
    UBYTE * cp;

    fputs("StartFontMetrics 2.0\n", outf);
    if (pfm.copyright[0])
    {   fputs("Comment ", outf);
        fputs(pfm.copyright, outf);
        fputc('\n', outf);
    }
    fputs("FontName ", outf);
    fputs(buffer+pfm.fontname, outf);
    fputs("\nEncodingScheme ", outf);
    if (pfm.charset)
        fputs("FontSpecific\n", outf);
    else
        fputs("AdobeStandardEncoding\n", outf);

    /*
     * The .pfm is missing full name, so construct from font name by
     * changing the hyphen to a space.  This actually works in a lot
     * of cases.
     */
    fputs("FullName ", outf);
    cp = buffer+pfm.fontname;
    while (*cp)
    {   if (*cp == '-')
            *cp = ' ';
        fputc(*cp, outf);
        cp++;
    }
    if (pfm.face)
    {   fputs("\nFamilyName ", outf);
        fputs(buffer+pfm.face, outf);
    }

    fputs("\nWeight ", outf);
    if (pfm.weight > 475)
        fputs("Bold", outf);
    else if (pfm.weight < 325 && pfm.weight)
        fputs("Light", outf);
    else
        fputs("Medium", outf);

    /*
     *  The mono flag in the pfm actually indicates whether there is a
     *  table of font widths, not if they are all the same.
     */
    fputs("\nIsFixedPitch ", outf);
    if (!(pfm.kind & 1)                   /* FLAG FOR MONO */
    || pfm.avgwidth == pfm.maxwidth)      /* AVG WIDTH = MAX WIDTH */
    {   fputs("true", outf);
        isMono = 1;
    }
    else
    {   fputs("false", outf);
        isMono = 0;
    }

    /*
     * The font bounding box is lost, but try to reconstruct it.
     * Much of this is just guess work.  The bounding box is required in
     * the .afm, but is not used by the PM font installer.
     */

    /* UNPACK RECORD INTO PSX STRUCT */
    memcpy(&psx, buffer+pfm.psext, sizeof(psx));
    revbyt((UBYTE *)&psx.capheight, sizeof(psx.capheight));
    revbyt((UBYTE *)&psx.xheight,   sizeof(psx.xheight));
    revbyt((UBYTE *)&psx.ascender,  sizeof(psx.ascender));
    revbyt((UBYTE *)&psx.descender, sizeof(psx.descender));

    fputs("\nFontBBox", outf);
    if (isMono)                   /* JUST GUESS AT LEFT BOUNDS */
        outval(-20);
    else
        outval(-100);

    /*
     * Some .PFMs give the descender as a positive value,
     * and others give it as negative...
     */
    outval(-(abs(psx.descender)+5));

    outval(pfm.maxwidth+10);
    outval(pfm.ascent+5);

    /*
     * GIVE OTHER METRICS THAT WERE KEPT
     */
    fputs("\nCapHeight", outf);
    outval(psx.capheight);
    fputs("\nXHeight", outf);
    outval(psx.xheight);
    fputs("\nDescender", outf);
    outval(-abs(psx.descender));
    fputs("\nAscender", outf);
    outval(psx.ascender);
    fputc('\n', outf);
}

/*
 *  Put out the character tables.  According to the .afm spec, the
 *  characters must be put out sorted in encoding order.
 *
 *  Most Windows .pfm files have the characters in the range 20-ff in
 *  the Windows code page (819 + quotes).
 */
VOID  putchartab(VOID)
{
    int     count, i, j;
    UWORD   spwidth;
    UWORD * ctab, chartab[256];
    UBYTE   back[256];


    count = pfm.lastchar - pfm.firstchar + 1;

    /* UNPACK CHARACTER TABLE INTO CHARTAB */
    for (i = 0; i < count; i++)
    {   memcpy(&chartab[i], buffer + pfm.chartab + i*sizeof(UWORD),
            sizeof(UWORD));
        revbyt((UBYTE *)(&chartab[i]), sizeof(UWORD));
    }

    /*
     * Compute the count by getting rid of non-existant chars.  This
     * is complicated by the fact that Windows encodes the .pfm file
     * with a space metric for non-existant chars.
     */
    memset(back, 0, 256);
    spwidth = 0;

    /* COMPUTE WIDTH OF SPACE */
    ctab = chartab;
    if (pfm.firstchar <= ' ' && pfm.lastchar >= ' ')
        spwidth = ctab[' ' - pfm.firstchar];

    /*
     *  Loop thru the chars, deleting those that we presume
     *  do not really exist.
     */
    if (!pfm.charset)
    {   for (i = pfm.firstchar; i <= (int)pfm.lastchar; i++)
        {   if (Win2PSStd[i])
                back[Win2PSStd[i]] = (UBYTE)i;
            else
            {   if (!allflag)
                {   if (*ctab == spwidth)
                    {
                        /* DEFAULT WIDTH */
                        if (!(WinClass[i] & 1))
                        {   *ctab = 0;
                            count--;
                        }
                    }
                    else
                    {
                        /* NOT DEFAULT WIDTH */
                        if (!WinClass[i])
                        {   *ctab = 0;
                            count--;
                        }
                    }
                }
            }
            ctab++;
        }
    }

    /* PUT OUT THE HEADER */
    fputs("StartCharMetrics", outf);
    outval(count);
    fputc('\n', outf);

    /*
     * If the charset is not the Windows standard, just put out
     * unnamed entries.
     */
    ctab = chartab;
    if (pfm.charset)
    {   for (i = pfm.firstchar; i <= (int)pfm.lastchar; i++)
        {   if (*ctab)
                outchar(i, *ctab, NULL);
            ctab++;
        }
    }
    else
    {   for (i = 0; i < 256; i++)
        {   j = back[i];
            if (j)
            {   outchar(i, ctab[j - pfm.firstchar], WinChars[j]);
                ctab[j - pfm.firstchar] = 0;
            }
        }
        /* PUT OUT ALL NON-ENCODED CHARS */
        for (i = pfm.firstchar; i <= (int)pfm.lastchar; i++)
        {   if (*ctab)
                outchar(-1, *ctab, WinChars[i]);
            ctab++;
        }
    }

    /* PUT OUT THE TRAILER */
    fputs("EndCharMetrics\n", outf);
}

/*
 *  Output a character entry
 */
VOID  outchar(code, width, name)
int           code;
UWORD               width;
UBYTE                     *name;
{
    fputs("C ", outf);
    outval(code);
    fputs(" ; WX ", outf);
    outval(width);
    if (name)
    {   fputs(" ; N ", outf);
        fputs(name, outf);
    }
    fputs(" ;\n", outf);
}

/*
 *  PUT OUT THE KERNING TABLES
 */
VOID  putkerntab(kerntab, kerncnt)
KERN            *kerntab;
UWORD                     kerncnt;
{
    int count, i;
    KERN  k, *kp;

    /* COUNT NON-ZERO KERN PAIRS */
    count = kerncnt;
    kp = kerntab;
    for (i = 0; i < kerncnt; i++)
    {   memcpy(&k, kp, 4);
        if (!k.kern)
            count--;
        kp++;
    }

    /* PUT OUT HEADER */
    fputs("StartKernData\nStartKernPairs", outf);
    outval(count);
    fputc('\n', outf);

    /* PUT OUT EACH NON-ZERO PAIR */
    kp = kerntab;
    while (kerncnt)
    {   memcpy(&k, kp, 4);
        if (k.kern)
        {   revbyt((UBYTE *)&k.kern, sizeof(k.kern));
            fputs("KPX ", outf);
            fputs(WinChars[k.first], outf);
            fputc(' ', outf);
            fputs(WinChars[k.second], outf);
            outval(k.kern);
            fputc('\n', outf);
        }
        kp++;
        kerncnt--;
    }

    /* PUT OUT TRAILER */
    fputs("EndKernPairs\nEndKernData\n", outf);
}

/*
 *  PUT OUT THE TRAILER OF THE .AFM FILE
 */
VOID  puttrailer(VOID)
{
    fputs("EndFontMetrics\n", outf);
}

/*
 *  OUTPUT A DECIMAL VALUE
 */
VOID outval(v)
int         v;
{
    UBYTE chx[16];

    /* LATTICE EQUIV. OF itoa() */
    stci_d(chx, v);

    fputc(' ', outf);
    fputs(chx, outf);
}

/*
 *  REVERSE THE BYTES IN A BINARY VALUE
 */
VOID revbyt    (tp,len)
REGISTER UBYTE *tp;
int                len;
{
    REGISTER UBYTE *hp;
    UBYTE ch;

    hp = tp + len - 1;
    while (tp < hp)
    {   ch    = *tp;
        *tp++ = *hp;
        *hp-- = ch;
    }
}
