home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
swCHIP 1991 January
/
swCHIP_95-1.bin
/
chip
/
stereo3d
/
polyray
/
ttfx.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-12-11
|
30KB
|
937 lines
/* TrueType font file processing */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#if defined(MAC)
#include <console.h>
#define FSCFG_BIG_ENDIAN
#else
#define PC_OS
#endif
#ifndef SEEK_SET
#define SEEK_SET 0 /* seek from start of file */
#define SEEK_CUR 1 /* relative to current position */
#define SEEK_END 2 /* relative to end of file */
#endif
#define EPSILON 1.0e-10
#define MAX_ITERATIONS 50
#define COEFF_LIMIT 1.0e-20
#define ONCURVE 0x01
#define XSHORT 0x02
#define YSHORT 0x04
#define REPEAT_FLAGS 0x08 /* repeat flag n times */
#define SHORT_X_IS_POS 0x10 /* the short vector is positive */
#define NEXT_X_IS_ZERO 0x10 /* the relative x coordinate is zero */
#define SHORT_Y_IS_POS 0x20 /* the short vector is positive */
#define NEXT_Y_IS_ZERO 0x20 /* the relative y coordinate is zero */
typedef char int8;
typedef unsigned char uint8;
typedef short int16;
typedef unsigned short uint16;
typedef long int32;
typedef unsigned long uint32;
typedef short FUnit;
typedef unsigned short uFUnit;
#ifdef PC_OS
typedef long Fixed;
#endif
#ifndef FAR
#define FAR
#endif
#ifndef NEAR
#define NEAR
#endif
#if defined(FSCFG_BIG_ENDIAN)
/* target byte order matches Motorola 68000 */
#define SWAPL(a) (a)
#define SWAPW(a) (a)
#else
/* Macros to turn little endian words/longs into big endian words/longs */
#define FS_2BYTE(p) (((unsigned short)((p)[0]) << 8) | (p)[1])
#define FS_4BYTE(p) (FS_2BYTE((p)+2) | ( (FS_2BYTE(p)+0L) << 16))
#define SWAPW(a) ((short) FS_2BYTE((unsigned char FAR *)(&a)))
#define SWAPL(a) ((long) FS_4BYTE((unsigned char FAR *)(&a)))
#endif
/* Constants defined in Motorola order (use SWAPL to correct) */
static uint32 tag_CharToIndexMap = 0x636d6170; /* 'cmap' */
static uint32 tag_FontHeader = 0x68656164; /* 'head' */
static uint32 tag_GlyphData = 0x676c7966; /* 'glyf' */
static uint32 tag_IndexToLoc = 0x6c6f6361; /* 'loca' */
static uint32 tag_Kerning = 0x6b65726e; /* 'kern' */
static uint32 tag_MaxProfile = 0x6d617870; /* 'maxp' */
typedef struct {
uint32 bc;
uint32 ad;
} BigDate;
typedef struct {
int32 tag;
uint32 checkSum;
uint32 offset;
uint32 length;
} sfnt_DirectoryEntry;
typedef struct {
int32 version; /* 0x10000 (1.0) */
uint16 numOffsets; /* number of tables */
uint16 searchRange; /* (max2 <= numOffsets)*16 */
uint16 entrySelector; /* log2 (max2 <= numOffsets) */
uint16 rangeShift; /* numOffsets*16-searchRange*/
sfnt_DirectoryEntry table[1]; /* table[numOffsets] */
} sfnt_OffsetTable;
typedef struct {
Fixed version; /* for this table, set to 1.0 */
Fixed fontRevision; /* For Font Manufacturer */
uint32 checkSumAdjustment;
uint32 magicNumber; /* signature, must be 0x5F0F3CF5 == MAGIC */
uint16 flags;
uint16 unitsPerEm; /* How many in Font Units per EM */
BigDate created;
BigDate modified;
/** This is the font wide bounding box in ideal space
(baselines and metrics are NOT worked into these numbers) **/
FUnit xMin;
FUnit yMin;
FUnit xMax;
FUnit yMax;
uint16 macStyle; /* macintosh style word */
uint16 lowestRecPPEM; /* lowest recommended pixels per Em */
/* 0: fully mixed directional glyphs,
1: only strongly L->R or T->B glyphs,
-1: only strongly R->L or B->T glyphs,
2: like 1 but also contains neutrals,
-2: like -1 but also contains neutrals */
int16 fontDirectionHint;
int16 indexToLocFormat;
int16 glyphDataFormat;
} sfnt_FontHeader;
typedef struct {
uint16 format;
uint16 length;
uint16 version;
} sfnt_mappingTable;
typedef struct {
uint16 platformID;
uint16 specificID;
uint32 offset;
} sfnt_platformEntry;
typedef sfnt_platformEntry FAR *sfnt_platformEntryPtr;
typedef sfnt_DirectoryEntry FAR *sfnt_DirectoryEntryPtr;
typedef struct {
int16 numContours;
int16 xMin;
int16 yMin;
int16 xMax;
int16 yMax;
} GlyphHeader;
typedef struct {
GlyphHeader header;
uint16 numPoints;
uint16 *endPoints;
uint8 *flags;
float *x, *y;
} GlyphOutline;
typedef struct {
uint8 inside_flag; /* 1 if this an inside contour, 0 if outside */
uint16 count; /* Number of points in the contour */
uint8 *flags; /* On/off curve flags */
float *x, *y; /* Coordinates of control vertices */
} Contour;
typedef struct FontFileInfoStruct *FontFileInfoPtr;
typedef struct GlyphStruct *GlyphPtr;
/* Contour information for a single glyph */
typedef struct GlyphStruct {
GlyphHeader header; /* Count and sizing information about this glyph */
uint16 glyph_index; /* Internal glyph index for this character */
Contour *contours; /* Array of outline contours */
FontFileInfoPtr parent; /* Parent font for this glyph */
GlyphPtr next; /* Next cached glyph */
char c; /* Character code */
} Glyph;
/* Useful general data about this font */
typedef struct FontFileInfoStruct {
char *filename;
FILE *fp;
uint16 numGlyphs;
uint16 unitsPerEm;
int16 loca_format;
uint32 *loca_table;
uint32 loca_table_offset;
uint32 cmap_table_offset;
uint32 glyph_table_offset;
GlyphPtr glyphs; /* Cached info for this font */
struct FontFileInfoStruct *next; /* Next font */
} FontFileInfo;
static FontFileInfo *TTFonts = NULL;
static FontFileInfo *
OpenFontFile(char *filename)
{
int i;
FontFileInfo *fontlist;
/* First look to see if we have already opened this font */
for (fontlist = TTFonts;fontlist!=NULL;fontlist=fontlist->next)
if (!strcmp(filename, fontlist->filename))
break;
if (fontlist != NULL) {
/* We have a match, use the previous information */
if ((fontlist->fp == NULL) &&
(fontlist->fp = fopen(fontlist->filename, "rb")) == NULL) {
fprintf(stderr, "Can't open font file: '%s'\n",
fontlist->filename);
exit(1);
}
}
else {
/* We haven't looked at this font before, let's allocate a
holder for the information and set some defaults */
fontlist = malloc(sizeof(FontFileInfo));
if (fontlist == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
i = strlen(filename) + 1;
fontlist->filename = malloc(i * sizeof(char));
strcpy(fontlist->filename, filename);
if ((fontlist->fp = fopen(fontlist->filename, "rb")) == NULL) {
fprintf(stderr, "Can't open font file: '%s'\n",
fontlist->filename);
exit(1);
}
fontlist->numGlyphs = 0;
fontlist->unitsPerEm = 16384;
fontlist->loca_format = 0;
fontlist->loca_table_offset = 0;
fontlist->loca_table = NULL;
fontlist->cmap_table_offset = 0;
fontlist->glyph_table_offset = 0;
fontlist->glyphs = NULL;
fontlist->next = NULL;
}
return fontlist;
}
static void
FreeFontInfo()
{
int i;
FontFileInfo *oldfont, *tempfont;
GlyphPtr glyphs, tempglyph;
for (oldfont=TTFonts;oldfont!=NULL;) {
if (oldfont->fp != NULL)
fclose(oldfont->fp);
free(oldfont->filename);
if (oldfont->loca_table != NULL)
free(oldfont->loca_table);
for (glyphs=oldfont->glyphs;glyphs!=NULL;) {
for (i=0;i<glyphs->header.numContours;i++) {
free(glyphs->contours[i].flags);
free(glyphs->contours[i].x);
free(glyphs->contours[i].y);
}
free(glyphs->contours);
tempglyph = glyphs;
glyphs = glyphs->next;
free(tempglyph);
}
tempfont = oldfont;
oldfont = oldfont->next;
free(tempfont);
}
TTFonts = NULL;
}
/* Transform a glyph from TrueType storage format to something a little
easier to manage */
static GlyphPtr
ConvertOutlineToGlyph(FontFileInfo *ffile, GlyphOutline *ttglyph)
{
GlyphPtr glyph;
float *temp_x, *temp_y;
uint8 *temp_f;
uint16 i, j, last_j;
/* Create storage for this glyph */
if ((glyph = malloc(sizeof(Glyph))) == NULL)
exit(1);
glyph->contours = malloc(ttglyph->header.numContours * sizeof(Contour));
if (glyph->contours == NULL)
exit(1);
/* Copy sizing information about this glyph */
memcpy(&glyph->header, &ttglyph->header, sizeof(GlyphHeader));
/* Keep track of the parent font for this glyph */
glyph->parent = ffile;
/* Now copy the vertex information into the contours */
for (i=0, last_j=0;i<ttglyph->header.numContours;i++) {
/* Figure out number of points in contour */
j = ttglyph->endPoints[i] - last_j + 1;
/* Copy the coordinate information into the glyph */
temp_x = malloc((j+1) * sizeof(float));
temp_y = malloc((j+1) * sizeof(float));
temp_f = malloc((j+1) * sizeof(uint8));
if (temp_x == NULL || temp_y == NULL || temp_f == NULL)
exit(1);
memcpy(temp_x, &ttglyph->x[last_j], j * sizeof(float));
memcpy(temp_y, &ttglyph->y[last_j], j * sizeof(float));
memcpy(temp_f, &ttglyph->flags[last_j], j * sizeof(uint8));
temp_x[j] = ttglyph->x[last_j];
temp_y[j] = ttglyph->y[last_j];
temp_f[j] = ttglyph->flags[last_j];
/* Figure out if this is an inside or outside contour */
glyph->contours[i].inside_flag = 0;
/* Plug in the reset of the contour components into the glyph */
glyph->contours[i].count = j;
glyph->contours[i].x = temp_x;
glyph->contours[i].y = temp_y;
glyph->contours[i].flags = temp_f;
/* Set last_j to point to the beginning of the next contour's
coordinate information */
last_j = ttglyph->endPoints[i] + 1;
}
/* Show statistics about this glyph */
/*
printf("Number of contours: %u\n",
glyph->header.numContours);
printf("X extent: [%f, %f]\n",
(float)glyph->header.xMin / (float)glyph->parent->unitsPerEm,
(float)glyph->header.xMax / (float)glyph->parent->unitsPerEm);
printf("Y extent: [%f, %f]\n",
(float)glyph->header.yMin / (float)glyph->parent->unitsPerEm,
(float)glyph->header.yMax / (float)glyph->parent->unitsPerEm);
printf("Converted coord list(%d):\n", (int)glyph->header.numContours);
for (i=0;i<glyph->header.numContours;i++) {
for (j=0;j<=glyph->contours[i].count;j++)
printf(" %c[%f, %f]\n",
(glyph->contours[i].flags[j] & ONCURVE ? '*' : ' '),
glyph->contours[i].x[j], glyph->contours[i].y[j]);
printf("\n");
}
*/
return glyph;
}
/* Read the contour information for a specific glyph */
static GlyphPtr
ExtractGlyphInfo(FontFileInfo *ffile, uint16 glyph_index, unsigned char c)
{
int i, j;
uint8 temp8, flag, repeat_count;
uint16 temp16, n, nc;
int16 coord, itemp16;
Fixed tempF;
GlyphOutline *ttglyph;
GlyphPtr glyph;
if ((ttglyph = malloc(sizeof(GlyphOutline))) == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
fseek(ffile->fp, ffile->glyph_table_offset +
ffile->loca_table[glyph_index], SEEK_SET);
fread(&ttglyph->header, 1, sizeof(GlyphHeader), ffile->fp);
ttglyph->header.numContours = SWAPW(ttglyph->header.numContours);
ttglyph->header.xMax = SWAPW(ttglyph->header.xMax);
ttglyph->header.yMax = SWAPW(ttglyph->header.yMax);
ttglyph->header.xMin = SWAPW(ttglyph->header.xMin);
ttglyph->header.yMin = SWAPW(ttglyph->header.yMin);
nc = ttglyph->header.numContours;
if (nc > 0) {
/* Grab the contour endpoints */
if ((ttglyph->endPoints = malloc(nc * sizeof(uint16))) == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
for (i=0;i<nc;i++) {
fread(&temp16, 1, sizeof(temp16), ffile->fp);
ttglyph->endPoints[i] = SWAPW(temp16);
}
/* Skip over the instructions */
fread(&temp16, 1, sizeof(temp16), ffile->fp);
fseek(ffile->fp, SWAPW(temp16), SEEK_CUR);
/* Determine the number of points making up this glyph */
n = ttglyph->numPoints = ttglyph->endPoints[nc-1] + 1;
/* Read the flags */
if ((ttglyph->flags = malloc(n * sizeof(uint8))) == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
for (i=0;i<ttglyph->numPoints;i++) {
fread(&ttglyph->flags[i], 1, 1, ffile->fp);
if (ttglyph->flags[i] & REPEAT_FLAGS) {
fread(&repeat_count, 1, 1, ffile->fp);
for (;repeat_count>0;repeat_count--,i++) {
ttglyph->flags[i+1] = ttglyph->flags[i];
}
}
}
/* Read the coordinate vectors */
if ((ttglyph->x = malloc(n * sizeof(float))) == NULL ||
(ttglyph->y = malloc(n * sizeof(float))) == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
coord = 0;
for (i=0;i<ttglyph->numPoints;i++) {
/* Read each x coordinate */
flag = ttglyph->flags[i];
if (flag & XSHORT) {
fread(&temp8, 1, 1, ffile->fp);
if (flag & SHORT_X_IS_POS)
coord += temp8;
else
coord -= temp8;
}
else if (!(flag & NEXT_X_IS_ZERO)) {
fread(&itemp16, 1, 2, ffile->fp);
coord += SWAPW(itemp16);
}
ttglyph->x[i] = (float)coord / (float)ffile->unitsPerEm;
}
coord = 0;
for (i=0;i<ttglyph->numPoints;i++) {
/* Read each y coordinate */
flag = ttglyph->flags[i];
if (flag & YSHORT) {
fread(&temp8, 1, 1, ffile->fp);
if (flag & SHORT_Y_IS_POS)
coord += temp8;
else
coord -= temp8;
}
else if (!(flag & NEXT_Y_IS_ZERO)) {
fread(&itemp16, 1, 2, ffile->fp);
coord += SWAPW(itemp16);
}
ttglyph->y[i] = (float)coord / (float)ffile->unitsPerEm;
}
/* Convert the glyph outline information from TrueType layout
into a more easily processed format */
glyph = ConvertOutlineToGlyph(ffile, ttglyph);
glyph->c = c;
glyph->glyph_index = glyph_index;
/* Free up outline information */
free(ttglyph->y);
free(ttglyph->x);
free(ttglyph->endPoints);
free(ttglyph->flags);
}
else if (nc == 0) {
/* Do nothing */
}
else {
printf("Can't handle multiple component glyphs\n");
}
return glyph;
}
/* The file pointer must be pointing immediately after the version entry
in the encoding table for the next two functions to work. */
static uint16
ProcessFormat0Glyph(FILE *fp, unsigned char search_char)
{
uint8 temp_index;
fseek(fp, search_char, SEEK_CUR);
fread(&temp_index, 1, 1, fp); /* Each index is exactly 1 byte */
return (uint16)temp_index;
}
static uint16
ProcessFormat4Glyph(FILE *fp, unsigned char search_char)
{
int i;
uint16 glyph_index;
long glyphIDoffset;
uint16 temp16;
uint16 segCount, searchRange, entrySelector, rangeShift;
uint16 *endCount, *startCount, *idDelta, *idRangeOffset;
fread(&temp16, 1, sizeof(uint16), fp);
segCount = SWAPW(temp16) >> 1;
fread(&temp16, 1, sizeof(uint16), fp);
searchRange = SWAPW(temp16);
fread(&temp16, 1, sizeof(uint16), fp);
entrySelector = SWAPW(temp16);
fread(&temp16, 1, sizeof(uint16), fp);
rangeShift = SWAPW(temp16);
/* Now allocate and read in the segment arrays */
endCount = malloc(segCount * sizeof(uint16));
startCount = malloc(segCount * sizeof(uint16));
idDelta = malloc(segCount * sizeof(uint16));
idRangeOffset = malloc(segCount * sizeof(uint16));
if (endCount == NULL || startCount == NULL || idDelta == NULL ||
idRangeOffset == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
for (i=0;i<segCount;i++) {
fread(&temp16, 1, sizeof(uint16), fp);
endCount[i] = SWAPW(temp16);
}
fread(&temp16, 1, sizeof(uint16), fp); /* Skip over 'reservedPad' */
for (i=0;i<segCount;i++) {
fread(&temp16, 1, sizeof(uint16), fp);
startCount[i] = SWAPW(temp16);
}
for (i=0;i<segCount;i++) {
fread(&temp16, 1, sizeof(uint16), fp);
idDelta[i] = SWAPW(temp16);
}
glyphIDoffset = ftell(fp);
for (i=0;i<segCount;i++) {
fread(&temp16, 1, sizeof(uint16), fp);
idRangeOffset[i] = SWAPW(temp16);
}
/* Now search the segments for our character */
glyph_index = 0;
for (i=0;i<segCount;i++) {
if ((uint8)search_char <= endCount[i]) {
if ((uint8)search_char > startCount[i]) {
/* Found mapping */
if (idRangeOffset[i] == 0)
glyph_index = (uint16)search_char + idDelta[i];
else {
/* Alternate encoding of glyph indices, relies on a quite
unusual way of storing the offsets. Not really sure about
the division by 2 on idRangeOffset, but the Microsoft
manual seemed to do it that way. */
glyphIDoffset += i * sizeof(uint16) + idRangeOffset[i]/2 +
(search_char - startCount[i]);
fseek(fp, glyphIDoffset, SEEK_SET);
fread(&temp16, 1, sizeof(uint16), fp);
glyph_index = SWAPW(temp16);
if (glyph_index != 0)
glyph_index = glyph_index + idDelta[i];
}
}
break;
}
}
/* Deallocate the memory we used for the segment arrays */
free(endCount);
free(startCount);
free(idDelta);
free(idRangeOffset);
return glyph_index;
}
/* Trimmed table mapping */
static uint16
ProcessFormat6Glyph(FILE *fp, unsigned char search_char)
{
uint16 temp16, firstCode, entryCount;
uint8 glyph_index;
fread(&temp16, 1, 2, fp);
firstCode = SWAPW(temp16);
fread(&temp16, 1, 2, fp);
entryCount = SWAPW(temp16);
if (search_char >= firstCode && search_char < firstCode + entryCount) {
fseek(fp, search_char - firstCode, SEEK_CUR);
fread(&temp16, 1, 2, fp);
glyph_index = SWAPW(temp16);
}
else
glyph_index = 0;
return glyph_index;
}
/* find the character mapping for 'search_char' */
static uint16
ProcessCharMap(FontFileInfo *ffile, unsigned char search_char)
{
long old_table_offset;
uint16 glyph_index, temp16;
uint16 entry_ID, entry_EID;
uint32 entry_offset;
sfnt_platformEntry cmapEntry;
sfnt_mappingTable encodingTable;
int i, table_count;
/* Move to the start of the character map, skipping over the format
entry, right to the number of entries. */
fseek(ffile->fp, ffile->cmap_table_offset + sizeof(uint16), SEEK_SET);
fread(&temp16, 1, sizeof(uint16), ffile->fp);
table_count = SWAPW(temp16);
/* Search the tables until we find the glyph index for the search
character. Just return the first one we find... */
for (i=0;i<table_count;i++) {
fread(&cmapEntry, 1, sizeof(cmapEntry), ffile->fp);
entry_ID = SWAPW(cmapEntry.platformID);
entry_EID = SWAPW(cmapEntry.specificID);
entry_offset = SWAPL(cmapEntry.offset);
old_table_offset = ftell(ffile->fp);
fseek(ffile->fp, ffile->cmap_table_offset + entry_offset, SEEK_SET);
fread(&encodingTable, 1, sizeof(encodingTable), ffile->fp);
/*
printf("Encoding table, format: %u, length: %u, version: %u\n",
SWAPW(encodingTable.format),
SWAPW(encodingTable.length),
SWAPW(encodingTable.version));
*/
if (SWAPW(encodingTable.format) == 0) {
/* Translation is simple - add 'entry_char' to the start
of the table and grab what's there */
/* printf("Apple standard index mapping\n"); */
glyph_index = ProcessFormat0Glyph(ffile->fp, search_char);
}
else if (SWAPW(encodingTable.format) == 4) {
/* Microsoft UGL encoding */
/* printf("Microsoft standard index mapping\n"); */
glyph_index = ProcessFormat4Glyph(ffile->fp, search_char);
}
else if (SWAPW(encodingTable.format) == 6) {
/* printf("Trimmed table mapping\n"); */
glyph_index = ProcessFormat6Glyph(ffile->fp, search_char);
}
else
printf("Unsupported index mapping format: %u\n",
SWAPW(encodingTable.format));
return glyph_index;
/* Go back to the set of table descriptions at the beginning of
the cmap table. */
fseek(ffile->fp, old_table_offset, SEEK_SET);
}
/* No character mapping was found - very odd, we should really have
had the character in at least one table. Perhaps getting here
means we didn't have any character mapping tables */
return 0;
}
/* This routine determines the total number of glyphs in a TrueType file.
Necessary so that we can allocate the proper amount of storage for
the glyph location table. */
static void
ProcessMaxpTable(FontFileInfo *ffile, sfnt_DirectoryEntry *Table)
{
long old_fp_offset;
Fixed temp_fixed;
uint16 temp16;
old_fp_offset = ftell(ffile->fp);
fseek(ffile->fp, SWAPL(Table->offset), SEEK_SET);
fread(&temp_fixed, 1, sizeof(temp_fixed), ffile->fp);
fread(&temp16, 1, sizeof(temp16), ffile->fp);
ffile->numGlyphs = SWAPW(temp16);
fseek(ffile->fp, old_fp_offset, SEEK_SET);
}
/* Process the font header table */
static void
ProcessHeadTable(FontFileInfo *ffile, sfnt_DirectoryEntry *Table)
{
long old_fp_offset;
sfnt_FontHeader fontHeader;
old_fp_offset = ftell(ffile->fp);
fseek(ffile->fp, SWAPL(Table->offset), SEEK_SET);
fread(&fontHeader, 1, sizeof(fontHeader), ffile->fp);
ffile->loca_format = SWAPW(fontHeader.indexToLocFormat);
ffile->unitsPerEm = SWAPW(fontHeader.unitsPerEm);
/* Back to where we started */
fseek(ffile->fp, old_fp_offset, SEEK_SET);
}
/* Determine the relative offsets of glyphs */
static void
ProcessLocaTable(FontFileInfo *ffile)
{
int i;
uint16 temp16;
uint32 temp32;
/* Move to location of table in file */
fseek(ffile->fp, ffile->loca_table_offset, SEEK_SET);
/* Make sure we understand the size of the entries in the table */
ffile->loca_table = malloc((ffile->numGlyphs + 1) * sizeof(uint32));
if (ffile->loca_table == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
/* Now read and save the loca table */
for (i=0;i<ffile->numGlyphs;i++) {
if (ffile->loca_format == 0) {
fread(&temp16, 1, sizeof(uint16), ffile->fp);
ffile->loca_table[i] = (uint32)SWAPW(temp16) << 1;
}
else {
fread(&temp32, 1, sizeof(uint32), ffile->fp);
ffile->loca_table[i] = SWAPL(temp32);
}
}
}
/* Gather information about a specific font. The font file must
be opened prior to calling this routine. */
static FontFileInfo *
ProcessFontFile(char *fontfilename)
{
unsigned i, c;
sfnt_OffsetTable OffsetTable;
sfnt_DirectoryEntry Table;
FontFileInfo *ffile;
/* Open the font file */
ffile = OpenFontFile(fontfilename);
/* Read the initial directory header on the TTF. The numOffsets variable
tells us how many tables are present in this file. */
fread(&OffsetTable, 1, sizeof(OffsetTable) - sizeof(sfnt_DirectoryEntry),
ffile->fp);
c = (unsigned)SWAPW(OffsetTable.numOffsets);
/* Process general font information and save it. */
for (i = 0; i < c && i < 40; i++) {
if (fread(&Table, 1, sizeof(Table), ffile->fp) != sizeof(Table)) {
printf("Read failed on table #%d\n", i);
exit(-1);
}
if (Table.tag == SWAPL(tag_CharToIndexMap))
ffile->cmap_table_offset = SWAPL(Table.offset);
else if (Table.tag == SWAPL(tag_MaxProfile))
ProcessMaxpTable(ffile, &Table);
else if (Table.tag == SWAPL(tag_IndexToLoc))
ffile->loca_table_offset = SWAPL(Table.offset);
else if (Table.tag == SWAPL(tag_FontHeader))
ProcessHeadTable(ffile, &Table);
else if (Table.tag == SWAPL(tag_GlyphData))
ffile->glyph_table_offset = SWAPL(Table.offset);
}
/* Close the font file descriptor (don't want to waste them) */
fclose(ffile->fp);
ffile->fp = NULL;
/* Return the information about this font */
return ffile;
}
static GlyphPtr
ProcessCharacter(FontFileInfo *ffile, unsigned char search_char)
{
uint16 glyph_index;
GlyphOutline *ttglyph;
GlyphPtr glyph;
/* See if we have already processed this glyph */
if (ffile->glyphs != NULL) {
for (glyph=ffile->glyphs;glyph!=NULL;glyph=glyph->next)
if (glyph->c == search_char) {
/* Found it, no need to do any more work */
return glyph;
}
}
/* Open the font file (if necessary) */
if ((ffile->fp == NULL) &&
(ffile->fp = fopen(ffile->filename, "rb")) == NULL) {
fprintf(stderr, "Can't open font file: '%s'\n", ffile->filename);
exit(1);
}
/* Now go extract information about the search character */
if (ffile->numGlyphs > 0 && ffile->loca_table_offset > 0 &&
ffile->cmap_table_offset > 0 && ffile->glyph_table_offset > 0) {
/* We have enough information on the file layout to go look for
the information about the search character */
ProcessLocaTable(ffile);
glyph_index = ProcessCharMap(ffile, search_char);
glyph = ExtractGlyphInfo(ffile, glyph_index, search_char);
/* Add this glyph to the ones we already know about */
glyph->next = ffile->glyphs;
ffile->glyphs = glyph;
}
/* Close the font file */
fclose(ffile->fp);
ffile->fp = NULL;
/* Glyph is all built */
return glyph;
}
static void
OutputPolyrayGlyph(FontFileInfo *ffile, Glyph *glyph, float offset)
{
int i, j;
printf("object { // Char: '%c'\n", (char)glyph->c);
printf(" glyph %u\n", glyph->header.numContours);
for (i=0;i<glyph->header.numContours;i++) {
printf(" contour %d, ", (int)glyph->contours[i].count);
for (j=0;j<glyph->contours[i].count;j++) {
printf("<%f, %f, %c>",
glyph->contours[i].x[j] + offset, glyph->contours[i].y[j],
(glyph->contours[i].flags[j] & ONCURVE ? '0' : '1'));
if (j < glyph->contours[i].count - 1) {
printf(", ");
if (j > 0 && !(j % 5))
printf("\n");
}
else
printf("\n");
}
}
printf(" }\n");
}
int
main(int argc, char **argv)
{
FontFileInfo *ffile;
int i, slen;
unsigned char *search_string = NULL;
GlyphPtr glyph;
float toffset, offset;
#ifdef MAC
/* Special case for Macintosh - this command pulls up a
dialog that simulates a command line */
argc = ccommand(&argv);
#endif
if (argc < 2) {
printf("usage: ttfx *.ttf string [char offset]\n");
printf("e.g.\n");
printf(" ttfx arialb.ttf Foo 0.1\n");
printf(" ttfx \\windows\\system\\times.ttf Foo\n");
exit(-1);
}
else if (argc > 2) {
slen = strlen(argv[2]);
search_string = malloc((slen + 1) * sizeof(unsigned char));
for (i=0;i<=slen;i++)
search_string[i] = (unsigned char)argv[2][i];
if (argc > 3)
offset = atof(argv[3]);
else
offset = 0.0;
}
else {
slen = 1;
search_string = malloc(2 * sizeof(unsigned char));
search_string[0] = 'X';
search_string[1] = '\0';
offset = 0.0;
}
/* Get general font info */
ffile = ProcessFontFile(argv[1]);
printf("// Font: '%s'\n", ffile->filename);
if (slen > 1)
/* Need to create the string as the union of the glyphs that
make up each character in the string */
printf("object {\n");
/* Get info about each character in the string */
for (i=0,toffset=0.0;i<slen;i++) {
glyph = ProcessCharacter(ffile, search_string[i]);
/* printf("Kick: %f\n",
glyph->header.xMin / (float)glyph->parent->unitsPerEm); */
/* Align this character to x = 0 on the left */
toffset -= glyph->header.xMin / (float)glyph->parent->unitsPerEm;
/* Additional spacing based on the kerning table could go right here */
/* Now we convert the glyph into Polyray format */
OutputPolyrayGlyph(ffile, glyph, toffset);
/*
printf("Bump: %f\n", offset +
(glyph->header.xMax - glyph->header.xMin) /
(float)glyph->parent->unitsPerEm);
*/
/* Adjust character spacing to account for the width of the character,
plus the user-specified character spacing */
toffset += offset +
(glyph->header.xMax - glyph->header.xMin) /
(float)glyph->parent->unitsPerEm;
/* If this is a union, then need to emit the "+" character */
if (slen > 1 && i < slen - 1)
printf("+\n");
}
/* Close up the union of glyphs */
if (slen > 1)
printf(" }\n");
FreeFontInfo();
if (search_string != NULL)
free(search_string);
return 0;
}