/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    pclxl.cpp

Abstract:

    PCL-XL command output high level function implementation

Environment:

    Windows Whistler

Revision History:

    08/23/99     
        Created initial framework.

--*/

#include "xlpdev.h"
#include "pclxlcmd.h"
#include "pclxle.h"
#include "xldebug.h"
#include "xlgstate.h"
#include "xloutput.h"
#include "xlbmpcvt.h"

//
// Hatch brush raster pattern
//
const BYTE   gubSizeOfHatchBrush = 32 * 32 / 8;
const USHORT gusWidthOfHatchBrush = 32;
const USHORT gusHeightOfHatchBrush = 32;
const BYTE gubHatchBrush[6][gubSizeOfHatchBrush] = 
{ {0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00,
   0xff, 0xff, 0xff, 0xff},
  {0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01},
  {0x80, 0x00, 0x00, 0x00,
   0x40, 0x00, 0x00, 0x00,
   0x20, 0x00, 0x00, 0x00,
   0x10, 0x00, 0x00, 0x00,
   0x08, 0x00, 0x00, 0x00,
   0x04, 0x00, 0x00, 0x00,
   0x02, 0x00, 0x00, 0x00,
   0x01, 0x00, 0x00, 0x00,
   0x00, 0x80, 0x00, 0x00,
   0x00, 0x40, 0x00, 0x00,
   0x00, 0x20, 0x00, 0x00,
   0x00, 0x10, 0x00, 0x00,
   0x00, 0x08, 0x00, 0x00,
   0x00, 0x04, 0x00, 0x00,
   0x00, 0x02, 0x00, 0x00,
   0x00, 0x01, 0x00, 0x00,
   0x00, 0x00, 0x80, 0x00,
   0x00, 0x00, 0x40, 0x00,
   0x00, 0x00, 0x20, 0x00,
   0x00, 0x00, 0x10, 0x00,
   0x00, 0x00, 0x08, 0x00,
   0x00, 0x00, 0x04, 0x00,
   0x00, 0x00, 0x02, 0x00,
   0x00, 0x00, 0x01, 0x00,
   0x00, 0x00, 0x00, 0x80,
   0x00, 0x00, 0x00, 0x40,
   0x00, 0x00, 0x00, 0x20,
   0x00, 0x00, 0x00, 0x10,
   0x00, 0x00, 0x00, 0x08,
   0x00, 0x00, 0x00, 0x04,
   0x00, 0x00, 0x00, 0x02,
   0x00, 0x00, 0x00, 0x01},
  {
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x02,
   0x00, 0x00, 0x00, 0x04,
   0x00, 0x00, 0x00, 0x08,
   0x00, 0x00, 0x00, 0x10,
   0x00, 0x00, 0x00, 0x20,
   0x00, 0x00, 0x00, 0x40,
   0x00, 0x00, 0x00, 0x80,
   0x00, 0x00, 0x01, 0x00,
   0x00, 0x00, 0x02, 0x00,
   0x00, 0x00, 0x04, 0x00,
   0x00, 0x00, 0x08, 0x00,
   0x00, 0x00, 0x10, 0x00,
   0x00, 0x00, 0x20, 0x00,
   0x00, 0x00, 0x40, 0x00,
   0x00, 0x00, 0x80, 0x00,
   0x00, 0x01, 0x00, 0x00,
   0x00, 0x02, 0x00, 0x00,
   0x00, 0x04, 0x00, 0x00,
   0x00, 0x08, 0x00, 0x00,
   0x00, 0x10, 0x00, 0x00,
   0x00, 0x20, 0x00, 0x00,
   0x00, 0x40, 0x00, 0x00,
   0x00, 0x80, 0x00, 0x00,
   0x01, 0x00, 0x00, 0x00,
   0x02, 0x00, 0x00, 0x00,
   0x04, 0x00, 0x00, 0x00,
   0x08, 0x00, 0x00, 0x00,
   0x10, 0x00, 0x00, 0x00,
   0x20, 0x00, 0x00, 0x00,
   0x40, 0x00, 0x00, 0x00,
   0x80, 0x00, 0x00, 0x00},
  {0xff, 0xff, 0xff, 0xff,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01,
   0x00, 0x00, 0x00, 0x01},
  {0x80, 0x00, 0x00, 0x01,
   0x40, 0x00, 0x00, 0x02,
   0x20, 0x00, 0x00, 0x04,
   0x10, 0x00, 0x00, 0x08,
   0x08, 0x00, 0x00, 0x10,
   0x04, 0x00, 0x00, 0x20,
   0x02, 0x00, 0x00, 0x40,
   0x01, 0x00, 0x00, 0x80,
   0x00, 0x80, 0x01, 0x00,
   0x00, 0x40, 0x02, 0x00,
   0x00, 0x20, 0x04, 0x00,
   0x00, 0x10, 0x08, 0x00,
   0x00, 0x08, 0x10, 0x00,
   0x00, 0x04, 0x20, 0x00,
   0x00, 0x02, 0x40, 0x00,
   0x00, 0x01, 0x80, 0x00,
   0x00, 0x01, 0x80, 0x00,
   0x00, 0x02, 0x40, 0x00,
   0x00, 0x04, 0x20, 0x00,
   0x00, 0x08, 0x10, 0x00,
   0x00, 0x10, 0x08, 0x00,
   0x00, 0x20, 0x04, 0x00,
   0x00, 0x40, 0x02, 0x00,
   0x00, 0x80, 0x01, 0x00,
   0x01, 0x00, 0x00, 0x80,
   0x02, 0x00, 0x00, 0x40,
   0x04, 0x00, 0x00, 0x20,
   0x08, 0x00, 0x00, 0x10,
   0x10, 0x00, 0x00, 0x08,
   0x20, 0x00, 0x00, 0x04,
   0x40, 0x00, 0x00, 0x02,
   0x80, 0x00, 0x00, 0x01}
};

//
// High level output functions
//
HRESULT
XLOutput::
BeginImage(
    ColorMapping CMapping,
    ULONG   ulOutputBPP,
    ULONG   ulSrcWidth,
    ULONG   ulSrcHeight,
    ULONG   ulDestWidth,
    ULONG   ulDestHeight)
