//-----------------------------------------------------------------------------
// This files contains the module name for this mini driver.  Each mini driver
// must have a unique module name.  The module name is used to obtain the
// module handle of this Mini Driver.  The module handle is used by the
// generic library to load in tables from the Mini Driver.
// 
// Copyright (C) 1994-1995 Microsoft Corporation
// Copyright (C) 1995      Advanced Peripherals Technologies, Inc.
//-----------------------------------------------------------------------------

char *rgchModuleName = "PAGESMS";

#define PRINTDRIVER
#include <print.h>
#include "mdevice.h"
#include "gdidefs.inc"
#include "unidrv.h"
#include <memory.h>

#ifndef _INC_WINDOWSX
#include <windowsx.h>
#endif

#define CCHMAXCMDLEN    128
#define MAXIMGSIZE      0x7FED   /* GDIからCBFilterGraphicsに送られてくる    */
                                 /* データは1ライン分だが、とりあえずESX86で送信  */
                                 /* 可能な最大IMAGEサイズを用意しておく。      */
                                 /* 0x7FFF - 18 =  7FED byte                 */

/*_ バイトランレングス 圧縮ルーチン */
extern WORD FAR PASCAL RL_ECmd(LPBYTE, LPBYTE, WORD);
/*_ ランレングス4 圧縮ルーチン */
extern WORD FAR PASCAL RL4_ECmd (LPBYTE, LPBYTE, WORD, WORD, WORD);

typedef struct
{
    BYTE  fGeneral;       // General purpose bitfield
    BYTE  bCmdCbId;       // Callback ID; 0 iff no callback
    WORD  wCount;         // # of EXTCD structures following
    WORD  wLength;        // length of the command
} CD, *PCD, FAR * LPCD;

typedef struct tagPAGES {
    short sHorzRes;
    short sVertRes;
    LPSTR  lpCompBuf;     // 圧縮データ・バッファ
} PAGES, FAR * LPPAGES;

static BYTE ShiftJisPAGES[256] = {
//     +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //00
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //10
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //20
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //30
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //40
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //50
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //60
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //70
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  //80
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  //90
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //A0
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //B0
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //C0
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  //D0
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  //E0
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0   //F0
};

static BYTE ESC_VERT_ON[]  = "\x1B\x7E\x0E\x00\x01\x0B";
static BYTE ESC_VERT_OFF[] = "\x1B\x7E\x0E\x00\x01\x0C";

short usHorzRes;
short usVertRes;

#ifndef WINNT
BYTE NEAR __loadds IsDBCSLeadBytePAGES(BYTE Ch)
#else
BYTE NEAR IsDBCSLeadBytePAGES(BYTE Ch)
#endif
{
return ShiftJisPAGES[Ch];
}

#ifdef WINNT
LPWRITESPOOLBUF WriteSpoolBuf;
LPALLOCMEM UniDrvAllocMem;
LPFREEMEM UniDrvFreeMem;
#endif // WINNT

