/*
 * ttindex.c
 *
 *		15 Oct. 1993  by I.Matsuda
 *		14 Nov. 1993  Slightly modified by I. Matsuda (Ver 0.2)
 *		19 Jan. 1994  UNIX version for VFlib.
 *		26 Jan. 1994  Slightly modified by I. Matsuda
 *		11 Feb. 1994  Slightly modified by I. Matsuda
 *		 9 May  1994  Supported shift-JIS encoding (Special thanks to iW)
 *		21 Oct. 1995  Supported new .ttc font used in WIN95. (Ver.0.4)
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if __TURBOC__
#  include <alloc.h>
#  include <io.h>
#endif
#include <fcntl.h>
#include "ttindex.h"

#ifndef SEEK_SET
#  define SEEK_SET 0
#endif

TTI_HEADER tti_header;
CharToIndexFormat4 C2Index;

#ifdef PROTOTYPE
int16   fget16(FILE *fp);
void    fput16(int16 num, FILE *fp);
int32   fget32(FILE *fp);
void    fput32(int32 num, FILE *fp);
void FAR *alloc_buff(unsigned int size);
void    ReadFontheader(int32 Offset, FILE *fp);
void    ReadMaxprofile(int32 Offset, FILE *fp);
void    ReadCharToIndexMap(int32 Offset, FILE *fp);
uint16  CharToIndex(uint16 ch, FILE *fp);
void    ShowFontname(int32 Offset, FILE *fp);
uint16  jis2uni(int ku, int ten);
uint16  jis2sjis(int ku, int ten);
void    usage(void);
uint16  (*codeconv)(int ku, int ten) = jis2uni;
uint16 jis2uni(int, int);
uint16 jis2sjis(int, int);
#endif

/* read 2 byte */
int16
fget16(fp)
FILE *fp;
{
	int16 c[2];
	int16 num;

	c[0] = (int16)getc(fp);
	c[1] = (int16)getc(fp);
	num =  (c[0] << 8) | (c[1] & 0xff);
	return (num);
}

/* write 2 byte */
void
fput16(num, fp)
int16 num;
FILE *fp;
{
	int c[2];

	c[0] = (num >> 8) & 0x00ff;
	c[1] = num & 0x00ff;
	fputc(c[0], fp);
	fputc(c[1], fp);
	return;
}

/* read 4 byte */
int32
fget32(fp)
FILE *fp;
{
	int32 num;
	int32 c[2];

	c[0] = (int32)fget16(fp);
	c[1] = (int32)fget16(fp);
	num =  (c[0] << 16) | (c[1] & 0xffff);
	return (num);
}

/* write 4 byte */
void
fput32(num, fp)
int32 num;
FILE *fp;
{
	uint16 c[2];

	c[0] = (num >> 16) & 0xffff;
	c[1] = num & 0xffff;
	fput16(c[0], fp);
	fput16(c[1], fp);
	return;
}

/* memory allocation */
void FAR *
alloc_buff(size)
unsigned size;
{
	void FAR *p;

	if ((p = (void FAR *)MALLOC(size)) == NULL) {
		fprintf(stderr, "Not enough memory.\n");
		exit(1);
	}
	return (p);
}

/* read Font Header table */
void
ReadFontheader(Offset, fp)
int32 Offset;
FILE *fp;
{
	long seekp;
	sfnt_FontHeader FontHeader;

	seekp = ftell(fp);
	fseek(fp, Offset, SEEK_SET);
	FontHeader.version = fget32(fp);
	FontHeader.fontRevision = fget32(fp);
	FontHeader.checkSumAdjustment = fget32(fp);
	FontHeader.magicNumber = fget32(fp);
	if (FontHeader.magicNumber != 0x5F0F3CF5L) {
		fprintf(stderr, "Invalid Magic Number in font header table 'head'.\n");
		exit(1);
	}
	FontHeader.flags = fget16(fp);
	FontHeader.unitsPerEm = fget16(fp);
	FontHeader.created.bc = fget32(fp);
	FontHeader.created.ad = fget32(fp);
	FontHeader.modified.bc = fget32(fp);
	FontHeader.modified.ad = fget32(fp);
	FontHeader.xMin = fget16(fp);
	FontHeader.yMin = fget16(fp);
	FontHeader.xMax = fget16(fp);
	FontHeader.yMax = fget16(fp);
	FontHeader.macStyle = fget16(fp);
	FontHeader.lowestRecPPEM = fget16(fp);
	FontHeader.fontDirectionHint = fget16(fp);
	FontHeader.indexToLocFormat = fget16(fp);
	FontHeader.glyphDataFormat = fget16(fp);
	tti_header.unitsPerEm = FontHeader.unitsPerEm;
	tti_header.xMin = FontHeader.xMin;
	tti_header.yMin = FontHeader.yMin;
	fseek(fp, seekp, SEEK_SET);
	return;
}