/*++

Routine Description:

     Sends BeginImage operator.

Arguments:

Return Value:

Note:

--*/
{
    DWORD dwI = 0;

    XL_VERBOSE(("XLOutput::BeginImage:SrcW=%d, SrcH=%d, DstH=%d, DstW=%d\n",
              ulSrcWidth, ulSrcHeight, ulDestHeight, ulDestWidth));


    SetOutputBPP(CMapping, ulOutputBPP);
    SetSourceWidth((uint16)ulSrcWidth);
    SetSourceHeight((uint16)ulSrcHeight);
    SetDestinationSize((uint16)ulDestWidth, (uint16)ulDestHeight);
    Send_cmd(eBeginImage);

    return S_OK;
}
  
HRESULT
XLOutput::
SetOutputBPP(
    ColorMapping CMapping,
    ULONG   ulOutputBPP)
/*++

Routine Description:

    Sends Color mapping and output depth.

Arguments:

Return Value:

Note:

--*/
{
    switch (CMapping)
    {
    case eDirectPixel:
        SetColorMapping(eDirectPixel);
        break;
    case eIndexedPixel:
        SetColorMapping(eIndexedPixel);
        break;
    default:
        SetColorMapping(eDirectPixel);
    }

    switch (ulOutputBPP)
    {
    case 1:
        SetColorDepth(e1Bit);
        break;
    case 4:
        SetColorDepth(e4Bit);
        break;
    case 8:
    case 24:
        SetColorDepth(e8Bit);
        break;
    default:
        XL_ERR(("ulOutputBPP = %d is not supported\n", ulOutputBPP));
        //
        // Send color depth anyway to avoid XL error.
        //
        SetColorDepth(e8Bit);
    }

    return S_OK;
}

HRESULT
XLOutput::
SetPalette(
    ULONG ulOutputBPP,
    DWORD dwCEntries,
    DWORD *pdwColor)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    HRESULT hResult;

    if (dwCEntries != 0 && pdwColor != NULL)
    {
        SetPaletteDepth(e8Bit);
        SetPaletteData(m_DeviceColorDepth, dwCEntries, pdwColor);
        hResult = S_OK;
    }
    else
    {
        XL_ERR(("XLOutput::SetPalette pxlo = NULL\n"));
        hResult = S_FALSE;
    }

    return hResult;
}

HRESULT
XLOutput::
SetClip(
    CLIPOBJ *pco)
/*++

Routine Description:

    Sends clip object.

Arguments:

Return Value:

Note:

--*/
{
    PATHOBJ *ppo;
    XLGState *pGState = this;
    HRESULT hResult;

    if (S_OK == pGState->CheckClip(pco))
        return S_OK;

    if ( NULL == pco )
    {
        XL_VERBOSE(("XLOutput::SetClip pco = NULL.\n"));
        Send_cmd(eSetClipToPage);
        pGState->ClearClip();
        return S_OK;
    }

    XL_VERBOSE(("XLOutput::SetClip: pco->iDComplexity=%d\n", pco->iDComplexity));

    switch(pco->iDComplexity)
    {
    case DC_RECT:
        SetClipMode(eClipEvenOdd);

        Send_cmd(eNewPath);
        RectanglePath(&(pco->rclBounds));

        SetClipRegion(eInterior);
        Send_cmd(eSetClipReplace);
        pGState->SetClip(pco);
        hResult = S_OK;
        break;

    case DC_COMPLEX:
        ppo = CLIPOBJ_ppoGetPath(pco);

        if (NULL == ppo)
        {
            XL_ERR(("XLOutput::SetClip ppo = NULL.\n"));
            Send_cmd(eSetClipToPage);
            pGState->ClearClip();
            hResult = S_FALSE;
            break;
        }

        SetClipMode(eClipEvenOdd);
        Path(ppo);
        SetClipRegion(eInterior);
        Send_cmd(eSetClipReplace);
        pGState->SetClip(pco);
        hResult = S_OK;
        break;

    case DC_TRIVIAL:
    default:
        Send_cmd(eSetClipToPage);
        pGState->ClearClip();
        hResult = S_OK;
        break;
    }

    return hResult;
}

HRESULT
XLOutput::
RoundRectanglePath(
    RECTL  *prclBounds)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    RECTL Rectl;

    if ( NULL == prclBounds )
    {
        XL_ERR(("XLOutput::RoundRectangle prclBounds = NULL.\n"));
        return E_UNEXPECTED;
    }

    XL_VERBOSE(("XLOutput::RoundRectanglePath:left=%d, top=%d, right=%d, bottom=%d\n",
             prclBounds->left,
             prclBounds->top,
             prclBounds->right,
             prclBounds->bottom));

    //
    // BoundingBox can handle only positive numbers.
    //
    Rectl = *prclBounds;

    if (Rectl.left < 0)
    {
        Rectl.left = 0;
    }
    if (Rectl.top < 0)
    {
        Rectl.top = 0;
    }
    if (Rectl.right < 0)
    {
        Rectl.right = 0;
    }
    if (Rectl.bottom < 0)
    {
        Rectl.bottom = 0;
    }


    //
    // DCR: Round value needs to be sent!
    //
    if (S_OK == SetBoundingBox((uint16)Rectl.left,
                               (uint16)Rectl.top,
                               (uint16)Rectl.right,
                               (uint16)Rectl.bottom) &&
        S_OK == Send_uint16_xy(0, 0) &&
        S_OK == Send_attr_ubyte(eEllipseDimension) &&
        S_OK == Send_cmd(eRoundRectanglePath))
        return S_OK;
    else
        return S_FALSE;

}

HRESULT
XLOutput::
SetCursor(
    LONG   lX,
    LONG   lY)
/*++

Routine Description:

    Set cursor.

Arguments:

Return Value:

Note:

--*/
{

    XL_VERBOSE(("XLOutput::SetCursor:X=%d, Y=%d\n", lX, lY));

    Send_sint16_xy((sint16)lX, (sint16)lY);
    Send_attr_ubyte(ePoint);
    Send_cmd(eSetCursor);

    m_lX = lX;
    m_lY = lY;

    return S_OK;
}

