#include "windows.h"
#include <windowsx.h>
#include "fontedit.h"
#include "fcntl.h"
#include "memory.h"
#include "stdio.h"
#include "commdlg.h"


/****************************************************************************/
/*              Shared Variables                                            */
/****************************************************************************/
extern CHAR *PASCAL VerifyHeaderContents();/* checks integrity of header */

extern FontHeaderType font;             /* Structure of Font File Header */
extern LONG lSizeOfOldGlyph20;          /* Old packed 2.0 glyph info size. */
extern LONG lSizeOfOldGlyph30;     		/* Old packed 3.0 glyph info size. */
extern LONG lSizeOfOldFontHeader;       /* Old packed font header size. */
extern LONG lSizeOfOldFontHeader30;     /* Old 3.0 packed font header size. */
extern CHAR szFaceName[];               /* Face Name of Font */
extern DWORD offsets[];                 /* Offsets Table */

extern BOOL fReadOnly;
extern BOOL fChanged;                   /* Note if we did anything */
extern BOOL fLoaded;                    /* Set if a font is loaded */
extern INT iFontFormatPrev;             /* Set to the id of prev.font format */
extern INT iFontFormat;             /* Set to the id of current font format */

extern HWND hFont;                      /* Handle to Show window */
extern HWND hBox;                       /* Handle to Edit Window */
extern HDC hMemDC;                      /* Handle to Memory Display Context */
extern HBITMAP hBitmap;                 /* Handle to our work bit map */
extern CHAR matBox [wBoxLim] [kBoxLim]; /* array to hold Box */

extern CHAR *vrgsz[];                   /* string table */
extern OFSTRUCT ofstrFile;
extern BOOL NewFile;                    /* flag indicating if file was opened
                                           by selecting NEW on menu */
FontHeaderType fontBuffer;          /* temporary buffer of Font File Header */
WORD cHeader, cTable;
HFILE nNewFile;                          /* NT file handle */


/****************************************************************************/
/*              Local Variables                                             */
/****************************************************************************/

DWORD Proport(DWORD, DWORD, DWORD, DWORD);  /* Reproportions a value */

CHAR  *lpFontBody = NULL;  /* Pointer to Font Body */
CHAR  *lpWork = NULL;      /* Pointer to Work Area */
HDC hNewMemDC;
HDC hBoxMemDC;
HBITMAP hNewBitmap;
HBITMAP hBoxBitmap;
HCURSOR hOldCursor = NULL;      /* Holds Arrow while we show Hourglass */
HCURSOR hHourGlass = NULL;
DWORD oldMode;                   /* For StretchBltMode */
BYTE  *lp1,  *lp2;    /* Pointers to bitmaps in format N */

FontHeader30  font30;

#define ALPHA_CNT        26
#define TOTAL_WEIGHTS  1000
#define FSF_FIXED             0x0001
#define FSF_PROPORTIONAL      0x0002

SHORT widthweights[ALPHA_CNT + 1] =
        {
                 64,        /* a
                 14,        /* b     */
                 27,        /* c     */
                 35,        /* d     */
                100,        /* e     */
                 20,        /* f     */
                 14,        /* g     */
                 42,        /* h     */
                 63,        /* i     */
                 3 ,        /* j     */
                 6 ,        /* k     */
                 35,        /* l     */
                 20,        /* m     */
                 56,        /* n     */
                 56,        /* o     */
                 17,        /* p     */
                 4 ,        /* q     */
                 49,        /* r     */
                 56,        /* s     */
                 71,        /* t     */
                 31,        /* u     */
                 10,        /* v     */
                 18,        /* w     */
                 3 ,        /* x     */
                 18,        /* y     */
                 2 ,        /* z     */
                166,        /* space, must be last, to use with the following
                               code */
        };

/****************************************************************************/
/*              Local Functions                                             */
/****************************************************************************/

VOID NewAverage(VOID);
BOOL GetNewMap(DWORD, DWORD);
VOID UseNewMap(VOID);
VOID ShrinkFont(DWORD, DWORD);

/****************************************************************************
 * WORD ConvertToBitmapFormat(width, phase, height)
 *
 * purpose  : Takes a part of the font file and converts it to a string of
 *            bytes for a single scan of bitmap for a character.
 *
 * params   : WORD width : width of the character in pixels(bits)
 *            WORD phase : current deviation from byte alignment (pixels)
 *            WORD height: height of character(pixels)
 *
 * returns  : WORD phase : new deviation from byte alignment (pixels)
 *
 * side effects : modifies pointers lp1 and lp2 (pointing to work space and
 *                font body respectively)
 *
 ****************************************************************************/
DWORD PASCAL
ConvertToBitmapFormat(
     DWORD width,     /* width of the character in pixels(bits) */
     DWORD phase,     /* current deviation from byte alignment (pixels) */
     DWORD height     /* height of character(pixels) */
     )
{
      INT w;
      WORD j;

      /* in the font file format characters are stored consecutively in
         column-major ordering (columns of char 1 followed by columns of char
         2 ... etc.). lp2 points to start of columns of current character.
         lp1 points to start of row of bitmap for character. */

      for (w = width; w > 0; w -= 8){
          if (phase == 0){
          /*  easy case */
              *lp1++ = *lp2;

              if (w < 8)
                  phase = w;
          }
          else{

              --lp1;
              j = (WORD)*lp1;
              j <<= 8;
              j |= (((WORD)*lp2) << (8 - phase));
              *lp1++ = (BYTE)(j >> 8);
              *lp1++ = (BYTE)j;
              if (w < 8){
                  phase += w;
                  if (phase <= 8)
                      lp1--;          /* back up pointer */
                  phase &= 7;
              }
          }
          lp2 += height;          /* move to next column */
      }
      return phase;
}

/****************************************************************************
 * char * VerifyTableContents()
 *
 * purpose  : scan the offsets table of file just read and check if
 *            width and offsets lie within limits
 *
 * params    : none
 *
 * returns   : char * szError : ptr to Error message if table not OK
 *                              NULL otherwise
 *
 * side effects : none
 *
 ****************************************************************************/
CHAR * PASCAL
VerifyTableContents(
    VOID
    )
{
	 PBYTE		  pjGlyphData;
     GLYPHINFO_20 gi2T20;        /* temp ptr to a 2.0 style table*/
     GLYPHINFO_30 gi3T30;        /* temp ptr to a 2.0 style table*/
     INT i;

     /* separate loops written for the 2.0 and 3.0 font processing because
        a single loop would involve too many checks (of font type) within
        the loop and slow down processing  */
     if (iFontFormat == ID_FORMAT2){
         pjGlyphData = (lpFontBody +1);

		/* view table as 2.0 table */

         /* check that each and every width and offset lie within limits */

         for (i=0; i < (fontBuffer.LastChar - fontBuffer.FirstChar + 1); i++) {

			  vGlyphInfo20FromBuffer (pjGlyphData, &gi2T20);

              if (gi2T20.GIwidth > 64)
                  return vszTableWidthsBad;

              if ((gi2T20.GIoffset > (UINT)WORD_LIMIT) ||
                  (gi2T20.GIoffset < 0) )
                  return vszTableOffsetsBad;

			  pjGlyphData += lSizeOfOldGlyph20;
         }
     }
     else{

         pjGlyphData = lpFontBody;

            /* view table as 3.0 table */
         /* check that each and every width and offset lie within limits */

         for (i=0; i< (fontBuffer.LastChar - fontBuffer.FirstChar +1); i++) {

			  vGlyphInfo30FromBuffer (pjGlyphData, &gi3T30);

              if (gi3T30.GIwidth > 64)
                  return vszTableWidthsBad;

              if (gi3T30.GIoffset < (DWORD)0)
                  return vszTableOffsetsBad;

			  pjGlyphData += lSizeOfOldGlyph30;
         }
     }
     return NULL;
}

/****************************************************************************
 * char * FontLoad()
 *
 * purpose: reads in the specified font file and creates a work bitmap for
 *          the editor. Also creates a local bitmap-offset table for easy
 *          access to the characters in the bitmap
 *
 * params : pszFileName - File Name to open.
 *
 * returns: ptr to NULL string if Open goes off OK
 *          ptr to error message string otherwise
 *
 * side effects: lots
 *
 ****************************************************************************/
