/*++

Copyright (c) 1990-1999  Microsoft Corporation


Module Name:

    textout.c


Abstract:

    This module contains the DrvTextOut entry point. This is the main routine
    called by the NT graphics engine in order to get text rendered on the
    target device. This implementation handles both drawing device paths that
    represent the glyphs of the STROBJ (the line of text to output), as well
    as outputing bitmaps that represent the glyphs on devices that can
    handle raster output.

Author:

    Written by AP on 8/17/92.

    15-Nov-1993 Mon 19:43:58 updated  -by-  DC, JB
        clean up / fixed / add debugging information


[Environment:]

    GDI Device Driver - Plotter.


[Notes:]


Revision History:


--*/

#include "precomp.h"
#pragma hdrstop

#define DBG_PLOTFILENAME    DbgTextOut

#define DBG_GETGLYPHMODE    0x00000001
#define DBG_TEXTOUT         0x00000002
#define DBG_TEXTOUT1        0x00000004
#define DBG_TEXTOUT2        0x00000008
#define DBG_DRAWLINE        0x00000010
#define DBG_TRUETYPE        0x00000020
#define DBG_TRUETYPE1       0x00000040
#define DBG_TRUETYPE2       0x00000080
#define DBG_BMPFONT         0x00000100
#define DBG_BMPTEXTCLR      0x00000200
#define DBG_DEFCHARINC      0x00000400
#define DBG_SET_FONTTYPE    0x20000000
#define DBG_SHOWRASFONT     0x40000000
#define DBG_NO_RASTER_FONT  0x80000000

DEFINE_DBGVAR(0);


extern PALENTRY HTPal[];




DWORD
DrvGetGlyphMode(
    DHPDEV  dhpdev,
    FONTOBJ *pfo
    )

/*++

Routine Description:

    Asks the driver what sort of font information should be cached for a
    particular font. For remote printer devices, this determines the format
    that gets spooled.  For local devices, this determines what GDI stores in
    its font cache.  This call will be made for each particular font
    realization.

Arguments:

    dhpdev  - Pointer to our PDEV

    pfo     - Pointer to the font object

Return Value:

    DWORD as FO_xxxx


Author:

    27-Jan-1994 Thu 12:51:59 created  -by-  DC

    10-Mar-1994 Thu 00:36:30 updated  -by-  DC
        Re-write, so we will pre-examine the Font type, source and its
        technology together with PDEV setting to let engine know which type of
        the font output we are interested in the DrvTextOut(). Currently this
        is broken in GDI which caused a GP in winsrv. (this is why a
        DBG_SET_FONTTYPE switch is on by default)


Revision History:


--*/

