/************************************************************************
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

************************************************************************/

/* $Header: fonts.c,v 1.36 87/06/22 21:02:24 toddb Exp $ */

#define NEED←REPLIES
#include "X.h"
#include "Xmd.h"
#include "Xproto.h"
#include "dixfontstr.h"
#include "fontstruct.h"
#include "scrnintstr.h"
#include "resource.h"
#include "osstruct.h"
#include "dix.h"
#include "cursorstr.h"
#include "misc.h"
#include "opaque.h"
#include <strings.h>

extern FID FiOpenForRead();

static FontPtr 	pFontHead = NullFont;

/*
 * adding RT←FONT prevents conflict with default cursor font
 */
SetDefaultFont( defaultfontname)
    char *	defaultfontname;
{
    FontPtr	pf;

    extern FontPtr 	defaultFont;

    if ((pf =OpenFont( strlen( defaultfontname), defaultfontname)) == NullFont)
	return FALSE;

    AddResource( FakeClientID(0), RT←FONT, (pointer)pf, CloseFont, RC←CORE);
    defaultFont = pf;
    return TRUE;
}

/*
 * Check reference count first, load font only if necessary.
 */
FontPtr 
OpenFont( lenfname, pfilename)
    int		lenfname;
    char *	pfilename;
{
    FontPtr 	pfont;
    int		lenpname;
    char *	ppathname;
    FID		pf;
    int		nscr;
    ScreenPtr	pscr;

    FontPtr 	ReadNFont();

    /*
     * call os-dependent code
     */
    if ( (lenpname = ExpandFontName( &ppathname, lenfname, pfilename)) == 0)
    {
	ErrorF( "OpenFont: ExpandFontName failed to find file '%s', (len = %d)\n",
		pfilename, lenfname);
	return NullFont;
    }
    /*
     * first look up font name in list of opened fonts 
     */
    for (   pfont = pFontHead;
	    pfont != NullFont;
	    pfont = pfont->next)
	if ( lenpname == pfont->lenpname
	  && strncmp( pfont->pathname, ppathname, pfont->lenpname) == 0)
	{
	    /*
	     * found it!
	     */
	    pfont->refcnt += 1;
	    /*
	    Xfree(ppathname);
	    */
	    return pfont;
	}
	
    /*
     * if not found in fonts list, read it off disk
     */

    if ( (pf = FiOpenForRead( lenpname, ppathname)) == NullFID)
    {
	ErrorF( 
		"OpenFont: failed to open font file %s\n",
		ppathname);
	Xfree(ppathname);
	return NullFont;
    }
    pfont = ReadNFont( pf);
    FiClose( pf);

    if ( pfont == NullFont)
    {
	ErrorF(  "OpenFont: ReadNFont failed on file %s\n", ppathname);
	Xfree(ppathname);
	return NullFont;
    }

    pfont->lenpname = lenpname;
    pfont->pathname = (char *)Xalloc( lenpname);	/* record pathname */
    strncpy( pfont->pathname, ppathname, lenpname);

    pfont->refcnt = 1;
    pfont->next = pFontHead;			/* prepend to fonts list */
    pFontHead = pfont;

    /*
     * since this font has been newly read off disk, ask each screen to
     * realize it.
     */
    for ( nscr=0, pscr=screenInfo.screen;
	  nscr<screenInfo.numScreens;
	  nscr++, pscr++)
    {
        if ( pscr->RealizeFont)
	    ( *pscr->RealizeFont)( pscr, pfont);
    }
    /*
    Xfree(ppathname);
    */
    return pfont;
}

/*
 * Decrement font's ref count, and free storage if ref count equals zero
 */
int
CloseFont( pfont, id)
    FontPtr 	pfont;
    long id;
{
    FontPtr 	ptf;
    FontPtr 	pbtf = NullFont;
    int		nscr;
    ScreenPtr	pscr;

    for (   ptf = pFontHead;
	    ptf != NullFont;
	    ptf = ptf->next)
    {
	if ( ptf == pfont || ptf == NullFont)
	    break;
	pbtf = ptf;
    }
    if ( ptf == NullFont)
    {
	ErrorF(  "CloseFont: couldn't find font to be closed %x\n", ptf);
	return;
    }
    if ( --ptf->refcnt == 0)
    {
	if ( pbtf == NullFont)		/* unlink ptf */
	    pFontHead = ptf->next;
	else
	    pbtf->next = ptf->next;

	/*
	 * since the last reference is gone, ask each screen to
	 * free any storage it may have allocated locally for it.
	 */
	for ( nscr=0, pscr=screenInfo.screen;
	      nscr<screenInfo.numScreens;
	      nscr++, pscr++)
	{
	    if ( pscr->UnrealizeFont)
		( *pscr->UnrealizeFont)( pscr, ptf);
	}
	Xfree( ptf->pathname);
	Xfree( ptf);
	if (pfont == defaultFont)
	    defaultFont = NULL;
    }
}