//---------------------------*OEMSendScalableFontCmd*--------------------------
// Action:  send Pages-style font selection command.
//-----------------------------------------------------------------------------
VOID FAR PASCAL OEMSendScalableFontCmd(lpdv, lpcd, lpFont)
LPDV    lpdv;
LPCD    lpcd;     // offset to the command heap
LPFONTINFO lpFont;
{
    LPSTR   lpcmd;
    short   ocmd;
    WORD    i;
    BYTE    rgcmd[CCHMAXCMDLEN];    // build command here
    LPPAGES lpPages = lpdv->lpMdv;

    if (!lpcd || !lpFont)
        return;

    // be careful about integer overflow.
    lpcmd = (LPSTR)(lpcd+1);
    ocmd = 0;

    for (i = 0; i < lpcd->wLength && ocmd < CCHMAXCMDLEN; )
        if (lpcmd[i] == '#' && lpcmd[i+1] == 'V')      // height
        {
            WORD    height;

            height = (lpFont->dfPixHeight - lpFont->dfInternalLeading)
                    * (1440 / lpFont->dfVertRes);
            rgcmd[ocmd++] = HIBYTE(height);
            rgcmd[ocmd++] = LOBYTE(height);
            i += 2;
        }
        else if (lpcmd[i] == '#' && lpcmd[i+1] == 'L')     // pitch
        {
            WORD    height;

            height = lpFont->dfPixHeight * (1440 / lpFont->dfVertRes);
            rgcmd[ocmd++] = HIBYTE(height);
            rgcmd[ocmd++] = LOBYTE(height);
            i += 2;
        }
        else if (lpcmd[i] == '#' && lpcmd[i+1] == 'H')     // width
        {
            if (lpFont->dfPixWidth > 0)
            {
                short tmpWidth;

                tmpWidth = lpFont->dfMaxWidth * (1440 / lpFont->dfVertRes);

                rgcmd[ocmd++] = HIBYTE(tmpWidth);
                rgcmd[ocmd++] = LOBYTE(tmpWidth);
            }
            i += 2;
            
        }
        else if (lpcmd[i] == '#' && lpcmd[i+1] == 'P')     // pitch
        {
            if (lpFont->dfPixWidth > 0)
            {
                short sWidth = (lpFont->dfMaxWidth * (1440/lpPages->sHorzRes));

                rgcmd[ocmd++] = HIBYTE(sWidth);
                rgcmd[ocmd++] = LOBYTE(sWidth);
            }
            i += 2;
        }
        else
            rgcmd[ocmd++] = lpcmd[i++];

    WriteSpoolBuf(lpdv, (LPSTR) rgcmd, ocmd);
}

//----------------------------*OEMScaleWidth*--------------------------------
// Action: return the scaled width which is calcualted based on the
//      assumption that ESC\Page assumes 72 points in one 1 inch.
//
// Formulas:
//  <extent> : <font units> = <base Width> : <hRes>
//  <base width> : <etmMasterHeight> = <newWidth> : <newHeight>
//  <etmMasterUnits> : <etmMasterHeight> = <font units> : <vRes>
// therefore,
//   <newWidth> = (<extent> * <hRes> * <newHeight>) / 
//                  (<etmMasterUnits> * <vRes>)
//---------------------------------------------------------------------------
short FAR PASCAL OEMScaleWidth(width, masterUnits, newHeight, vRes, hRes)
short width;        // in units specified by 'masterUnits'.
short masterUnits;
short newHeight;    // in units specified by 'vRes'.
short vRes, hRes;   // height and width device units.
{
    DWORD newWidth10;
    short newWidth;

    // assert that hRes == vRes to avoid overflow problem.
    if (vRes != hRes)
        return 0;

    newWidth10 = (DWORD)width * (DWORD)newHeight * 10;
    newWidth10 /= (DWORD)masterUnits;

    // we multiplied 10 first in order to maintain the precision of
    // the width calcution. Now convert it back and round to the
    // nearest integer.
    newWidth = (short)((newWidth10 + 5) / 10);

    return newWidth;
}

#ifndef WINNT
short FAR PASCAL __loadds OEMOutputChar( lpdv, lpstr, len, rcID)
#else
short FAR PASCAL OEMOutputChar( lpdv, lpstr, len, rcID)
#endif
LPDV	lpdv;
LPSTR	lpstr;
short	len;
short	rcID;
{

    short rSize = 0;

    if (rcID == 6 || rcID == 8)
    {
        LPSTR lpChar = lpstr, 
              lpStrTmp = lpstr;
        WORD  fLeadByteFlag = TRUE;
        int   i, j;

        for (i = 0,j = 0; i < len; j ++, i++, lpChar++)
        { 
            if (!IsDBCSLeadBytePAGES((BYTE)*lpChar)) // SBCS
            {
                if (fLeadByteFlag)
                    {
                    WriteSpoolBuf(lpdv, lpStrTmp, j);
                    WriteSpoolBuf(lpdv, ESC_VERT_OFF, sizeof(ESC_VERT_OFF));
                    lpStrTmp += j;
                    j = 0;
                    fLeadByteFlag = FALSE;
                    rSize += sizeof(ESC_VERT_OFF);
                    }
            }
            else                                 // DBCS
            {
                if (!fLeadByteFlag)
                    {
                    WriteSpoolBuf(lpdv,  lpStrTmp, j);
                    WriteSpoolBuf(lpdv, ESC_VERT_ON, sizeof(ESC_VERT_ON));
                    lpStrTmp += j;
                    j = 0;
                    fLeadByteFlag = TRUE;
                    rSize += sizeof(ESC_VERT_ON);
                    }
                j ++; i++; lpChar++;
            }
        } 
        WriteSpoolBuf(lpdv, lpStrTmp, j);
    }
    else
        WriteSpoolBuf(lpdv, lpstr, len);

    return len+rSize;

}

