/*
 * fontVF.c --
 *
 *      This file implements support for virtual fonts.
 *
 * Copyright © 1999 Anselm Lingnau <lingnau@tm.informatik.uni-frankfurt.de>
 * See file COPYING for conditions on use and distribution.
 */

#include <math.h>

#include "dviInt.h"

#ifndef lint
static char rcsid[] VAR_UNUSED = "$Id: fontVF.c,v 1.1 1999/06/15 08:08:50 lingnau Exp $";
#endif /* lint */

typedef struct VfCharInfo {
    S32 tfmWidth;		/* Device-independent width of character */
    S32 pixelWidth;		/* Horizontal escapement */
} VfCharInfo;

typedef struct VfInfo {
    S32 minChar, maxChar;
    Dvi_FontList *fontListPtr;	/* List of fonts used by this virtual font */
    Dvi_Font *firstFont;	/* First font in list (default font) */
    VfCharInfo **charInfoPtr;
} VfInfo;

/*
 * VF file `commands'.
 */

#define VF_LONG 242
#define VF_PRE 247
#define VF_POST 248
#define VF_ID 202

#include "dviOps.h"

/*
 * Macros for conversion of scaled quantities, inspired by `dvicopy'.
 */

#define SCALED3(a,b,c) ((((((a)*(z))/0400)+((b)*(z)))/0400)+((c)*(z)))/(beta)

#define SCALED(c) SCALED3((c)[3],(c)[2],(c)[1])

#define TFM_FIX4(c) ((c)[0] == 255 ? (SCALED(c) - alpha) : SCALED(c))
#define TFM_FIX3X(a,b,c) \
    ((c) > 127 ? (SCALED3((a),(b),(c)) - alpha) \
               : SCALED3((a),(b),(c)))
#define TFM_FIX3(c) TFM_FIX3X((c)[2],(c)[1],(c)[0])
#define TFM_FIX2(c) \
    ((c)[0] > 127 ? TFM_FIX3X((c)[0],(c)[1],255) : TFM_FIX3X((c)[0],(c)[1],0))
#define TFM_FIX1(c) \
    ((c)[0] > 127 ? TFM_FIX3X((c)[0],255,255) : TFM_FIX3X((c)[0],0,0))

/*
 * Prototypes for procedures referenced only in this file:
 */

static int CheckPreamble _ANSI_ARGS_((U8 *, U32, double, U32 *, U8 **));
static Dvi_FontList *ReadFontDefs _ANSI_ARGS_((Dvi_Interp *, Dvi_Font *,
					       int, int, Dvi_Font **, U8**));
static VfCharInfo **GetCharInfo _ANSI_ARGS_((U8 *, S32 *, S32 *));
static int VfLoad _ANSI_ARGS_((Dvi_Interp *dviInterp, Dvi_Font *dviFont));
static U8 *OutputDviOp _ANSI_ARGS_((int op, U8 *outPtr, S32 value));
static Dvi_Glyph *VfGlyph _ANSI_ARGS_((Dvi_Font *dviFont, S32 character,
				       S32 *tfmWidthPtr, S32 *pixelWidthPtr));
static int VfClose _ANSI_ARGS_((Dvi_Font *dviFont));

/*
 * ------------------------------------------------------------------------
 *
 * Dvi_CreateFontType_VF --
 *
 *      Installs the routines for handling virtual fonts.
 *
 * Results:
 *      TCL_OK if the virtual font routines could be installed properly,
 *      TCL_ERROR otherwise.
 *
 * Side effects:
 *      The virtual font handling routines are installed.
 *
 * ------------------------------------------------------------------------
 */

int
Dvi_CreateFontType_VF ()
{
    return Dvi_CreateFontType(dvi_font_vf, "vf", VfLoad, VfGlyph, VfClose);
}

/*
 * ------------------------------------------------------------------------
 *
 * CheckPreamble --
 *
 *      Does a basic consistency check and determines a number of
 *      parameters of a virtual font.
 *
 * Results:
 *      TCL_OK if the preamble is acceptable; TCL_ERROR if an error is
 *      detected.
 *
 * Side effects:
 *      Sets the parameters *designSizeVFPtr and *fontDefPtrPtr to the
 *      appropriate values.
 *
 * ------------------------------------------------------------------------
 */

