home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 2 / crawlyvol2.bin / apps / dtp / pfm2afm / pfm2afm.c < prev    next >
C/C++ Source or Header  |  1992-10-27  |  21KB  |  666 lines

  1. /********************************************************************
  2.  *                                                                  *
  3.  *  Title:  pfm2afm - Convert Windows .pfm files to .afm files      *
  4.  *                                                                  *
  5.  *  Author: Ken Borgendale   10/9/91  Version 1.0                   *
  6.  *                                                                  *
  7.  *  Function:                                                       *
  8.  *      Convert a Windows .pfm (Printer Font Metrics) file to a     *
  9.  *      .afm (Adobe Font Metrics) file.  The purpose of this is     *
  10.  *      to allow fonts put out for Windows to be used with OS/2.    *
  11.  *                                                                  *
  12.  *  Syntax:                                                         *
  13.  *      pfm2afm  infile  [outfile] -a                               *
  14.  *                                                                  *
  15.  *  Copyright:                                                      *
  16.  *      pfm2afm - Copyright (C) IBM Corp., 1991                     *
  17.  *                                                                  *
  18.  *      This code is released for public use as long as the         *
  19.  *      copyright remains intact.  This code is provided asis       *
  20.  *      without any warrenties, express or implied.                 *
  21.  *                                                                  *
  22.  *  Notes:                                                          *
  23.  *      1. Much of the information in the original .afm file is     *
  24.  *         lost when the .pfm file is created, and thus cannot be   *
  25.  *         reconstructed by this utility.  This is especially true  *
  26.  *         of data for characters not in the Windows character set. *
  27.  *                                                                  *
  28.  *      2. This module is coded to be compiled by the MSC 6.0.      *
  29.  *         For other compilers, be careful of the packing of the    *
  30.  *         PFM structure.                                           *
  31.  *                                                                  *
  32.  ********************************************************************/
  33. #ifndef ATARI
  34. #include <libriaries/dos.h>  /* For the return code #defines */
  35. #else
  36. #include <portab.h>
  37. #define VOID void
  38. #define RETURN_OK    0
  39. #define RETURN_WARN    1
  40. #define RETURN_ERROR    2
  41. #define REGISTER
  42. #endif
  43. #include <stdio.h>
  44. #include <string.h>
  45. #include <stdlib.h>
  46. #include <ctype.h>
  47.  
  48. #include "pfm2afm.h"
  49.  
  50. #define BUFSIZE 4096
  51.  
  52. /*
  53.  *  Function Prototypes
  54.  */
  55. VOID    help        (VOID);
  56. VOID    parseargs   (int, UBYTE **);
  57. VOID    openpfm     (VOID);
  58. VOID    openafm     (VOID);
  59. VOID    putheader   (VOID);
  60. VOID    putchartab  (VOID);
  61. VOID    outchar     (int, UWORD, UBYTE *);
  62. VOID    putkerntab  (KERN *, UWORD);
  63. VOID    puttrailer  (VOID);
  64. VOID    outval      (int);
  65. VOID    revbyt      (UBYTE *, int);
  66.  
  67. /*
  68.  *  GLOBAL VARIABLES
  69.  */
  70. FILE   *inf;                 /* INPUT FILE        */
  71. FILE   *outf;                /* OUTPUT FILE       */
  72. UBYTE   mmsg[256];           /* BUFFER FOR MSGS   */
  73. UBYTE   infname[272];        /* INPUT FILE NAME   */
  74. UBYTE   outfname[272];       /* OUTPUT FILE NAME  */
  75. UBYTE   buffer[BUFSIZE];     /* .PFM READ BUFFER  */
  76. PFM     pfm;                 /* .PFM HEADER       */
  77. PSX     psx;                 /* METRICS EXTENSION */
  78.  
  79. UBYTE   debugflag;           /* DEBUG INFORMATION FLAG */
  80. UBYTE   allflag;
  81. UBYTE   isMono;              /* Font is mono-spaced */
  82.  
  83. /*
  84.  *  DO THE FUNCTION
  85.  */
  86. int main(int argc,UBYTE *argv[])
  87. {
  88.     UWORD pairs;
  89.  
  90.     /* PARSE ARGUMENTS */
  91.     parseargs(argc, argv);
  92.  
  93.     /* OPEN AND CHECK INPUT FILE */
  94.     openpfm();
  95.  
  96.     /* MAKE OUTPUT FILE NAME AND OPEN */
  97.     openafm();
  98.  
  99.     /* PUT OUT HEADER INFORMATION */
  100.     putheader();
  101.  
  102.     /* PUT OUT CHARACTER TABLE */
  103.     putchartab();
  104.  
  105.     /* PUT OUT KERNING TABLE */
  106.     if (pfm.kernpairs)
  107.     {   memcpy(&pairs, buffer+pfm.kernpairs, sizeof(pairs));
  108.         revbyt((UBYTE *)&pairs, sizeof(pairs));
  109.         putkerntab((KERN *)(buffer+pfm.kernpairs+2), pairs);
  110.     }
  111.  
  112.     /* PUT OUT TRAILER LINE */
  113.     puttrailer();
  114.  
  115.     return(RETURN_OK);
  116. }
  117.  
  118. /*
  119.  *  PUT OUT NORMAL HELP
  120.  */
  121. VOID  help (VOID)
  122. {
  123.     puts("\nPFM2AFM - Convert Windows .PFM to .AFM - Version 1.0\n");
  124.     puts("This utility converts Windows .PFM files for Adobe type 1 fonts");
  125.     puts("to .AFM files for use on OS/2.  This allows fonts created for");
  126.     puts("Windows, and shipped without the .AFM file to be used on OS/2.\n");
  127.     puts("PFM2AFM  infile  [outfile]  -opts");
  128.     puts("    The extension .PFM is added to the infile if it has none.");
  129.     puts("    The outfile is defaulted from the input file name.");
  130.     puts("    -a = All codepoints in range\n");
  131.     puts("Note that .PFM files are missing some of the data necessary to");
  132.     puts("construct .AFM files, so the conversion may not be perfect.\n");
  133.     puts("Ken Borgendale  -  kwb@betasvm2.vnet.ibm.com\n");
  134.     puts("Ported to Amiga by Kevin Ross  -  GEnie: K.ROSS12  BIX: kross\n");
  135.     exit (RETURN_WARN);
  136. }
  137.  
  138.  
  139. /*
  140.  *  Parse arguments.  This is the full arg treatment, which is sort of
  141.  *  overkill for one option, but it allows more to be added later.
  142.  */
  143. VOID  parseargs (argc, argv)
  144. int              argc;
  145. UBYTE                 *argv[];
  146. {
  147.     UBYTE swchar, *argp;
  148.     int argcnt, filecnt;
  149.  
  150.     argcnt = 1;
  151.     filecnt = 0;
  152.  
  153.     /* READ THE ARGUMENTS AND DECIDE WHAT WE ARE DOING */
  154.     while (argcnt<argc)
  155.     {   argp = argv[argcnt];
  156.  
  157.         switch(argp[0])
  158.         {
  159.             /* PROCESS SWITCHES.  FILES MAY NOT START WITH '-' */
  160.             case OPTSEP:
  161.                 swchar = (UBYTE)tolower(argp[1]);
  162.                 argp += 2;
  163.                 switch (swchar)
  164.                 {
  165.                     /* ALL CODEPOINTS */
  166.                     case 'a':
  167.                         allflag = 0;
  168.                         break;
  169.  
  170.                     /* DEBUG OPTION */
  171.                     case 'd':
  172.                         debugflag = 1;
  173.                         break;
  174.  
  175.                     default:
  176.                         fputs("Unknown option: ", stderr);
  177.                         fputs(argp-2, stderr);
  178.                         fputc('\n', stderr);
  179.                 }
  180.                 break;
  181.  
  182.             case '?':
  183.                 help();      /* DOES NOT RETURN */
  184.  
  185.             default:
  186.                 switch(++filecnt)
  187.                 {   case 1:
  188.                         strcpy(infname, argp);
  189.                         break;
  190.                     case 2:
  191.                         strcpy(outfname, argp);
  192.                         break;
  193.                     default:
  194.                         fputs("Extra parameter ignored: ", stderr);
  195.                         fputs(argp, stderr);
  196.                         fputc('\n', stderr);
  197.                 }
  198.         }
  199.         argcnt++;
  200.     }
  201.  
  202.     /* WE REQUIRE THE INPUT FILE NAME */
  203.     if (!filecnt)
  204.         help();
  205. }
  206.  
  207.  
  208. /*
  209.  *  OPEN THE .PFM FILE AND CHECK IT
  210.  */
  211. VOID  openpfm(VOID)
  212. {
  213.     UBYTE *cp;
  214.     int len;
  215.  
  216.     /* CHECK FOR A FILE EXTENSION */
  217.     cp = infname + strlen(infname) - 1;
  218.     while (cp >= infname && *cp != '.' && *cp != '/' && *cp != ':')
  219.         cp--;
  220.     if (*cp != '.')
  221.         strcat(infname, ".pfm");
  222.  
  223.     /* OPEN THE FILE */
  224.     inf = fopen(infname, "rb");
  225.     if (!inf)
  226.     {   strcpy(mmsg, "Unable to open input file \"");
  227.         strcat(mmsg, infname);
  228.         strcat(mmsg, "\"");
  229.         perror(mmsg);
  230.         exit(RETURN_ERROR);
  231.     }
  232.  
  233.     /* READ THE FILE */
  234.     len = fread(buffer, 1, BUFSIZE, inf);
  235.     if (len < 256 || len == BUFSIZE)
  236.     {   strcpy(mmsg, "Input file \"");
  237.         strcat(mmsg, infname);
  238.         strcat(mmsg, "\" read error");
  239.         perror(mmsg);
  240.         exit(RETURN_ERROR);
  241.     }
  242.  
  243.     /* UNPACK RECORD INTO PFM STRUCT */
  244.     cp = buffer;
  245.     memcpy(&pfm.vers,      cp, sizeof(pfm.vers));      cp += sizeof(pfm.vers);
  246.     memcpy(&pfm.len,       cp, sizeof(pfm.len));       cp += sizeof(pfm.len);
  247.     memcpy(&pfm.copyright, cp, sizeof(pfm.copyright)); cp += sizeof(pfm.copyright);
  248.     memcpy(&pfm.type,      cp, sizeof(pfm.type));      cp += sizeof(pfm.type);
  249.     memcpy(&pfm.points,    cp, sizeof(pfm.points));    cp += sizeof(pfm.points);
  250.     memcpy(&pfm.verres,    cp, sizeof(pfm.verres));    cp += sizeof(pfm.verres);
  251.     memcpy(&pfm.horres,    cp, sizeof(pfm.horres));    cp += sizeof(pfm.horres);
  252.     memcpy(&pfm.ascent,    cp, sizeof(pfm.ascent));    cp += sizeof(pfm.ascent);
  253.     memcpy(&pfm.intleading,cp, sizeof(pfm.intleading));cp += sizeof(pfm.intleading);
  254.     memcpy(&pfm.extleading,cp, sizeof(pfm.extleading));cp += sizeof(pfm.extleading);
  255.     memcpy(&pfm.italic,    cp, sizeof(pfm.italic));    cp += sizeof(pfm.italic);
  256.     memcpy(&pfm.uline,     cp, sizeof(pfm.uline));     cp += sizeof(pfm.uline);
  257.     memcpy(&pfm.overs,     cp, sizeof(pfm.overs));     cp += sizeof(pfm.overs);
  258.     memcpy(&pfm.weight,    cp, sizeof(pfm.weight));    cp += sizeof(pfm.weight);
  259.     memcpy(&pfm.charset,   cp, sizeof(pfm.charset));   cp += sizeof(pfm.charset);
  260.     memcpy(&pfm.pixwidth,  cp, sizeof(pfm.pixwidth));  cp += sizeof(pfm.pixwidth);
  261.     memcpy(&pfm.pixheight, cp, sizeof(pfm.pixheight)); cp += sizeof(pfm.pixheight);
  262.     memcpy(&pfm.kind,      cp, sizeof(pfm.kind));      cp += sizeof(pfm.kind);
  263.     memcpy(&pfm.avgwidth,  cp, sizeof(pfm.avgwidth));  cp += sizeof(pfm.avgwidth);
  264.     memcpy(&pfm.maxwidth,  cp, sizeof(pfm.maxwidth));  cp += sizeof(pfm.maxwidth);
  265.     memcpy(&pfm.firstchar, cp, sizeof(pfm.firstchar)); cp += sizeof(pfm.firstchar);
  266.     memcpy(&pfm.lastchar,  cp, sizeof(pfm.lastchar));  cp += sizeof(pfm.lastchar);
  267.     memcpy(&pfm.defchar,   cp, sizeof(pfm.defchar));   cp += sizeof(pfm.defchar);
  268.     memcpy(&pfm.brkchar,   cp, sizeof(pfm.brkchar));   cp += sizeof(pfm.brkchar);
  269.     memcpy(&pfm.widthby,   cp, sizeof(pfm.widthby));   cp += sizeof(pfm.widthby);
  270.     memcpy(&pfm.device,    cp, sizeof(pfm.device));    cp += sizeof(pfm.device);
  271.     memcpy(&pfm.face,      cp, sizeof(pfm.face));      cp += sizeof(pfm.face);
  272.     memcpy(&pfm.bits,      cp, sizeof(pfm.bits));      cp += sizeof(pfm.bits);
  273.     memcpy(&pfm.bitoff,    cp, sizeof(pfm.bitoff));    cp += sizeof(pfm.bitoff);
  274.     memcpy(&pfm.extlen,    cp, sizeof(pfm.extlen));    cp += sizeof(pfm.extlen);
  275.     memcpy(&pfm.psext,     cp, sizeof(pfm.psext));     cp += sizeof(pfm.psext);
  276.     memcpy(&pfm.chartab,   cp, sizeof(pfm.chartab));   cp += sizeof(pfm.chartab);
  277.     cp += sizeof(pfm.res1);
  278.     memcpy(&pfm.kernpairs, cp, sizeof(pfm.kernpairs)); cp += sizeof(pfm.kernpairs);
  279.     cp += sizeof(pfm.res2);
  280.     memcpy(&pfm.fontname,  cp, sizeof(pfm.fontname));  cp += sizeof(pfm.fontname);
  281.  
  282.     /*
  283.      *  Adjust ordering of bytes in binary values
  284.      *  68000 orders values MSB to LSB  --  8086 orders values LSB to MSB
  285.      */
  286.     revbyt((UBYTE *)&pfm.vers,       sizeof(pfm.vers));
  287.     revbyt((UBYTE *)&pfm.len,        sizeof(pfm.len));
  288.     revbyt((UBYTE *)&pfm.type,       sizeof(pfm.type));
  289.     revbyt((UBYTE *)&pfm.points,     sizeof(pfm.points));
  290.     revbyt((UBYTE *)&pfm.verres,     sizeof(pfm.verres));
  291.     revbyt((UBYTE *)&pfm.horres,     sizeof(pfm.horres));
  292.     revbyt((UBYTE *)&pfm.ascent,     sizeof(pfm.ascent));
  293.     revbyt((UBYTE *)&pfm.intleading, sizeof(pfm.intleading));
  294.     revbyt((UBYTE *)&pfm.extleading, sizeof(pfm.extleading));
  295.     revbyt((UBYTE *)&pfm.weight,     sizeof(pfm.weight));
  296.     revbyt((UBYTE *)&pfm.pixwidth,   sizeof(pfm.pixwidth));
  297.     revbyt((UBYTE *)&pfm.pixheight,  sizeof(pfm.pixheight));
  298.     revbyt((UBYTE *)&pfm.avgwidth,   sizeof(pfm.avgwidth));
  299.     revbyt((UBYTE *)&pfm.maxwidth,   sizeof(pfm.maxwidth));
  300.     revbyt((UBYTE *)&pfm.widthby,    sizeof(pfm.widthby));
  301.     revbyt((UBYTE *)&pfm.device,     sizeof(pfm.device));
  302.     revbyt((UBYTE *)&pfm.face,       sizeof(pfm.face));
  303.     revbyt((UBYTE *)&pfm.bits,       sizeof(pfm.bits));
  304.     revbyt((UBYTE *)&pfm.bitoff,     sizeof(pfm.bitoff));
  305.     revbyt((UBYTE *)&pfm.extlen,     sizeof(pfm.extlen));
  306.     revbyt((UBYTE *)&pfm.psext,      sizeof(pfm.psext));
  307.     revbyt((UBYTE *)&pfm.chartab,    sizeof(pfm.chartab));
  308.     revbyt((UBYTE *)&pfm.kernpairs,  sizeof(pfm.kernpairs));
  309.     revbyt((UBYTE *)&pfm.fontname,   sizeof(pfm.fontname));
  310.  
  311.     /* DO CONSISTENCY CHECK */
  312.     if (len != (int)pfm.len    /* CHECK LENGTH FIELD MATCHES FILE LENGTH */
  313.     && pfm.extlen != 30        /* CHECK LENGTH OF PostScript EXTENSION   */
  314.     && pfm.fontname > 75 && pfm.fontname < 512)   /* FONT NAME SPECIFIED */
  315.     {   fputs("Not a valid Windows type 1 .PFM file - ", stderr);
  316.         fputs(infname, stderr);
  317.         fputc('\n', stderr);
  318.         exit(RETURN_ERROR);
  319.     }
  320. }
  321.  
  322. /*
  323.  *  CREATE THE .AFM FILE
  324.  */
  325. VOID  openafm(VOID)
  326. {
  327.     UBYTE *cp;
  328.  
  329.     /* ADD .AFM IF THERE IS NONE */
  330.     if (!*outfname)
  331.     {   strcpy(outfname, infname);
  332.         cp = outfname+strlen(outfname)-1;
  333.         while (cp >= outfname && *cp != '.' && *cp != '/' && *cp != ':')
  334.            cp--;
  335.         if (*cp == '.')
  336.             *cp = 0;
  337.         strcat(outfname, ".afm");
  338.     }
  339.  
  340.     /* OPEN THE FILE */
  341.     outf = fopen(outfname, "w");
  342.     if (!outf)
  343.     {   strcpy(mmsg, "Unable to open output file \"");
  344.         strcat(mmsg, outfname);
  345.         strcat(mmsg, "\"");
  346.         perror(mmsg);
  347.         exit(RETURN_ERROR);
  348.     }
  349. }
  350.  
  351. /*
  352.  *  PUT OUT THE HEADER OF THE .AFM FILE
  353.  */
  354. VOID  putheader(VOID)
  355. {
  356.     UBYTE * cp;
  357.  
  358.     fputs("StartFontMetrics 2.0\n", outf);
  359.     if (pfm.copyright[0])
  360.     {   fputs("Comment ", outf);
  361.         fputs(pfm.copyright, outf);
  362.         fputc('\n', outf);
  363.     }
  364.     fputs("FontName ", outf);
  365.     fputs(buffer+pfm.fontname, outf);
  366.     fputs("\nEncodingScheme ", outf);
  367.     if (pfm.charset)
  368.         fputs("FontSpecific\n", outf);
  369.     else
  370.         fputs("AdobeStandardEncoding\n", outf);
  371.  
  372.     /*
  373.      * The .pfm is missing full name, so construct from font name by
  374.      * changing the hyphen to a space.  This actually works in a lot
  375.      * of cases.
  376.      */
  377.     fputs("FullName ", outf);
  378.     cp = buffer+pfm.fontname;
  379.     while (*cp)
  380.     {   if (*cp == '-')
  381.             *cp = ' ';
  382.         fputc(*cp, outf);
  383.         cp++;
  384.     }
  385.     if (pfm.face)
  386.     {   fputs("\nFamilyName ", outf);
  387.         fputs(buffer+pfm.face, outf);
  388.     }
  389.  
  390.     fputs("\nWeight ", outf);
  391.     if (pfm.weight > 475)
  392.         fputs("Bold", outf);
  393.     else if (pfm.weight < 325 && pfm.weight)
  394.         fputs("Light", outf);
  395.     else
  396.         fputs("Medium", outf);
  397.  
  398.     /*
  399.      *  The mono flag in the pfm actually indicates whether there is a
  400.      *  table of font widths, not if they are all the same.
  401.      */
  402.     fputs("\nIsFixedPitch ", outf);
  403.     if (!(pfm.kind & 1)                   /* FLAG FOR MONO */
  404.     || pfm.avgwidth == pfm.maxwidth)      /* AVG WIDTH = MAX WIDTH */
  405.     {   fputs("true", outf);
  406.         isMono = 1;
  407.     }
  408.     else
  409.     {   fputs("false", outf);
  410.         isMono = 0;
  411.     }
  412.  
  413.     /*
  414.      * The font bounding box is lost, but try to reconstruct it.
  415.      * Much of this is just guess work.  The bounding box is required in
  416.      * the .afm, but is not used by the PM font installer.
  417.      */
  418.  
  419.     /* UNPACK RECORD INTO PSX STRUCT */
  420.     memcpy(&psx, buffer+pfm.psext, sizeof(psx));
  421.     revbyt((UBYTE *)&psx.capheight, sizeof(psx.capheight));
  422.     revbyt((UBYTE *)&psx.xheight,   sizeof(psx.xheight));
  423.     revbyt((UBYTE *)&psx.ascender,  sizeof(psx.ascender));
  424.     revbyt((UBYTE *)&psx.descender, sizeof(psx.descender));
  425.  
  426.     fputs("\nFontBBox", outf);
  427.     if (isMono)                   /* JUST GUESS AT LEFT BOUNDS */
  428.         outval(-20);
  429.     else
  430.         outval(-100);
  431.  
  432.     /*
  433.      * Some .PFMs give the descender as a positive value,
  434.      * and others give it as negative...
  435.      */
  436.     outval(-(abs(psx.descender)+5));
  437.  
  438.     outval(pfm.maxwidth+10);
  439.     outval(pfm.ascent+5);
  440.  
  441.     /*
  442.      * GIVE OTHER METRICS THAT WERE KEPT
  443.      */
  444.     fputs("\nCapHeight", outf);
  445.     outval(psx.capheight);
  446.     fputs("\nXHeight", outf);
  447.     outval(psx.xheight);
  448.     fputs("\nDescender", outf);
  449.     outval(-abs(psx.descender));
  450.     fputs("\nAscender", outf);
  451.     outval(psx.ascender);
  452.     fputc('\n', outf);
  453. }
  454.  
  455. /*
  456.  *  Put out the character tables.  According to the .afm spec, the
  457.  *  characters must be put out sorted in encoding order.
  458.  *
  459.  *  Most Windows .pfm files have the characters in the range 20-ff in
  460.  *  the Windows code page (819 + quotes).
  461.  */
  462. VOID  putchartab(VOID)
  463. {
  464.     int     count, i, j;
  465.     UWORD   spwidth;
  466.     UWORD * ctab, chartab[256];
  467.     UBYTE   back[256];
  468.  
  469.  
  470.     count = pfm.lastchar - pfm.firstchar + 1;
  471.  
  472.     /* UNPACK CHARACTER TABLE INTO CHARTAB */
  473.     for (i = 0; i < count; i++)
  474.     {   memcpy(&chartab[i], buffer + pfm.chartab + i*sizeof(UWORD),
  475.             sizeof(UWORD));
  476.         revbyt((UBYTE *)(&chartab[i]), sizeof(UWORD));
  477.     }
  478.  
  479.     /*
  480.      * Compute the count by getting rid of non-existant chars.  This
  481.      * is complicated by the fact that Windows encodes the .pfm file
  482.      * with a space metric for non-existant chars.
  483.      */
  484.     memset(back, 0, 256);
  485.     spwidth = 0;
  486.  
  487.     /* COMPUTE WIDTH OF SPACE */
  488.     ctab = chartab;
  489.     if (pfm.firstchar <= ' ' && pfm.lastchar >= ' ')
  490.         spwidth = ctab[' ' - pfm.firstchar];
  491.  
  492.     /*
  493.      *  Loop thru the chars, deleting those that we presume
  494.      *  do not really exist.
  495.      */
  496.     if (!pfm.charset)
  497.     {   for (i = pfm.firstchar; i <= (int)pfm.lastchar; i++)
  498.         {   if (Win2PSStd[i])
  499.                 back[Win2PSStd[i]] = (UBYTE)i;
  500.             else
  501.             {   if (!allflag)
  502.                 {   if (*ctab == spwidth)
  503.                     {
  504.                         /* DEFAULT WIDTH */
  505.                         if (!(WinClass[i] & 1))
  506.                         {   *ctab = 0;
  507.                             count--;
  508.                         }
  509.                     }
  510.                     else
  511.                     {
  512.                         /* NOT DEFAULT WIDTH */
  513.                         if (!WinClass[i])
  514.                         {   *ctab = 0;
  515.                             count--;
  516.                         }
  517.                     }
  518.                 }
  519.             }
  520.             ctab++;
  521.         }
  522.     }
  523.  
  524.     /* PUT OUT THE HEADER */
  525.     fputs("StartCharMetrics", outf);
  526.     outval(count);
  527.     fputc('\n', outf);
  528.  
  529.     /*
  530.      * If the charset is not the Windows standard, just put out
  531.      * unnamed entries.
  532.      */
  533.     ctab = chartab;
  534.     if (pfm.charset)
  535.     {   for (i = pfm.firstchar; i <= (int)pfm.lastchar; i++)
  536.         {   if (*ctab)
  537.                 outchar(i, *ctab, NULL);
  538.             ctab++;
  539.         }
  540.     }
  541.     else
  542.     {   for (i = 0; i < 256; i++)
  543.         {   j = back[i];
  544.             if (j)
  545.             {   outchar(i, ctab[j - pfm.firstchar], WinChars[j]);
  546.                 ctab[j - pfm.firstchar] = 0;
  547.             }
  548.         }
  549.         /* PUT OUT ALL NON-ENCODED CHARS */
  550.         for (i = pfm.firstchar; i <= (int)pfm.lastchar; i++)
  551.         {   if (*ctab)
  552.                 outchar(-1, *ctab, WinChars[i]);
  553.             ctab++;
  554.         }
  555.     }
  556.  
  557.     /* PUT OUT THE TRAILER */
  558.     fputs("EndCharMetrics\n", outf);
  559. }
  560.  
  561. /*
  562.  *  Output a character entry
  563.  */
  564. VOID  outchar(code, width, name)
  565. int           code;
  566. UWORD               width;
  567. UBYTE                     *name;
  568. {
  569.     fputs("C ", outf);
  570.     outval(code);
  571.     fputs(" ; WX ", outf);
  572.     outval(width);
  573.     if (name)
  574.     {   fputs(" ; N ", outf);
  575.         fputs(name, outf);
  576.     }
  577.     fputs(" ;\n", outf);
  578. }
  579.  
  580. /*
  581.  *  PUT OUT THE KERNING TABLES
  582.  */
  583. VOID  putkerntab(kerntab, kerncnt)
  584. KERN            *kerntab;
  585. UWORD                     kerncnt;
  586. {
  587.     int count, i;
  588.     KERN  k, *kp;
  589.  
  590.     /* COUNT NON-ZERO KERN PAIRS */
  591.     count = kerncnt;
  592.     kp = kerntab;
  593.     for (i = 0; i < kerncnt; i++)
  594.     {   memcpy(&k, kp, 4);
  595.         if (!k.kern)
  596.             count--;
  597.         kp++;
  598.     }
  599.  
  600.     /* PUT OUT HEADER */
  601.     fputs("StartKernData\nStartKernPairs", outf);
  602.     outval(count);
  603.     fputc('\n', outf);
  604.  
  605.     /* PUT OUT EACH NON-ZERO PAIR */
  606.     kp = kerntab;
  607.     while (kerncnt)
  608.     {   memcpy(&k, kp, 4);
  609.         if (k.kern)
  610.         {   revbyt((UBYTE *)&k.kern, sizeof(k.kern));
  611.             fputs("KPX ", outf);
  612.             fputs(WinChars[k.first], outf);
  613.             fputc(' ', outf);
  614.             fputs(WinChars[k.second], outf);
  615.             outval(k.kern);
  616.             fputc('\n', outf);
  617.         }
  618.         kp++;
  619.         kerncnt--;
  620.     }
  621.  
  622.     /* PUT OUT TRAILER */
  623.     fputs("EndKernPairs\nEndKernData\n", outf);
  624. }
  625.  
  626. /*
  627.  *  PUT OUT THE TRAILER OF THE .AFM FILE
  628.  */
  629. VOID  puttrailer(VOID)
  630. {
  631.     fputs("EndFontMetrics\n", outf);
  632. }
  633.  
  634. /*
  635.  *  OUTPUT A DECIMAL VALUE
  636.  */
  637. VOID outval(v)
  638. int         v;
  639. {
  640.     UBYTE chx[16];
  641.  
  642.     /* LATTICE EQUIV. OF itoa() */
  643.     stci_d(chx, v);
  644.  
  645.     fputc(' ', outf);
  646.     fputs(chx, outf);
  647. }
  648.  
  649. /*
  650.  *  REVERSE THE BYTES IN A BINARY VALUE
  651.  */
  652. VOID revbyt    (tp,len)
  653. REGISTER UBYTE *tp;
  654. int                len;
  655. {
  656.     REGISTER UBYTE *hp;
  657.     UBYTE ch;
  658.  
  659.     hp = tp + len - 1;
  660.     while (tp < hp)
  661.     {   ch    = *tp;
  662.         *tp++ = *hp;
  663.         *hp-- = ch;
  664.     }
  665. }
  666.