/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = QPSTD.C
 *
 * DESCRIPTIVE NAME = PM Spooler processor
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 * This module contains the code for printing a PM_Q_STD format spooler file
 * (i.e. a GPI Metafile).
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define  INCL_GPICONTROL
#define  INCL_GPITRANSFORMS
#define  INCL_GPIMETAFILES
#define  INCL_GPIREGIONS
#define  INCL_DEV
#include "pmprint.h"

/*
** Function local to this routine
*/
BOOL DoMetaFileChecks(VOID);

/****************************************************************************
 *
 * FUNCTION NAME = SplQpStdPrintFile
 *
 * DESCRIPTION   = Print a spool file in "PM_Q_STD" format( Gpi Metafile )
 *
 * INPUT         = pQProc      - QProc instance pointer for print job
 *                 pszFileName - Path to Metafile to print
 *
 * OUTPUT        = TRUE   - Job printed successfully to our knowledge
 *                 FALSE  - Unable to print job due to Gpi/Dev failure
 *                          WinGetLastError() gets failure code.
 *                          Message displayed to user.
 *
 * RETURN-NORMAL = TRUE
 *
 *
 * RETURN-ERROR  = FALSE
 *
 *
 ****************************************************************************/

BOOL SplQpStdPrintFile(PQPROCINST pQProc,PSZ pszFileName)
{
  BOOL result;
  HDC hdcDisplay;
  PSZ devOpenData[DRIVER_NAME+1];
  LONG pmfOptions[PMF_COLORREALIZABLE+1];
  SIZEL ptSize;
  HRGN Region;
  HDC hInfodc;                         /* OD_INFO DC handle                  */
  BOOL fDcOk;                          /* set flag to FALSE if openDC failed */
  BOOL fMetafileOK;                    /* TRUE if (somewhat) valid metafile  */

  /*
  ** Init to failure return code and null handles
  */
  result = FALSE;
  pQProc->hmf = (HMF)0;
  hdcDisplay = (HDC)0;
  fDcOk = TRUE;

  /*
  ** Attempt to open the metafile and load it
  */
  if (OpenQPInputFile(pQProc, pszFileName, FALSE) && (pQProc->hmf =
     GpiLoadMetaFile(HABX, pszFileName)))
  {

    /*
    ** Setup OD_INFO DC parameters
    */
    devOpenData[ADDRESS] = pQProc->pszPortName;
    devOpenData[DRIVER_NAME] = pQProc->pszDriverName;
    devOpenData[DRIVER_DATA] = (PSZ)pQProc->pDriverData;
    hInfodc = DevOpenDC(HABX, OD_INFO, "*", (LONG)DRIVER_DATA+1, devOpenData,
                                                                      (HDC)0);

    /*
    ** If unable to open DC, issue error message
    */
    if (hInfodc)
    {
      /*
      ** If user paused the print job
      **   wait till user releases the job
      */
      if (pQProc->fsStatus&QP_PAUSED)
        DosWaitEventSem(pQProc->semPaused, SEM_INDEFINITE_WAIT);
      ptSize.cx = 0;
      ptSize.cy = 0;

      /*
      ** Create Presentation Space and associate it with
      **  the Informational Device Context
      */
      if (pQProc->hps = GpiCreatePS(HABX, hInfodc, &ptSize, PU_PELS|GPIA_ASSOC))
      {
        /*
        ** Play the metafile, resetting the Presentation Space
        **  and suppressing the drawing commands
        **
        */
        pmfOptions[PMF_SEGBASE] = 0L;
        pmfOptions[PMF_LOADTYPE] = LT_ORIGINALVIEW;
        pmfOptions[PMF_RESOLVE] = RS_DEFAULT;
        pmfOptions[PMF_LCIDS] = LC_LOADDISC;
        pmfOptions[PMF_RESET] = RES_RESET;
        pmfOptions[PMF_SUPPRESS] = SUP_SUPPRESS;
        pmfOptions[PMF_COLORTABLES] = 0;
        pmfOptions[PMF_COLORREALIZABLE] = 0;
        fMetafileOK = GpiPlayMetaFile(pQProc->hps, pQProc->hmf, (LONG)
           PMF_SUPPRESS+1, pmfOptions, NULL, 0L, NULL);

        if (!fMetafileOK)
        {
          SplQpMessage(pQProc->pszPortName, SPL_ID_QP_INVALID_METAFILE, 0);
          result = FALSE;
        }
        /*
        ** Disassociate Informational DC from PS and close
        */
        GpiAssociate(pQProc->hps, (HDC)0);
        DevCloseDC(hInfodc);

        if (fMetafileOK)
        {
          /*
          ** Attempt to open OD_DIRECT DC(and assoiate it with our PS).
          **
          ** if OpenQPOutputDC fails
          **    user was given popup explaining failure by OpenQPOutputDC
          **    make sure we don't try to use this DC and
          **     that we don't give another popup for same problem
          */
          fDcOk = OpenQPOutputDC(pQProc, ASSOCIATE);
        }

        else
        {
          fDcOk = FALSE;
        }

        if (fDcOk)
        {
          /*
          ** Apply transforms if given in pszQProcParms for job
          */
          if ((!pQProc->qparms.fTransform) || (SetViewMatrix(pQProc)))
          {
            /*
            ** Set to successful print(so far)
            */
            result = TRUE;
          }
          /*
          ** Support undocumented translation parameter if given
          */
          if (pQProc->qparms.fTranslate)
            ApplyTranslation(pQProc->hps);
        }

        if (fDcOk)
        {
          /*
          ** Setup to play metafile contents
          **
          ** Do NOT reset the PS, since we already set it up
          **  with our transforms.
          */
          pmfOptions[PMF_SEGBASE] = 0L;
          pmfOptions[PMF_LOADTYPE] = LT_ORIGINALVIEW;
          pmfOptions[PMF_RESOLVE] = RS_DEFAULT;
          pmfOptions[PMF_LCIDS] = LC_LOADDISC;
          pmfOptions[PMF_RESET] = RES_NORESET;
          pmfOptions[PMF_SUPPRESS] = SUP_NOSUPPRESS;
          pmfOptions[PMF_COLORTABLES] = CTAB_REPLACE;
          pmfOptions[PMF_COLORREALIZABLE] = CREA_NOREALIZE;
          pmfOptions[PMF_DEFAULTS] = DDEF_LOADDISC;

          /*
          ** If user paused the print job
          **   wait till user releases the job
          */
          if (pQProc->fsStatus&QP_PAUSED)
            DosWaitEventSem(pQProc->semPaused, SEM_INDEFINITE_WAIT);

          /*
          ** If user aborted the job
          **   do NOT print it now
          */
          if (!(pQProc->fsStatus&QP_ABORTED))
          {

            if ((GpiPlayMetaFile(pQProc->hps, pQProc->hmf, (LONG)PMF_DEFAULTS+1
               , pmfOptions, NULL, 0L, NULL) == GPI_ERROR) &&
               !(pQProc->fsStatus&QP_ABORTED))
            {
              /*
              ** If GpiPlayMetaFile failed(and job not aborted)
              **   check to see if we should display error message.
              **
              ** Note: This defaults to NOT giving an error when
              **        the GpiPlayMetaFile() fails because some
              **        applications generate metafiles which print
              **        but somewhere have an invalid drawing command.
              **        Many users were unhappy when they received
              **        their correct printout, but also had an
              **        error from the print queue processor
              **        (and job was left in held state).
              **
              **        At some point we should be able to check
              **        the error severity(from WinGetLastError)
              **        to determine if the printer driver actually
              **        printed the job(if just SEVERITY_WARNING).
              **
              **        The temporary solution is to only issue
              **        an error if a flag is set in the
              **        system INI file.
              **
              */
              if (DoMetaFileChecks() == TRUE)
              {
                SplQpMessage(pQProc->pszPortName, SPL_ID_QP_INVALID_METAFILE, 0
                   );
                result = FALSE;
              }
            }
          }

          /*
          ** Clear STOP_DRAW condition if job got aborted
          **  via other thread(SplQpControl)
          */
          GpiSetStopDraw(pQProc->hps, SDW_OFF);

          /*
          ** If user paused the print job
          **   wait till user releases the job
          */
          if (pQProc->fsStatus&QP_PAUSED)
            DosWaitEventSem(pQProc->semPaused, SEM_INDEFINITE_WAIT);

          /*
          ** End the document, according to if an abort has occurred
          */
          if (pQProc->fsStatus&QP_ABORTED)
          {
            DevEscape(pQProc->hdc, DEVESC_ABORTDOC, 0L, (PBYTE)NULL, (PLONG)
               NULL, (PBYTE)NULL);
          }

          else
          {

            if ((DevEscape(pQProc->hdc, DEVESC_ENDDOC, 0L, (PBYTE)NULL, (PLONG)
               NULL, (PBYTE)NULL) == DEVESC_ERROR) && !(pQProc->fsStatus
               &QP_ABORTED))
            {

              /*
              ** if ENDDOC fails(and job not aborted)
              **    fail print job because most printer drivers
              **    do their processing at ENDDOC time.
              */
              result = FALSE;
            }
          }
          /*
          ** Set EndDoc processing complete to ensure we don't
          **  issue an AbortDoc after an EndDoc
          */
          pQProc->fsStatus |= QP_ENDDOC_COMPLETE;

          /*
          ** If user paused the print job
          **   wait till user releases the job
          **
          ** This is done in here to allow user to pause a job
          **   after it has completed printing.
          ** This allows them to make another copy of the job
          */
          if (pQProc->fsStatus&QP_PAUSED)
            DosWaitEventSem(pQProc->semPaused, SEM_INDEFINITE_WAIT);

          /*
          ** Tidy up and dissociate PS
          */
          if (pQProc->region)
          {
            GpiSetClipRegion(pQProc->hps, pQProc->region, &Region);
            GpiDestroyRegion(pQProc->hps, Region);
          }
          GpiAssociate(pQProc->hps, (HDC)0);
        }                              /* end if OpenDC Direct successful    */
      }

      else
      {

        /*
        ** Creation of the Presentation Space failed
        ** Give error to user and fail print job
        ** Note: The message we have available is to have the user
        **       check the "Printer-specific" checkbox.
        **       This will force all jobs for the print queue to
        **       be spooled in Raw format.
        */
        Panic("QP: GpiCreatePS failed for %0s on %4s", pQProc->pszPortName, 1);
        SplQpMessage(pQProc->pszPortName, SPL_ID_QP_INVALID_METAFILE, 0);
      }
                                           /* end if Open InfoDC successful */
    }

    else
    {
      Panic("QP: DevOpenDC failed for %0s on %4s", pQProc->pszPortName, 2);
      SplQpMessage(pQProc->pszPortName, SPL_ID_QP_OPENDC_ERROR, 0);
    }
  }

  /*
  ** Delete the metafile and presentation space if created
  */
  if (pQProc->hmf)
  {
    GpiDeleteMetaFile(pQProc->hmf);
    pQProc->hmf = (HMF)0;
  }

  if (pQProc->hps)
  {
    GpiDestroyPS(pQProc->hps);
    pQProc->hps = (HPS)0;
  }
  CloseQPOutputDC(pQProc, FALSE);
  CloseQPInputFile(pQProc);

  if (!result)
    Panic("SplQpStdPrintFile failed for %0s", pszFileName, 0);
  return (result);
}