{
#define pPDev   ((PPDEV)dhpdev)

    PIFIMETRICS pifi;
    DWORD       FOType;


    PLOTDBG(DBG_GETGLYPHMODE, ("DrvGetGlyphMode: Type=%08lx, cxMax=%ld",
                        pfo->flFontType, pfo->cxMax));

    //
    // If we cannot get the IFI metrics for the passed FONTOBJ, only
    // ask for PATHS.
    //

    if (!(pifi = FONTOBJ_pifi(pfo))) {

        PLOTERR(("DrvGetGlyphMode: FONTOBJ_pifi()=NULL, return FO_PATHOBJ"));

        return(FO_PATHOBJ);
    }

    FOType = FO_PATHOBJ;

    //
    // If its a bitmap font, ask for BITS
    //

    if (pifi->flInfo & FM_INFO_TECH_BITMAP) {

        PLOTDBG(DBG_GETGLYPHMODE, ("DrvGetGlyphMode: BITMAP FONT, return FO_GLYPHBITS"));

        FOType = FO_GLYPHBITS;

    } else if (pifi->flInfo & FM_INFO_TECH_STROKE) {

        PLOTDBG(DBG_GETGLYPHMODE, ("DrvGetGlyphMode: STROKE (Vector) FONT, return FO_PATHOBJ"));

    } else if (pifi->flInfo & FM_INFO_RETURNS_BITMAPS) {

        //
        // Now make a decision on whether to ask for glyphbits or paths.
        // This decision is based on the target device being raster, that
        // bitmap fonts are okay to use, and that the threshold for doing
        // raster fonts versus paths is met.
        //

        DWORD   cxBMFontMax = (DWORD)pPDev->pPlotGPC->RasterXDPI;

        if (pPDev->PlotDM.dm.dmPrintQuality == DMRES_HIGH) {

            cxBMFontMax <<= 3;

        } else {

            cxBMFontMax >>= 2;
        }

        PLOTDBG(DBG_GETGLYPHMODE,
                ("DrvGetGlyphMode: Font CAN return BITMAP, cxBMFontMax=%ld",
                                                    cxBMFontMax));

#if DBG
        if ((!(DBG_PLOTFILENAME & DBG_NO_RASTER_FONT))  &&
            (IS_RASTER(pPDev))                          &&
            (!NO_BMP_FONT(pPDev))                       &&
            (pfo->cxMax <= cxBMFontMax)) {
#else
        if ((IS_RASTER(pPDev))      &&
            (!NO_BMP_FONT(pPDev))   &&
            (pfo->cxMax <= cxBMFontMax)) {
#endif
            PLOTDBG(DBG_GETGLYPHMODE, ("DrvGetGlyphMode: Convert to BITMAP FONT, FO_GLYPHBITS"));

            FOType = FO_GLYPHBITS;

        } else {

            PLOTDBG(DBG_GETGLYPHMODE, ("DrvGetGlyphMode: Return as FO_PATHOBJ"));
        }

    } else if (pifi->flInfo & FM_INFO_RETURNS_OUTLINES) {

        PLOTDBG(DBG_GETGLYPHMODE, ("DrvGetGlyphMode: Font CAN return OUTLINES"));

    } else if (pifi->flInfo & FM_INFO_RETURNS_STROKES) {

        PLOTDBG(DBG_GETGLYPHMODE, ("DrvGetGlyphMode: Font CAN return STROKES"));
    }

#if DBG
    if (DBG_PLOTFILENAME & DBG_SET_FONTTYPE) {

        if ((FOType == FO_GLYPHBITS) &&
            (!(pfo->flFontType & FO_TYPE_RASTER))) {

            PLOTWARN(("DrvGetGlyphMode: Set FontType to RASTER"));

            pfo->flFontType &= ~(FO_TYPE_TRUETYPE | FO_TYPE_DEVICE);
            pfo->flFontType |= FO_TYPE_RASTER;
        }
    }
#endif
    return(FOType);

#undef pPDev
}




BOOL
BitmapTextOut(
    PPDEV       pPDev,
    STROBJ      *pstro,
    FONTOBJ     *pfo,
    PRECTL      pClipRect,
    LPDWORD     pOHTFlags,
    DWORD       Rop3
    )

/*++

Routine Description:

    This routine outputs the passed STROBJ with bitmaps that represent
    each of the glyphs, rather than converting the glyphs to paths that
    will be filled in the target device.

Arguments:

    pPDev           - Pointer to our PDEV

    pstro           - We pass a string object to be drawn

    pfo             - Pointer to the FONTOBJ

    pClipRect       - Current enumerated clipping rectangle

    pOHTFlags       - Pointer to the current OutputHTBitmap() flags

    Rop3            - Rop3 to be used in the device


Return Value:

    TRUE/FALSE


Author:

    18-Feb-1994 Fri 12:41:57 updated  -by-  DC
        change that so if pfo=NULL then the font already in BITMAP format

    14-Feb-1994 Mon 18:16:25 create  -by-  DC

Revision History:


--*/

{
    GLYPHPOS    *pgp;
    GLYPHBITS   *pgb;
    SURFOBJ     soGlyph;
    POINTL      ptlCur;
    SIZEL       sizlInc;
    RECTL       rclSrc;
    RECTL       rclDst;
    BOOL        MoreGlyphs;
    BOOL        Ok;
    BOOL        FirstCh;
    ULONG       cGlyphs;


    //
    // The public fields of SURFOBJ is what will be used by OutputHTBitmap
    // instead of actually creating a SURFOBJ from the graphics engine. This
    // is a safe thing to do, since only we look at these fields.
    //

    ZeroMemory(&soGlyph, sizeof(SURFOBJ));

    soGlyph.dhsurf        = (DHSURF)'PLOT';
    soGlyph.hsurf         = (HSURF)'TEXT';
    soGlyph.dhpdev        = (DHPDEV)pPDev;
    soGlyph.iBitmapFormat = BMF_1BPP;
    soGlyph.iType         = STYPE_BITMAP;
    soGlyph.fjBitmap      = BMF_TOPDOWN;

    //
    // We will now enumerate each of the glyphs in the STROBJ such that
    // we can image them. If the STROBJ has a non NULL pgp field, this means
    // that the GLYPH definitions are already available, and no enumeration
    // is required. If not, we will make a sequence of calls to STROBJ_bEnum
    // (an engine helper) to enumerate the glyphs. The actual imaging code
    // is the same, regardless of the stat of STROBJ->pgp
    //

    if (pstro->pgp) {

        pgp        = pstro->pgp;
        MoreGlyphs = FALSE;
        cGlyphs    = pstro->cGlyphs;

        PLOTDBG(DBG_BMPFONT, ("BitmapTextOut: Character info already there (%ld glyphs)", cGlyphs));

    } else {

        STROBJ_vEnumStart(pstro);
        MoreGlyphs = TRUE;

        PLOTDBG(DBG_BMPFONT, ("BitmapTextOut: STROBJ enub"));
    }

    //
    // Now straring drawing the glyphs, if we have MoreGlyphs = TRUE  then we
    // will initially do a STROBJ_bEnum first to initialize enumeration of
    // the glyphs
    //

    Ok          = TRUE;
    Rop3       &= 0xFF;
    sizlInc.cx  =
    sizlInc.cy  = 0;
    FirstCh     = TRUE;

    do {

        //
        // Verify the job is not aborting, if it is break out now.
        //

        if (PLOT_CANCEL_JOB(pPDev)) {

           break;
        }


        //
        // Check to see if we need to do an enumeration and start it if
        // it is required.
        //

        if (MoreGlyphs) {

            MoreGlyphs = STROBJ_bEnum(pstro, &cGlyphs, &pgp);

            if (MoreGlyphs == DDI_ERROR) {

                PLOTERR(("DrvTextOut: STROBJ_bEnum()=DDI_ERROR"));
                return(FALSE);
            }
        }

        PLOTDBG(DBG_BMPFONT,
                ("BitmapTextOut: New batch of cGlyphs=%d", cGlyphs));

        //
        // Get the first character position
        //

        if ((FirstCh) && (cGlyphs)) {

            ptlCur  = pgp->ptl;
            FirstCh = FALSE;
        }

        //
        // Start sending each bitmap to the device
        //

        for ( ; (Ok) && (cGlyphs--); pgp++) {

            GLYPHDATA   gd;
            GLYPHDATA   *pgd;


            if (PLOT_CANCEL_JOB(pPDev)) {

                break;
            }

            if (pfo) {

                //
                // This is true type font, so query the bitmap
                //

                pgd = &gd;

                if (FONTOBJ_cGetGlyphs(pfo,
                                       FO_GLYPHBITS,
                                       1,
                                       &(pgp->hg),
                                       (LPVOID)&pgd) != 1) {

                    PLOTRIP(("BitmapTextOut: FONTOBJ_cGetGlyphs() FAILED"));
                    return(FALSE);
                }

                pgb = pgd->gdf.pgb;

            } else {

                //
                // For bitmap font, we already have the bitmap
                //

                pgb = pgp->pgdf->pgb;
            }

            //
            // Get the size of the bitmap
            //

            soGlyph.sizlBitmap = pgb->sizlBitmap;

            //
            // Compute new destination position for the text, based on the
            // passed accelerators.
            //

            if (pstro->ulCharInc) {

                sizlInc.cx =
                sizlInc.cy = (LONG)pstro->ulCharInc;

            } else if (pstro->flAccel & SO_CHAR_INC_EQUAL_BM_BASE) {

                sizlInc = soGlyph.sizlBitmap;

            } else {

                ptlCur = pgp->ptl;
            }

            if (!(pstro->flAccel & SO_HORIZONTAL)) {

                sizlInc.cx = 0;
            }

            if (!(pstro->flAccel & SO_VERTICAL)) {

                sizlInc.cy = 0;
            }

            if (pstro->flAccel & SO_REVERSED) {

                sizlInc.cx = -sizlInc.cx;
                sizlInc.cy = -sizlInc.cy;
            }


            //
            // The pgp->ptl informs us where to position the glyph origin in
            // the device surface, and pgb->ptlOrigin informs us of the
            // relationship between character origin and bitmap origin. For
            // example, if (2,-24) is passed in as the character origin, then
            // we would need to reposition rclDst.left right 2 pixels and
            // rclDst.top up 24 pixels.
            //

            rclDst.left    = ptlCur.x + pgb->ptlOrigin.x;
            rclDst.top     = ptlCur.y + pgb->ptlOrigin.y;
            rclDst.right   = rclDst.left + soGlyph.sizlBitmap.cx;
            rclDst.bottom  = rclDst.top + soGlyph.sizlBitmap.cy;
            ptlCur.x      += sizlInc.cx;
            ptlCur.y      += sizlInc.cy;


            //
            // NOTE: If the bitmap size is 1x1 and the value of the glyphdata
            //       is 0 (background only) then we skip this glyph. This is
            //       GDI's way of telling us we have an empty glyph (like a
            //       space).

            if ((soGlyph.sizlBitmap.cx == 1) &&
                (soGlyph.sizlBitmap.cy == 1) &&
                ((pgb->aj[0] & 0x80) == 0x0)) {

                PLOTDBG(DBG_BMPFONT,
                        ("BitmapTextOut: Getting (1x1)=0 bitmap, SKIP it"));

                soGlyph.sizlBitmap.cx =
                soGlyph.sizlBitmap.cy = 0;

            } else {

                rclSrc = rclDst;

                PLOTDBG(DBG_BMPFONT, ("BitmapTextOut: pgp=%08lx, pgb=%08lx, ptl=(%ld, %ld) Inc=(%ld, %ld)",
                                            pgp, pgb, pgp->ptl.x, pgp->ptl.y,
                                            sizlInc.cx, sizlInc.cy));
                PLOTDBG(DBG_BMPFONT, ("BitmapTextOut: Bmp=%ld x %ld, pgb->ptlOrigin=[%ld, %ld]",
                                            soGlyph.sizlBitmap.cx,
                                            soGlyph.sizlBitmap.cy,
                                            pgb->ptlOrigin.x, pgb->ptlOrigin.y));
                PLOTDBG(DBG_BMPFONT, ("BitmapTextOut: rclDst=(%ld, %ld)-(%ld, %ld)",
                        rclDst.left, rclDst.top, rclDst.right, rclDst.bottom));

            }

            //
            // Now verify that we have a glyph to send, and that the glyphs
            // destination position in the target device, lies inside
            // the clipping region.
            //


            if ((soGlyph.sizlBitmap.cx)                 &&
                (soGlyph.sizlBitmap.cy)                 &&
                (IntersectRECTL(&rclDst, pClipRect))) {

                //
                // We will pass the internal version of soGlyph without making
                // a temp. copy.
                //

                soGlyph.pvBits  =
                soGlyph.pvScan0 = (LPVOID)pgb->aj;
                soGlyph.lDelta  = (LONG)((soGlyph.sizlBitmap.cx + 7) >> 3);
                soGlyph.cjBits  = (LONG)(soGlyph.lDelta *
                                         soGlyph.sizlBitmap.cy);
                rclSrc.left     = rclDst.left - rclSrc.left;
                rclSrc.top      = rclDst.top - rclSrc.top;
                rclSrc.right    = rclSrc.left + (rclDst.right - rclDst.left);
                rclSrc.bottom   = rclSrc.top + (rclDst.bottom - rclDst.top);

                PLOTDBG(DBG_BMPFONT, ("BitmapTextOut: rclSrc=(%ld, %ld)-(%ld, %ld)",
                        rclSrc.left, rclSrc.top, rclSrc.right, rclSrc.bottom));

#if DBG
                if (DBG_PLOTFILENAME & DBG_SHOWRASFONT) {

                    LPBYTE  pbSrc;
                    LPBYTE  pbCur;
                    UINT    x;
                    UINT    y;
                    UINT    Size;
                    BYTE    bData;
                    BYTE    Mask;
                    BYTE    Buf[128];

                    DBGP(("================================================="));
                    DBGP(("BitmapTextOut: Size=%ld x %ld, Origin=(%ld, %ld), Clip=(%ld, %ld)-(%ld, %ld)",
                            soGlyph.sizlBitmap.cx, soGlyph.sizlBitmap.cy,
                            pgb->ptlOrigin.x, pgb->ptlOrigin.y,
                            rclSrc.left, rclSrc.top,
                            rclSrc.right, rclSrc.bottom));

                    pbSrc = soGlyph.pvScan0;

                    for (y = 0; y < (UINT)soGlyph.sizlBitmap.cy; y++) {

                        pbCur  = pbSrc;
                        pbSrc += soGlyph.lDelta;
                        Mask   = 0x0;
                        Size   = 0;

                        for (x = 0; x < (UINT)soGlyph.sizlBitmap.cx; x++) {

                            if (!(Mask >>= 1)) {

                                Mask  = 0x80;
                                bData = *pbCur++;
                            }

                            if ((y >= (UINT)rclSrc.top)     &&
                                (y <  (UINT)rclSrc.bottom)  &&
                                (x >= (UINT)rclSrc.left)    &&
                                (x <  (UINT)rclSrc.right)) {

                                Buf[Size++] = (BYTE)((bData & Mask) ? 219 :
                                                                      177);

                            } else {

                                Buf[Size++] = (BYTE)((bData & Mask) ? 178 :
                                                                      176);
                            }
                        }

                        Buf[Size] = '\0';

                        DBGP((Buf));
                    }
                }
#endif
                //
                // Now output the bitmap that represents the glyph
                //

                Ok = OutputHTBitmap(pPDev,              // pPDev
                                    &soGlyph,           // psoHT
                                    NULL,               // pco
                                    (PPOINTL)&rclDst,   // pptlDst
                                    &rclSrc,            // prclSrc
                                    Rop3,               // Rop3
                                    pOHTFlags);         // pOHTFlags
            }
        }

    } while ((Ok) && (MoreGlyphs));

    return(Ok);
}




BOOL
OutlineTextOut(
    PPDEV       pPDev,
    STROBJ      *pstro,
    FONTOBJ     *pfo,
    PRECTL      pClipRect,
    BRUSHOBJ    *pboBrush,
    POINTL      *pptlBrushOrg,
    DWORD       OutlineFlags,
    ROP4        Rop4
    )

/*++

Routine Description:


    This routine outputs the passed STROBJ by outputing a path that
    represents each glyph to the target device.

Arguments:

    pPDev           - Pointer to our PDEV

    pstro           - We pass a string object to be drawn

    pfo             - Pointer to the FONTOBJ

    pClipRect       - Current enumerated clipping rectangle

    pboBrush        - Brush object to be used for the text

    pptlBrushOrg    - Brush origin alignment

    OutlineFlags    - specified how to do outline font from FPOLY_xxxx flags

    Rop4            - Rop4 to be used


Return Value:

    TRUE/FALSE


Author:

    18-Feb-1994 Fri 12:41:17 updated  -by-  DC
        Adding the OutlineFlags to specified how to do fill/stroke

    27-Jan-1994 Thu 13:10:34 updated  -by-  DC
        re-write, style update, and arrange codes

    25-Jan-1994 Wed 16:30:08 modified -by-  JB
        Added FONTOBJ as a parameter and now we only FILL truetype fonts,
        all others are stroked

    18-Dec-1993 Sat 10:38:08 created  -by-  DC
        Change style

    [t-kenl]  Mar 14, 93    taken from DrvTextOut()

Revision History:


--*/

{
    GLYPHPOS    *pgp;
    PATHOBJ     *ppo;
    RECTFX      rectfxBound;
    RECTFX      rclfxClip;
    POINTL      ptlCur;
    SIZEL       sizlInc;
    BOOL        MoreGlyphs;
    BOOL        Ok;
    BOOL        FirstCh;
    ULONG       cGlyphs;

    //
    // We will enumerate each of the glyphs in the passed STROBJ and use
    // the core polygon routine (DoPolygon) to draw each of them as a path.
    // If the STROBJ has a non NULL pgp field, then all the data is already
    // available on each gpyph. If not, we need to make a sequence of calls
    // to the engine helper function STROBJ_bEnum in order to enumerate the
    // glyphs. We will use the same code to output the data in both cases.
    //

    if (pClipRect) {

        rclfxClip.xLeft   = LTOFX(pClipRect->left);
        rclfxClip.yTop    = LTOFX(pClipRect->top);
        rclfxClip.xRight  = LTOFX(pClipRect->right);
        rclfxClip.yBottom = LTOFX(pClipRect->bottom);
    }

    if (pstro->pgp) {

        pgp        = pstro->pgp;
        MoreGlyphs = FALSE;
        cGlyphs    = pstro->cGlyphs;

        PLOTDBG(DBG_TRUETYPE, ("OutlineTextOut: Character info already there (%ld glyphs)", cGlyphs));

    } else {

        STROBJ_vEnumStart(pstro);
        MoreGlyphs = TRUE;

        PLOTDBG(DBG_TRUETYPE, ("OutlineTextOut: STROBJ enub"));
    }

    //
    // Now start drawing the glyphs, if we have MoreGlyphs = TRUE  then we
    // will do a STROBJ_bEnum first, in order to load up the Glyph data.
    //
    // Check the fill flags and set the flag appropriately out of the DEVMODE.
    // We will ONLY fill TrueType fonts, all other types (vector) will only be
    // stroked.
    //

    Ok         = TRUE;
    sizlInc.cx =
    sizlInc.cy = 0;
    FirstCh    = TRUE;

    do {

        //
        // Check to see if the job is being aborted, and exit out if such
        // is the case.
        //

        if (PLOT_CANCEL_JOB(pPDev)) {

           break;
        }

        //
        // We need to enum for more glyph data so do it now.
        //

        if (MoreGlyphs) {

            MoreGlyphs = STROBJ_bEnum(pstro, &cGlyphs, &pgp);

            if (MoreGlyphs == DDI_ERROR) {

                PLOTERR(("DrvTextOut: STROBJ_bEnum()=DDI_ERROR"));
                return(FALSE);
            }
        }

        PLOTDBG(DBG_TRUETYPE1,
                ("OutlineTextOut: New batch of cGlyphs=%d", cGlyphs));

        //
        // Stroke each glyph in this batch, then check if there are more.
        // Getting the first character position
        //

        if ((FirstCh) && (cGlyphs)) {

            ptlCur  = pgp->ptl;
            FirstCh = FALSE;
        }

        for ( ; (Ok) && (cGlyphs--); pgp++) {

            #ifdef USERMODE_DRIVER

            GLYPHDATA   gd;
            GLYPHDATA   *pgd;

            #endif // USERMODE_DRIVER

            if (PLOT_CANCEL_JOB(pPDev)) {

                break;
            }

            //
            // Set up to enumerate path
            //

            #ifdef USERMODE_DRIVER

                pgd = &gd;

                if (FONTOBJ_cGetGlyphs(pfo,
                                       FO_PATHOBJ,
                                       1,
                                       &(pgp->hg),
                                       (LPVOID)&pgd) != 1) {

                    PLOTRIP(("OutlineTextOut: FONTOBJ_cGetGlyphs() FAILED"));
                    return(FALSE);
                }

                ppo = pgd->gdf.ppo;

            #else

            ppo = pgp->pgdf->ppo;

            #endif // USERMODE_DRIVER

            //
            // If the clip rect is not null then verify the glyph actually lies
            // within the clipping rect then OUTPUT!!!
            //

            if (pstro->ulCharInc) {

                PLOTDBG(DBG_DEFCHARINC, ("OutlineTextOut: CharInc=(%ld, %ld)->(%ld, %ld), [%ld]",
                                ptlCur.x, ptlCur.y,
                                ptlCur.x + pstro->ulCharInc, ptlCur.y,
                                pstro->ulCharInc));

                sizlInc.cx =
                sizlInc.cy = (LONG)pstro->ulCharInc;


                //
                // Check the text Accelators and adjust accordingly.
                //

                if (!(pstro->flAccel & SO_HORIZONTAL)) {

                    sizlInc.cx = 0;
                }

                if (!(pstro->flAccel & SO_VERTICAL)) {

                    sizlInc.cy = 0;
                }

                if (pstro->flAccel & SO_REVERSED) {

                    sizlInc.cx = -sizlInc.cx;
                    sizlInc.cy = -sizlInc.cy;
                }

                ptlCur.x += sizlInc.cx;
                ptlCur.y += sizlInc.cy;

            } else {

                ptlCur = pgp->ptl;
            }

            if (pClipRect) {

                //
                // Create a rect in correct device space and compare to the
                // clip rect
                //

                PATHOBJ_vGetBounds(ppo, &rectfxBound);

                //
                // Since the glyph positioning is based on the glyph origin
                // transform now to device space, in order to check if the
                // glyph lies inside the current clipping region.
                //

                rectfxBound.xLeft   += LTOFX(ptlCur.x);
                rectfxBound.yTop    += LTOFX(ptlCur.y);
                rectfxBound.xRight  += LTOFX(ptlCur.x);
                rectfxBound.yBottom += LTOFX(ptlCur.y);

                if ((rectfxBound.xLeft   > rclfxClip.xRight)    ||
                    (rectfxBound.xRight  < rclfxClip.xLeft)     ||
                    (rectfxBound.yTop    > rclfxClip.yBottom)   ||
                    (rectfxBound.yBottom < rclfxClip.yTop)) {

                    PLOTDBG(DBG_TRUETYPE1, ("OutlineTextOut: Outside of CLIP, skipping glyph ..."));
                    continue;
                }
            }

            //
            // Utilize the core path building function, taking advantage of
            // its ability to offset the passed PATH by a specific amount.
            //

            if (!(Ok = DoPolygon(pPDev,
                                 &ptlCur,
                                 NULL,
                                 ppo,
                                 pptlBrushOrg,
                                 pboBrush,
                                 pboBrush,
                                 Rop4,
                                 NULL,
                                 OutlineFlags))) {

                PLOTERR(("OutlineTextOut: Failed in DoPolygon(Options=%08lx)",
                                                        OutlineFlags));

                //
                // If we failed to draw it, then try just stroking it, since
                // that won't depend on any polygon constraints used in the
                // target device, and failing DrvStrokePath, won't make the
                // Text output get broken down to any simpler format.
                //

                if ((OutlineFlags & FPOLY_MASK) != FPOLY_STROKE) {

                    //
                    // If we failed then just stroke it
                    //

                    PLOTERR(("OutlineTextOut: Now TRY DoPolygon(FPOLY_STROKE)"));

                    Ok = DoPolygon(pPDev,
                                   &ptlCur,
                                   NULL,
                                   ppo,
                                   pptlBrushOrg,
                                   NULL,
                                   pboBrush,
                                   Rop4,
                                   NULL,
                                   FPOLY_STROKE);
                }
            }

            //
            // Go to next position

            ptlCur.x += sizlInc.cx;
            ptlCur.y += sizlInc.cy;
        }

    } while ((Ok) && (MoreGlyphs));

    return(TRUE);
}





BOOL
DrvTextOut(
    SURFOBJ     *pso,
    STROBJ      *pstro,
    FONTOBJ     *pfo,
    CLIPOBJ     *pco,
    RECTL       *prclExtra,
    RECTL       *prclOpaque,
    BRUSHOBJ    *pboFore,
    BRUSHOBJ    *pboOpaque,
    POINTL      *pptlBrushOrg,
    MIX         mix
    )

/*++

Routine Description:

    The Graphics Engine will call this routine to render a set of glyphs at
    specified positions. This function will review the passed data, and
    image the glyph either as a path to be filled or stroked, or as a bitmap.

Arguments:

    pso         - pointer to our surface object

    pstro       - pointer to the string object

    pfo         - pointer to the font object

    pco         - clipping object

    prclExtra   - pointer to array of rectangles to be merge with glyphs

    prclOpaque  - Pointer to a rectangle to be fill with pboOpaque brush

    pboFore     - pointer to the brush object for the foreground color

    pboOpqaue   - pointer to the brush object for the opaque rectangle

    pptlBrushOrg- Pointer to the brush alignment

    mix         - Two Rop2 mode


Return Value:

    TRUE/FALSE


Author:

    23-Jan-1994 Thu  2:59:31 created  -by-  JB

    27-Jan-1994 Thu 12:56:11 updated  -by-  DC
        Style, re-write, commented

    10-Mar-1994 Thu 00:30:38 updated  -by-  DC
        1. Make sure we not fill the stroke type of font
        2. Move rclOpqaue and rclExtra process out from the do loop, so that
           when it in the RTL mode for the font it will be correctly processed
           and it will also save output data size by not switching in/out
           RTL/HPGL2 mode just try to do the prclOpaque/prclExtra
        3. Process FO_TYPE correctly for all type of fonts (outline, truetype,
           bitmap, vector, stroke and others)

    11-Mar-1994 Fri 19:24:56 updated  -by-  DC
        Bug# 10276, the clipping window is set for the raster font and clear
        clipping window is done before the exit to HPGL2 mode, this causes
        all raster font after the first clip is not visible to end of the
        page.   Now changed it so we only do clipping window when the font is
        NOT RASTER.

Revision History:


--*/

{
#define pDrvHTInfo  ((PDRVHTINFO)(pPDev->pvDrvHTData))


    PPDEV       pPDev;
    PRECTL      pCurClipRect;
    HTENUMRCL   EnumRects;
    DWORD       RTLPalDW[2];
    DWORD       rgbText;
    DWORD       OHTFlags;
    DWORD       OutlineFlags;
    BOOL        DoRasterFont;
    BOOL        bMore;
    BOOL        bDoClipWindow;
    BOOL        Ok;
    DWORD       BMFontRop3;
    ROP4        Rop4;


    //
    // Transform the MIX to ROP4
    //

    Rop4 = MixToRop4(mix);

    PLOTDBG(DBG_TEXTOUT, ("DrvTextOut: prclOpaque       = %08lx", prclOpaque));
    PLOTDBG(DBG_TEXTOUT, ("            prclExtra        = %08lx", prclExtra));
    PLOTDBG(DBG_TEXTOUT, ("            pstro->flAccel   = %08lx", pstro->flAccel));
    PLOTDBG(DBG_TEXTOUT, ("            pstro->ulCharInc = %ld", pstro->ulCharInc));
    PLOTDBG(DBG_TEXTOUT, ("            pfo->cxMax       = %ld", pfo->cxMax));
    PLOTDBG(DBG_TEXTOUT, ("            FontType         = %08lx", pfo->flFontType));
    PLOTDBG(DBG_TEXTOUT, ("            MIX              = %04lx (Rop=%04lx)", mix, Rop4));

    if (!(pPDev = SURFOBJ_GETPDEV(pso))) {

        PLOTERR(("DoTextOut: Invalid pPDev in pso"));
        return(FALSE);
    }

    if (pPDev->PlotDM.Flags & PDMF_PLOT_ON_THE_FLY) {

        PLOTWARN(("DoTextOut: POSTER Mode IGNORE All Texts"));
        return(TRUE);
    }

    //
    // Since we dont support device fonts, make sure we are not getting one
    // now.
    //

    if (pfo->flFontType & FO_TYPE_DEVICE) {

        PLOTASSERT(1, "DrvTextOut: Getting DEVICE font (%08lx)",
                        !(pfo->flFontType & FO_TYPE_DEVICE ), pfo->flFontType);
        return(FALSE);
    }

    if (DoRasterFont = (BOOL)(pfo->flFontType & FO_TYPE_RASTER)) {

        PLOTDBG(DBG_TEXTOUT1, ("DrvTextOut: We got the BITMAP Font from GDI"));

        //
        // Make pfo = NULL so later we will not try to do FONTOBJ_cGetGlyph in
        // BitmapTextOut
        //

        #ifndef USERMODE_DRIVER

        pfo = NULL;

        #endif // !USERMODE_DRIVER

    } else {

        PIFIMETRICS pifi;

        //
        // Try to find out if we need to fill the font, or just stroke it.
        //

        if ((pifi = FONTOBJ_pifi(pfo)) &&
            (pifi->flInfo & FM_INFO_RETURNS_STROKES)) {

            PLOTDBG(DBG_TEXTOUT1, ("DrvTextOut() Font can only do STROKE"));

            OutlineFlags = FPOLY_STROKE;

        } else {

            PLOTDBG(DBG_TEXTOUT1, ("DrvTextOut() Font We can do FILL, User Said=%hs",
                    (pPDev->PlotDM.Flags & PDMF_FILL_TRUETYPE) ? "FILL" : "STROKE"));

            OutlineFlags = (pPDev->PlotDM.Flags & PDMF_FILL_TRUETYPE) ?
                                (DWORD)FPOLY_FILL : (DWORD)FPOLY_STROKE;
        }
    }

    //
    // Check if we need to opaque the area
    //

    if (prclOpaque) {

        PLOTDBG(DBG_TEXTOUT2, ("prclOpaque=(%ld, %ld) - (%ld, %ld)",
                           prclOpaque->left, prclOpaque->top,
                           prclOpaque->right, prclOpaque->bottom));

        if (!DrvBitBlt(pso,             // Target
                       NULL,            // Source
                       NULL,            // Mask Obj
                       pco,             // Clip Obj
                       NULL,            // XlateOBj
                       prclOpaque,      // Dest Rect Ptr
                       NULL,            // Source Pointl
                       NULL,            // Mask Pointl
                       pboOpaque,       // Brush Obj
                       pptlBrushOrg,    // Brush Origin
                       0xF0F0)) {       // ROP4 (PATCOPY)

            PLOTERR(("DrvTextOut: DrvBitBltBit(pboOpqaue) FAILED!"));
            return(FALSE);
        }
    }

    //
    // We will do prclExtra only if it is not NULL, this simulates the
    // underline or strikeout effects.
    //

    if (prclExtra) {

        //
        // The prclExtra terminated only if all points in rectangle coordinate
        // are all set to zeros
        //

        while ((prclExtra->left)    ||
               (prclExtra->top)     ||
               (prclExtra->right)   ||
               (prclExtra->bottom)) {

            PLOTDBG(DBG_TEXTOUT2, ("prclExtra=(%ld, %ld) - (%ld, %ld)",
                               prclExtra->left, prclExtra->top,
                               prclExtra->right, prclExtra->bottom));

            if (!DrvBitBlt(pso,             // Target
                           NULL,            // Source
                           NULL,            // Mask Obj
                           pco,             // Clip Obj
                           NULL,            // XlateOBj
                           prclExtra,       // Dest Rect Ptr
                           NULL,            // Source Pointl
                           NULL,            // Mask Pointl
                           pboFore,         // Brush Obj
                           pptlBrushOrg,    // Brush Origin
                           Rop4)) {         // ROP4

                PLOTERR(("DrvTextOut: DrvBitBltBit(pboFore) FAILED!"));
                return(FALSE);
            }

            //
            // Now try next EXTRA rectangle
            //

            ++prclExtra;
        }
    }

    //
    // If we are using Raster Font then the mode will be set as following
    //

    if (DoRasterFont) {

        RTLPalDW[0] = pDrvHTInfo->RTLPal[0].dw;
        RTLPalDW[1] = pDrvHTInfo->RTLPal[1].dw;

        //
        // Get the color to use.
        //

        if (!GetColor(pPDev,
                      pboFore,
                      &(pDrvHTInfo->RTLPal[1].dw),
                      NULL,
                      Rop4)) {

            PLOTERR(("DrvTextOut: Get Raster Font Text Color failed! use BLACK"));

            rgbText = 0x0;
        }

        if (pDrvHTInfo->RTLPal[1].dw == 0xFFFFFF) {

            //
            // White Text, our white is 1 and 0=black, so do:"not S and D"
            //

            PLOTDBG(DBG_BMPTEXTCLR, ("DrvTextOut: Doing WHITE TEXT (0xEEEE)"));

            pDrvHTInfo->RTLPal[0].dw = 0x0;
            OHTFlags                 = 0;
            BMFontRop3               = 0xEE;                // S | D

        } else {

            pDrvHTInfo->RTLPal[0].dw = 0xFFFFFF;
            OHTFlags                 = OHTF_SET_TR1;
            BMFontRop3               = 0xCC;                // S
        }

        PLOTDBG(DBG_BMPTEXTCLR,
                ("DrvTextOut: BG=%02x:%02x:%02x, FG=%02x:%02x:%02x, Rop3=%04lx",
                        (DWORD)pDrvHTInfo->RTLPal[0].Pal.R,
                        (DWORD)pDrvHTInfo->RTLPal[0].Pal.G,
                        (DWORD)pDrvHTInfo->RTLPal[0].Pal.B,
                        (DWORD)pDrvHTInfo->RTLPal[1].Pal.R,
                        (DWORD)pDrvHTInfo->RTLPal[1].Pal.G,
                        (DWORD)pDrvHTInfo->RTLPal[1].Pal.B,
                        BMFontRop3));

        //
        // We do not need clip window command in RTL mode
        //

        bDoClipWindow = FALSE;

    } else {

        bDoClipWindow = TRUE;
    }

    bMore       = FALSE;
    Ok          = TRUE;
    EnumRects.c = 1;

    if ((!pco) || (pco->iDComplexity == DC_TRIVIAL)) {

        //
        // The whole output destination rectangle is visible
        //

        PLOTDBG(DBG_TEXTOUT, ("DrvTextOut: pco=%hs",
                                            (pco) ? "DC_TRIVIAL" : "NULL"));

        EnumRects.rcl[0] = pstro->rclBkGround;
        bDoClipWindow    = FALSE;

    } else if (pco->iDComplexity == DC_RECT) {

        //
        // The visible area is one rectangle intersect with the destinaiton
        //

        PLOTDBG(DBG_TEXTOUT, ("DrvTextOut: pco=DC_RECT"));

        EnumRects.rcl[0] = pco->rclBounds;

    } else {

        //
        // We have complex clipping region to be computed, call engine to start
        // enumerating the rectangles and set More = TRUE so we can get the
        // first batch of rectangles.
        //

        PLOTDBG(DBG_TEXTOUT, ("DrvTextOut: pco=DC_COMPLEX, EnumRects now"));

        CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
        bMore = TRUE;
    }

    do {

        //
        // If More is true then we need to get next batch of rectangles. In
        // this mode, we have a set of rectangles that represents the
        // clipping region in the target device. Since none of the devices
        // we handle can acommodate a complex clipping path, we enumerate the
        // cliping path (CLIPOBJ) as rectangles and image the entire STROBJ
        // through these rectangles, trying to determine as quickly as possible
        // when a glyph does not lie in the current clipping rect.
        //

        if (bMore) {

            bMore = CLIPOBJ_bEnum(pco, sizeof(EnumRects), (ULONG *)&EnumRects);
        }

        //
        // prcl will point to the first enumerated rectangle, which may just
        // be the RECT of the clipping area if its DC_RECT.
        //

        pCurClipRect = (PRECTL)&EnumRects.rcl[0];

        while ((Ok) && (EnumRects.c--)) {

            PLOTDBG(DBG_TEXTOUT, ("DrvTextOut: Clip=(%ld, %ld)-(%ld, %ld) %ld x %ld, Bound=(%ld, %d)-(%ld, %ld), %ld x %ld",
                         pCurClipRect->left, pCurClipRect->top,
                         pCurClipRect->right, pCurClipRect->bottom,
                         pCurClipRect->right - pCurClipRect->left,
                         pCurClipRect->bottom - pCurClipRect->top,
                         pstro->rclBkGround.left, pstro->rclBkGround.top,
                         pstro->rclBkGround.right, pstro->rclBkGround.bottom,
                         pstro->rclBkGround.right - pstro->rclBkGround.left,
                         pstro->rclBkGround.bottom - pstro->rclBkGround.top));

            //
            // If we will output the STROBJ as bitmaps that represent the
            // glyphs of the STROBJ, do it now.
            //

            if (DoRasterFont) {

                if (!(Ok = BitmapTextOut(pPDev,
                                         pstro,
                                         pfo,
                                         pCurClipRect,
                                         &OHTFlags,
                                         BMFontRop3))) {

                    PLOTERR(("DrvTextOut: BitmapTypeTextOut() FAILED"));
                    break;
                }

            } else {

                //
                // If we have a clip window, set it now, this will allow
                // the target device to do any clipping
                //

                if (bDoClipWindow) {

                    SetClipWindow(pPDev, pCurClipRect);
                }

                if (!(Ok = OutlineTextOut(pPDev,
                                          pstro,
                                          pfo,
                                          pCurClipRect,
                                          pboFore,
                                          pptlBrushOrg,
                                          OutlineFlags,
                                          Rop4))) {

                    PLOTERR(("DrvTextOut: TrueTypeTextOut() FAILED!"));
                    break;
                }
            }

            //
            // Goto next clip rectangle
            //

            pCurClipRect++;
        }

    } while ((Ok) && (bMore));


    if (DoRasterFont) {

        pDrvHTInfo->RTLPal[0].dw = RTLPalDW[0];
        pDrvHTInfo->RTLPal[1].dw = RTLPalDW[1];

        if (OHTFlags & OHTF_MASK) {

            OHTFlags |= OHTF_EXIT_TO_HPGL2;

            OutputHTBitmap(pPDev, NULL, NULL, NULL, NULL, 0xAA, &OHTFlags);
        }
    }

    //
    // If we had set a clip window now is the time to clear it after exit from
    // RTL Mode
    //

    if (bDoClipWindow) {

        ClearClipWindow(pPDev);
    }

    return(Ok);


#undef  pDrvHTInfo
}