/*
 * description
 *
 * Allocates storage with Xalloc(), and returns a pointer to it.
 * Client can call Xfree() to deallocate font.
 *
 * file I/O is all in this routine
 */
FontPtr 
ReadNFont( fp)
    FID		fp;	/* caller has to FiOpenForRead the file himself */
{
    FontPtr 	pfont;
    FontInfoRec	fi;
    DIXFontProp *pdfp;
    FontPropPtr	pffp;
    char	*propspace;
    int		bytesread, bytestoread, bytestoalloc;
    int		bytesdata;
    int		i;
    char *strings;
	
    /* swap bytes? XXX */

    bytestoread = BYTESOFFONTINFO(&fi);
    if ( (bytesread = FiRead( (char *)&fi, 1, bytestoread, fp)) != bytestoread)
    {
	ErrorF("ReadNFont: unexpected EOF\n");
        return NullFont;
    }

    if (fi.version1 != FONT←FILE←VERSION || fi.version2 != FONT←FILE←VERSION)
    {
	ErrorF("ReadNFont: bad font version; expected %d, got %d and %d\n",
		FONT←FILE←VERSION, fi.version1, fi.version2);
	return NullFont;
    }

    bytesdata = BYTESOFFONTINFO(&fi);
    bytesdata += BYTESOFCHARINFO(&fi);
    bytesdata += BYTESOFGLYPHINFO(&fi);

    bytestoalloc = BYTESOFFONTINFO(&fi)+bytesdata+BYTESOFPROPINFO(&fi);
    pfont = (FontPtr ) Xalloc(bytestoalloc);

    bytestoread = bytesdata - BYTESOFFONTINFO(&fi);
    if ( (bytesread = FiRead(
		(char *)(&pfont[1]) + BYTESOFFONTINFO(&fi),
		1, bytestoread,
		fp )) != bytestoread)
    {
	ErrorF("ReadNFont: unexpected EOF\n");
	Xfree(pfont);
        return NullFont;
    }

    /*
     * now fix up pointers
     */
    pfont->pFI = (FontInfoPtr)&pfont[1];
    *pfont->pFI = fi;	/* copy date previously read */
    
    pfont->pCI = ADDRCharInfoRec(pfont->pFI);

    pfont->pGlyphs = ADDRCHARGLYPHS(pfont->pFI);

    pfont->pFP = ADDRXFONTPROPS(pfont->pFI);

    /* now read and atom'ize properties */

    bytestoalloc = BYTESOFPROPINFO(pfont->pFI) + BYTESOFSTRINGINFO(pfont->pFI);
    propspace = (char *) Xalloc(bytestoalloc);
    
    pffp = (FontPropPtr)propspace;
    strings = propspace + BYTESOFPROPINFO(pfont->pFI);
    
    bytestoread = bytestoalloc;
    if ( (bytesread = FiRead(
		(char *)pffp, 1,
		bytestoread,
		fp)) != bytestoread)
    {
	ErrorF("ReadNFont: unexpected EOF\n");
	Xfree(pfont);
	Xfree(propspace);
        return NullFont;
    }

    for (i=0, pdfp=pfont->pFP; i<fi.nProps; i++, pdfp++, pffp++)
    {
	pdfp->name = MakeAtom(
		&strings[pffp->name], strlen(&strings[pffp->name]), 1);
	if (pffp->indirect)
		pdfp->value = (INT32)MakeAtom(
		    &strings[pffp->value], strlen(&strings[pffp->value]), 1);
    }

    Xfree(propspace);

    return pfont;
}