static int
CheckPreamble (codePtr, checkSumDVI, tfmConv, designSizeVFPtr, fontDefPtrPtr)
    U8 *codePtr;		/* Pointer to VF file contents */
    U32 checkSumDVI;		/* Font checksum from DVI file */
    double tfmConv;		/* Conversion factor for VF (TFM) units */
    U32 *designSizeVFPtr;	/* For design size in VF file */
    U8 **fontDefPtrPtr;		/* Pointer to font defs after preamble */
{
    U32 checkSumVF;		/* Font checksum in VF file */

    /*
     * Check the identification bytes and skip the header comment.
     */

    if (*codePtr != VF_PRE || *(codePtr+1) != VF_ID) {
	return TCL_ERROR;
    }
    codePtr += 2; codePtr += *codePtr + 1;

    /*
     * Compare the checksums. (This doesn't do anything right now.)
     */

    checkSumVF = DviGetU32(codePtr);
    if (checkSumDVI && checkSumVF && checkSumDVI != checkSumVF) {
	/*
	 * Warn about checksum mismatch.
	 */
	;
    }

    codePtr += 4;
    *designSizeVFPtr = floor(tfmConv * DviGetU32(codePtr) + 0.5);

    *fontDefPtrPtr = codePtr + 4;

    return TCL_OK;
}

/*
 * ------------------------------------------------------------------------
 *
 * ReadFontDefs --
 *
 *      Reads font definitions from a VF preamble and locates the
 *      defined fonts. The fonts are added to the general pool of loaded
 *      fonts if they aren't found there in the first place. The font list
 *      returned by this function is stored in the VF info record for use
 *      when rendering.
 *
 * Results:
 *      A pointer to the list of fonts used by this virtual font.
 *
 * Side effects:
 *      *firstFontPtr is set to a pointer to the first font defined
 *      in the VF preamble. When a virtual font is accessed during
 *      rendering, this first font is the default font in the DVI
 *      interpreter, so having this accessible directly makes the
 *      VF rendering process more efficient -- in particular because
 *      otherwise the first font would be at the end of the list!
 *      *codePtrPtr is set to a pointer to the first character record
 *      in the VF file (just after the preamble).
 *
 * ------------------------------------------------------------------------
 */

static Dvi_FontList *
ReadFontDefs (dviInterp, dviFont, z, beta, firstFontPtr, codePtrPtr)
    Dvi_Interp *dviInterp;
    Dvi_Font *dviFont;
    int z, beta;
    Dvi_Font **firstFontPtr;
    U8 **codePtrPtr;
{
    U8 *codePtr;
    Dvi_FontList *fontListPtr = (Dvi_FontList *)0;
    Dvi_Font *firstFont = (Dvi_Font *)0;

    codePtr = *codePtrPtr;
    while (*codePtr >= D_FNTDEF1 && *codePtr <= D_FNTDEF4) {
	S32 k = 0;		/* Internal font number */
	U32 check;
	U32 designSize;
	U32 fontScale;
	size_t nameLen;

	switch (*codePtr++) {
	case D_FNTDEF4: k = DviGetS32(codePtr); codePtr += 4; break;
	case D_FNTDEF3: k |= *codePtr++ << 16;
	case D_FNTDEF2: k |= *codePtr++ << 8;
	case D_FNTDEF1: k |= *codePtr++;
	}

	check = DviGetU32(codePtr);
	fontScale = SCALED(codePtr + 4);
	designSize = floor(dviInterp->tfmConv * DviGetU32(codePtr + 8) + 0.5);
	nameLen = DviGetU8(codePtr + 12) + DviGetU8(codePtr + 13);

	Dvi_FontAdd(dviInterp, &fontListPtr, k, check, fontScale,
		    designSize, nameLen, (char *)(codePtr + 14));

	if (firstFont == 0) {
	    firstFont = fontListPtr->fontPtr;
	}

	codePtr += nameLen + 14;
    }
    *firstFontPtr = firstFont;
    *codePtrPtr = codePtr;
    return fontListPtr;
}

/*
 * ------------------------------------------------------------------------
 *
 * GetCharInfo --
 *
 *      Locates the minimum and maximum character codes defined by the
 *      font and allocates a vector of pointers to character information
 *      records. The method used here is inefficient for sparse fonts.
 *
 * Results:
 *      A pointer to a vector of VfCharInfo pointers. These are all
 *      set to the null pointer and will be populated later.
 *
 * Side effects:
 *      The variables *minCharPtr and *maxCharPtr are set to the minimum
 *      and maximum character code found in the virtual font, respectively.
 *
 * ------------------------------------------------------------------------
 */