CHAR *
FontLoad(
    CHAR 		*pszFileName,
	OFSTRUCT	*pofsReOpenInfo
    )
{

    CHAR  *lpFontBodySav = NULL;  /* local pointer to font body */
    DWORD len, fontlen;               /* length of font body (bytes)*/
	UINT i;
    INT mf;                      /* menu function ID */
    DWORD iWorkLen;                   /* length of work area (bytes) */
    HDC hDC;
    CHAR * pszError;                   /* error msg if header is messed up */

    DWORD row, height, width,phase;
    DWORD offset;
    GLYPHINFO_20 gi2GlyphTable20;    /* Pointer into 2.0 style offsets table */
    GLYPHINFO_30 gi3GlyphTable30;    /* Pointer into 3.0 style offsets table */
	PBYTE 	pjGlyphData;			 /* Pointer to Glyph information buffer. */

    BYTE  cDummy [CCHEXTRA];         /* dummy buffer for unneeded 3.0 header
                                    * info
                                    */

    /* Put up an hourglass ... this may take a while */
    if (!hHourGlass)
        hHourGlass = LoadCursor(NULL, IDC_WAIT);        /* Get Hourglass */
    hOldCursor = SetCursor(hHourGlass);         /* Show hourglass */

    /* open file for read. */
	nNewFile = (HFILE)OpenFile (pszFileName, pofsReOpenInfo, OF_READ);
	
	if (nNewFile < 0) {

		return vszErrorOpeningFile;
	}

	//- ReadFile:  Here is where we need to make some adjustments in order to
	//- read the file in to a new DWORD aligned format.
	{
		BYTE    jBuffer [200];


		/* Read the header */
		if (_lread ((HFILE) nNewFile, (LPSTR)&jBuffer, lSizeOfOldFontHeader) !=
				(UINT)lSizeOfOldFontHeader) {

			_lclose((HFILE)nNewFile);
			return vszErrorReadingHdr;
		}

		//
		// Call conversion routine to give us a properly aligned buffer.
		//

		vFontStructFromBuffer (
				 (PBYTE)&jBuffer,
				 &fontBuffer
				);

	}

    /* Check the version number -- make sure it's a font */
    switch (fontBuffer.Version)
        {
        case 0x200:
                iFontFormat = ID_FORMAT2;
                break;
        case 0x300:        /* added 1/19/88 */
                iFontFormat = ID_FORMAT3;
                /* read in the extra fields into a dummy buffer ... they don't
                 * mean anything to us  at this stage.
                 */
                if ((_lread((HFILE)nNewFile, (LPSTR)cDummy, CCHEXTRA))
                            != CCHEXTRA){
                    _lclose((HFILE)nNewFile);
                    return vszErrorReadingHdr;
                }

                break;
        default:                /* Anything else -- toughies */
                _lclose((HFILE)nNewFile);
                return vszUnknownFormat;
        }

    /* check if contents of font header are sensible. If not,
       give an error message and quit */

    if ((pszError = VerifyHeaderContents()) != NULL) {

		_lclose((HFILE)nNewFile);
        return pszError;
	}

    /* Ready to load -- Check if we will be overwriting */
    if (fLoaded)
	{
        DeleteGlobalBitmap();
        fLoaded = FALSE;
	}

    /* Allocate space for font body and Face Name and read them in */
    len= (fontBuffer.Size - lSizeOfOldFontHeader);  /* Compute size */

    if ((lpFontBody = (LPSTR)GlobalAlloc(GMEM_ZEROINIT, (LONG)len)) == 0) {

		_lclose((HFILE)nNewFile);
        return vszNotEnoughMem;                       /* Get Handle to space */
	}

    if (iFontFormat == ID_FORMAT3)
        fontlen = len - CCHEXTRA;
    else
        fontlen = len;

    lpFontBodySav = lpFontBody;                    /* save ptr to font body */
    while (fontlen >= (DWORD)SEGMENT_SIZE) {

        /*  file read method if font file size is 64k bytes or greater.
            file is read in in chunks of 65536 (SEGMENT_SIZE), taking care to
            position buffer ptr at 64k boundary each time */

        /* First read in the maximum number of bytes _lread can read (65534) */
        if ((_lread((HFILE)nNewFile, lpFontBodySav, WORD_LIMIT)) == WORD_LIMIT) {

            lpFontBodySav += WORD_LIMIT; /* buffer ptr moved up by 64k bytes */
            fontlen -= WORD_LIMIT;     /* fontlen = no of bytes left to read */

		} else {

			GlobalFree (lpFontBody);
			_lclose((HFILE)nNewFile);
            return vszErrorReadingBody;
		}

        /* read in an additional two bytes to reach end of segment */
        if ((_lread((HFILE)nNewFile, lpFontBodySav, 2)) == 2) {

            lpFontBodySav += 2; /* buffer ptr moved up by 2 bytes */
            fontlen -= 2;       /* fontlen = no of bytes left to read */

        } else {

			GlobalFree (lpFontBody);
			_lclose((HFILE)nNewFile);
            return vszErrorReadingBody;
        }
	}

    /* read the partially filled segment  */
    if ((_lread((HFILE)nNewFile, lpFontBodySav, (DWORD)fontlen)) != (UINT) fontlen) {

        GlobalFree (lpFontBody);
        _lclose((HFILE)nNewFile);
        return vszErrorReadingBody;
	}

    /* Close the file */
    _lclose((HFILE)nNewFile);

    /* check if the offset table entries are within allowable limits.
       If not give an error message, clean up and quit */
    if ((pszError = VerifyTableContents()) != NULL) {

        GlobalFree (lpFontBody);
        return pszError;
	}

    /* now that everything has been checked, move buffer to font */
    font = fontBuffer;

    /* Make local copies of FaceName and Device Name */
    if (font.Face){

        lstrcpy((LPSTR)szFaceName, lpFontBody + font.Face -
                                         (iFontFormat == ID_FORMAT2 ?
                                                    lSizeOfOldFontHeader   :
                                                    lSizeOfOldFontHeader30-1));
    } else {

        lstrcpy((LPSTR)szFaceName, (LPSTR)"");
	}

    for (i = 0; i <= 256; i++)      /* Zero offsets Table for below */
        offsets[i] = 0;

    /* compute work space needed if a 3.0 file. This has to be done since
       3.0 files are compressed versions of 2.0 files and may need a
       work bitmap bigger than the actual font body size */

    if (iFontFormat == ID_FORMAT3) {

        cTable = (WORD) (6 * (font.LastChar - font.FirstChar + 2));
        iWorkLen = 0;

        pjGlyphData = lpFontBody;

        /* work bitmap size = sum of sizes of all characters in font */

        for (i = font.FirstChar; i <= font.LastChar; i++) {

			 vGlyphInfo30FromBuffer (pjGlyphData, &gi3GlyphTable30);

             iWorkLen += ((gi3GlyphTable30.GIwidth +7) >> 3) * font.PixHeight;

			 pjGlyphData += lSizeOfOldGlyph30;
		}

    } else { /* 2.0 file */

        cTable = (WORD)(4 * (font.LastChar - font.FirstChar + 2));
           /* compute table length */
        iWorkLen = len;  /* work space for a 2.0 file is the same as the
                             length of the font body */
    }

	//- Add some extra space for dword alignment.
	iWorkLen += (font.PixHeight * sizeof (DWORD));
      /* Get work space */

    if ((lpWork = (LPSTR)GlobalAlloc (GMEM_ZEROINIT, (LONG)iWorkLen)) == 0) {

        GlobalFree (lpFontBody);
        return vszNotEnoughMem;
    }

	lp1 = lpWork;

    height = (DWORD) font.PixHeight;
    offset = 0;
    /* put the font file into bitmap format */
    if (iFontFormat == ID_FORMAT2){         /* table in 2.0 format */
        for (row = 0; row < height; row++){

             /* view table as a 2.0 style table */
             pjGlyphData = lpFontBody + 1;

             phase = 0;

             for (i = 0; i < (UINT)(font.LastChar - font.FirstChar + 1); i++) {

				vGlyphInfo20FromBuffer (pjGlyphData, &gi2GlyphTable20);

                width = (DWORD) gi2GlyphTable20.GIwidth;

                /* size of each table element = 4bytes */
                lp2 = lpFontBody + (gi2GlyphTable20.GIoffset -
							lSizeOfOldFontHeader) + row;

				pjGlyphData += lSizeOfOldGlyph20;

                /* offset ends up as the sum of the widths */
                if (row == 0)              /* Once is enough */
						offsets[i + font.FirstChar + 1] = offset += width;

                /* create a single scan of bitmap for character */
                phase = ConvertToBitmapFormat (width, phase, height);
             }
             if ((lp1 - lpWork) & 1)
                 *lp1++ = 0;             /* Round lp1 up to Word Boundary */
#ifdef DWORDROUND
             if ((lp1 - lpWork) & 2) {
                 *lp1++ = 0;             /* Round lp1 up to DWord Boundary */
                 *lp1++ = 0;             /* Round lp1 up to DWord Boundary */
			 }
#endif
             //if (((offset + 7) >> 3) & 1)
                 //*lp1++ = 0;             /* Round lp1 up to Word Boundary */
             //if (((offset + 7) >> 3) & 2) {
                 //*lp1++ = 0;             /* Round lp1 up to DWord Boundary */
                 //*lp1++ = 0;             /* Round lp1 up to DWord Boundary */
			 //}
        }
    }
     /* separate loops written for the 2.0 and 3.0 font processing because
        a single loop would involve too many checks (of font type) within
        the loop and slow down processing  */
    else {                                 /* table in 3.0 format */

        for (row = 0; row < height; row++){

             phase = 0;
             /* view table as a 3.0 style table */
             pjGlyphData = lpFontBody;

             for (i = 0; i < (UINT)(font.LastChar - font.FirstChar + 1); i++) {

				vGlyphInfo30FromBuffer (pjGlyphData, &gi3GlyphTable30);

                width = gi3GlyphTable30.GIwidth;

                  /* size of each table element = 6bytes */
                lp2 = lpFontBody + (gi3GlyphTable30.GIoffset -
						lSizeOfOldFontHeader30 +1) + row;

				pjGlyphData += lSizeOfOldGlyph30;

                 /* offset ends up as the sum of the widths */
                if (row == 0)              /* Once is enough */
                    offsets[i + font.FirstChar + 1] = offset += width;

                 /* create a single scan of bitmap for character */
                phase = ConvertToBitmapFormat (width, phase, height);
             }
             if ((lp1 - lpWork) & 1)
                 *lp1++ = 0;             /* Round lp1 up to Word Boundary */
#ifdef DWORDROUND
             if ((lp1 - lpWork) & 2) {
                 *lp1++ = 0;             /* Round lp1 up to DWord Boundary */
                 *lp1++ = 0;             /* Round lp1 up to DWord Boundary */
			 }
#endif
        }
    }
             // FontLoad(): width byte is DWORD align
    font.WidthBytes = (WORD) CJ_DIB_SCAN(offset);
       /* fixup NEWFON error */

    GlobalFree(lpFontBody);
    lpFontBody = lpWork;          /* So that below we free the other buffer */
    /* Create a WINDOWS bitmap to move the font definition bits into */

    hDC = GetDC (hFont);                         /* DC to be compatible with */
    hBitmap = CreateBitmap(
                    (INT)font.WidthBytes << 3, /* Width of font in pixels */
                    (INT)font.PixHeight,
                    1, 1, (LPBYTE)NULL);
    hMemDC = CreateCompatibleDC(hDC);           /* Create a DC */
    SelectObject(hMemDC, hBitmap);              /* Relate the two of them */
    ReleaseDC(hFont, hDC);                      /* Done with font DC */

    /* Move the bits in */
    SetBitmapBits(hBitmap,
          (DWORD)font.WidthBytes * (DWORD)font.PixHeight,(CHAR  *)lpWork);

    /* Free up the space we loaded the file into */
    GlobalFree(lpFontBody);
    fLoaded = TRUE;
    {
        HMENU hMenu;

        hMenu = GetMenu(hBox);  /* Gray menu if no clipboard bitmap */
        mf = (font.Family & 1) ? MF_ENABLED : MF_GRAYED;
        EnableMenuItem(hMenu, FONT_SAVE, MF_ENABLED);
        EnableMenuItem(hMenu, FONT_SAVEAS, MF_ENABLED);
        EnableMenuItem(hMenu, 1, MF_BYPOSITION | MF_ENABLED);
        EnableMenuItem(hMenu, 2, MF_BYPOSITION | MF_ENABLED);
        EnableMenuItem(hMenu, 3, MF_BYPOSITION | MF_ENABLED);
        EnableMenuItem(hMenu, 4, MF_BYPOSITION | mf);
        EnableMenuItem(hMenu, 5, MF_BYPOSITION | MF_ENABLED);
        EnableMenuItem(hMenu, 6, MF_BYPOSITION | MF_ENABLED);
        DrawMenuBar(hBox);
    }
    SetCursor(hOldCursor);              /* Restore regular cursor */
    return "";
}