HRESULT
XLOutput::
GetCursorPos(
    PLONG plX,
    PLONG plY)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{

    XL_VERBOSE(("XLOutput::GetCursor:X=%d, Y=%d\n", *plX, *plY));

    if (plX == NULL || plY == NULL)
        return E_UNEXPECTED;

    *plX = m_lX;
    *plY = m_lY;

    return S_OK;
}

HRESULT
XLOutput::
ReadImage(
    DWORD  dwBlockHeight,
    CompressMode CMode)
/*++

Routine Description:

    Sends ReadImage operator

Arguments:

Return Value:

Note:

--*/
{
    XL_VERBOSE(("XLOutput::ReadImage:dwBlockHeight=%d\n", dwBlockHeight));

    Send_uint16((uint16)0);
    Send_attr_ubyte(eStartLine);

    Send_uint16((uint16)dwBlockHeight);
    Send_attr_ubyte(eBlockHeight);

    //
    // DCR: Need to support JPEG
    //
    SetCompressMode(CMode);
    Send_cmd(eReadImage);

    return S_OK;
}

HRESULT
XLOutput::
ReadImage(
    DWORD  dwStart,
    DWORD  dwBlockHeight,
    CompressMode CMode)
/*++

Routine Description:

    Sends ReadImage operator

Arguments:

Return Value:

Note:

--*/
{
    XL_VERBOSE(("XLOutput::ReadImage:dwBlockHeight=%d\n", dwBlockHeight));

    Send_uint16((uint16)dwStart);
    Send_attr_ubyte(eStartLine);

    Send_uint16((uint16)dwBlockHeight);
    Send_attr_ubyte(eBlockHeight);

    //
    // DCR: Need to support JPEG
    //
    SetCompressMode(CMode);
    Send_cmd(eReadImage);

    return S_OK;
}

HRESULT
XLOutput::
ReadRasterPattern(
    DWORD  dwBlockHeight,
    CompressMode CMode)
/*++

Routine Description:

    Sends ReadRasterPattern operator.

Arguments:

Return Value:

Note:

--*/
{
    XL_VERBOSE(("XLOutput::ReadRasterPattern:dwBlockHeight=%d\n", dwBlockHeight));

    Send_uint16((uint16)0);
    Send_attr_ubyte(eStartLine);
    Send_uint16((uint16)dwBlockHeight);
    Send_attr_ubyte(eBlockHeight);

    //
    // DCR: Need to support JPEG
    //
    SetCompressMode(CMode);
    Send_cmd(eReadRastPattern);

    return S_OK;
}

HRESULT
XLOutput::
SetRGBColor(
    uint32 uint32_RGB)
/*++

Routine Description:

    Sends SetGrayLevel attribute.

Arguments:

Return Value:

Note:

--*/
{

    //
    // RGB color 3 bytes
    //
    Send_ubyte_array_header(3);
    WriteByte(RED(uint32_RGB));
    WriteByte(GREEN(uint32_RGB));
    WriteByte(BLUE(uint32_RGB));
    Send_attr_ubyte(eRGBColor);

    return S_OK;
}

HRESULT
XLOutput::
SetGrayLevel(
    ubyte ubyte_gray)
/*++

Routine Description:

    Sends SetGrayLevel attribute.

Arguments:

Return Value:

Note:

--*/
{
    Send_ubyte(ubyte_gray);
    Send_attr_ubyte(eGrayLevel);

    return S_OK;
}

HRESULT
XLOutput::
RectanglePath(
    RECTL  *prclRect)
/*++

Routine Description:

    Sends Rectangle Path

Arguments:

Return Value:

Note:

--*/
{
    RECTL Rectl;

    if (NULL == prclRect)
    {
        XL_ERR(("XLOutput::RectanglePath: prclRect == NULL\n"));
        return E_UNEXPECTED;
    }

    XL_VERBOSE(("XLOutput::RectanglePath:left=%d, top=%d, right=%d, bottom=%d\n",
             prclRect->left,
             prclRect->top,
             prclRect->right,
             prclRect->bottom));

    Rectl = *prclRect;

    if (prclRect->left < 0)
    {
        Rectl.left = 0;
    }
    if (prclRect->top < 0)
    {
        Rectl.top = 0;
    }
    if (prclRect->right < 0)
    {
        Rectl.right = 0;
    }
    if (prclRect->bottom < 0)
    {
        Rectl.bottom = 0;
    }


    if (S_OK == SetBoundingBox((uint16)Rectl.left,
                               (uint16)Rectl.top,
                               (uint16)Rectl.right,
                               (uint16)Rectl.bottom) &&
        S_OK == Send_cmd(eRectanglePath) )
        return S_OK;
    else
        return S_FALSE;
}

HRESULT
XLOutput::
Path(
    PATHOBJ *ppo)
/*++

Routine Description:

    Sends path.

Arguments:

Return Value:

Note:

--*/
{
    POINTFIX* pptfx;
    PATHDATA  PathData;
    LONG      lPoints;
    HRESULT   hResult;
    BOOL      bMore;

    XL_VERBOSE(("XLOutput::Path\n"));

    if (ppo == NULL)
    {
        XL_ERR(("XLOutput::Path ppo = NULL.\n"));
        return E_UNEXPECTED;
    }


    //
    // Emit newpath operator
    // Don't do it if we're between path escapes
    //
    hResult = Send_cmd(eNewPath);

    //
    // Path object case
    //

    PATHOBJ_vEnumStart(ppo);

    do
    {
        bMore   = PATHOBJ_bEnum(ppo, &PathData);

        pptfx   = PathData.pptfx;
        if ( 0 == (lPoints = PathData.count))
        {
            XL_VERBOSE(("XLOutput::Path PathData.Count == 0\n"));
            hResult = S_FALSE;
            continue;
        }

        //
        // Begin new sub path
        //

        if (PathData.flags & PD_BEGINSUBPATH)
        {
            //
            // start new path
            //

            if (hResult == S_OK)
                hResult = SetCursor(FXTOL(pptfx->x), FXTOL(pptfx->y));;
            pptfx++;
            lPoints--;
        }


        if (lPoints > 0)
        {
            if (PathData.flags & PD_BEZIERS)
            {
                //
                // Output a Bezier curve segment
                //

                ASSERTMSG((lPoints % 3) == 0,
                          ("Incorrect number of points for a Bezier curve: %d\n", lPoints));

                if (hResult == S_OK)
                    hResult = BezierPath(pptfx, lPoints);
            }
            else
            {
                //
                // Draw straight line segment
                //

                if (hResult == S_OK)
                    hResult = LinePath(pptfx, lPoints);
            }
        }

        //
        // Close subpath
        //

        if (PathData.flags & PD_CLOSEFIGURE)
        {
            if (hResult == S_OK)
                hResult = Send_cmd(eCloseSubPath);
        }

    }
    while (bMore);

    return hResult;
}