static VfCharInfo **
GetCharInfo (codePtr, minCharPtr, maxCharPtr)
    U8 *codePtr;
    S32 *minCharPtr;
    S32 *maxCharPtr;
{
    S32 minChar, maxChar;
    VfCharInfo **charInfoPtr;
    int i;
    
    /*
     * Make a pass through the file to determine the minimum and maximum
     * charcter codes.
     */

    minChar = MAX_S32;
    maxChar = MIN_S32;

    while (*codePtr != VF_POST) {
	U32 pl;
	S32 cc;
	if (*codePtr == VF_LONG) {
	    pl = DviGetU32(codePtr + 1);
	    cc = DviGetU32(codePtr + 5);
	    codePtr += 13 + pl;
	} else {
	    pl = *codePtr;
	    cc = codePtr[1];
	    codePtr += 5 + pl;
	}
	if (cc < minChar) {
	    minChar = cc;
	}
	if (cc > maxChar) {
	    maxChar = cc;
	}
    }

    charInfoPtr = (VfCharInfo **)ckalloc((maxChar - minChar + 1)
					 * sizeof(VfCharInfo *));
    if (charInfoPtr == (VfCharInfo **)0) {
	return (VfCharInfo **)0;
    }

    for (i = 0; i < maxChar - minChar + 1; i++) {
	charInfoPtr[i] = (VfCharInfo *)0;
    }

    *minCharPtr = minChar;
    *maxCharPtr = maxChar;
    return charInfoPtr;
}

/*
 * ------------------------------------------------------------------------
 *
 * OutputDVIOp --
 *
 *      Outputs a scaled DVI value with a suitable opcode. When we read
 *      DVI code from the VF file, some commands have parameters which
 *      are relative to the design size of the virtual font. These are
 *      converted to suitable absolute distances. However, we must take
 *      care when outputting them that the opcodes match the byte size of
 *      the scaled parameters, since that can change when the values
 *      are scaled.
 *
 * Results:
 *      A pointer to a position in the output buffer just after the
 *      last byte output by the current invocation of this function.
 *
 * Side effects:
 *      A DVI opcode and parameter is written to the output buffer
 *      at the position pointed to by outPtr.
 *
 * ------------------------------------------------------------------------
 */

static U8 *
OutputDviOp (op, outPtr, value)
    int op;
    U8 *outPtr;
    S32 value;
{
    U8 b[4];
    int length;
    int i;

    /*
     * Separate the value into individual bytes.
     */

    b[0] = value & 0xff; value >>= 8;
    b[1] = value & 0xff; value >>= 8;
    b[2] = value & 0xff; value >>= 8;
    b[3] = value & 0xff;

    /*
     * Determine the number of bytes that are actually occupied by the
     * value. We must take care of the case of negative values that do
     * not have the most significant bit set in the first non-0xff byte
     * -- these have a 0xff byte added at the beginning to make clear
     * that they are really negative.
     */

    for (length = 3; length > 0; length--) {
	if (b[length] != 0 && b[length] != 0xff) {
	    break;
	}
    }
    if (length < 3 && b[length] < 0x80 && b[length+1] == 0xff) {
	length++;
    }

    /*
     * The opcode passed to us is the `shortest' variant. We add the
     * length that we have determined to produce the correct opcode
     * for the value that we are writing.
     */

    *outPtr++ = op + length;
    for (i = length; i >= 0; i--) {
	*outPtr++ = b[i];
    }
    return outPtr;
}

/*
 * ------------------------------------------------------------------------
 *
 * VfLoad --
 *
 *      Loads a virtual font file into memory and constructs the internal
 *      tables that describe its contents.
 *
 * Results:
 *      A standard Tcl result. If an error occurs and dviInterp is
 *      non-zero, a more detailed error message is left in the Tcl
 *      interpreter result.
 *
 * Side effects:
 *      The virtual font is loaded.
 *
 * ------------------------------------------------------------------------
 */