/****************************************************************************
 *
 * FUNCTION NAME = SetViewMatrix
 *
 * DESCRIPTION   = Set page viewport for print job using QProcParms
 *                 supplied with job.
 *
 *
 * INPUT         = pQProc      - QProc instance pointer for print job
 *
 * OUTPUT        = TRUE   - Transforms successful
 *                 FALSE  - Unable to use printer driver to apply transforms
 *                          WinGetLastError() gets failure code.
 *                          No message displayed to user.
 *
 * RETURN-NORMAL = TRUE
 *
 *
 * RETURN-ERROR  = FALSE
 *
 *
 * NOTE:  Following parameters are supported:
 *
 *        ARE=C | w,h,l,t -> determine size and position of output area
 *                           C - [default] output area is whole page
 *
 *                           To size and position the output area at a
 *                           specific point on the page use ARE=w,h,l,t
 *                           These are given as percentages of the maximum
 *                           output dimensions.  The max output area is
 *                           the area within the device clip limits.
 *
 *                            w,h - width and height of desired output area
 *                            l,t - offsets of uppler-left corner of the
 *                                  output area from left(l) and from
 *                                  the top(t) of the maximum output area
 *
 *        FIT=S | l,t     -> determines which part of picture to print
 *                           S - [default] output is scaled until larger
 *                               of the height or width just fits within
 *                               the defined output area.  The aspect
 *                               ratio of the picture is maintained.
 *
 *                           C - Center the picture.
 *                               (Undocumented parameter!)
 *
 *                           l,t Print picture in actual size.
 *                               l and t are coordinates of the point
 *                               in the picture that you want positioned
 *                               at the center of the output area:
 *
 *                               l is measured from the left edge of picture
 *                               t is measured from the top edge
 *
 *                               Each are given as percentages of the
 *                               actual dimensions of the picture.
 *
 *        XFM=0 | 1       -> override ARE and FIT parameters
 *                           1 - [default] allow appearance of the output
 *                               to be determined by settings of ARE and FIT
 *        COL=M | C       -> specify Color or Monochrome output
 *                           M - black foreground with no background color
 *                               is supported by all print devices
 *                           C - create color output
 *                               If requested on a monochrome device
 *                                 print driver attempts to satisfy request
 *                                 but can only use color black.
 *                           Default is M for monochrome printer
 *                                   and C for color printer
 *        MAP=N | A       -> determine how neutral colors are printed
 *                           N - [default] normal presentation of the screen
 *                               page background white, foreground black
 *                           A - reverse of normal presentation
 *                               page background black, foreground white
 *
 *
 *        XLT=0 | 1       -> Remove translate component from Device Transform
 *                           by applying a translation.
 *                           (Undocumented parameter!)
 *                           0 - [default] leave translation in metafile
 *                           1 - override translation in metafile
 *
 ****************************************************************************/