/**************************************
 * compares nBytes bytes of s1 and s2 *
 **************************************/
BOOL
ByteCompare (
    CHAR  *s1,
    CHAR  *s2,
    DWORD nBytes
    )
{
    for ( ; nBytes > 0; nBytes--)
        if (*s1++ != *s2++)
            return FALSE;
    return TRUE;
}

/***********************************
 * copies nBytes bytes of s2 to s1 *
 ***********************************/
BOOL
ByteCopy(
    CHAR  *s1,
    CHAR  *s2,
    LONG  nBytes
    )
{
    for ( ; nBytes > 0; nBytes--)
        *s1++ = *s2++;
    return TRUE;
}

/****************************************************************************
 * VOID ConvertToFileFormat(width, phase, height)
 *
 * purpose  : Takes a part of the bitmap (corresponding to a single character)
 *            and converts it to a string of bytes in the font file format
 *
 * params   : WORD width :  width of the character in pixels(bits)
 *            WORD phase :  current deviation from byte alignment (pixels)
 *            WORD height:  height of character(pixels)
 *
 * returns:   WORD phase :  new deviation from byte alignment (pixels)
 *
 * side effects : modifies pointers lp1 and lp2 (pointing to font body and work
 *                space respectively)
 *
 ****************************************************************************/
DWORD PASCAL
ConvertToFileFormat(
     DWORD width,
     DWORD phase,
     DWORD height
     )
{
      INT w;

      for (w = width; w > 0; w -= 8){  /* for each byte of font */
           if (phase == 0){             /* easy case */

               BYTE b;

               b = *lp1++;
               if (w < 8){

                   phase = (DWORD) w;
                   b >>= 8 - w;    /* Clear left side bits */
                   b <<= 8 - w;
               }
               *lp2 = b;
           }
           else{

               DWORD j;

               lp1--;          /* Re-read byte prevously read */
               //j = (DWORD) ((BYTE)*lp1++ << 8) | ((BYTE)*lp1++);
               j = (DWORD)((BYTE)*lp1++ << 8);
               j |= (DWORD) ((BYTE)*lp1++);
               if (w < 8){

                   j >>= 16 - phase - (w & 7);  /* shove it right */
                   j <<= 8 - (w & 7); /* Left justify in low byte */
                   phase += (DWORD) w;
                   if (phase <=  8)
                       lp1--;         /* back up pointer */
                   phase &= 7;
               }
               else
                   j >>= 8 - phase;
               *lp2 = (BYTE)j;
           }
           lp2 += height;          /* move to next column */
       }
       return phase;
}

/****************************************************************************
 * char * FontSave()
 *
 * purpose: saves the work bitmap in the required font file format (2.0 or
 *          3.0 and cleans up
 *
 * params : none
 *
 * returns: ptr to a NULL string if Save goes off OK
 *          ptr to error message string otherwise
 *
 * side effects: lots
 *
 ****************************************************************************/