#ifndef WINNT
short FAR PASCAL Enable( lpdv, style, lpModel, lpPort, lpStuff)
LPDV    lpdv;
WORD    style;
LPSTR   lpModel;
LPSTR   lpPort;
LPDM    lpStuff;
{
    CUSTOMDATA      cd;
    short           sRet;
    LPPAGES         lpPages;


    cd.cbSize               = sizeof( CUSTOMDATA );
    cd.hMd                  = GetModuleHandle( (LPSTR)rgchModuleName );
    cd.fnOEMDump            = NULL;
    cd.fnOEMOutputChar      = (LPFNOEMOUTPUTCHAR)OEMOutputChar;

    // In order to the Style vlalue, following process is performed.
    //      0x0000 Initialize device block.
    //      0x0001 Inquire Device GDIINFO.
    //      0x8000 Initialize device block without output. CreateIC()
    //      0x8001 Inquire Device GDIINFO without output.  CreateIC()

    sRet = UniEnable( lpdv, style, lpModel, lpPort, lpStuff, &cd );

    if (style == 0x0000)
    {
        lpdv->fMdv = FALSE;
        if (!(lpPages = lpdv->lpMdv = GlobalAllocPtr(GHND,sizeof(PAGES))))
        {
            UniDisable( lpdv );
            return FALSE;
        }
        lpdv->fMdv = TRUE;

        lpPages->sHorzRes = usHorzRes;
        lpPages->sVertRes = usVertRes;
        
    } else 
    if( style == 0x0001)
    {
        //INQUIREINFO
        usHorzRes = ((LPGDIINFO)lpdv)->dpAspectX;
        usVertRes = ((LPGDIINFO)lpdv)->dpAspectY;

    }

   return sRet;
}

//-------------------------------------------------------------------
// Function: Disable()
// Action  : free Mdv and call Mdv
//-------------------------------------------------------------------
void FAR PASCAL Disable(lpdv)
LPDV lpdv;
{
    if (lpdv->fMdv)
    {
        GlobalFreePtr (lpdv->lpMdv);
        lpdv->fMdv = FALSE;
    }

    UniDisable(lpdv);
}
#else //WINNT
/*************************** Function Header *******************************
 *  MiniDrvEnablePDEV
 *
 * HISTORY:
 *  30 Apl 1996    -by-    Sueya Sugihara    [sueyas]
 *      Created it,  from NT/DDI spec.
 *
 ***************************************************************************/
BOOL
MiniDrvEnablePDEV(
LPDV      lpdv,
ULONG    *pdevcaps)
{
    LPPAGES   lpPages;


        usHorzRes = (short)((PGDIINFO)pdevcaps)->ulAspectX;
        usVertRes = (short)((PGDIINFO)pdevcaps)->ulAspectY;

        lpdv->fMdv = FALSE;
        if (!(lpPages = lpdv->lpMdv = UniDrvAllocMem(sizeof(PAGES))))
        {
            return FALSE;
        }
        if (!(lpPages->lpCompBuf = UniDrvAllocMem(MAXIMGSIZE)))
        {
            return FALSE;
        }
        lpdv->fMdv = TRUE;

        lpPages->sHorzRes = usHorzRes;
        lpPages->sVertRes = usVertRes;


    return TRUE;


}
/*************************** Function Header *******************************
 *  MiniDrvDisablePDEV
 *
 * HISTORY:
 *  30 Apl 1996    -by-    Sueya Sugihara    [sueyas]
 *      Created it,  from NT/DDI spec.
 *
 ***************************************************************************/