static int
VfLoad (dviInterp, dviFont)
    Dvi_Interp *dviInterp;
    Dvi_Font *dviFont;
{
    U32 designSizeVF;
    Dvi_FontList *fontListPtr = (Dvi_FontList *)0;
    Dvi_Font *firstFont;
    U8 *codePtr;
    long int z, alpha, beta;
    VfInfo *vfInfoPtr;
    VfCharInfo *charInfo;
    VfCharInfo **charInfoPtr;
    S32 minChar, maxChar;
    size_t length;

    /*
     * Load the font file.
     */

    dviFont->bytes = Dvi_LoadFileBinary(dviFont->fileName);
    if (dviFont->bytes == (U8 *)0) {
	if (dviInterp != (Dvi_Interp *)0) {
	    Tcl_SetResult(dviInterp->interp, "couldn't load file", TCL_STATIC);
	}
	return TCL_ERROR;
    }

    /*
     * Check the preamble and bail out if it is not acceptable.
     */

    if (CheckPreamble(dviFont->bytes, dviFont->check, dviInterp->tfmConv,
		      &designSizeVF, &codePtr) != TCL_OK) {
	if (dviInterp != (Dvi_Interp *)0) {
	    Tcl_SetResult(dviInterp->interp, "error in VF preamble",
			  TCL_STATIC);
	}
	ckfree((char *)dviFont->bytes);
	return TCL_ERROR;
    }

    /*
     * Set up the parameters for the scaling calculations. This is done
     * in a tricky manner to avoid overflow; see paragraph 571 of
     * `TeX: The Program' for more information. (A bunch of macros that
     * help with the calculations have been defined earlier in this file.)
     */

    z = dviFont->fontScale;
    alpha = 16;
    while (z >= 040000000) {
	z /= 2, alpha += alpha;
    }
    beta = 256 / alpha; alpha *= z;

    /*
     * Read the font definitions and allocate space for the pointers
     * to the individual character information records.
     */

    fontListPtr = ReadFontDefs(dviInterp, dviFont, z, beta, &firstFont,
			       &codePtr);

    if ((charInfoPtr = GetCharInfo(codePtr, &minChar, &maxChar))
	== (VfCharInfo **)0) {
	if (dviInterp != (Dvi_Interp *)0) {
	    Tcl_SetResult(dviInterp->interp, "not enough memory",
			  TCL_STATIC);
	}
	/* TODO: Free the fonts */
	ckfree((char *)dviFont->bytes);
	return TCL_ERROR;
    }

    /*
     * Now we go through the DVI code snippets for the individual
     * characters in this font to convert the `relative' parameters
     * in the DVI code to absolute parameters as per the current
     * Dvi_Interp. This ties this virtual font in its loaded form to
     * the parameters defined in the Dvi_Interp, but this is not a
     * problem since it will be stored with the other fonts that
     * belong with these parameters, anyway.
     */

    while (*codePtr != VF_POST) {
	U32 pl;			/* Packet length */
	S32 cc;			/* Character code */
	S32 tfm;		/* TFM width */
	U8 *limit;		/* Just beyond the end of the DVI code */
	U8 buf[2000];		/* Buffer for conversion. This should be */
				/* dynamic. */
	U8 *outPtr;		/* Pointer into output conversion buffer */

	/*
	 * Determine the font parameters.
	 */

	if (*codePtr == VF_LONG) {
	    pl = DviGetU32(codePtr + 1);
	    cc = DviGetU32(codePtr + 5);
	    tfm = TFM_FIX4(codePtr + 9);
	    codePtr += 13;
	} else {
	    pl = *codePtr++;
	    cc = *codePtr++;
	    tfm = TFM_FIX3(codePtr); codePtr += 3;
	}

	/*
	 * Go through the DVI code for the font and scale all relevant
	 * values.
	 */

	limit = codePtr + pl;
	outPtr = buf;
	while (codePtr < limit) {
	    U32 result;
	    if (*codePtr < 128 || *codePtr == D_PUSH || *codePtr == D_POP
		|| *codePtr == D_W0 || *codePtr == D_X0
		|| *codePtr == D_Y0 || *codePtr == D_Z0
		|| (*codePtr >= D_FNTNUM0 && *codePtr < D_FNT1)
		|| *codePtr == D_NOP) {
		*outPtr++ = *codePtr++;
	    } else if (*codePtr == D_SET1 || *codePtr == D_PUT1
		       || *codePtr == D_FNT1) {
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
	    } else if (*codePtr == D_W1 || *codePtr == D_X1
		      || *codePtr == D_Y1 || *codePtr == D_Z1) {
		outPtr = OutputDviOp(*codePtr, outPtr, TFM_FIX1(codePtr + 1));
		codePtr += 2;
	    } else if (*codePtr == D_SET2 || *codePtr == D_PUT2
		       || *codePtr == D_FNT2) {
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
	    } else if (*codePtr == D_W2 || *codePtr == D_X2
		      || *codePtr == D_Y2 || *codePtr == D_Z2) {
		outPtr = OutputDviOp(*codePtr - 1, outPtr,
				     TFM_FIX2(codePtr + 1));
		codePtr += 3;
	    } else if (*codePtr == D_SET3 || *codePtr == D_PUT3
		       || *codePtr == D_FNT3) {
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
	    } else if (*codePtr == D_W3 || *codePtr == D_X3
		      || *codePtr == D_Y3 || *codePtr == D_Z3) {
		outPtr = OutputDviOp(*codePtr - 2, outPtr,
				     TFM_FIX3(codePtr + 1));
		codePtr += 4;
	    } else if (*codePtr == D_SET4 || *codePtr == D_PUT4
		       || *codePtr == D_FNT4) {
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
		*outPtr++ = *codePtr++;
	    } else if (*codePtr == D_W4 || *codePtr == D_X4
		      || *codePtr == D_Y4 || *codePtr == D_Z4) {
		outPtr = OutputDviOp(*codePtr - 3, outPtr,
				     TFM_FIX4(codePtr + 1));
		codePtr += 5;
	    } else if (*codePtr == D_SETRULE || *codePtr == D_PUTRULE) {
		int i;
		*outPtr++ = *codePtr++;
		result = TFM_FIX4(codePtr);
		for (i = 3; i >= 0; i--) {
		    *outPtr++ = (result >> (i*8)) & 0xff;
		}
		result = TFM_FIX4(codePtr + 4);
		for (i = 3; i >= 0; i--) {
		    *outPtr++ = (result >> (i*8)) & 0xff;
		}
		codePtr += 8;
	    } else if (*codePtr == D_XXX1 || *codePtr == D_XXX2
		       || *codePtr == D_XXX3 || *codePtr == D_XXX4) {
		U32 len = 0;
		*outPtr++ = *codePtr++;
		switch (codePtr[-1]) {
		  case D_XXX4: len = *outPtr++ = *codePtr++ << 24;
		  case D_XXX3: len |= *outPtr++ = *codePtr++ << 16;
		  case D_XXX2: len |= *outPtr++ = *codePtr++ << 8;
		  case D_XXX1: len |= *outPtr++ = *codePtr++;
		}
		while (len-- > 0) {
		    *outPtr++ = *codePtr++;
		}
	    } else {
		fprintf(stderr, "VfLoad: Forgotten case %d\n", *codePtr);
	    }
	}
	*outPtr++ = D_EOP;

	/*
	 * Store the character information and modified DVI code. We
	 * allocate a chunk of dynamic memory big enough for the
	 * header and code and put the code immediately after the header.
	 */

	length = outPtr - buf; 
	charInfo = (VfCharInfo *)ckalloc(sizeof(VfCharInfo) + length);
	if (charInfo == (VfCharInfo *)0) {
	    return TCL_ERROR;
	}

	charInfo->tfmWidth = tfm;
	charInfo->pixelWidth = floor(tfm*dviInterp->xConv + 0.5);
	memcpy((U8 *)charInfo + sizeof(VfCharInfo), buf, length);

	charInfoPtr[cc - minChar] = charInfo;

#if 0
	{
	    U8 *pp = buf;
	    fprintf(stderr, "Packet:");
	    while (pp < outPtr) { fprintf(stderr, " %02x", *pp++); }
	    fprintf(stderr, " (%d bytes)\n", outPtr - buf);
	}
#endif
    }

    /*
     * Construct a descriptive record for the font as a whole, using
     * the information that we have just determined.
     */

    vfInfoPtr = (VfInfo *)ckalloc(sizeof(VfInfo));
    if (vfInfoPtr == (VfInfo *)0) {
	return TCL_ERROR;
    }

    vfInfoPtr->minChar = minChar;
    vfInfoPtr->maxChar = maxChar;
    vfInfoPtr->charInfoPtr = charInfoPtr;
    vfInfoPtr->fontListPtr = fontListPtr;
    vfInfoPtr->firstFont = firstFont;

    dviFont->fontData = (ClientData)vfInfoPtr;

    return TCL_OK;
}