CHAR *
FontSave(
	CHAR 		*pszFileName,
	OFSTRUCT	*pofsReOpenInfo
    )
{
    DWORD bytecount;       /* number of bytes returned by lwrite */
    DWORD size;            /* total size of font */

    WORD height, row;
    DWORD i, fontlen;
    DWORD cBody, cFont,cFontsav, cFace;
    CHAR 			*lpFont;          /* ponter to font body */
    CHAR * sz;
    DWORD iMax;
    WORD widthsav;
	PBYTE			pjGlyphData;
	PBYTE			pjGlyphSave;
    GLYPHINFO_20 	gi2GlyphTable20;    /* 2.0 style Glyph data struct */
    GLYPHINFO_30 	gi3GlyphTable30;    /* 3.0 style Glyph data struct */

    NewAverage(); /* force ave width to be recomputed 8/17/87 BobM */

    /* reset file pointer */
	nNewFile = (HFILE)OpenFile (pszFileName, pofsReOpenInfo, OF_WRITE | OF_REOPEN);

	if (nNewFile < (HFILE) 0) {

		return vszErrorOpeningFile;
	}

    /* Put up an houglass ... this may take a while */
    if (!hHourGlass)
        hHourGlass = LoadCursor (NULL, IDC_WAIT);        /* Get Hourglass */
    hOldCursor = SetCursor (hHourGlass);         /* Show hourglass */

    height = font.PixHeight;
    cBody = (DWORD)height * (DWORD)font.WidthBytes;

    /* Recompute file size and update header */
    if (iFontFormat == ID_FORMAT2)
       cHeader = (WORD)(lSizeOfOldFontHeader +1);
    else
       cHeader = (WORD)(lSizeOfOldFontHeader30 - 1);

    /* if of 2.0 type, check if size will exceed 64kbytes. If yes, saving
       in 2.0 format will result in loss of information (because offset table
       of 2.0 file is composed of 16bit words). Warn user and ask if file can
       be saved as a 3.0 file */

    if (iFontFormat == ID_FORMAT2)
    { 	
		if (((DWORD)cHeader + (DWORD)(lSizeOfOldGlyph20 * (font.LastChar -
			font.FirstChar+2)) + (DWORD)cBody) >= WORD_LIMIT)
               if (MessageBox(hBox, vszTooBigFor20, vszWarning,
                              IDOK | IDCANCEL |IDNO) == IDYES)
                    iFontFormat = ID_FORMAT3;
	}

	if (iFontFormat == ID_FORMAT2) {

		/* allocate space for a 2.0 style offsets table */
		cTable = (WORD)(lSizeOfOldGlyph20 *
				(font.LastChar - font.FirstChar + 2));

	} else {

		/* allocate space for a 3.0 style offsets table */
		cTable = (WORD)(lSizeOfOldGlyph30 *
				(font.LastChar - font.FirstChar + 2));
	}

	pjGlyphData = (LPSTR)GlobalAlloc (GMEM_ZEROINIT, (DWORD)cTable);

	if (pjGlyphData == 0) {

		_lclose((HFILE)nNewFile);
		return vszNotEnoughMem;
	}

	pjGlyphSave = pjGlyphData;

    size = cHeader + cTable;
    iMax = font.LastChar - font.FirstChar + 1;

    // Recreate maximum character width when the font is proportional
    // since the WIDTH.. command may grow maxwidth but may not reduce.
    if (font.Family & 1)
	font.MaxWidth = 0;

    /* create offsets table of font file */
    for (i = 0; i <= iMax; i++){

         DWORD width, charSize;

         width = offsets[i + font.FirstChar + 1] - offsets[i + font.FirstChar];

         if (i == iMax) {
             width = 8;      /* Sentinal blank */
		 }

         if (iFontFormat == ID_FORMAT2){

            gi2GlyphTable20.GIwidth  = (SHORT)width;
            gi2GlyphTable20.GIoffset = (SHORT)size;

			vBufferFromGlyphInfo20 (&gi2GlyphTable20, pjGlyphData);

			pjGlyphData += lSizeOfOldGlyph20;

         } else {

            gi3GlyphTable30.GIwidth  = (SHORT)width;
            gi3GlyphTable30.GIoffset = (INT)size;

			vBufferFromGlyphInfo30 (&gi3GlyphTable30, pjGlyphData);

			pjGlyphData += lSizeOfOldGlyph30;
         }
        // update max width
        if(font.Family & 1 ) {
            if(width > font.MaxWidth )
                font.MaxWidth = (WORD)width ;
        }
        if(i == 0x81 && font.CharSet == SHIFTJIS_CHARSET) {
            if(width * 2 > font.MaxWidth) {
                font.MaxWidth = (WORD)width * 2 ;
            }
        }

         charSize = height * ((width + 7) >> 3);  /* size in bytes */

         if ((size + charSize) < size){           /* Overflow? */

             GlobalFree (pjGlyphData);
             _lclose((HFILE)nNewFile);
             return vszFileTooLarge;

         }
         size += charSize;
    }

    /* Update stuff in the header */

    font.Face = (DWORD)size;
    cFace = (WORD)lstrlen (szFaceName) + 1;   /* Allow for \0 */
    size += cFace;
    font.Size = (DWORD)size;                                /* new file size */
    font.BitsOffset = (DWORD)(cHeader + cTable);
    font.Device = (DWORD)NULL;                   /* Device Name must be NULL */
    cFontsav = size - cHeader - cTable;
    cFont =cFontsav;

	/* alloc extra byte for lp1 in case it needs it */
    if (!(lpFontBody = (LPSTR)GlobalAlloc (GMEM_ZEROINIT, (LONG)cBody +
			height * 4))) {

		_lclose((HFILE)nNewFile);
        return vszNotEnoughMem;
	}

    GetBitmapBits (hBitmap, (DWORD)cBody, lpFontBody);

    /* save current WidthBytes */
    widthsav = font.WidthBytes;

    /*  MD - reset WidthBytes from computed width */
    font.WidthBytes = (WORD) (cFont - cFace)/ height;

    /* Allocate a block to put bitmap into */
    if ((lpFont = lpWork = GlobalAlloc (GMEM_ZEROINIT,(LONG)cFont)) == 0)
	{
        GlobalFree (lpFontBody);
		_lclose((HFILE)nNewFile);
        return vszNotEnoughMem;
	}

    lp1 = lpFontBody;

    /* convert bitmap to file format */
    if (iFontFormat == ID_FORMAT2){   /* offsets table in 2.0 format */
        INT nChars;
        nChars =  font.LastChar - font.FirstChar +1;

        for (row = 0; row < height; row++){

            DWORD phase;

            phase = 0;

            pjGlyphData = pjGlyphSave;

            for (i = 0; i < (DWORD) nChars; i++) {

                INT width;

				vGlyphInfo20FromBuffer (pjGlyphData, &gi2GlyphTable20);

                width = gi2GlyphTable20.GIwidth;

                lp2 = (BYTE  *)(lpWork + (gi2GlyphTable20.GIoffset -
						cHeader - cTable + row));

				pjGlyphData += lSizeOfOldGlyph20;

                phase = ConvertToFileFormat (width, phase, height);
            }
            if ((lp1 - lpWork) & 1)
                 lp1++;             /* Round lp1 up to Word Boundary */
#ifdef DWORDROUND
            if ((lp1 - lpWork) & 2) {
                 lp1++;             /* Round lp1 up to DWord Boundary */
                 lp1++;             /* Round lp1 up to DWord Boundary */
			}
#endif
            //if(((offsets[font.LastChar + 1] + 7) >> 3) & 1)
                //lp1++;          /* Round lp1 up to Word Boundary */
        }
    }
     /* separate loops written for the 2.0 and 3.0 font processing because
        a single loop would involve too many checks (of font type) within
        the loop and slow down processing  */
    else{  /* table in 3.0 format */

        INT nChars;
        nChars =  font.LastChar - font.FirstChar +1;
        for (row = 0; row < height; row++){

            DWORD phase;

            phase = 0;
			pjGlyphData = pjGlyphSave;

            for (i = 0; i < (DWORD) nChars; i++) {

                INT width;

				vGlyphInfo30FromBuffer (pjGlyphData, &gi3GlyphTable30);

                width = gi3GlyphTable30.GIwidth;

                lp2 = (BYTE  *)(lpWork + (gi3GlyphTable30.GIoffset -
						cHeader - cTable + row));

				pjGlyphData += lSizeOfOldGlyph30;

                phase = ConvertToFileFormat (width, phase, height);
            }
            if ((lp1 - lpWork) & 1)
                 lp1++;             /* Round lp1 up to Word Boundary */
#ifdef DWORDROUND
            if ((lp1 - lpWork) & 2) {
                 lp1++;             /* Round lp1 up to DWord Boundary */
                 lp1++;             /* Round lp1 up to DWord Boundary */
			}
#endif
            //if(((offsets[font.LastChar + 1] + 7) >> 3) & 1)
                //lp1++;          /* Round lp1 up to Word Boundary */
        }
    }

	/* Restore start of data. */
	pjGlyphData = pjGlyphSave;

    lp2 -= height - 1;              /* Back up to start of character */
    for (i = 0; i < height; i++)
         *lp2++ = 0;             /* Fill in guaranteed blank character */

    font.Version = 0x200;

    /******  code for compaction in 3.0 fmt borrowed from CMPCTFON ******/
    if (iFontFormat== ID_FORMAT3){
        DWORD iDefBitmapSize = 0;     /* size of default char */
        DWORD cch = 0;                /* count of number of default char */
#if 0
        GLYPHINFO_30  *cur_char;  /* current element of offset table */
        GLYPHINFO_30  *move_char;
            /* element from which moving is to be done */
        LONG iDefBitmapOffset;        /* offset of default char */
        INT iDefBitmapWidth;          /* width of default char */
        static LONG iEndOfBitmaps;    /* address of end of font body */
        INT iFirstC;                  /* first char in font */
        INT iLastC;                   /* last char in font */
        INT i,j;
        WORD width;
        CHAR  *rgbFont;         /* pointer to font body */
        DWORD Offset;

        GDI seems to understand compressed 2.0 external formats but not
        compressed 3.0 formats, and this is causing it to crash loading a 3.0
        format compressed font. Since compression does not affect the
        validity of the font, This code is disabled temporarily till
        this problem is verified. Again, the font is perfectly usable in this
        form, though a trifle bigger than desired -  LR 20/26/90


		Note that this will now not work after the conversion to NT.
		the glyph table needs to accessed as above.  t-davema 8/20/91

        iFirstC = font.FirstChar & 0x0ff;
        iLastC  = font.LastChar & 0x0ff;

        iEndOfBitmaps = (LONG)font.BitsOffset+((LONG)font.WidthBytes *
                (LONG)font.PixHeight);
        rgbFont = (CHAR  *)lpFont;   /* start of font body */
        cur_char =(GLYPHINFO_30  *) lpTable; /* start of offset table */

        /* calculate some parameters for the default char */
        iDefBitmapOffset = cur_char[font.DefaultChar].GIoffset;
        iDefBitmapWidth  = cur_char[font.DefaultChar].GIwidth;
        iDefBitmapSize   = (DWORD)cur_char[font.DefaultChar+1].GIoffset
                                           - (DWORD)iDefBitmapOffset;

        /* scan the font body via the offsets table. If a default char
           is recognised, move all the bytes to it's right left by the
           size of the default char.Make all offset table entries of default
           char point to one image of the char (the earliest occuring image) */
        for (i = iFirstC; i <= iLastC; ++i) {

            /* important: Check for limiting conditions (in case break char
               was modified?)*/
            if (cur_char->GIoffset == iDefBitmapOffset){
                cur_char++;
                continue;
            }

            Offset = cur_char->GIoffset -cHeader -cTable;
            /* proceed to compare images only if widths are equal */
            if ((cur_char->GIwidth  == iDefBitmapWidth) &&
                (ByteCompare (&rgbFont [Offset],
                &rgbFont[iDefBitmapOffset-cHeader-cTable],
                                (DWORD)iDefBitmapSize) == TRUE)){

                /* set offset to earliest occurence of the default char */
                if (cur_char->GIoffset <  iDefBitmapOffset){
                    iDefBitmapOffset = cur_char->GIoffset;
                }
                else    {
                    if (i != iLastC){
                        /* move bytes to right of default char left by the
                           size of the char */
                        ByteCopy (&rgbFont [ Offset],
                                  &rgbFont [ Offset+ iDefBitmapSize],
                                  (LONG)(iEndOfBitmaps -
                                  ((cur_char + 1)->GIoffset)));

                        /* correct the offset table entries */
                        move_char = cur_char + 1;
                        for (j=i; j < iLastC; ++j){
                             move_char->GIoffset -= (LONG)iDefBitmapSize;
                             move_char++;
                        }
                    }
                    iEndOfBitmaps -= iDefBitmapSize;
                    /* move End-of-font to the left */
                    cur_char->GIoffset = iDefBitmapOffset;
                    /* point offset of cuurent char to default char */
                    cch++;
                }
            }
            cur_char++;
        }
#endif
        /* recalculate some font attributes */
        lp2   -= (cch * iDefBitmapSize + height);
        for (i = 0; i < height; i++)
            *lp2++ = 0;             /* Fill in guaranteed blank character */

        cFont -= cch * iDefBitmapSize;
        font.WidthBytes = (WORD) (cFont - cFace)/ height;
        font.Size = (DWORD) (cFont + cHeader + cTable);
        font.Face = font.Size - cFace ;
        font.Version = 0x300;

        /* copy info into 3.0 (new) format header and set the additional
           fields */
        ByteCopy ((LPSTR)&font30, (LPSTR)&font, (DWORD)sizeof (font));
        font30.fsFlags        = 0;
        font30.fsFlags = (font.Family & 1 ? FSF_PROPORTIONAL : FSF_FIXED);
        font30.fsAspace       = 0;
        font30.fsBspace       = 0;
        font30.fsCspace       = 0;
        font30.fsColorPointer = 0L;
        for (i = 0; i < 4 ; i++)
            font30.fsReserved[i] = 0L;
    }

    /* Add the FaceName, if any, to the end of the bitmap */
    lstrcpy((LPSTR)lp2, (LPSTR)szFaceName);

	/*
	 * Again we need to do some tricky things to get the header output
	 * correct.  We want to run it through the conversion backwards until
	 * we get the packed structure.
	 */
	{
		BYTE jOutputBuffer [sizeof (font30)];

		if (iFontFormat == ID_FORMAT2) {

			vBufferFromFontStruct (&font, (PBYTE)&jOutputBuffer);

		} else {

			vBufferFromFont30Struct (&font30, (PBYTE)&jOutputBuffer);
		}

		bytecount = _lwrite((HFILE)nNewFile, (PBYTE)&jOutputBuffer, (DWORD)cHeader);
	}

    /* Write out Header Information */
    if (bytecount == cHeader) {

            /* Write out OffsetsTable */
         if (cTable == 0 || (_lwrite((HFILE)nNewFile, pjGlyphData, (DWORD)cTable)
				== (UINT)cTable)){
              /* Write out Body */
              fontlen = cFont;
              while (fontlen >= SEGMENT_SIZE){

                 /* file write method if font size is 64k or greater
                 file is written in chunks of 65536 bytes (SEGMENT_SIZE)-lr */

                 /* First write as many bytes as _lwrite will allow(65534) */
                  if (_lwrite((HFILE)nNewFile, (CHAR  *)lpFont, WORD_LIMIT) ==
                        WORD_LIMIT){
                      fontlen -= WORD_LIMIT;
                            /* fontlen = no of bytes left to write*/
                      lpFont+= WORD_LIMIT; /* buffer ptr moved up by 64k */
                  }
                  else
                      sz = vszErrorWritingBody;

                  /* write the two bytes remaining in the segment */
                  if (_lwrite((HFILE)nNewFile, (CHAR  *)lpFont, 2) == 2){
                      fontlen -= 2;  /* fontlen = no of bytes left to write*/
                      lpFont+= 2;    /* buffer ptr moved up by 2 */
                  }
                  else
                      sz = vszErrorWritingBody;
              }
              /* segment only partially filled. Write the remaining bytes */
              if (_lwrite((HFILE)nNewFile, (CHAR  *)lpFont, (DWORD)fontlen) ==
                    (UINT) fontlen){
                   fChanged = FALSE;
                   sz= "";
              }
              else
                  sz = vszErrorWritingBody;
         }
         else
             sz = vszErrorWritingOffsets;
    }
    else
         sz = vszErrorWritingHdr;

    /* hack: Restore saved value of widthbytes, eliminating a variety
       of minor scrolling problems that follow after a File/Save */
    font.WidthBytes = widthsav;

    _lclose((HFILE)nNewFile);

	/* Tidy up */

    GlobalFree(lpFontBody);
    GlobalFree(pjGlyphData);
    GlobalFree(lpWork);

    SetCursor(hOldCursor);              /* Restore regular cursor */

    return sz;
}