/* read Maximum Profile table */
void
ReadMaxprofile(Offset, fp)
int32 Offset;
FILE *fp;
{
	long seekp;
	sfnt_maxProfileTable MaxProfile;

	seekp = ftell(fp);
	fseek(fp, Offset, SEEK_SET);
	MaxProfile.version = fget32(fp);
	MaxProfile.numGlyphs = fget16(fp);
	MaxProfile.maxPoints = fget16(fp);
	MaxProfile.maxContours = fget16(fp);
	MaxProfile.maxCompositePoints = fget16(fp);
	MaxProfile.maxCompositeContours = fget16(fp);
	MaxProfile.maxElements = fget16(fp);
	MaxProfile.maxTwilightPoints = fget16(fp);
	MaxProfile.maxStorage = fget16(fp);
	MaxProfile.maxFunctionDefs = fget16(fp);
	MaxProfile.maxInstructionDefs = fget16(fp);
	MaxProfile.maxStackElements = fget16(fp);
	MaxProfile.maxSizeOfInstructions = fget16(fp);
	MaxProfile.maxComponentElements = fget16(fp);
	MaxProfile.maxComponentDepth = fget16(fp);
/*	if (MaxProfile.maxComponentElements > 1) {
		fprintf(stderr, "Sorry, can't use this font "
						"(composite glyph is not supported).\n");
		exit(1);
	}*/
	tti_header.maxPoints = MaxProfile.maxPoints;
	tti_header.maxContours = MaxProfile.maxContours;
	fseek(fp, seekp, SEEK_SET);
	return;
}

/* read cmap table */
void
ReadCharToIndexMap(Offset, fp)
int32 Offset;
FILE *fp;
{
	int i, j;
	int segCount;
	long seekp, seekpp;
	sfnt_char2IndexDirectory char2IndexDirectory;
	sfnt_platformEntry *platformEntry;
	sfnt_mappingTable mappingTable;

	seekp = ftell(fp);
	fseek(fp, Offset, SEEK_SET);
	char2IndexDirectory.version = fget16(fp);
	char2IndexDirectory.numTables = fget16(fp);
	platformEntry = char2IndexDirectory.platform;
	for (i = 0; i < char2IndexDirectory.numTables; i++) {
		platformEntry->platformID = fget16(fp);
		platformEntry->specificID = fget16(fp);
		platformEntry->offset = fget32(fp);
		seekpp = ftell(fp);
		fseek(fp, Offset + platformEntry->offset, SEEK_SET);
		mappingTable.format = fget16(fp);
		mappingTable.length = fget16(fp);
		mappingTable.version = fget16(fp);
		if (mappingTable.format == 4) {
			if (platformEntry->specificID == 2) codeconv = jis2sjis;
			C2Index.segCountX2 = fget16(fp);
			C2Index.searchRange = fget16(fp);
			C2Index.entrySelector = fget16(fp);
			C2Index.rangeShift = fget16(fp);
			C2Index.endCount = (uint16 FAR *)alloc_buff(C2Index.segCountX2);
			C2Index.startCount = (uint16 FAR *)alloc_buff(C2Index.segCountX2);
			C2Index.idDelta = (uint16 FAR *)alloc_buff(C2Index.segCountX2);
			C2Index.idRangeOffset = (uint16 FAR *)alloc_buff(C2Index.segCountX2);
			segCount = C2Index.segCountX2 / 2;
			for (j = 0; j < segCount; j++) {
				C2Index.endCount[j] = fget16(fp);
			}
			fget16(fp);
			for (j = 0; j < segCount; j++) {
				C2Index.startCount[j] = fget16(fp);
			}
			for (j = 0; j < segCount; j++) {
				C2Index.idDelta[j] = fget16(fp);
			}
			C2Index.idRangeBase = ftell(fp);
			for (j = 0; j < segCount; j++) {
				C2Index.idRangeOffset[j] = fget16(fp);
			}
			fseek(fp, seekp, SEEK_SET);
			return;
		}
		fseek(fp, seekpp, SEEK_SET);
	}
	fprintf(stderr,
		"Sorry, can't use this font (no Char2Index table of format 4).\n");
	exit(1);
}