HRESULT
XLOutput::
BezierPath(
    POINTFIX* pptfx,
    LONG      lPoints)
/*++

Routine Description:

    Sends bezier path

Arguments:

Return Value:

Note:

--*/
{
    LONG lValue, lI;
    DWORD dwDataLength;

    if (NULL == pptfx)
    {
         XL_ERR(("XLOutput::BezierPath: pptfx == NULL\n"));
         return E_UNEXPECTED;
    }

    XL_VERBOSE(("XLOutput::BezierPath(lPoints=%d)\n",lPoints));

    Send_uint16((uint16)lPoints);
    Send_attr_ubyte(eNumberOfPoints);
    Send_ubyte(eSint16);
    Send_attr_ubyte(ePointType);
    Send_cmd(eBezierPath);

    dwDataLength = lPoints * 2 * sizeof(sint16);

    if (dwDataLength > 0xff)
    {
        WriteByte(PCLXL_dataLength);
        Write((PBYTE)&dwDataLength, sizeof(DWORD));
    }
    else
    {
        WriteByte(PCLXL_dataLengthByte);
        WriteByte((ubyte)dwDataLength);
    }

    for (lI = 0; lI < lPoints; lI++)
    {
        lValue = FXTOL(pptfx->x);
        Write((PBYTE)&lValue, sizeof(sint16));
        lValue = FXTOL(pptfx->y);
        Write((PBYTE)&lValue, sizeof(sint16));
        pptfx++;
    }

    //
    // Update the last coordinate.
    // Make sure that there are some points.
    if (lPoints > 0)
    {
        pptfx--;
        m_lX = FXTOL(pptfx->x);
        m_lY = FXTOL(pptfx->y);
    }
    else
    {
        m_lX = 0;
        m_lY = 0;
    }


    return S_OK;
}
    
HRESULT
XLOutput::
LinePath(
    POINTFIX* pptfx,
    LONG      lPoints)
/*++

Routine Description:

   Sends line path.

Arguments:

Return Value:

Note:

--*/
{
    LONG lValueX, lValueY, lI, lJ;
    LONG lx, ly;
    DWORD dwDataLength;

    if (NULL == pptfx)
    {
         XL_ERR(("XLOutput::LinePath: pptfx == NULL\n"));
         return E_UNEXPECTED;
    }

    XL_VERBOSE(("XLOutput::LinePath(lPoints=%d)\n", lPoints));

    //
    // Optimization. Use byte relpath to minimize the output size
    // First, check if the difference from the previous position is in a byte.
    //
    BOOL bModeChange;
    enum { eMode_SByte, eMode_SInt, eMode_None} Mode;
    LONG lStart, lEnd, lNumOfSByte;
    LONG lStartX, lStartY;
    POINTFIX *pptfx_tmp = pptfx;

    //
    // Get current cursor position
    //
    lStartX = lx = m_lX;
    lStartY = ly = m_lY;

    //
    // Reset
    //
    lStart = 0;
    Mode = eMode_None;
    bModeChange = FALSE;
    lNumOfSByte = 0;

    for (lI = 0; lI < lPoints; )
    {
        XL_VERBOSE(("XLOutput::LinePath: (%d)=(%d,%d)\n",lI, lx, ly));
        lValueX = FXTOL(pptfx_tmp->x) - (LONG)lx;
        lValueY = FXTOL(pptfx_tmp->y) - (LONG)ly;

        //
        // Mode needs to be in SByte or SInt?
        //
        if ( -128 <= lValueX && lValueX <= 127
        &&   -128 <= lValueY && lValueY <= 127 )
        {
            if (Mode == eMode_SInt)
            {
                //
                // Optimization
                //
                // To switch mode between SInt and SByte, it needs 7 bytes.
                //
                // uint16 XX NumberOfPoints
                // ubyte eSByte PointType
                // LineRelPath
                //
                // 4 points with SInt consumes 2 x 4 = 8 bytes extra data.
                // 3 points with SInt consumes 2 x 3 = 6 bytes extra data.
                //
                // 4 points is the threshold to swith mode to SInt.
                //
                // Number of points: lEnd - lStart + 1
                // if (lI - 1 - lStartSByte + 1 >= 4)
                //
                // If SByte continues more than 4 points, switch mode from
                // SByte to SInt.
                //
                if (lNumOfSByte >= 4)
                {
                    bModeChange = TRUE;
                    lI -= lNumOfSByte;
                    pptfx_tmp -= lNumOfSByte;
                    lEnd = (lI - 1);
                    lNumOfSByte = 0;
                }
            
                //
                // Reset starting point of SByte
                //
                lNumOfSByte ++;
            }
            else
            {
                Mode = eMode_SByte;
            }

            XL_VERBOSE(("XLOutput::LinePath: (SByte) lx1=%d, lx2=%d\n", lx, FXTOL(pptfx_tmp->x)));
        }
        else
        {
            if (Mode == eMode_SByte)
            {
                bModeChange = TRUE;
                lEnd = lI - 1;
            }
            else
            {
                Mode = eMode_SInt;
                lNumOfSByte = 0;
            }
            XL_VERBOSE(("XLOutput::LinePath: (SInt) lx1=%d, lx2=%d\n", lx, FXTOL(pptfx_tmp->x)));
        }

        if (!bModeChange && lI + 1 == lPoints)
        {
            bModeChange = TRUE;
            lEnd = lI;
            lI ++;
        }

        if (bModeChange)
        {
            XL_VERBOSE(("XLOutput::LinePath: Draw\n"));

            //
            // Get start cursor position
            //
            lx = lStartX;
            ly = lStartY;

            if (Mode == eMode_SByte)
            {
                //
                // SByte
                //
                Send_uint16((uint16)(lEnd - lStart + 1));
                Send_attr_ubyte(eNumberOfPoints);
                Send_ubyte(eSByte);
                Send_attr_ubyte(ePointType);
                Send_cmd(eLineRelPath);

                dwDataLength = (lEnd - lStart + 1) * 2 * sizeof(ubyte);

                if (dwDataLength <= 0xFF)
                {
                    WriteByte(PCLXL_dataLengthByte);
                    WriteByte((ubyte)dwDataLength);
                }
                else
                {
                    WriteByte(PCLXL_dataLength);
                    Write((PBYTE)&dwDataLength, sizeof(DWORD));
                }

                for (lJ = 0; lJ <= (lEnd - lStart); lJ++)
                {
                    lValueX = FXTOL(pptfx->x) - (LONG)lx;
                    lValueY = FXTOL(pptfx->y) - (LONG)ly;
                    Write((PBYTE)&lValueX, sizeof(ubyte));
                    Write((PBYTE)&lValueY, sizeof(ubyte));
                    lx = FXTOL(pptfx->x);
                    ly = FXTOL(pptfx->y);
                    pptfx++;
                }

                Mode = eMode_SInt;
            }
            else if (Mode == eMode_SInt)
            {
                //
                // SInt16
                //
                Send_uint16((uint16)(lEnd - lStart + 1));
                Send_attr_ubyte(eNumberOfPoints);
                Send_ubyte(eSint16);
                Send_attr_ubyte(ePointType);
                Send_cmd(eLineRelPath);
                dwDataLength = (lEnd - lStart + 1) * 2 * sizeof(uint16);

                if (dwDataLength <= 0xFF)
                {
                    WriteByte(PCLXL_dataLengthByte);
                    WriteByte((ubyte)dwDataLength);
                }
                else
                {
                    WriteByte(PCLXL_dataLength);
                    Write((PBYTE)&dwDataLength, sizeof(DWORD));
                }

                for (lJ = 0; lJ <= (lEnd - lStart); lJ++)
                {
                    lValueX = FXTOL(pptfx->x) - (LONG)lx;
                    lValueY = FXTOL(pptfx->y) - (LONG)ly;
                    Write((PBYTE)&lValueX, sizeof(sint16));
                    Write((PBYTE)&lValueY, sizeof(sint16));
                    lx = FXTOL(pptfx->x);
                    ly = FXTOL(pptfx->y);
                    pptfx++;
                }

                Mode = eMode_SByte;
            }

            bModeChange = FALSE;

            lStartX = lx = FXTOL((pptfx_tmp-1)->x);
            lStartY = ly = FXTOL((pptfx_tmp-1)->y);
            lStart = lI;
        }
        else
        {
            lx = FXTOL((pptfx_tmp)->x);
            ly = FXTOL((pptfx_tmp)->y);
            pptfx_tmp ++;
            lI ++;
        }
    }

    //
    // Update cursor position
    //
    m_lX = FXTOL((pptfx_tmp)->x);
    m_lY = FXTOL((pptfx_tmp)->y);

    return S_OK;
}