/****************************************************************************
 * BOOL ResizeWidths(wChar)
 *
 * params : WORD wChar : new width of a single character
 *
 * purpose: resize work bitmap according to new character width by stretching/
 *          compressing all characters as neccesary
 *
 * returns: none
 *
 * side effects: Work bitmap changes.Some header info (regarding font dimensions
 *               altered as well
 ****************************************************************************/


BOOL
ResizeWidths(
    DWORD wChar
    )
{
    DWORD width;
    DWORD offset, i;

    /* Create a new bitmap to move the font definition bits into */
    width = (font.LastChar - font.FirstChar + 1) * wChar; /* In pixels */
             // ResizeWidths(): width byte is DWORD align
    width = CJ_DIB_SCAN(width);
    if (!GetNewMap(width, font.PixHeight))
        return (FALSE);

    /* Move the bits in */
    offset = 0;
    oldMode = SetStretchBltMode(hNewMemDC, COLORONCOLOR);
    for (i = font.FirstChar; i <= font.LastChar; i++)
        {

        StretchBlt(hNewMemDC, offset, 0,
                wChar, font.PixHeight,                  /* New character */
                hMemDC, offsets[i], 0,
                font.PixWidth, font.PixHeight,          /* Old character */
                SRCCOPY);
        offsets[i] = offset;
        offset += wChar;
        }
    SetStretchBltMode(hNewMemDC, oldMode);
    offsets[font.LastChar + 1] = offset;

    UseNewMap();                /* Switch Pointers and release the space */

    font.HorizRes = (WORD) Proport(font.HorizRes, wChar, font.PixWidth, 999);
    font.WidthBytes = (WORD) width;            /* Misc. ajustments */
    font.PixWidth = font.AvgWidth = (font.MaxWidth = (WORD) wChar);
    font.MaxWidth = (WORD) wChar * 2;	// set MaxWidth as DBCS width.

    return (TRUE);
}