/* character -> index code */
uint16
CharToIndex(ch, fp)
uint16 ch;
FILE *fp;
{
	int i, segCount, segment;
	uint16 Index;

	segment = -1;
	segCount = C2Index.segCountX2 / 2;
	for (i = 0; i < segCount; i++) {
		if (ch <= C2Index.endCount[i]) {
			if (ch >= C2Index.startCount[i]) {
				segment = i;
			}
			break;
		}
	}
	if (segment < 0) {
		/* no segment */
		Index = 0;
	} else if (C2Index.idRangeOffset[i] == 0) {
		Index = (ch + C2Index.idDelta[i]) & 0xffff;
	} else {
		Index = (C2Index.idRangeOffset[i] / 2 + ch - C2Index.startCount[i])
			& 0xffff;
		fseek(fp, C2Index.idRangeBase + (i + Index) * 2, SEEK_SET);
		Index = (fget16(fp) + C2Index.idDelta[i]) & 0xffff;
	}
	return (Index);
}

/* get font name */
void
ShowFontname(Offset, fp)
int32 Offset;
FILE *fp;
{
#define MAX_STR 256

	static char *nameID[] = {
		"Copyright", "Family",  "Subfamily",  "UniqueName",
		"FullName",  "Version", "Postscript", "Trademark"
	};
	sfnt_NamingTable NamingTable;
	sfnt_NameRecord NameRecord;
	int i, length;
	long seekp, seekpp, stringOffset;
	char str[MAX_STR];

	seekp = ftell(fp);
	fseek(fp, Offset, SEEK_SET);
	NamingTable.format = fget16(fp);
	NamingTable.count = fget16(fp);
	NamingTable.stringOffset = fget16(fp);
	for (i = 0; i < NamingTable.count; i++) {
		NameRecord.platformID = fget16(fp);
		NameRecord.specificID = fget16(fp);
		NameRecord.languageID = fget16(fp);
		NameRecord.nameID = fget16(fp);
		NameRecord.length = length = fget16(fp);
		NameRecord.offset = fget16(fp);
		if (NameRecord.platformID == plat_Macintosh) {	/* ShiftJIS */
#ifndef __MSDOS__
			if ((NameRecord.nameID != 0) &&
			    (NameRecord.nameID != 6)) break;
#endif
			if (length >= MAX_STR) length = MAX_STR - 1;
			seekpp = ftell(fp);
			stringOffset =
				Offset + NamingTable.stringOffset + NameRecord.offset;
			fseek(fp, stringOffset, SEEK_SET);
			fread(str, length, 1, fp);
			str[length] = '\0';
			printf("%-10s : %s\n", nameID[NameRecord.nameID], str);
			fseek(fp, seekpp, SEEK_SET);
		}
	}
	fseek(fp, seekp, SEEK_SET);
	return;
}

/* JIS (ku-ten) -> unicode */
uint16
jis2uni(ku, ten)
int ku;
int ten;
{

	if (ku > MAXJIS) return (0);
	if (ten < 1 || ten > 94) return (0);
	return (UnicodeTbl[ku - 1][ten - 1]);
}

/* JIS (ku-ten) -> shift-JIS */
uint16
jis2sjis(ku, ten)
int ku;
int ten;
{
	int ch, cl;

	if (ku > 92) return (0);
	if (ten < 1 || ten > 94) return (0);
	if (ku & 1) {
		cl = ten + 0x3f;
		if (ten > 63) cl++;
	} else {
		cl = ten + 0x9e;
	}
	ch = (ku - 1) / 2 + 0x81;
	if (ku > 62) ch += 0x40;
	return ((ch << 8) + cl);
}

void
usage()
{
	fprintf(stderr,
		"\t<<< ttindex: Make TrueType index file for dviout/dviprt/VFlib>>>\n");
	fprintf(stderr,
		"\t\t\tVer 0.4 written by I.Matsuda, Oct. 21, 1995\n\n");
	fprintf(stderr,
		"Usage: ttindex file.[ttf|ttc]\n");
	exit(1);
}