static void
DescribeFont(pfontname, lenfname, pfi, ppfp)
    char *pfontname;
    int lenfname;
    FontInfoPtr pfi;
    DIXFontProp **ppfp;
{
    FontPtr pfont;
    FID pf;
    int i;
    char *ppathname;
    int lenpname;
    int bytesskip, bytesprops;
    FontInfoRec fi;
    DIXFontProp *pdfp;
    FontPropPtr pffp, temp;
    char *strings;

    bzero(pfi, sizeof(FontInfoRec));
    *ppfp = NullDIXFontProp;		/* in case of failure */
    
    if ((lenpname = ExpandFontName(&ppathname, lenfname, pfontname)) == 0)
        return;
    /*
     * first look up font name in list of opened fonts 
     */
    for (   pfont = pFontHead;
	    pfont != NullFont;
	    pfont = pfont->next)
	if ( lenpname == pfont->lenpname
	  && strncmp( pfont->pathname, ppathname, pfont->lenpname) == 0)
	{
	    *pfi = *pfont->pFI;
	    *ppfp = (DIXFontProp *)Xalloc(sizeof(DIXFontProp)*pfi->nProps);
	    if (*ppfp == NullDIXFontProp)
	        return;
	    bcopy((char *)pfont->pFP, (char *)*ppfp,
	        (int)sizeof(DIXFontProp)*pfi->nProps);
	    return;
	}

    /* have to read it off disk */
    if ((pf = FiOpenForRead(lenpname, ppathname)) == NullFID)
	return;
    if ((FiRead((char *)&fi, 1, sizeof(FontInfoRec), pf) != sizeof(FontInfoRec)) ||
	fi.version1 != FONT←FILE←VERSION ||
	fi.version2 != FONT←FILE←VERSION)
	    return;

    bytesskip = BYTESOFFONTINFO(&fi) - sizeof(FontInfoRec) + BYTESOFCHARINFO(&fi) + BYTESOFGLYPHINFO(&fi);
    bytesprops = BYTESOFPROPINFO(&fi) + BYTESOFSTRINGINFO(&fi);

    temp = (FontPropPtr)Xalloc(max(bytesskip, bytesprops));

    if (FiRead((char *)temp, 1, bytesskip, pf) != bytesskip)
    {
	Xfree((char *)temp);
        return;
    }
    if (FiRead((char *)temp, 1, bytesprops, pf) != bytesprops)
    {
	Xfree((char *)temp);
        return;
    }
    *ppfp = (DIXFontProp *)Xalloc(pfi->nProps * sizeof(DIXFontProp));
    if (*ppfp == NullDIXFontProp)
    {
	Xfree((char *)temp);
	return;
    }
    strings = (char *)&temp[fi.nProps];
    pffp = temp;
    for (i=0, pdfp=(*ppfp); i<fi.nProps; i++, pdfp++, pffp++)
    {
	pdfp->name = MakeAtom(
		&strings[pffp->name], strlen(&strings[pffp->name]), 1);
	if (pffp->indirect)
		pdfp->value = (INT32)MakeAtom(
		    &strings[pffp->value], strlen(&strings[pffp->value]), 1);
    }
    Xfree((char *)temp);
    *pfi = fi;
}

void
DescribeFontList(paths, info)
    FontPathPtr paths;
    FontDataPtr info;
{
    int i;
    
    for (i=0; i<paths->npaths; i++)
    {
	DescribeFont(paths->paths[i], paths->length[i],
	    &info->pFI[i], &info->pFP[i]);
    }

}

#ifdef notdef
/*
 * assumes short is 2 bytes and long is 4, but nothing about padding
 */
#define ←us	unsigned short
#define ←ul	unsigned long
#define sw2( p)		\
	*((←us *)p) =	\
	      *(←us *)p>>8	\
	    | *(←us *)p<<8;
#define sw4( p)		\
	*((←ul *)p) =	\
	      *(←ul *)p>>24	\
	    | *(←ul *)p>>8 & 0x0000ff00	\
	    | *(←ul *)p<<8 & 0x00ff0000	\
	    | *(←ul *)p<<24;

#else

#define sw2( p) {	\
		int	t;	\
		t = ((char *)p)[0];	\
		((char *)p)[0] = ((char *)p)[1];	\
		((char *)p)[1] = t;	\
	}
#define sw4( p)	{	\
		int	t;	\
		t = ((char *)p)[0];	\
		((char *)p)[0] = ((char *)p)[3];	\
		((char *)p)[3] = t;	\
		t = ((char *)p)[1];	\
		((char *)p)[1] = ((char *)p)[2];	\
		((char *)p)[2] = t;	\
	}