/****************************************************************************
 * BOOL SpreadWidths(wChar)
 *
 * purpose: spread/compress work bitmap (with variable width characters)
 *          in proportion with the maximum character width
 *
 * params : WORD wChar : new width of a character
 *
 * returns: none
 *
 * side effects: Work bitmap changes.Some header info (regarding font dimensions
 *               altered as well
 ****************************************************************************/

BOOL
SpreadWidths(
    DWORD wChar
    )
{
    DWORD offset, i;
    DWORD width, oldWidth, newWidths[257];

    /* Create a new bitmap to move the font definition bits into */
    width = 0;          /* Compute the new width */
    for (i = (DWORD) font.FirstChar; i <= (DWORD) font.LastChar; i++)
        {
        oldWidth = offsets[i + 1] - offsets[i];
        /* Compute new width to nearest whole number */
        newWidths[i] = Proport(oldWidth, wChar, font.MaxWidth, wBoxLim - 1);
        width += newWidths[i];
        }
             // SpreadWidths(): byte width is DWORD align
    width = CJ_DIB_SCAN(width);
    if (!GetNewMap(width, font.PixHeight))
        return(FALSE);

    /* Move the bits in */
    offset = 0;
    oldMode = SetStretchBltMode(hNewMemDC, COLORONCOLOR);
    for (i = font.FirstChar; i <= font.LastChar; i++)
        {
        oldWidth = offsets[i + 1] - offsets[i];
        StretchBlt(hNewMemDC, offset, 0,
                newWidths[i], font.PixHeight,           /* New character */
                hMemDC, offsets[i], 0,
                oldWidth, font.PixHeight,               /* Old character */
                SRCCOPY);
        offsets[i] = offset;
        offset += newWidths[i];
        }
    SetStretchBltMode(hNewMemDC, oldMode);
    offsets[font.LastChar + 1] = offset;

    UseNewMap();                /* Switch Pointers and release the space */
    font.HorizRes = (WORD) Proport(font.HorizRes, wChar, font.MaxWidth, 999);
    font.WidthBytes = (WORD) width;            /* Misc. ajustments */
    font.MaxWidth = (WORD) wChar;
    NewAverage();                       /* Compute new average width */

    return (TRUE);
}


/****************************************************************************
 * NewAverage()
 *
 * purpose: recalculate average width of a character in font
 *
 * params : none
 *
 * returns: none
 *
 * side effects: alters the average width parameter in font header
 *
 ****************************************************************************/
VOID
NewAverage(
    VOID
    )
{
#ifdef FOO
    WORD i, totalwidth;
    /* 12/23/85 -- use weighted avg of lower case letters (mikecr) */

    /* width of the space */
    totalwidth = (offsets[' ' + 1] - offsets[' ']) * widthweights[ALPHA_CNT];

    for (i = 0; i < ALPHA_CNT; i++)
            totalwidth += (offsets['a' + i + 1] - offsets['a' + i]) *
            widthweights[i];

    font.AvgWidth = totalwidth / TOTAL_WEIGHTS;

    /* round up if necessary */
    if (totalwidth % TOTAL_WEIGHTS >= (TOTAL_WEIGHTS >> 1))
            font.AvgWidth++;

#endif

    /* lets do a simple average here */
    font.AvgWidth = (WORD) (((offsets[font.LastChar+1] -
            offsets[font.FirstChar]) + (font.LastChar - font.FirstChar)/2) /
            (font.LastChar - font.FirstChar + 1));

    if (font.AvgWidth == 0) {
       font.AvgWidth++;
    }
    if (font.AvgWidth < 1) {
        font.AvgWidth = 1;
    }
}


/****************************************************************************
 * BOOL ResizeBody(width, height)
 *
 * purpose: adjust work bitmap according to new specified dimensions
 *
 * params : WORD width  : new width of font in pixels
 *          WORD height : new height of font in pixels
 *
 * returns: none
 *
 * side effects: Work bitmap changes.Some header info (regarding font dimensions
 *               altered as well
 ****************************************************************************/

BOOL
ResizeBody(
    DWORD width,
    DWORD height
    )
{

    /* Create a new bitmap to move the font definition bits into */
    if (!GetNewMap(width, height))
        return(FALSE);

    /* Move the bits in */
    oldMode = SetStretchBltMode(hNewMemDC, COLORONCOLOR);
    StretchBlt(hNewMemDC, 0, 0,
            width << 3, height,         /* New Char. */
            hMemDC, 0, 0,
            width << 3, font.PixHeight, /* Old Char. */
            SRCCOPY);
    SetStretchBltMode(hNewMemDC, oldMode);

    UseNewMap();                /* Switch Pointers and release the space */

    font.ExtLeading = (WORD) Proport(font.ExtLeading, height, font.PixHeight, 999);
    font.IntLeading = (WORD) Proport(font.IntLeading, height, font.PixHeight, 999);
    font.Ascent = (WORD) Proport(font.Ascent, height, font.PixHeight, 32);
    font.VertRes = (WORD) Proport(font.VertRes, height, font.PixHeight, 999);
    font.Points = (WORD) Proport(font.Points, height, font.PixHeight, 999);
    font.PixHeight = (WORD) height;            /* Fix misc. header values */
    font.WidthBytes = (WORD) width;

    return (TRUE);
}


/****************************************************************************
 * BOOL NewFirstChar(first)
 *
 * purpose: redefines first character in font and resizes work bitmap
 *          accordingly
 *
 * params : WORD first : new first character to be defined
 *
 * returns: none
 *
 * side effects: Work bitmap changes.Some header info (regarding font dimensions
 *               altered as well
 ****************************************************************************/