VOID
MiniDrvDisablePDEV(
LPDV lpdv)
{

    if (lpdv->fMdv)
    {
        UniDrvFreeMem(((LPPAGES)(lpdv->lpMdv))->lpCompBuf);
        UniDrvFreeMem(lpdv->lpMdv);
        lpdv->fMdv = FALSE;
    }

}

#endif //WINNT


/*f***************************************************************************/
/*  PAGES PRINTER DRIVER for MS-Windows95                                    */
/*                                                                           */
/*  名称:  CBFilterGraphics                                                 */
/*                                                                           */
/*  機能:  イメージデータをESX86コマンドを使用して出力する。                        */
/*                                                                           */
/*  書式:  WORD FAR PASCAL CBFilterGraphics(lpdv, lpBuf, wLen)              */
/*                                                                           */
/*  入力:  LPDV     lpdv      UNIDRV.DLLが使用するPDEVICE構造体             */
/*          LPSTR    lpBuf     ラスターグラフィックスデータのバッファのポインタ                */
/*          WORD     wLen      lpBufのサイズ(バイト数)                           */
/*                                                                           */
/*                                                                           */
/*  出力:  return             出力したバイト数                               */
/*                                                                           */
/*  注記:                                                                   */
/*                                                                           */
/*  履歴:  1995.11.xx  Ver 1.00                                             */
/*****************************************************************************/
WORD FAR PASCAL CBFilterGraphics (lpdv, lpBuf, wLen)
LPDV      lpdv;     // Points to private data required by the Unidriver.dll
LPSTR     lpBuf;    // points to buffer of graphics data
WORD      wLen;     // length of buffer in bytes

{  
    LPSTR   lpCompImage;
    WORD    wCompLen;
    LONG    lHorzPixel;
    WORD    wLength;      // Let's use a temporary LEN
    LPPAGES lpPages = lpdv->lpMdv;

//#define MAXIMGSIZE  0x7FED          // 32K-18 bytes
static BYTE params[] = {(0x1B), (0x7E), (0x86), 00,00, 01, 00, 00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,01};


    /*_ LPDVに保管したイメージバッファのポインタをセット */
    lpCompImage = lpPages->lpCompBuf;

    /*_ イメージのi軸方向のサイズISIZを計算する。 */
    lHorzPixel = (LONG)(wLen * 8);

    /*_ イメージデータをBtye Run Length Algorithmで圧縮。 */
    wCompLen = RL_ECmd((LPBYTE)lpBuf, (LPBYTE)lpCompImage, wLen);

    /*_ 圧縮後のデータのサイズにESX86のLEN以外のパラメータ分のサイズを加える。 */
    wLength = wCompLen + 18;

    /*_ ESX86コマンドのLENをセット。 */
    params[3] = (BYTE) (wLength >>8 & 0x00ff);    // get higher byte
    params[4] = (BYTE) (wLength & 0x00ff);        // get lower byte

    /*_ 圧縮方法のパラメータをByte Run Lengthにセット。 */
    params[6] = 0x02;

    /*_ ESX86コマンドのISIZをセット。 */
    params[17] = (BYTE) (lHorzPixel >> 8 & 0x000000ffL);   // get ISZ higher byte
    params[18] = (BYTE) (lHorzPixel & 0x000000ffL);        // get ISZ lower byte

    /*_ ESX86コマンドをスプール出力する。 */
    WriteSpoolBuf((LPDV)lpdv, (LPSTR)params, 23);

    /*_ 圧縮されたデータをスプール出力する。 */
    WriteSpoolBuf((LPDV)lpdv, lpCompImage, wCompLen);

    return wLen;
}

// The following is implemented in MiniDrvEnablePDEV/DisablePDEV
// on NT-J.  We do not simulate Control DDI call, and it is not
// guranteed that STARTOC, etc. always corresponds to minidriver
// enable/disable.