inline
VOID
XLOutput::
SetupBrush(
    BRUSHOBJ *pbo,
    POINTL *pptlBrushOrg,
    CMNBRUSH *pcmnbrush)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    DWORD dwHatchID;
    XLBRUSH *pBrush;

    if (NULL == pcmnbrush)
    {
        //
        // Make sure that pcmnbrush is valid.
        //
        XL_ERR(("SetupBrush:pcmnbrush is invalid.\n"));
        return;
    }

    //
    // Initialize CMNBRUSH
    //
    pcmnbrush->dwSig            = BRUSH_SIGNATURE;
    pcmnbrush->BrushType        = kNoBrush;
    pcmnbrush->ulHatch          = 0XFFFFFFFF;
    pcmnbrush->dwColor          = 0x00FFFFFF;
    pcmnbrush->dwCEntries       = 0;
    pcmnbrush->dwPatternBrushID = 0xFFFFFFFF;

    XL_VERBOSE(("XLOutput::SetupBrush\n"));

    if (NULL == pbo)
    {
        XL_VERBOSE(("XLOutput::SetupBrush: pbo == NULL, set NULL brush\n"));
    }
    else
    {

        #ifndef WINNT_40
        if ( !(pbo->flColorType & BR_CMYKCOLOR)     &&
              (pbo->iSolidColor == NOT_SOLID_COLOR)  )
        #else
        if (pbo->iSolidColor == NOT_SOLID_COLOR)
        #endif
        {
            pcmnbrush->ulSolidColor     = BRUSHOBJ_ulGetBrushColor(pbo);
            pBrush = (XLBRUSH*)BRUSHOBJ_pvGetRbrush(pbo);
            if (NULL == pBrush)
            {
                XL_ERR(("SetupBrush:BRUSHOBJ_pvGetRbrush failed.\n"));
                dwHatchID = HS_DDI_MAX;
            }
            else
            {
                dwHatchID = pBrush->dwHatch;
            }
        }
        else
        {
            dwHatchID = HS_DDI_MAX;
            pcmnbrush->ulSolidColor     = pbo->iSolidColor;
            pBrush = NULL;
        }

        pcmnbrush->ulHatch          = dwHatchID;

        switch (dwHatchID)
        {
        case HS_HORIZONTAL:
        case HS_VERTICAL:
        case HS_BDIAGONAL:
        case HS_FDIAGONAL:
        case HS_CROSS:
        case HS_DIAGCROSS:
            XL_VERBOSE(("XLOutput::SetupBrush(uiSolidColor=%d,dwHatchID=%d)\n",
                        pbo->iSolidColor,
                        dwHatchID));

            pcmnbrush->BrushType = kBrushTypeHatch;
            pcmnbrush->dwPatternBrushID = dwHatchID;

            if (pBrush)
            {
                pcmnbrush->dwColor = pBrush->dwColor;
            }
            if (GetDeviceColorDepth() == e24Bit)
            {
                SetColorSpace(eRGB);
            }
            else
            {
                SetColorSpace(eGray);
            }
            SetPaletteDepth(e8Bit);
            if (pBrush->dwCEntries)
            {
                SetPaletteData(m_DeviceColorDepth, pBrush->dwCEntries, pBrush->adwColor);
            }
            else
            {
                DWORD dwColorTableTmp[2] = {0x00ffffff, 0x00ffffff};
                dwColorTableTmp[1] = pBrush->dwColor;
                SetPaletteData(m_DeviceColorDepth, 2, dwColorTableTmp);
            }
            Send_cmd(eSetColorSpace);

            if (!(m_dwHatchBrushAvailability & (HORIZONTAL_AVAILABLE << dwHatchID)))
            {

                SetColorMapping(eIndexedPixel);
                SetColorDepth(e1Bit);
                SetSourceWidth((uint16)gusWidthOfHatchBrush);
                SetSourceHeight((uint16)gusHeightOfHatchBrush);

                //
                // Pattern scaling factor
                // 160 is an experimentally introduced number.
                //
                WORD wScale = (WORD)(160 * m_dwResolution / 1200);

                SetDestinationSize((uint16)wScale, (uint16)wScale);
                SetPatternPersistence(eSessionPattern);
                SetPatternDefineID((sint16)dwHatchID);
                Send_cmd(eBeginRastPattern);

                Send_uint16((uint16)0);
                Send_attr_ubyte(eStartLine);
                Send_uint16((uint16)gusHeightOfHatchBrush);
                Send_attr_ubyte(eBlockHeight);
                SetCompressMode(eNoCompression);
                Send_cmd(eReadRastPattern);

                WriteByte(PCLXL_dataLengthByte);
                WriteByte(gubSizeOfHatchBrush);
                Write((PBYTE)gubHatchBrush[dwHatchID], gubSizeOfHatchBrush);
                Send_cmd(eEndRastPattern);

                m_dwHatchBrushAvailability |= HORIZONTAL_AVAILABLE << dwHatchID;
            }

            //
            //SendPatternSelectID();
            //
            Send_sint16((sint16)dwHatchID);
            Send_attr_ubyte(ePatternSelectID);

            break;
        case HS_DDI_MAX:
            pcmnbrush->BrushType = kBrushTypeSolid;
            //pcmnbrush->dwColor = BRUSHOBJ_ulGetBrushColor(pbo); 
            pcmnbrush->dwColor = pcmnbrush->ulSolidColor;

            XL_VERBOSE(("XLOutput::SetupBrush(RGB=0x%x)\n", pcmnbrush->dwColor));
            if (e24Bit == GetDeviceColorDepth())
            {
                SetRGBColor(pcmnbrush->dwColor);
            }
            else
            {
                ubyte ubyte_gray = (ubyte) DWORD2GRAY(pcmnbrush->dwColor);
                SetGrayLevel(ubyte_gray);
            }
            break;

        default:
            if (NULL == pBrush)
            {
                XL_ERR(("XLOutput:SetupBrush: invalid pBrush\n"));
                return;
            }

            XL_VERBOSE(("XLOutput::SetupBrush(PatternID=%d)\n", pBrush->dwPatternID));

            pcmnbrush->dwColor = pBrush->dwColor;
            pcmnbrush->dwPatternBrushID = pBrush->dwPatternID;
            pcmnbrush->BrushType = kBrushTypePattern;

            if (e24Bit == GetDeviceColorDepth())
            {
                SetColorSpace(eRGB);
            }
            else
            {
                SetColorSpace(eGray);
            }
            if (pBrush->dwCEntries)
            {
                SetPaletteDepth(e8Bit);
                SetPaletteData(m_DeviceColorDepth, pBrush->dwCEntries, pBrush->adwColor);
            }
            Send_cmd(eSetColorSpace);

            //SendPatternSelectID();
            Send_sint16((sint16)pBrush->dwPatternID);
            Send_attr_ubyte(ePatternSelectID);

        }
    }

    return;
}