BOOL
NewFirstChar(
    DWORD first
    )
{
    DWORD  width, wDefault;
    DWORD offset, i;
    INT dw;

    if (first > font.FirstChar)         /* Smaller? */
        {
        ShrinkFont(first, font.LastChar);
        font.FirstChar = (BYTE) first;
        /*return(FALSE);*/
        return(TRUE);
        }

    /* If not smaller we must pad with the default character */
    wDefault = offsets[font.DefaultChar + 1] - offsets[font.DefaultChar];
    dw = wDefault * (font.FirstChar - first);           /* Extra width */
    width = offsets[font.LastChar + 1] + dw;    /* New width (pixels) */
             // NewFirstChar(): byte width is DWORD align
    width = CJ_DIB_SCAN(width);
    if (!GetNewMap(width, font.PixHeight))
        return(FALSE);           /* New work area */

    /* Move it in in two parts */
    /* First move in default characters */
    offset = 0;
    for (i = first; i < font.FirstChar; i++)
        {
        BitBlt(hNewMemDC, offset, 0,
                wDefault, font.PixHeight,
                hMemDC, offsets[font.DefaultChar], 0,
                SRCCOPY);
        offsets[i] = offset;
        offset += wDefault;
        }
    /* Now move in the rest */
    BitBlt(hNewMemDC, offset, 0,
            offsets[font.LastChar + 1], font.PixHeight,
            hMemDC, 0, 0,
            SRCCOPY);

    UseNewMap();                /* Switch Pointers and release the space */

    /* Now fix up offsets table */
    for (i = font.FirstChar; i <= (DWORD)(font.LastChar + 1); i++)
        offsets[i] = offsets[i] + dw;           /* Shift the rest right */
    font.WidthBytes = (WORD) width;
    font.FirstChar = (BYTE) first;

    return (TRUE);
}


/****************************************************************************
 * ShrinkFont(first, last)
 *
 * purpose:  redefine the first and last charcter in the font and shrink
 *           work bitmap accordingly
 *
 * params :  WORD first : new first character to be defined
 *           WORD last  : new last character  "  "   "
 *
 * returns:  none
 *
 * side effects: Work bitmap changes.Some header info (regarding font dimensions
 *               altered as well
 ****************************************************************************/

VOID
ShrinkFont(
    DWORD first,
    DWORD last
    )
{
    DWORD width, widthPixels;
    DWORD i;
    INT dw;

    dw = offsets[first] - offsets[font.FirstChar];      /* left shift if any */
    widthPixels = offsets[last + 1] - offsets[first];   /* Width in pixels */
             // ShrinkFont(): byte width is DWORD align
    width = CJ_DIB_SCAN(widthPixels);
    if (!GetNewMap(width, font.PixHeight))
        return;           /* New work area.*/

    /* Now move the font into the reduced space */

    BitBlt(hNewMemDC, 0, 0,
            widthPixels, font.PixHeight,
            hMemDC, offsets[first], 0,
            SRCCOPY);

    UseNewMap();                /* Switch Pointers and release the space */

    if (dw)                             /* Ajust offsets */
        {
        for (i = first; i <= last + 1; i++)
            offsets[i] -= dw;
        }

    font.WidthBytes = (WORD) width;

}


/****************************************************************************
 * BOOL NewLastChar(last)
 *
 * purpose:  redefines the last character in the font
 *
 * params :  WORD last : number of character to be made the last character
 *
 * returns:  none
 *
 * side effects: Work bitmap changes.Some header info (regarding font dimensions
 *               altered as well
 *
 ****************************************************************************/

BOOL
NewLastChar(
    DWORD last
    )
{
    DWORD  width, wDefault;
    DWORD offset, i;
    INT dw;

    if (last < font.LastChar)           /* Smaller? */
        {
        ShrinkFont(font.FirstChar, last);
        font.LastChar = (BYTE) last;
        return(FALSE);
        }

    /* If not smaller we must pad with the default character */
    wDefault = offsets[font.DefaultChar + 1] - offsets[font.DefaultChar];
    dw = wDefault * (last - font.LastChar);             /* Extra width */
    offset = offsets[font.LastChar + 1];        /* Current end */
    width = offset + dw;                        /* New width (pixels) */
             // NewLastChar(): byte width is DWORD align
    width = CJ_DIB_SCAN(width);
    if (!GetNewMap(width, font.PixHeight))
        return(FALSE);           /* New work area */

    /* Move it in in two parts */
    /* First move in the existing font */
    BitBlt(hNewMemDC, 0, 0,
            offset, font.PixHeight,
            hMemDC, 0, 0,
            SRCCOPY);
    /* Then move in default characters */
    for (i = font.LastChar + 1; i <= last;)
        {
        BitBlt(hNewMemDC, offset, 0,
                wDefault, font.PixHeight,
                hMemDC, offsets[font.DefaultChar], 0,
                SRCCOPY);
        offset += wDefault;
        offsets[++i] = offset;
        }

    UseNewMap();                /* Switch Pointers and release the space */

    font.WidthBytes = (WORD) width;
    font.LastChar = (BYTE) last;

    return (TRUE);
}


/****************************************************************************
 * BOOL CharWidth(iChar, wBox)
 *
 * purpose: resizes selected char according to new dimensions. (only for
 *          variable pitch)
 *
 * params : BYTE iChar : character to resize
 *          WORD wBox  : new width of char in pixels
 *
 * returns: none
 *
 * side effects: work bitmap pixel values and header info(regarding font
 *               dimensions) altered
 *
 ****************************************************************************/

BOOL
CharWidth(
    BYTE iChar,                             /* Character to change */
    DWORD wBox                               /* New width */
    )
{
    DWORD  width, nChars;
    DWORD  w1, w2, i;
    INT dw;

    nChars = font.LastChar - font.FirstChar + 1;        /* Character count */
    dw = wBox - (offsets[iChar + 1] - offsets[iChar]);  /* Width change */
    width = offsets[font.LastChar + 1] + dw;            /* New width (pixels) */
             // CharWidth(): byte width is DWORD align
    width = CJ_DIB_SCAN(width);
    if (!GetNewMap(width, font.PixHeight))
        return(FALSE);                   /* New work area */

    /* Move it in in two parts */
    /* First move up to and including iChar */
    w1 = offsets[iChar + 1];            /* Width (in pixels) to move */
    BitBlt(hNewMemDC, 0, 0,
            w1 + dw, font.PixHeight,
            hMemDC, 0, 0,
            SRCCOPY);
    /* Now move in the rest */
    if (iChar < (BYTE) font.LastChar)        /* Part to right of elision */
        {
        w2 = offsets[font.LastChar + 1] - offsets[iChar + 1];
        BitBlt(hNewMemDC, offsets[iChar] + wBox, 0,
                w2, font.PixHeight,
                hMemDC, offsets[iChar + 1], 0,
                SRCCOPY);
        }

    UseNewMap();                /* Switch Pointers and release the space */

    /* Now fix up offsets table */
    for (i = iChar + 1;                         /* Where changes start */
        i <= (DWORD)(font.LastChar + 1); i++)            /* Ajust offsets */
        offsets[i] = offsets[i] + dw;           /*  .. by adding dw */
    font.WidthBytes = (WORD) width;
    NewAverage();

    return (TRUE);
}

/****************************************************************************
 * BoxToClipboard(ptA, width, height)
 *
 * purpose: write char (or part of it) to clipboard
 *
 * params : POINT ptA    : upper left coordinate
 *          DWORD width  : width of char in pixels
 *          DWORD height : height of char in pixels
 * returns: none
 *
 * side effects: none
 *
 ****************************************************************************/
VOID
BoxToClipboard(
    POINT ptA,                               /* Upper left point */
    DWORD width,
    DWORD height                      /* Size */
    )
{
    HDC hDC;
    DWORD x, y;

    hDC = GetDC(hFont);                         /* DC to be compatible with */
    hNewBitmap = CreateBitmap(
                    width, height,
                    1, 1, (LPBYTE)NULL);
    hNewMemDC = CreateCompatibleDC(hDC);                /* Create a DC */
    SelectObject(hNewMemDC, hNewBitmap);                /* Relate them */
    ReleaseDC(hFont, hDC);                      /* Done with font DC */

    for (x = 0; x < width; x++)
        for (y = 0; y < height; y++)
            SetPixel(hNewMemDC, x, y, matBox[x + ptA.x][y + ptA.y] == TRUE ?
            BLACK : WHITE);

    /* Now wake up Clipboard and empty it */
    if (!OpenClipboard(hFont))
        ErrorBox(hBox, vszCannotOpenClip); // , vszCopyingToClip);
    else        /* Ok: We got the Clipboard */
        {
        EmptyClipboard();
        SetClipboardData(CF_BITMAP, hNewBitmap);        /* Tell Clipboard */
        }

    /* Tidy things up */
    CloseClipboard();
    DeleteDC(hNewMemDC);
}


