/*
 * 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  Support for shift-JIS encoding (Special thanks to iW)
 *	21 Oct. 1995  Support for new .ttc fonts used in WIN95. (Ver.0.4)
 *      10 Apr. 1996  Support for DynaFont by Shigeru Makino.
 *      28 Apr. 1996  Support for Dynafont by k-chinen. 
 *                    (for fonts whose mappingTable.format is 2)
 */


/* This file is part of VFlib
 *
 * Copyright (C) 1995,1996 Hirotsugu KAKUGAWA.   All rights reserved.
 *
 * VFlib is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
 * to anyone for the consequences of using it or for whether it serves any
 * particular purpose or works at all, unless he says so in writing.  Refer
 * to the GNU General Public License for full details.
 *
 * Everyone is granted permission to copy, modify and redistribute
 * VFlib, but only under the conditions described in the GNU
 * General Public License.  A copy of this license is supposed to have been
 * given to you along with VFlib so you can know your rights and
 * responsibilities.  It should be in a file named COPYING.  Among other
 * things, the copyright notice and this notice must be preserved on all
 * copies.
 */


/*
 * Define `OS2FONT' if you use japanese fonts for OS2.
 */
/*#define OS2FONT*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if __TURBOC__
#  include <alloc.h>
#  include <io.h>
/*
#else
#  include <malloc.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);
#else
UInt16  jis2uni();
UInt16  jis2sjis();
UInt16  (*codeconv)() = jis2uni;
#endif

/* read 2 bytes */
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 bytes */
void
fput16(num, fp)
Int16 num;
FILE *fp;
{
#if 0
	int c[2];

	c[0] = (num >> 8) & 0x00ff;
	c[1] = num & 0x00ff;
	fputc(c[0], fp);
	fputc(c[1], fp);
	return;
#else
 	unsigned char z[2];
 
 	z[0] = (unsigned char)(((UInt16)num >> 8) & 0x00ff);
 	z[1] = (unsigned char)(((UInt16)num ) & 0x00ff);
 
 	fwrite(z, 1, 2, fp);
#endif
}

/* read 4 bytes */
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 bytes */
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 0
	if (MaxProfile.maxComponentElements > 1) {
		fprintf(stderr, "Sorry, can't use this font "
				"(composite glyphs are not supported).\n");
		exit(1);
	}
#endif
	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, k;
 	int u, v, s, t;
	int segCount;
	long seekp, seekpp;
	sfnt_char2IndexDirectory char2IndexDirectory;
	sfnt_platformEntry *platformEntry;
	sfnt_mappingTable mappingTable;
 	int segno[256];

	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;
		}
 		if (mappingTable.format == 2) {
 			/*
 			 * Dynafont use this format.
 			 * 		I(k-chinen) test with 'dfgot_ub.ttf'.
 			 */
 
 			if (platformEntry->specificID == 1)
 				codeconv = jis2sjis;
 
 			segCount = 0;
 			for(j = 0; j < 256; j++) {
 				k = fget16(fp)/8;
 				if( k > segCount) {
 					segCount = k;
 				}
 				if(k>0)
 					segno[k] = j;
 			}
 			segCount++;
 
 			C2Index.segCountX2 = segCount * 2;
 
 			C2Index.endCount =
 					(UInt16 FAR *)alloc_buff(sizeof(UInt16)*segCount);
 			C2Index.startCount =
 					(UInt16 FAR *)alloc_buff(sizeof(UInt16)*segCount);
 			C2Index.idDelta =
 				 	(UInt16 FAR *)alloc_buff(sizeof(UInt16)*segCount);
 			C2Index.idRangeOffset = 
 					(UInt16 FAR *)alloc_buff(sizeof(UInt16)*segCount);
 			for (j = 0; j < segCount; j++) {
 				u = fget16(fp);
 				v = fget16(fp);
 				s = fget16(fp);
 				t = fget16(fp);
 	
 				C2Index.startCount[j]
 		 	 		= ((segno[j] & 0xff) << 8 ) + u;
 				C2Index.endCount[j]
  	 				= ((segno[j] & 0xff) << 8 ) + u + v;
 				C2Index.idDelta[j]
  		 			= s;
 				C2Index.idRangeOffset[j]
 					= t + j*6  - (segCount-1)*8 - 2;
 
 			}
 			C2Index.idRangeBase = ftell(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 2 or 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
ols_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);
}

UInt16
jis2sjis(jk, jt)
int jk;
int jt;
{
	UInt16 s1, s2;
	UInt16 ret;

	if(jk<1 || jk>94)
		return 0;
	if(jk>=1 && jk<=62)
		s1 = (UInt16)(jk-1)/2 + (UInt16)0x81;
	else
	if(jk>=63 && jk<=94)
		s1 = (UInt16)(jk-1)/2 + (UInt16)0xc1;
	else {
		fprintf(stderr, "jis2sjis: ??? strange 1 ku %d\n", jk);
		exit(1);
	}

	if((jk & 1) !=0 ) {
		if(jt>=1 && jt<=63)
			s2 = (UInt16)jt + (UInt16)0x3f;
		else
		if(jt>=64 && jt<=94)
 			s2 = (UInt16)jt + (UInt16)0x40;
 		else {
			fprintf(stderr, "jis2sjis: ??? strange 2 ten %d\n", jt);
			exit(1);
		}
	}
	else {
		s2 = (UInt16)jt + (UInt16)0x9e;
	}
	ret = (s1 << 8) + s2;

	return ret;
}


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);
}

int
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 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):
#ifndef OS2FONT
				ReadCharToIndexMap(DirectoryEntry.offset, fp_ttf);
#endif /*OS2FONT*/
				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_VertHeader):
			case (tag_VerticalMetrics):
			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):
				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 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++) {
#ifdef OS2FONT
		        index = ku * 94 + ten + 162;
#else
			code = codeconv(ku, ten);
			index = CharToIndex(code, fp_ttf);
#endif
			fput16(index, fp_tti);
		}
	}
	printf("\ndone!\n");
	return 0;
}