#endif

void
QueryFont( pf, pr, nprotoxcistructs)
    FontPtr 		pf;
    xQueryFontReply *	pr;	/* caller must allocate this storage */
    int		nprotoxcistructs;
{
    FontInfoPtr 	pfi = pf->pFI;
    CharInfoPtr 	pci;
    DIXFontProp *	pfp;
    int		ct;
    xFontProp *	prfp;
    xCharInfo *	prci;

    void	queryCharInfo();

    /* pr->length set in dispatch */
    pr->minCharOrByte2 = pfi->firstCol;
    pr->defaultChar = pfi->chDefault;
    pr->maxCharOrByte2 = pfi->lastCol;
    pr->drawDirection = pfi->drawDirection;
    pr->allCharsExist = pfi->allExist;
    pr->minByte1 = pfi->firstRow;
    pr->maxByte1 = pfi->lastRow;
    pr->fontAscent = pfi->fontAscent;
    pr->fontDescent = pfi->fontDescent;

    queryCharInfo( &pfi->minbounds, &pr->minBounds); 
    queryCharInfo( &pfi->maxbounds, &pr->maxBounds); 

    pr->nFontProps = pfi->nProps; 
    pr->nCharInfos = nprotoxcistructs; 


    for ( ct=0,
	    pfp=pf->pFP,
	    prfp=(xFontProp *)(&pr[1]);
	  ct < pfi->nProps;
	  ct++, pfp++, prfp++)
    {
	prfp->name = pfp->name;
	prfp->value = pfp->value;
    }

    for ( ct=0,
	    pci = &pf->pCI[0],
	    prci=(xCharInfo *)(prfp);
	  ct<nprotoxcistructs;
	  ct++, pci++, prci++)
	queryCharInfo( pci, prci);
}

/* static */ void
queryCharInfo( pci, pr)
    CharInfoPtr 		pci;
    xCharInfo *		pr;	/* protocol packet to fill in */
{
    *pr = pci->metrics;
}

/* static */ void
SwapFont( pr)
    xQueryFontReply *	pr;
{
    int		i;
    xCharInfo *	pxci;
    int		nchars, nprops;
    char	*pby;
    register char n;

    swapl(&pr->length, n);
    nchars = pr->nCharInfos;
    nprops = pr->nFontProps;
    SwapFontInfo(pr);
    pby = (char *) &pr[1];
    /* Font properties are an atom and either an int32 or a CARD32, so
     * they are always 2 4 byte values */
    for(i = 0; i < nprops; i++)
    {
	swapl(pby, n);
	pby += 4;
	swapl(pby, n);
	pby += 4;
    }
    pxci = (xCharInfo *)pby;
    for(i = 0; i< nchars; i++, pxci++)
	SwapCharInfo(pxci);
}

SwapFontInfo(pr)
    xQueryFontReply *pr;
{
    register char		n;

    swaps(&pr->minCharOrByte2, n);
    swaps(&pr->maxCharOrByte2, n);
    swaps(&pr->defaultChar, n);
    swaps(&pr->nFontProps, n);
    swaps(&pr->fontAscent, n);
    swaps(&pr->fontDescent, n);
    SwapCharInfo( &pr->minBounds);
    SwapCharInfo( &pr->maxBounds);
    swapl(&pr->nCharInfos, n);
}
SwapCharInfo(pInfo)
    xCharInfo	*pInfo;
{
    register char n;

    swaps(&pInfo->leftSideBearing, n);
    swaps(&pInfo->rightSideBearing, n);
    swaps(&pInfo->characterWidth, n);
    swaps(&pInfo->ascent, n);
    swaps(&pInfo->descent, n);
    swaps(&pInfo->attributes, n);
}


/* text support routines. A charinfo array builder, and a bounding */
/* box calculator */