BOOL SetViewMatrix(PQPROCINST pQProc)
{
  SIZEL ptSize;
  RECTL ClipRectl;
  RECTL PageView;
  POINTL Centre;
  POINTL l_t_in_pels;
  POINTL shift_value;
  BOOL Clip;                           /* TRUE if page will be clipped       */

  if (!DevQueryCaps(pQProc->hdc, CAPS_WIDTH, 2L, (PLONG)&ptSize))
    return  FALSE;

  if (GpiQueryPageViewport(pQProc->hps, (PRECTL)&PageView) == GPI_ERROR)
    return  FALSE;

  /*
  ** We count from zero so reduce width and height by one
  */
  ptSize.cx--;
  ptSize.cy--;

  if (!pQProc->qparms.fArea)
  {
    /*
    ** "ARE=C"[default]: output area is whole page
    */
    ClipRectl.xLeft = 0L;
    ClipRectl.yBottom = 0L;
    ClipRectl.xRight = ptSize.cx;
    ClipRectl.yTop = ptSize.cy;
    Clip = FALSE;
  }

  else
  {
    /*
    ** "ARE=w,h,l,t" : position output area at a specific point
    **                 each is a percentage(100 is max)
    */
    if (!pQProc->qparms.ptAreaOrigin.x.chLeft)
      ClipRectl.xLeft = 0L;

    else

      if (pQProc->qparms.ptAreaOrigin.x.chLeft == 100)
        ClipRectl.xLeft = ptSize.cx;

      else
        ClipRectl.xLeft = pQProc->qparms.ptAreaOrigin.x.chLeft *ptSize.cx/100L;

    if (!pQProc->qparms.ptAreaOrigin.y.chTop)
      ClipRectl.yTop = ptSize.cy;

    else

      if (pQProc->qparms.ptAreaOrigin.y.chTop == 100)
        ClipRectl.yTop = 0L;

      else
        ClipRectl.yTop = (100-pQProc->qparms.ptAreaOrigin.y.chTop)*ptSize.cy/
           100L;
    pQProc->qparms.ptAreaSize.x.chWidth = min
       (pQProc->qparms.ptAreaSize.x.chWidth, (CHAR)100-
       pQProc->qparms.ptAreaOrigin.x.chLeft);
    pQProc->qparms.ptAreaSize.y.chDepth = min
       (pQProc->qparms.ptAreaSize.y.chDepth, (CHAR)100-
       pQProc->qparms.ptAreaOrigin.y.chTop);
    ClipRectl.xRight = ClipRectl.xLeft+(pQProc->qparms.ptAreaSize.x.chWidth
       *ptSize.cx/100L);
    ClipRectl.yBottom = ClipRectl.yTop-(pQProc->qparms.ptAreaSize.y.chDepth
       *ptSize.cy/100L);
    Clip = TRUE;
  }

  if (!pQProc->qparms.fFit)
  {
    /*
    ** "FIT=S"[default]: scale until larger of height or width
    **                   just fits inside output area
    */
    PageView.xLeft = ClipRectl.xLeft;
    PageView.yBottom = ClipRectl.yBottom;

    if (((ClipRectl.yTop-ClipRectl.yBottom)*PageView.xRight) <=
       ((ClipRectl.xRight-ClipRectl.xLeft)*PageView.yTop))
    {
      PageView.xRight = PageView.xLeft+((PageView.xRight *(ClipRectl.yTop-
         ClipRectl.yBottom))/PageView.yTop);
      PageView.yTop = ClipRectl.yTop;
    }

    else
    {
      PageView.yTop = PageView.yBottom+((PageView.yTop *(ClipRectl.xRight-
         ClipRectl.xLeft))/PageView.xRight);
      PageView.xRight = ClipRectl.xRight;
    }

    if (pQProc->qparms.uFit == 1)
    {

      /*
      ** "FIT=C" center the picture(undocumented parameter to FIT)
      */
      shift_value.x = (ClipRectl.xRight-PageView.xRight)/2;
      shift_value.y = (ClipRectl.yTop-PageView.yTop)/2;
      PageView.xLeft += shift_value.x;
      PageView.xRight += shift_value.x;
      PageView.yBottom += shift_value.y;
      PageView.yTop += shift_value.y;
    }
  }

  else
  {
    /*
    ** "FIT=l,t" : print picture in actual size using l,t percentages
    **             to determine the point at the center of the output area
    */
    Centre.x = (ClipRectl.xRight+ClipRectl.xLeft)/2;
    Centre.y = (ClipRectl.yTop+ClipRectl.yBottom)/2;
    l_t_in_pels.x = PageView.xRight *pQProc->qparms.ptFit.x.chLeft/100;
    l_t_in_pels.y = PageView.yTop *(100-pQProc->qparms.ptFit.y.chTop)/100;
    shift_value.x = Centre.x-l_t_in_pels.x;
    shift_value.y = Centre.y-l_t_in_pels.y;
    PageView.xLeft += shift_value.x;
    PageView.yTop += shift_value.y;
    PageView.xRight += shift_value.x;
    PageView.yBottom += shift_value.y;
  }
  ClipRectl.xRight++;
  ClipRectl.yTop++;
  GpiSetPageViewport(pQProc->hps, &PageView);
  pQProc->region = FALSE;

  if (Clip)
  {

    if (pQProc->region = GpiCreateRegion(pQProc->hps, 1L, &ClipRectl))
      GpiSetClipRegion(pQProc->hps, pQProc->region, &pQProc->region);
  }
  return  TRUE;
}