HRESULT
XLOutput::
SetPenColor(
    BRUSHOBJ *pbo,
    POINTL     *pptlBrushOrg)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    XL_VERBOSE(("XLOutput::SetPenColor\n"));
    XLPen *pPen = this;
    CMNBRUSH cmnbrush;

    if (S_OK == pPen->CheckCurrentBrush(pbo))
        return S_OK;

    if (NULL == pbo)
    {
        Send_ubyte(0);
        Send_attr_ubyte(eNullPen);
    }
    SetupBrush(pbo, pptlBrushOrg, &cmnbrush);
    Send_cmd(eSetPenSource);

    pPen->SetBrush(&cmnbrush);

    return S_OK;
}

HRESULT
XLOutput::
SetPen(
    LINEATTRS *plineattrs,
    XFORMOBJ   *pxo)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    LineCap linecap;
    XLLineEndCap xllinecap;
    LineJoin linejoin;
    XLLineJoin xllinejoin;
    FLOATOBJ fLineWidth;
    uint16 uint16_linewidth;

    XL_VERBOSE(("XLOutput::SetPen\n"));

    if (NULL == plineattrs)
    {
        XL_ERR(("XLOutput:SetPen: invalid parameters\n"));
        return E_UNEXPECTED;
    }

    XLGState *pGState = this;

    DWORD dwLine = pGState->GetDifferentAttribute(plineattrs);

    //
    // DCR: need to check each attribute.
    //
    if (XLLINE_NONE ==  dwLine)
        return S_OK;

    if (plineattrs->fl & LA_GEOMETRIC)
    {
        //
        // Line joint
        //
        switch(plineattrs->iJoin)
        {
            case JOIN_ROUND:
                linejoin = eRoundJoin;
                xllinejoin = kXLLineJoin_Round;
                break;
            case JOIN_BEVEL:
                linejoin = eBevelJoin;
                xllinejoin = kXLLineJoin_Bevel;
                break;
            case JOIN_MITER:
                linejoin = eMiterJoin;
                xllinejoin = kXLLineJoin_Miter;
                break;
            default:
                linejoin = eRoundJoin;
                xllinejoin = kXLLineJoin_Round;
                break;
        }

        //
        // Line endcap
        //
        switch(plineattrs->iEndCap)
        {
            case ENDCAP_ROUND:
                linecap = eRoundCap;
                xllinecap = kXLLineEndCapRound;
                break;
            case ENDCAP_SQUARE:
                linecap = eSquareCap;
                xllinecap = kXLLineEndCapSquare;
                break;
            case ENDCAP_BUTT:
                linecap = eButtCap;
                xllinecap = kXLLineEndCapButt;
                break;
            default:
                linecap = eRoundCap;
                xllinecap = kXLLineEndCapRound;
                break;
        }
        
        //
        // Line width
        //

        fLineWidth = plineattrs->elWidth.e;

    }
    else
    {
        linejoin = eRoundJoin;
        linecap = eRoundCap;
        FLOATOBJ_SetLong(&fLineWidth, plineattrs->elWidth.l);
    }

    if (dwLine & XLLINE_WIDTH)
    {
        uint16_linewidth = (uint16)FLOATOBJ_GetLong(&fLineWidth);
        SetPenWidth(uint16_linewidth);
        pGState->SetLineWidth(plineattrs->elWidth);
    }
    if (dwLine & XLLINE_ENDCAP)
    {
        SetLineCap(linecap);
        pGState->SetLineEndCap(xllinecap);
    }
    if (dwLine & XLLINE_JOIN)
    {
        SetLineJoin(linejoin);
        pGState->SetLineJoin(xllinejoin);
    }


    //
    // Line style
    //
    if (dwLine & XLLINE_STYLE)
    {
        if (plineattrs->cstyle == 0)
        {
            Send_ubyte((ubyte)0);
            Send_attr_ubyte(eSolidLine);
            Send_cmd(eSetLineDash);
        }
        else
        {
            DWORD dwI, dwSegCount;
            PFLOAT_LONG plSize;
            FLOAT_LONG lSize[2];
            FLOATOBJ fSize;
            uint16 uint16_linesize;

            if (plineattrs->fl & LA_ALTERNATE)
            {
                if (plineattrs->fl & LA_GEOMETRIC)
                {
                    FLOATOBJ_SetLong(&lSize[0].e, 1);
                    FLOATOBJ_SetLong(&lSize[1].e, 1);
                }
                else
                {
                    lSize[0].l = 1;
                    lSize[1].l = 1;
                }

                dwSegCount = 2;
                plSize     = lSize;
            }
            else
            {
                dwSegCount = plineattrs->cstyle;
                plSize = plineattrs->pstyle;
            }
            if (plSize)
            {
                Send_uint16_array_header(dwSegCount);
                for (dwI = 0; dwI < dwSegCount; dwI ++, plSize ++)
                {
                    if (plineattrs->fl & LA_GEOMETRIC)
                    {
                        fSize = plSize->e;
                    }
                    else
                    {
                        FLOATOBJ_SetLong(&fSize, plSize->l);

                        //
                        // It is necessary to scale the line pattern. The number
                        // 24 on 1200 dpi was introduced experimentally.
                        // Here is an assumption. Resolution could be 300, 600,
                        // or 1200.
                        //
                        if (m_dwResolution > 50)
                        {
                            FLOATOBJ_MulLong(&fSize, m_dwResolution / 50);
                        }
                    }
                    uint16_linesize = (uint16)FLOATOBJ_GetLong(&fSize);
                    Write((PBYTE)&uint16_linesize, sizeof(uint16_linesize));
                }
                Send_attr_ubyte(eLineDashStyle);
                Send_cmd(eSetLineDash);
            }
        }

        pGState->SetLineStyle(plineattrs->cstyle,
                              plineattrs->pstyle,
                              plineattrs->elStyleState);
    }

    if (dwLine & XLLINE_MITERLIMIT)
    {
        FLOATOBJ fMiter;
        FLOATOBJ_SetFloat(&fMiter, plineattrs->eMiterLimit);
        uint16 uint16_miter = (uint16)FLOATOBJ_GetLong(&fMiter);

        //
        // PCLXL interpreter doesn't accept miterlimiter less than 1.
        // If it is less than 1, it replaces the value with 10.
        // We'd better set 1 instead.
        //
        // Actuall less than 1 means 0 here, though.
        //
        if (uint16_miter < 1)
        {
            uint16_miter = 1;
        }
        SetMiterLimit(uint16_miter);
        pGState->SetMiterLimit(plineattrs->eMiterLimit);
    }

    pGState->SetLineType((XLLineType)plineattrs->fl);
    return S_OK;
}