/*
 * ------------------------------------------------------------------------
 *
 * Dvi_GetVFInfo --
 *
 *      Returns information about a character in a virtual font. This
 *      function is an analogue to the Dvi_FontGetGlyph and xxxGlyph
 *      function used by `real' fonts. It is invoked by the DVI interpreter
 *      when rendering a DVI file using virtual fonts, in order to find
 *      the TFM and pixel widths and the font list, default font and
 *      code address of the virtual character definition in question.
 *
 * Results:
 *      A standard Tcl result; TCL_ERROR if there is no corresponding
 *      character in the font, TCL_OK otherwise.
 *
 * Side effects:
 *      If the character is found, the variables pointed to by
 *      tfmWidthPtr, pixelWidthPtr, firstFontPtr, codePtrPtr and
 *      fontListPtrPtr are assigned the appropriate values.
 *
 * ------------------------------------------------------------------------
 */

int
Dvi_GetVFInfo (dviFont, character, tfmWidthPtr, pixelWidthPtr,
	       firstFontPtr, codePtrPtr, fontListPtrPtr)
    Dvi_Font *dviFont;
    S32 character;
    S32 *tfmWidthPtr;		/* for returning width in DVI coordinates */
    S32 *pixelWidthPtr;		/* for returning width in device coordinates */
    Dvi_Font **firstFontPtr;	/* first font defined in preamble (default) */
    U8 **codePtrPtr;		/* start of code snippet for this character */
    Dvi_FontList **fontListPtrPtr; /* font list for this virtual font */
{
    VfInfo *vfInfoPtr = (VfInfo *)(dviFont->fontData);
    VfCharInfo *charInfoPtr;

    if (character < vfInfoPtr->minChar
	|| character > vfInfoPtr->maxChar) {
	return TCL_ERROR;
    }
    charInfoPtr = vfInfoPtr->charInfoPtr[character-vfInfoPtr->minChar];
    if (charInfoPtr == 0) {
	return TCL_ERROR;
    }

    *tfmWidthPtr = charInfoPtr->tfmWidth;
    *pixelWidthPtr = charInfoPtr->pixelWidth;
    *codePtrPtr = (U8 *)charInfoPtr + sizeof(VfCharInfo);

    *firstFontPtr = vfInfoPtr->firstFont;
    *fontListPtrPtr = vfInfoPtr->fontListPtr;
    return TCL_OK;
}