/****************************************************************************
 *
 * FUNCTION NAME = ApplyTranslation
 *
 * DESCRIPTION   = Remove the translate component from Device Transform
 *                 by applying a translation.
 *
 *
 * INPUT         = hps   - PS handle for printing
 *
 * OUTPUT        = PS is updated
 *
 * RETURN-NORMAL = TRUE
 *
 *
 * RETURN-ERROR  = FALSE
 *
 *
 * NOTE:  This is an undocumented Queue processor parameter
 *
 ****************************************************************************/

BOOL ApplyTranslation(HPS hps)
{
  RECTL rclViewport;
  BOOL fRet;
  POINTL aptl[1];
  aptl[0].x = 0L;
  aptl[0].y = 0L;
  fRet = GpiConvert(hps, CVTC_PAGE, CVTC_DEVICE, 1L, aptl);

  if (fRet)
    fRet = GpiQueryPageViewport(hps, &rclViewport);
  rclViewport.xLeft -= aptl[0].x;
  rclViewport.xRight -= aptl[0].x;
  rclViewport.yBottom -= aptl[0].y;
  rclViewport.yTop -= aptl[0].y;

  if (fRet)
    fRet = GpiSetPageViewport(hps, &rclViewport);
  return (fRet);
}

/****************************************************************************
 *
 * FUNCTION NAME = DoMetaFileChecks
 *
 * DESCRIPTION   = Check for allowing error message to be displayed when
 *                 GpiPlayMetaFile fails.
 *
 *
 * INPUT         = None
 *
 * OUTPUT        = TRUE  - display error message and fail print job
 *               = FALSE - do not display message and succeed print
 *
 * RETURN-NORMAL = FALSE
 *
 *
 * RETURN-ERROR  = TRUE
 *
 *
 ****************************************************************************/

BOOL DoMetaFileChecks(VOID)
{
  CHAR buf[12];
  ULONG cbKey;

  /*
  ** Check special key in system INI file for enabling MetaFile checks
  ** This key is normally not in INI file, and must be put there
  ** to show metafile failures to users
  */
  cbKey = PrfQueryProfileString(HINI_SYSTEMPROFILE, "PM_SPOOLER_PMPRINT",
     "CHECKERRORS", (PSZ)NULL,         /* default string                     */
     buf, (ULONG)(sizeof(buf)-1));

  if (cbKey && (buf[0] == '1'))
    return (TRUE);

  else
    return (FALSE);
}