HRESULT
XLOutput::
SetBrush(
    BRUSHOBJ *pbo,
    POINTL   *pptlBrushOrg)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    XL_VERBOSE(("XLOutput::SetBrush\n"));
    XLBrush *pBrush = this;
    CMNBRUSH cmnbrush;

    if (S_OK == pBrush->CheckCurrentBrush(pbo))
        return S_OK;

    if (NULL == pbo)
    {
        Send_ubyte(0);
        Send_attr_ubyte(eNullBrush);
    }

    SetupBrush(pbo, pptlBrushOrg, &cmnbrush);
    Send_cmd(eSetBrushSource);

    pBrush->SetBrush(&cmnbrush);
    return S_OK;
}

HRESULT
XLOutput::
Paint(
    VOID)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    XL_VERBOSE(("XLOutput::Paint\n"));
    return Send_cmd(ePaintPath);
}

HRESULT
XLOutput::
SetPaletteData(
    ColorDepth value,
    DWORD      dwPaletteNum,
    DWORD     *pdwColorTable)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    DWORD dwI;

    if (NULL == pdwColorTable)
    {
        XL_ERR(("XLOutput::SetPaletteData pdwColorTable == NULL\n"));
        return E_UNEXPECTED;
    }
    switch (value)
    {
    case e8Bit:
        WriteByte(PCLXL_ubyte_array);
        Send_uint16((uint16)dwPaletteNum);
        for (dwI = 0; dwI < dwPaletteNum; dwI ++)
            WriteByte((ubyte)DWORD2GRAY(*(pdwColorTable+dwI)));
        Send_attr_ubyte(ePaletteData);
        break;
    case e24Bit:
        WriteByte(PCLXL_ubyte_array);
        Send_uint16((uint16)dwPaletteNum * 3);
        for (dwI = 0; dwI < dwPaletteNum; dwI ++)
        {
            Write((PBYTE)(pdwColorTable+dwI), 3);
        }
        Send_attr_ubyte(ePaletteData);
        break;
    default:
        //
        // DCR: only supports 8bits gray scale
        //
        XL_ERR(("XLOutput::SetPaletteData: unsupported ColorDepth:%d\n", value));
    }

    return S_OK;
}

