/******************************************************************** * * * 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. * * * ********************************************************************/ /******************************************************************** * Modified: Russell Lang * * 1994-01-06 Version 1.1 * * Compiles with EMX/GCC * * Changed to AFM 3.0 * * Put PFM Copyright in Notice instead of Comment * * Added ItalicAngle * * Added UnderlinePosition * * Added UnderlineThickness * * * * Modified 1995-03-10 rjl (fixes from Norman Walsh) * * Dodge compiler bug when creating descender * ********************************************************************/ /******************************************************************** * Modified: Olivier Plathey * * 2002-08-10 Version 1.11 * * Compiles with MinGW * * Output Descender as negative value * * Removed double spaces in character entries * * * * 2005-03-12 Version 1.12 * * Doubled size of BUFSIZE * ********************************************************************/ #include #include #include #include "pfm2afm.h" #define BUFSIZE 8192 /* * Function Prototypes */ void help (void); void parseargs (int argc, uchar * * argv); void openpfm(void); void openafm(void); void putheader(void); void putchartab(void); void outchar(int code, ushort width, const uchar * name); void putkerntab(KERN * kerntab, int kerncnt); void puttrailer(void); void outval(int val); void outreal(float val); /* * Global variables */ FILE * inf; /* Input file */ FILE * outf; /* Output file */ uchar infname[272]; /* Input file name */ uchar outfname[272]; /* Output file name */ uchar * buffer; /* .pfm read buffer */ PFM * pfm; /* .pfm header */ PSX * psx; /* Metrics extension */ uchar debugflag; /* Debug information flag */ uchar allflag; uchar isMono; /* Font is mono-spaced */ /* * Do the function */ MAINENT main(int argc, uchar * *argv) { /* 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) { putkerntab((KERN *)(buffer+pfm->kernpairs+2), *(ushort *)(buffer+pfm->kernpairs)); } if (pfm->kerntrack) { /* rjl */ fprintf(stderr, "Ignoring track kern table\n"); } /* Put out trailer line */ puttrailer(); /* Cleanup */ if (buffer) free(buffer); fclose(inf); fclose(outf); return 0; } /* * Put out normal help */ void help (void) { puts("\npfm2afm - Convert Windows pfm to afm - Version 1.11\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"); puts("\nNote 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("Russell Lang - rjl@monu1.cc.monash.edu.au\n"); exit (1); } /* * 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 (int argc, uchar * * argv) { uchar swchar; int argcnt; int filecnt; uchar * argp; argcnt = 1; filecnt = 0; /* Read the arguments and decide what we are doing */ while (argcnt=infname && *cp!='.' && *cp!='\\' && *cp!='/' && *cp!=':') cp--; if (*cp!='.') strcat(infname, ".pfm"); /* Open the file */ inf = fopen(infname, "rb"); if (!inf) { fputs("Unable to open input file - ", stderr); fputs(infname, stderr); fputc('\n', stderr); exit(4); } /* Read the file */ buffer = malloc(BUFSIZE); len = fread(buffer, 1, BUFSIZE, inf); if (len<256 || len==BUFSIZE) { fputs("Input file read error - ", stderr); fputs(infname, stderr); fputc('\n', stderr); exit(6); } /* Do consistency check */ pfm = (PFM *) buffer; 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(6); } } /* * Create the .afm file */ void openafm(void) { uchar * cp; /* Add .pfm if there is none */ if (!*outfname) { strcpy(outfname, infname); cp = outfname+strlen(outfname)-1; while (cp >= outfname && *cp!='.' && *cp!='\\' && *cp!='/' && *cp!=':') cp--; if (*cp=='.') *cp=0; strcat(outfname, ".afm"); } /* Open the file */ outf = fopen(outfname, "w"); if (!outf) { fputs("Unable to open output file - ", stderr); fputs(outfname, stderr); fputc('\n', stderr); exit(5); } } /* * Put out the header of the .afm file */ void putheader(void) { uchar * cp; int temp; /* rjl 1995-03-10 */ fputs("StartFontMetrics 3.0\n", outf); if (*pfm->copyright) { fputs("Notice ", 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. */ psx = (PSX *)(buffer+pfm->psext); fputs("\nFontBBox", outf); if (isMono) outval(-20); /* Just guess at left bounds */ else outval(-100); temp = psx->descender; /* rjl 1995-03-10 */ temp = -(temp+5); /* rjl 1995-03-10 */ outval(temp); /* Descender is given as positive value */ /* rjl 1995-03-10 */ /* outval(-(psx->descender+5)); /* Descender is given as positive value */ outval(pfm->maxwidth+10); outval(pfm->ascent+5); /* * Give other metrics that were kept */ fputs("\nCapHeight", outf); outval((int)psx->capheight); fputs("\nXHeight", outf); outval((int)psx->xheight); fputs("\nDescender", outf); outval((int)-psx->descender); /* output negative value*/ fputs("\nAscender", outf); /* outval((int)psx->ascender); */ outval((int)pfm->ascent); /* rjl */ /* extra keys added by rjl */ if (psx->len >= sizeof(psx)) { fputs("\nItalicAngle", outf); outreal(psx->slant/10.0); fputs("\nUnderlinePosition", outf); outval((int)-psx->underlineoffset); fputs("\nUnderlineThickness", outf); outval((int)psx->underlinewidth); } 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; ushort spwidth; ushort * ctab; uchar back[256]; /* * 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); count = pfm->lastchar - pfm->firstchar + 1; spwidth = 0; /* Compute width of space */ ctab = (ushort *)(buffer+pfm->chartab); if (pfm->firstchar>=' ' && pfm->lastchar<=' ') { spwidth = ctab[' '-pfm->firstchar]; } if (!pfm->charset) { /* * Loop thru the chars, deleting those that we presume * do not really exist. */ for (i=pfm->firstchar; i<=(int)pfm->lastchar; i++) { if (Win2PSStd[i]) { back[Win2PSStd[i]] = (uchar)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); /* Put out all encoded chars */ if (pfm->charset) { /* * If the charset is not the Windows standard, just put out * unnamed entries. */ ctab = (ushort *)(buffer+pfm->chartab); for (i=pfm->firstchar; i<=(int)pfm->lastchar; i++) { if (*ctab) { outchar(i, *ctab, NULL); } ctab++; } } else { ctab = (ushort *)(buffer+pfm->chartab); 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 */ ctab = (ushort *)(buffer+pfm->chartab); 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(int code, ushort width, const uchar * 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(KERN * kerntab, int kerncnt) { int count, i; KERN * kp; /* Count non-zero kern pairs */ count = kerncnt; kp = kerntab; for (i=0; ikern) count--; kp++; } /* Put out header */ fputs("StartKernData\nStartKernPairs", outf); outval(count); fputc('\n', outf); /* Put out each non-zero pair */ kp = kerntab; while (kerncnt) { if (kp->kern) { fputs("KPX ", outf); fputs(WinChars[kp->first], outf); fputc(' ', outf); fputs(WinChars[kp->second], outf); outval((int)kp->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(int v) { char chx[16]; #ifdef unix sprintf(chx, "%d", v); /* rjl 1995-03-10 */ #else itoa(v, chx, 10); #endif fputc(' ', outf); fputs(chx, outf); } /* * Output a real value */ void outreal(float v) { fprintf(outf," %g",v); }