void
GetGlyphs(font, count, chars, fontEncoding, glyphcount, glyphs)
    FontPtr font;
    int count;
    register unsigned char *chars;
    FontEncoding fontEncoding;
    unsigned int *glyphcount;	/* RETURN */
    CharInfoPtr glyphs[];	/* RETURN */
{
    CharInfoPtr		pCI = font->pCI;
    FontInfoPtr		pFI = font->pFI;
    unsigned int	firstCol = pFI->firstCol;
    unsigned int	numCols = pFI->lastCol - firstCol + 1;
    unsigned int	firstRow = pFI->firstRow;
    unsigned int	numRows = pFI->lastRow - firstRow + 1;
    unsigned int	chDefault = pFI->chDefault;
    register int	i;
    int			n;
    register unsigned int	c;
    register CharInfoPtr	ci;

    n = 0;
    switch (fontEncoding) {

	case Linear8Bit:
	case TwoD8Bit:
	    for (i=0; i < count; i++) {

		c = (*chars++) - firstCol;
		if (c < numCols) {
		    ci = &pCI[c];
		    if (ci->exists) {glyphs[n++] = ci; continue;}
		}

		c = chDefault - firstCol;
		if (c < numCols) {
		    ci = &pCI[c];
		    if (ci->exists) glyphs[n++] = ci;
		}
	    }
	    break;

	case Linear16Bit:
	    for (i=0; i < count; i++) {

		chars++;
		c = (*chars++) - firstCol;
		if (c < numCols) {
		    ci = &pCI[c];
		    if (ci->exists) {glyphs[n++] = ci; continue;}
		}

		c = chDefault - firstCol;
		if (c < numCols) {
		    ci = &pCI[c];
		    if (ci->exists) glyphs[n++] = ci;
		}
	    }
	    break;

	case TwoD16Bit:
	    for (i=0; i < count; i++) {
		register unsigned int row;
		register unsigned int col;

		row = (*chars++) - firstRow;
		col = (*chars++) - firstCol;
		if ((row < numRows) && (col < numCols)) {
		    c = row*numCols + col;
		    ci = &pCI[c];
		    if (ci->exists) {glyphs[n++] = ci; continue;}
		}

		row = (chDefault >> 8)-firstRow;
		col = (chDefault & 0xff)-firstCol;
		if ((row < numRows) && (col < numCols)) {
		    c = row*numCols + col;
		    ci = &pCI[c];
		    if (ci->exists) glyphs[n++] = ci;
		}
	    }
	    break;
    }
    *glyphcount = n;
}



void
QueryGlyphExtents(font, charinfo, count, info)
    FontPtr font;
    CharInfoPtr *charinfo;
    unsigned int count;
    ExtentInfoRec *info;
{
    int	i;

    info->drawDirection = font->pFI->drawDirection;

    info->fontAscent = font->pFI->fontAscent;
    info->fontDescent = font->pFI->fontDescent;

    if (count != 0) {

	info->overallAscent  = charinfo[0]->metrics.ascent;
	info->overallDescent = charinfo[0]->metrics.descent;
	info->overallLeft    = charinfo[0]->metrics.leftSideBearing;
	info->overallRight   = charinfo[0]->metrics.rightSideBearing;
	info->overallWidth   = charinfo[0]->metrics.characterWidth;

	for (i=1; i < count; i++) {
	    info->overallAscent = max(
	        info->overallAscent,
		charinfo[i]->metrics.ascent);
	    info->overallDescent = max(
	        info->overallDescent,
		charinfo[i]->metrics.descent);
	    info->overallLeft = min(
		info->overallLeft,
		info->overallWidth+charinfo[i]->metrics.leftSideBearing);
	    info->overallRight = max(
		info->overallRight,
		info->overallWidth+charinfo[i]->metrics.rightSideBearing);
	    /* yes, this order is correct; overallWidth IS incremented last */
	    info->overallWidth += charinfo[i]->metrics.characterWidth;
	}

    } else {

	info->overallAscent  = 0;
	info->overallDescent = 0;
	info->overallWidth   = 0;
	info->overallLeft    = 0;
	info->overallRight   = 0;

    }
}

void
QueryTextExtents(font, count, chars, info)
    FontPtr font;
    unsigned int count;
    unsigned short *chars;
    ExtentInfoRec *info;
{
    CharInfoPtr *charinfo =
	(CharInfoPtr *)ALLOCATE←LOCAL((int)count*sizeof(CharInfoPtr));
    unsigned int n;

    if(!charinfo)
	return;
    if (font->pFI->lastRow == 0)
	GetGlyphs(font, (int)count, (unsigned char *)chars, Linear16Bit, &n, charinfo);
    else
	GetGlyphs(font, (int)count, (unsigned char *)chars, TwoD16Bit, &n, charinfo);

    QueryGlyphExtents(font, charinfo, n, info);

    DEALLOCATE←LOCAL(charinfo);
}