HRESULT
XLOutput::
SetFont(
    FontType fonttype,
    PBYTE    pFontName,
    DWORD    dwFontHeight,
    DWORD    dwFontWidth,
    DWORD    dwSymbolSet,
    DWORD    dwFontSimulation)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    FLOATOBJ fSize;
    LONG lvalue;

    if (NULL == pFontName)
    {
        XL_ERR(("XLOutput::SetFont: Invalie pFontName parameter\n"));
        return E_UNEXPECTED;
    }

    XLGState *pGState = this;

    if (S_OK == pGState->CheckCurrentFont(fonttype,
	          pFontName,
	          dwFontHeight,
	          dwFontWidth,
	          dwSymbolSet,
	          dwFontSimulation))
        return S_OK;

    FLOATOBJ_SetLong(&fSize, dwFontHeight);
    lvalue = FLOATOBJ_GetFloat(&fSize);

    //
    // Select font
    //
    Send_ubyte_array_header(PCLXL_FONTNAME_SIZE);
    Write(pFontName, PCLXL_FONTNAME_SIZE);
    Send_attr_ubyte(eFontName);
    Send_real32(lvalue);
    Send_attr_ubyte(eCharSize);
    Send_uint16((uint16)dwSymbolSet);
    Send_attr_ubyte(eSymbolSet);
    Send_cmd(eSetFont);

    //
    // TrueType font outline or device font
    // Font Scale
    // Font bold/italic simulaiton
    //
    if (fonttype == kFontTypeTTOutline ||
        fonttype == kFontTypeDevice     )
    {

        if (dwFontWidth != pGState->GetFontWidth() ||
            dwFontHeight != pGState->GetFontHeight()  )
        {
            //
            // Scale X and Y
            //
            if (dwFontWidth != 0 && dwFontHeight != dwFontWidth)
            {
                FLOATOBJ fTemp;
                FLOATOBJ_SetLong(&fTemp, dwFontWidth);
                FLOATOBJ_DivFloat(&fTemp, fSize);
                lvalue = FLOATOBJ_GetFloat(&fTemp);
                Send_real32_xy((real32)lvalue, (real32)real32_IEEE_1_0F);
                Send_attr_ubyte(eCharScale);
                Send_cmd(eSetCharScale);
            }
            else
            {
                Send_real32_xy((real32)real32_IEEE_1_0F, (real32)real32_IEEE_1_0F);
                Send_attr_ubyte(eCharScale);
                Send_cmd(eSetCharScale);
            }
        }

        DWORD dwCurrentFontSim = pGState->GetFontSimulation();

        //
        // Bold simulation
        //
        if ((dwFontSimulation & XLOUTPUT_FONTSIM_BOLD) !=
            (dwCurrentFontSim& XLOUTPUT_FONTSIM_BOLD))
        {
            if (dwFontSimulation & XLOUTPUT_FONTSIM_BOLD)
            {
                //
                // Hardcoded bold value 0.01500
                //
                #define XL_BOLD_VALUE 0x3c75c28f

                Send_real32((real32)XL_BOLD_VALUE);
            }
            else
                Send_real32((real32)0);

            Send_attr_ubyte(eCharBoldValue);
            Send_cmd(eSetCharBoldValue);
        }

        //
        // Italic simulation
        //
        if ((dwFontSimulation & XLOUTPUT_FONTSIM_ITALIC) !=
            (dwCurrentFontSim & XLOUTPUT_FONTSIM_ITALIC))
        {
            if (dwFontSimulation & XLOUTPUT_FONTSIM_ITALIC)
            {
                //
                // Hardcoded italic value 0.316200
                //
                #define XL_ITALIC_VALUE 0x3ea1e4f7
                Send_real32_xy((real32)XL_ITALIC_VALUE, (real32)0);
            }
            else
                Send_real32_xy((real32)0, (real32)0);

            Send_attr_ubyte(eCharShear);
            Send_cmd(eSetCharShear);
        }

        //
        // Vertical font simulation
        //
        if ((dwFontSimulation & XLOUTPUT_FONTSIM_VERTICAL) !=
            (dwCurrentFontSim & XLOUTPUT_FONTSIM_VERTICAL))
        {
            if (dwFontSimulation & XLOUTPUT_FONTSIM_VERTICAL)
            {
                Send_ubyte(eVertical);
            }
            else
            {
                Send_ubyte(eHorizontal);
            }
            Send_attr_ubyte(eWritingMode);
            Send_cmd(eSetCharAttributes);
        }
    }
    else
    {
        if (kFontTypeTTBitmap != pGState->GetFontType())
        {
            //
            // Bitmap font can't be scaled x and y. Need to set 1 : 1.
            //
            Send_real32_xy((real32)real32_IEEE_1_0F, (real32)real32_IEEE_1_0F);
            Send_attr_ubyte(eCharScale);
            Send_cmd(eSetCharScale);
        }
    }

    //
    // Change GState to set current selected font.
    //
    pGState->SetFont(fonttype,
                     pFontName,
                     dwFontHeight,
                     dwFontWidth,
                     dwSymbolSet,
                     dwFontSimulation);


    return S_OK;
}


HRESULT
XLOutput::
SetSourceTxMode(
    TxMode SrcTxMode)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    XLGState *pGState = this;
    if (SrcTxMode == pGState->GetSourceTxMode())
    {
        return S_OK;
    }

    if (S_OK == SetTxMode(SrcTxMode) &&
        S_OK == Send_cmd(eSetSourceTxMode))
    {
        pGState->SetSourceTxMode(SrcTxMode);
        return S_OK;
    }
    else
    {
        return S_FALSE;
    }
}

HRESULT
XLOutput::
SetPaintTxMode(
    TxMode PaintTxMode)
/*++

Routine Description:

Arguments:

Return Value:

Note:

--*/
{
    XLGState *pGState = this;
    if (PaintTxMode == pGState->GetPaintTxMode())
    {
        return S_OK;
    }

    if (S_OK == SetTxMode(PaintTxMode) &&
        S_OK == Send_cmd(eSetPatternTxMode))
    {
        pGState->SetPaintTxMode(PaintTxMode);
        return S_OK;
    }
    else
    {
        return S_FALSE;
    }
}