/****************************************************************************
 * WORD ClipboardToBox(ptA, width, height, fFit)
 *
 * purpose: copies char (or part of char ) from clipboard to work bitmap
 *          stretching it if need be
 *
 * params : PIONT ptA    : upper left coordinate
 *          DWORD width  : width of char in pixels
 *          DWORD height : height of char in pixels
 *          BOOL fFit   : flag to indicate if default width is to be used
 * returns: none
 *
 * side effects: pixel values of bitmap may change for char
 *
 ****************************************************************************/

DWORD
ClipboardToBox(
    POINT ptA,                               /* Upper left point */
    DWORD width,
    DWORD height,                     /* Size */
    BOOL fFit                               /* Use default width if TRUE */
    )
{
    BITMAP bitmap;
    HDC hDC;
    DWORD x, y;
    HANDLE hT;

    if (!OpenClipboard(hFont)) {
        ErrorBox(hBox, vszCannotOpenClip);
        return 0;
    }
    hNewBitmap = GetClipboardData(CF_BITMAP);

    /* Check if we got something like a character */
    if (GetObject(hNewBitmap, sizeof(BITMAP), (LPSTR)&bitmap) != sizeof(BITMAP))
        {   /* What did we get */
        ErrorBox(hBox, vszErrorClip);
        CloseClipboard();
        return 0;
    }


    if (fFit && ((WORD)bitmap.bmWidth <= font.MaxWidth))
        width = bitmap.bmWidth;

    hDC = GetDC(hFont);                         /* DC to be compatible with */
    hBoxBitmap = CreateBitmap(
                    width, height,
                    1, 1, (LPBYTE)NULL);
    hBoxMemDC = CreateCompatibleDC(hDC);                /* Create a DC */
    hT = SelectObject(hBoxMemDC, hBoxBitmap);           /* Relate them */
    if (hT == NULL || hDC == NULL || hBoxBitmap == NULL || hBoxMemDC == NULL) {
        ErrorBox(hBox, vszErrorClip);
	CloseClipboard();
	DeleteDC(hBoxMemDC);
	DeleteObject(hBoxBitmap);
        return 0;
    }

    /* Get a DC to relate to the Bitmap we just got */
    hNewMemDC = CreateCompatibleDC(hDC);                /* Create a DC */
    hT = SelectObject(hNewMemDC, hNewBitmap);           /* Relate them */
    ReleaseDC(hFont, hDC);                      /* Done with font DC */
    if (hT == NULL || hNewMemDC == NULL) {
        ErrorBox(hBox, vszErrorClip);
	CloseClipboard();
	DeleteDC(hNewMemDC);
	DeleteDC(hBoxMemDC);
	DeleteObject(hBoxBitmap);
        return 0;
    }

    /* Now StretchBlt whatever was on the clipboard into the character */
    oldMode = SetStretchBltMode(hBoxMemDC, COLORONCOLOR);
    fFit = StretchBlt(hBoxMemDC, 0, 0, width, height,
                hNewMemDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
    if (!fFit || !oldMode) {
        ErrorBox(hBox, vszErrorClip);
	CloseClipboard();
	DeleteDC(hNewMemDC);
	DeleteDC(hBoxMemDC);
	DeleteObject(hBoxBitmap);
        return 0;
    }
    (void)SetStretchBltMode(hBoxMemDC, oldMode);
    for (x = 0; x < width; x++)
        for (y = 0; y < height; y++)
            matBox[x + ptA.x] [y + ptA.y] = (CHAR)(GetPixel(hBoxMemDC, x, y) ?
                0 : 1);
    /* Tidy things up */
    DeleteDC(hNewMemDC);
    DeleteDC(hBoxMemDC);
    DeleteObject(hBoxBitmap);
    CloseClipboard();
    return(width);
}


/****************************************************************************
 * ToClipboard(iChar, width, height)
 *
 * purpose: write char in edit box to clipboard
 *
 * params : BYTE iChar  : number of char to be copied to clipboard
 *          DWORD width  : width of char in pixels
 *          DWORD height : height of char in pixels
 *
 * returns: none
 *
 * side effects: none
 *
 ****************************************************************************/

VOID
ToClipboard(
    BYTE iChar,
    DWORD width,             /* Here in Pixels */
    DWORD height             /* Also in Pixels */
    )
{
    HDC hDC;

    hDC = GetDC(hFont);                         /* DC to be compatible with */
    hNewBitmap = CreateBitmap(
                    width,                      /* Width of font in pixels */
                    height,
                    1, 1, (LPBYTE)NULL);
    hNewMemDC = CreateCompatibleDC(hDC);                /* Create a DC */
    SelectObject(hNewMemDC, hNewBitmap);                /* Relate them */
    ReleaseDC(hFont, hDC);                      /* Done with font DC */

    BitBlt(hNewMemDC, 0, 0, width, height,      /* Move Character in */
            hMemDC, offsets[iChar], 0, NOTSRCCOPY);

    /* Now wake up Clipboard and empty it */
    if (!OpenClipboard(hFont))
        ErrorBox(hBox, vszCannotOpenClip); // , vszCopyingToClip);
    else        /* Ok: We got the Clipboard */
        {
        EmptyClipboard();
        SetClipboardData(CF_BITMAP, hNewBitmap);        /* Tell Clipboard */
        }

    /* Tidy things up */
    CloseClipboard();
    DeleteDC(hNewMemDC);
}


/****************************************************************************
 * GetNewMap(width, height)
 *
 * purpose: create new bitmap of the given width and height.
 *
 * params : WORD width  : width of bitmap in pixels
 *          WORD height : height of bitmap in pixels
 *
 * returns: TRUE if successful, FALSE otherwise
 *
 * side effects: Handle and DC values of new DC assigned
 *
 ****************************************************************************/

BOOL
GetNewMap(
    DWORD width,
    DWORD height              /* New size */
    )
{
    HDC hDC;

    if (height==0) /* Check if something stupid is happening */
        height=font.PixHeight; /* Fix it */

    /* Put up an houglass ... this may take a while */
    if (!hHourGlass)
        hHourGlass = LoadCursor(NULL, IDC_WAIT);        /* Get Hourglass */
    hOldCursor = SetCursor(hHourGlass);         /* Show hourglass */

    /* Create a new bitmap to move the font definition bits into */
    hDC = GetDC(hFont);                         /* DC to be compatible with */
    hNewBitmap = CreateBitmap(
                    width << 3,                 /* Width of font in pixels */
                    height,
                    1, 1, (LPBYTE)NULL);
    if (!hNewBitmap)
        {
        ErrorBox(hBox, vszNotEnoughMem); // , vszAllocatingSpace);
        ReleaseDC(hFont, hDC);    /* bug# 2380 */
        return FALSE;
        }

    hNewMemDC = CreateCompatibleDC(hDC);                /* Create a DC */
    SelectObject(hNewMemDC, hNewBitmap);                /* Relate them */
    ReleaseDC(hFont, hDC);                      /* Done with font DC */
    PatBlt(hNewMemDC, 0, 0, width << 3, height, BLACKNESS);     /* Clear it */
    return TRUE;
}


/****************************************************************************
 * UseNewMap()
 *
 * params : none
 *
 * purpose: discard old bitmap and replace it with new one
 *
 * returns: none
 *
 * side effects: Handle to old bitmap and handle to old bitmap DC replaced
 *               by those of new bitmap respectively
 *
 ****************************************************************************/

VOID
UseNewMap(
    VOID
    )
{
    DeleteDC(hMemDC);
    DeleteObject(hBitmap);              /* Release old space */
    hBitmap = hNewBitmap;
    hMemDC = hNewMemDC;              /* Release old space */
    SetCursor(hOldCursor);              /* Restore regular cursor */
}



VOID
DeleteGlobalBitmap(
    VOID
    )
{
    if (hMemDC)
        DeleteDC(hMemDC);
    if (hBitmap)
        DeleteObject(hBitmap);
}


DWORD
Proport(
    DWORD value,
    DWORD top,
    DWORD bottom,
    DWORD limit
    )
    /* Reproportion a value by the ratio top/bottom to the nearest integer
     * and make sure we are still in range */
{
    return min(limit, (DWORD)((1 + 2 * value * top) / (2 * bottom)));
}