#ifndef WINNT

/*f***************************************************************************/
/*  PAGES PRINTER DRIVER for MS-Windows95                                    */
/*                                                                           */
/*  名称:  Control                                                          */
/*                                                                           */
/*  機能:  Calls Escape function from applications		                     */
/*                                                                           */
/*  書式:  short FAR PASCAL Control(lpdv, nFunction,                        */
/*                                             lpInData, lpOutData)          */
/*                                                                           */
/*  入力:  LPDV        lpdv        PDEVICE structure                        */
/*          WORD        function    Subfunction ID                           */
/*          LPSTR       lpInData    Input data                               */
/*          LPSTR       lpOutData   Output data                              */
/*                                                                           */
/*  出力:  short ret   Positive :    Normal exit                            */
/*                      Negative :    Error exit                             */
/*                      FALSE  :      No escape subfunction                  */
/*                                                                           */
/*  注記:  nFunction and Escape numbers are the same                        */
/*                                                                           */
/*  履歴:  1995.12.xx  Ver 1.00                                             */
/*                                                                           */
/*****************************************************************************/
short FAR PASCAL Control(LPDV  lpdv,
                         WORD  function,
                         LPSTR lpInData,
                         LPSTR lpOutData)
{
    LPPAGES lpPages = lpdv->lpMdv;

    switch (function)
    {
        /*_ STARTDOCのときは、圧縮データ用のバッファを確保する。 */
        case STARTDOC :
            lpPages->lpCompBuf = GlobalAllocPtr(GHND,MAXIMGSIZE);
            break;

        /*_ ABORTDOC,ENDDOCのときは、圧縮データ用のバッファを解放する。 */
        case ABORTDOC :
        case ENDDOC :
            GlobalFreePtr (lpPages->lpCompBuf);
            break;
    }

    /*_ UNIDRVのControl DDIをコール */
    return UniControl(lpdv, function, lpInData, lpOutData);
}
#endif //!WINNT


#ifdef WINNT
DRVFN  MiniDrvFnTab[] =
{
    {  INDEX_MiniDrvEnablePDEV,       (PFN)MiniDrvEnablePDEV  },
    {  INDEX_MiniDrvDisablePDEV,      (PFN)MiniDrvDisablePDEV  },
    {  INDEX_OEMWriteSpoolBuf,        (PFN)CBFilterGraphics  },
    {  INDEX_OEMSendScalableFontCmd,  (PFN)OEMSendScalableFontCmd  },
    {  INDEX_OEMScaleWidth1,          (PFN)OEMScaleWidth  },
    {  INDEX_OEMOutputChar,           (PFN)OEMOutputChar  }
};

BOOL
MiniDrvEnableDriver(
    MINIDRVENABLEDATA  *pEnableData
    )
{
    if (pEnableData == NULL)
        return FALSE;

    if (pEnableData->cbSize == 0)
    {
        pEnableData->cbSize = sizeof (MINIDRVENABLEDATA);
        return TRUE;
    }

    if (pEnableData->cbSize < sizeof (MINIDRVENABLEDATA)
            || HIBYTE(pEnableData->DriverVersion)
            < HIBYTE(MDI_DRIVER_VERSION))
    {
        // Wrong size and/or mismatched version
        return FALSE;
    }

    // Load callbacks provided by the Unidriver

    if (!bLoadUniDrvCallBack(pEnableData,
            INDEX_UniDrvWriteSpoolBuf, (PFN *) &WriteSpoolBuf)
        ||!bLoadUniDrvCallBack(pEnableData,
            INDEX_UniDrvAllocMem, (PFN *) &UniDrvAllocMem)
        ||!bLoadUniDrvCallBack(pEnableData,
            INDEX_UniDrvFreeMem, (PFN *) &UniDrvFreeMem))
    {
        return FALSE;
    }

    pEnableData->cMiniDrvFn
        = sizeof (MiniDrvFnTab) / sizeof(MiniDrvFnTab[0]);
    pEnableData->pMiniDrvFn = MiniDrvFnTab;

    return TRUE;
}

#endif //WINNT