/*
 *  ANSI C code that converts a .pfb file to a PostScript file.
 *
 *	For a discussion of this code, see the
 *	September 2002 version of the Acumen Journal
 *	http://www.acumentraining.com/ajournal.asp.
 *
 *	Written by John Deubert, Acumen Training
 *
 *
 *	History
 *
 *	Sept 5, 2002	Fixed bug that would make the program fail if the pfb file
 *					did not end with an explicite end-of-file code. (Added an
 *					feof check.) Thanks to Tom Brodhead for stumbling onto this
 *					problem.
 *
 *	Mar 28, 2003	Fixed another bug (again, thanks to Tom Brodhead). Minor problem
 *					with shifting signed char's in the Binary to ASCII Hex converter.
 *					(Can't understand how this passed even my minimal testing.)
 */
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* ************************
 *	Constants and typedefs
 */
 
#define		kFontFileName		"COB_____.PFB"		// Source pfb file
#define		kDestFileName		"CourierBold.ps"	// Destination PS file
#define		kBigEndian			true				// Set to false if necessary


enum ErrCode {				// Error codes returned by procedures
	kErrNoErr,
	kErrFileOpenFailed,
	kErrMemory
};

enum	{					// Header codes indicating how to handle
	kTypeASCII = 1,			// each section in the .pfb file.
	kTypeBinary,
	kTypeEOF
};

enum { false, true };		// The usual

typedef	int boolean;

typedef struct	{
	char	firstByte;
	char	segmentType;
	long	length;
} SegmentHeader;


/* ************************
 *	Procedure Declarations
 */

int main(void);
boolean GetSegmentHeader(FILE *src, SegmentHeader *hdr);
enum ErrCode SendSegment(FILE *src, FILE *end, SegmentHeader *hdr);
void ReverseBytes(long *val);
char * ConvertToASCII(char *src, long count);


/* ************************
 *	Procedure Definitions
 */

int main(void)
{
	FILE			*srcFile;
	FILE			*destFile;
	SegmentHeader	hdr;
	boolean			notDone = true;
	enum ErrCode	err;
	
	// Open the source and destination files
	srcFile = fopen(kFontFileName, "rb");
	if (!srcFile)
		return kErrFileOpenFailed;
	
	destFile = fopen(kDestFileName, "wb");
	if (!destFile)	{
		fclose(srcFile);
		return kErrFileOpenFailed;
	}
	
	// Loop through all of the segments
	err = kErrNoErr;
	do	{
		notDone = GetSegmentHeader(srcFile, &hdr);
		if (notDone)
			err = SendSegment(srcFile, destFile, &hdr);
	} while (notDone && !err);
	
	fclose(srcFile);
	fclose(destFile);
	
	return err;
}


// Get the header of the next segment
boolean GetSegmentHeader(FILE *src, SegmentHeader *hdr)
{
	fread(&(hdr->firstByte), 1, 1, src);
	fread(&(hdr->segmentType), 1, 1, src);
	fread(&(hdr->length), 1, 4, src);
	
	// The length is always in the .pfb file low-byte-first
	if (kBigEndian)
		ReverseBytes(&(hdr->length));
		
	// Return 'true' if the segment type indicates EOF
	return ((hdr->segmentType != kTypeEOF) && !feof(src));
}


// This reverses the bytes in a long int.
// (I've never found a very elegant way to do this.)
void ReverseBytes(long *val)
{
	char temp;
	char *b;
	
	b = (char *)val;
	
	temp = b[3];
	b[3] = b[0];
	b[0] = temp;
	
	temp = b[2];
	b[2] = b[1];
	b[1] = temp;
}


// Send the data associated with the current segment.
enum ErrCode SendSegment(FILE *src, FILE *dest, SegmentHeader *hdr)
{
	char *srcData;
	
	// Allocate an appropriately-sized buffer
	srcData = (char *)malloc(hdr->length);
	if (!srcData)
		return kErrMemory;
	
	// Get the segment data, converting to ASCII if needed.
	fread(srcData, 1, hdr->length, src);
	if (hdr->segmentType == kTypeBinary)	{
		srcData = ConvertToASCII(srcData, hdr->length);
		hdr->length *= 2;
	}
	
	if (!srcData)
		return kErrMemory;
	
	// Send the data on its way.
	fwrite(srcData, 1, hdr->length, dest);
	free(srcData);
	return kErrNoErr;
}


// Converts binary to ASCII in-place.
// (Well, sort of. It reallocates the buffer, returning
// a pointer to the new, double-size buffer.)
// Changed 3/28/03: asc & bin became unsigned char *
char * ConvertToASCII(char *src, long count)
{
	unsigned char	*asc, *bin;
	long			i;
	static char		*numerals = "0123456789ABCDEF";
	
	src = (char *)realloc(src, 2 * count);
	if (!src)
		return src;
		
	bin = (unsigned char *)src + count - 1;
	asc = (unsigned char *)src + (2 * count - 1);
	
	for (i = count; i > 0; i--)	{
		*asc-- = numerals[*bin & 0x0f];
		*asc-- = numerals[*bin >> 4];
		bin--;
	}
	
	return src;
}