/*
 * ------------------------------------------------------------------------
 *
 * VfGlyph --
 *
 *      This function doesn't do anything and is included only for
 *      completeness.
 *
 * ------------------------------------------------------------------------
 */

static Dvi_Glyph *
VfGlyph (dviFont, character, tfmWidthPtr, pixelWidthPtr)
    Dvi_Font *dviFont;		/* font to be searched */
    S32 character;		/* glyph to be found */
    S32 *tfmWidthPtr;		/* for returning width in DVI coordinates */
    S32 *pixelWidthPtr;		/* for returning width in device coordinates */
{
    return (Dvi_Glyph *)0;
}

/*
 * ------------------------------------------------------------------------
 *
 * VfClose --
 *
 *      Deletes a virtual font that is no longer required. All the fonts
 *      it uses are freed (so may be deleted as well), and the memory
 *      used by the tables is given back to the process for reuse.
 *
 * ------------------------------------------------------------------------
 */

static int
VfClose (dviFont)
    Dvi_Font *dviFont;
{
    VfInfo *vfInfoPtr = (VfInfo *)(dviFont->fontData);
    VfCharInfo **charInfoPtr = vfInfoPtr->charInfoPtr;
    int i;
    Dvi_FontList *fontListPtr;

    for (i = vfInfoPtr->minChar; i <= vfInfoPtr->maxChar; i++) {
	if (charInfoPtr[i - vfInfoPtr->minChar] != (VfCharInfo *)0) {
	    ckfree((char *)charInfoPtr[i - vfInfoPtr->minChar]);
	}
    }
    ckfree((char *)charInfoPtr);

    for (fontListPtr = vfInfoPtr->fontListPtr;
	 fontListPtr;
	 fontListPtr = fontListPtr->nextPtr) {
	Dvi_FontFree(fontListPtr->fontPtr);
    }

    ckfree((char *)vfInfoPtr);

    return TCL_OK;
}