void
main(argc, argv)
int argc;
char **argv;
{
	FILE *fp_ttf, *fp_tti;
	int i, ku, ten, len;
	uint16 code, index;
	char *name, *extp;
	char tmp[5];
	sfnt_OffsetTable OffsetTable;
	sfnt_DirectoryEntry DirectoryEntry;

	if (argc != 2) usage();
	len = strlen(argv[1]);
	name = (char *)malloc(len + 5);
	strcpy(name, argv[1]);
	for (i = len;; i--) {
		if (name[i] == '/' || name[i] == '\\' || i == 0) {
			extp = &name[len];
			break;
		}
		if (name[i] == '.') {
			extp = &name[i];
			break;
		}
	}
	if ((fp_ttf = fopen(name, "rb")) == NULL) {
		if (*extp == '\0') {
			strcpy(extp, ".ttf");
			fp_ttf = fopen(name, "rb");
		}
	}
	if (fp_ttf == NULL) {
		fprintf(stderr, "Can't open the TrueType font file %s.\n", name);
		exit(1);
	}
	/* read table directory */
	if ((OffsetTable.version = fget32(fp_ttf)) == 0x74746366L) { /* ttcf */
		fget32(fp_ttf);								/* version of ttcf? */
		fget32(fp_ttf);								/* num of font? */
		fseek(fp_ttf, fget32(fp_ttf), SEEK_SET);	/* seek to first font */
		OffsetTable.version = fget32(fp_ttf);
	}
	OffsetTable.numOffsets = fget16(fp_ttf);
	OffsetTable.searchRange = fget16(fp_ttf);
	OffsetTable.entrySelector = fget16(fp_ttf);
	OffsetTable.rangeShift = fget16(fp_ttf);
	for (i = 0; i < OffsetTable.numOffsets; i++) {
		DirectoryEntry.tag = fget32(fp_ttf);
		DirectoryEntry.checkSum = fget32(fp_ttf);
		DirectoryEntry.offset = fget32(fp_ttf);
		DirectoryEntry.length = fget32(fp_ttf);
		switch (DirectoryEntry.tag)
		{
			case (tag_FontHeader):
				ReadFontheader(DirectoryEntry.offset, fp_ttf);
				break;
			case (tag_MaxProfile):
				ReadMaxprofile(DirectoryEntry.offset, fp_ttf);
				break;
			case (tag_IndexToLoc):
				tti_header.IndexToLocBase = DirectoryEntry.offset;
				break;
			case (tag_CharToIndexMap):
				ReadCharToIndexMap(DirectoryEntry.offset, fp_ttf);
				break;
			case (tag_GlyphData):
				tti_header.GlyphBase = DirectoryEntry.offset;
				break;
			case (tag_NamingTable):
				ShowFontname(DirectoryEntry.offset, fp_ttf);
				break;
			case (tag_ControlValue):
			case (tag_Editor0):
			case (tag_Editor1):
			case (tag_Encryption):
			case (tag_FontProgram):
			case (tag_GlyphDirectory):
			case (tag_HoriDeviceMetrics):
			case (tag_HoriHeader):
			case (tag_HorizontalMetrics):
			case (tag_Kerning):
			case (tag_LinearThreeshold):
			case (tag_OS_2):
			case (tag_Postscript):
			case (tag_PreProgram):
			case (tag_mort):
			case (tag_WIN):
			case (tag_VDMX):
			case (tag_FOCA):
			case (tag_PCLT):
			case (tag_BASE):
			case (tag_EBDT):
			case (tag_EBLC):
			case (tag_GSUB):
			case (tag_gasp):
			case (tag_VertHeader):
			case (tag_VerticalMetrics):
				break;
			default:
				for (len = 0; len < 4; len ++) {
					tmp[3 - len] = (DirectoryEntry.tag >> (len << 3)) & 0x00ff;
				}
				tmp[4] = '\0';
				fprintf(stderr,
					"Warning! : \'%s\'(%08lX) is unknown table tag.\n",
					tmp, DirectoryEntry.tag);
			/* exit(1); */
		}
	}
	/* header of .tti file */
	strcpy(extp, ".tti");
	if ((fp_tti = fopen(name, "wb")) == NULL) {
		fprintf(stderr, "Can't create the TrueType index file %s.\n", name);
		exit(1);
	}
	tti_header.CodeTableOffset = SIZE_OF_TTI_HEADER;
	tti_header.MaxJIS = MAXJIS;
	fput16(tti_header.CodeTableOffset, fp_tti);
	fput16(tti_header.MaxJIS, fp_tti);
	fput16(tti_header.unitsPerEm, fp_tti);
	fput16(tti_header.xMin, fp_tti);
	fput16(tti_header.yMin, fp_tti);
	fput16(tti_header.maxPoints, fp_tti);
	fput16(tti_header.maxContours, fp_tti);
	fput32(tti_header.IndexToLocBase, fp_tti);
	fput32(tti_header.GlyphBase, fp_tti);
	for (ku = 1; ku <= MAXJIS; ku++) {
		printf("[%02d]", ku);
		for (ten = 1; ten <= 94; ten++) {
			code = codeconv(ku, ten);
			index = CharToIndex(code, fp_ttf);
			fput16(index, fp_tti);
		}
	}
	printf("\ndone!\n");
	exit(0);
}
