/******************************************************************** * * * 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 /* For the return code #defines */ #else #include #define VOID void #define RETURN_OK 0 #define RETURN_WARN 1 #define RETURN_ERROR 2 #define REGISTER #endif #include #include #include #include #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= 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; } }