/*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.                                */
/*                                                                           */
/*****************************************************************************/
#pragma  pagesize(55)

/**************************************************************************
 *
 * SOURCE FILE NAME = OUTPUT.C
 *
 * DESCRIPTIVE NAME = Utility Output Routines
 *
 *
 * VERSION = V2.0
 *
 * DATE      12/18/88
 *
 * DESCRIPTION Contains utility routines for producing output on a
 *             vector plotter supporting HPGL.
 *
 * FUNCTIONS
 *             refresh_color
 *             set_empty_rect
 *             check_fill_type
 *             check_line_construction
 *             construct_fixed
 *             construct_float
 *             construct_point
 *             convert_point
 *             draw_line
 *             draw_point
 *             end_doc
 *             end_job
 *             end_page
 *             get_closest_point
 *             move_pen
 *             lift_pen
 *             next_band
 *             output_comma
 *             output_number
 *             output_point
 *             plot_line
 *             set_default_p1p2
 *             set_default_scale
 *             set_line_type
 *             set_p1p2
 *             set_scale
 *             set_rotation
 *             start_doc
 *             start_job
 *             start_page
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/
#define  INCL_WINSHELLDATA
#define  INCL_DOSSEMAPHORES
#define  INCL_SPL
#define  INCL_DOS
#include "plotters.h"
#include "color.h"
#include "clip.h"
#include "dialog.h"
#include "dosio.h"
#include "error.h"
#include "init.h"
#include "output.h"
#include "utils.h"
#include "band.h"

/*
** LOCAL NUMERIC DEFINES
*/

#define  TEMP_BUF_SIZE 16

/*
** LOCAL FUNCTION PROTOTYPES
*/

LOCAL VOID  set_empty_rect(PRECTL);
LONG  round_divide(LONG, LONG);

/*
** LOCAL FUNCTIONS
*/
/***************************************************************************
 *
 * FUNCTION NAME = set_empty_rect
 *
 * DESCRIPTION   = initialize a rectangle (RECTL) structure to
 *                 enclose a null (unit) rectangle
 *
 * INPUT         = pRect - rectangle
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL VOID  set_empty_rect(PRECTL pRect)
{
  pRect->xLeft  = pRect->yBottom = 1L;
  pRect->xRight = pRect->yTop    = 0L;
}

/***************************************************************************
 *
 * FUNCTION NAME = send_header
 *
 * DESCRIPTION   = send prerequisite commands to the plotter to
 *                 initialize device for next and/or new page
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL BOOL  send_header(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  SHORT Plotter = pPDevice->Plotter;
  RECTL Rectl;
  ULONG  OD_CAPS = PlotterClass[Plotter].fsOdd;

  /*
  ** Set HPGL2 printers into HPGL2 MODE
  ** ESC-1B Some HP plotters understand both HP-GL/2 and PCL and
  **        require this command to insure that the plotter is in
  **        standalone HP-GL/2 mode.
  **        NOTE: Do Not follow ESC%-1B with a semicolon and a BP
  **              instruction, the plotter may interpret the
  **              semicolon as a complete plot file.
  ** BP     Tells the plotter that a new picture is coming.
  **        Old HP 7600 240D/E will NOP this command.
  **
  ** Moved init string to PlotterClass Structure.
  */
  if (PlotterClass[Plotter].pszDeviceModeInit)
    output_bytes(pDDC, PlotterClass[Plotter].pszDeviceModeInit);

  /*
  ** Send the pageprotect command to let the printer manage its memory.
  */
  //if (OD_CAPS & OD_PAGEPROTECT)
  //{
  //   output_bytes(pDDC,pszPJLPageProtON);
  //}

  /*
  ** Set the Orientation to Reverse LandScape, if it is LandScape.
  ** Set the Raster Presentation Mode. This changes the raster row direction.
  ** Or set it to Reverse Portrait if it is Portrait.
  ** Do not change the sequence of these escape sequences.  Kran
  */
  if (OD_CAPS & OD_PCL)
  {
     LONG lRes = MAX(pPDevice->lRes_XPelsPerInch,pPDevice->lRes_YPelsPerInch);


     /*
     ** Set PCL resolution
     */
     output_bytes(pDDC,"@PJL SET RESOLUTION=");
     output_number(pDDC,lRes);
     output_bytes(pDDC," \x00a");

     /*
     ** Send the pageprotect command to let the printer manage its memory.
     */
     if (OD_CAPS & OD_PAGEPROTECT)
     {
        output_bytes(pDDC,pszPJLPageProtLTR);
     }

     output_bytes(pDDC,"@PJL ENTER LANGUAGE=PCL\x00a\033E");

     /*---------------------------------------------------------------------*/
     /* Handle special case for PCL5 600 DPI printers                       */
     /*---------------------------------------------------------------------*/
     if (lRes == 600)
     {
       output_bytes(pDDC,"\033&u600D");
     }

     if (pPDevice->fsTransform & DXF_PORTRAIT)
     {
       output_bytes(pDDC,"\033&l2o0E");  // reverse portrait
       output_bytes(pDDC,"\033*c");
       output_number(pDDC,pPDevice->lxPicFrame);
       output_bytes(pDDC,"x");
       output_number(pDDC,pPDevice->lyPicFrame);
       output_bytes(pDDC,"Y");
     }
     else
     {
       output_bytes(pDDC,"\033&l3o0E");  // reverse landscape
       output_bytes(pDDC,"\033*r0F");    // raster presentation mode
       output_bytes(pDDC,"\033*c");      // Size of Picture Frame
       output_number(pDDC,pPDevice->lyPicFrame);
       output_bytes(pDDC,"x");
       output_number(pDDC,pPDevice->lxPicFrame);
       output_bytes(pDDC,"Y");
     }
     DBPRINTF(("lxPictureFrame = %d\n",pPDevice->lxPicFrame));
     DBPRINTF(("lyPictureFrame = %d\n",pPDevice->lyPicFrame));
     output_bytes(pDDC,"\033*c0T");    // set anchor point to CAP
     output_bytes(pDDC,"\033%1B");     // Enter HPGL/2 at PCL current position.
  }
  /*
  ** The initialization string contains the following:
  **
  **   ETX   - terminate any previously active strings
  **   IN    - HPGL Initialize
  **   < HANDSHAKE >
  **         - As required.
  **           The older <.@> COMMAND IS BECAUSE THIS IS
  **           GUARENTEED TO BE UNDERSTOOD BY ALL PLOTTERS.
  **
  **
  **   PTR ???????
  **
  **   HANDSHAKING TO A PARALLEL ATTACHED DEVICE IS POINTLESS.
  */
  if (IsIta_COM_Port_Name(pPDevice->pszLogAddress))
  {
    output_bytes(pDDC, "\003IN;");
    output_bytes(pDDC, "\033.@;");

    if (pPDevice->HandShake == HNDSHK_HARD)
      output_bytes(pDDC, "1");
    else
      output_bytes(pDDC, "0");
    output_bytes(pDDC, ":");

    /*
    ** set te XON/XOFF char (change for WARP beta feedback on 7475A)
    */
    if (pPDevice->HandShake != HNDSHK_HARD)
    { /* assume XON/XOFF */
      output_bytes(pDDC, "\033.I;;17:\033.N;19:");
    }

    if (pPDevice->HandShake == HNDSHK_HARD && Plotter == CLASS_HP7580B)
    {
      output_bytes(pDDC, "\033.T1024;3072;;450:\033.@750;1:");
    }
  }
  else
  {
    /*
    ** Initialize the plotter
    */
    output_bytes(pDDC, "IN;");
  }
  pPDevice->lCurrentRotate = 0;

  /*
  **  If PCL device and in portrait then rotate by 90
  */
  if (OD_CAPS & OD_PCL)
  {
    if (pPDevice->fsTransform & DXF_PORTRAIT)
       set_rotation(pDDC,90L,FALSE);
  }

  if (Plotter == CLASS_HP7475A)
  {
    if (pPDevice->pSetup->Size == 0 || pPDevice->pSetup->Size == 5)
      output_bytes(pDDC, "PS4");
    else
      output_bytes(pDDC, "PS3");
  }

  /*
  ** Set P1P2 and scale
  */
  set_default_p1p2(pDDC);
  set_default_scale(pDDC);

  /*
  ** if HP DRAFTPRO DXL(HP7570A) avoid using the AP3 instruction. The plotter
  ** has a     which puts the pen down after pen changes.  The bug
  ** happens when the AP3 instruction is used whith a RA just prior to
  ** a SP(select pen). MarkV PTR B727727
  ** NOTE: HP says all plotter jobs should begin with "IN;".
  */
  if (Plotter != CLASS_HP7570A)
  {
    if (PlotterClass[Plotter].usHPGLCaps & HPGL_AUTOPEN)
      output_bytes(pDDC, "AP3");
  }

  if (pDDC->usDCType != OD_DIRECT || !pPDevice->bSomeRawData)
  {
    /*
    ** if HPGL2 device-
    ** 1. set the transparency mode to ON.
    ** Overlaying white are transparent
    **
    ** 2. Set Number of Pens &
    **    Pen Color Assignments
    */
    if ((PlotterClass[Plotter].usHPGLCaps & HPGL2)  &&
        !(PlotterClass[Plotter].fsOdd & OD_HPGL2_PENPLOTTER))
    {
      /*
      ** Set Transparency ON
      */
      set_transparency_mode(pDDC, 1);

      /*
      ** Set Color Palette size and pen assignments
      */
      set_HPGL2_color_palette(pDDC);
    }
  }

  // I do not believe we need this PG command.
  // It is meaningless immediatly after an IN command.
  // If anyone desides this command is necessary it must
  // be moved above the the set P1P2 because it cancels
  // the PS command.  MV
  //
  //if (pPDevice->pSetup->PaperFeed == 1)
  //  output_bytes(pDDC, "PG");

  if (pPDevice->AltCharSet == 7)
    output_bytes(pDDC, "CA7");
  else
    output_bytes(pDDC, "CA2");

  if (pPDevice->fsTransform & DXF_PORTRAIT)
    output_bytes(pDDC, "DI0,-1SL");
  else
    output_bytes(pDDC, "DISL");

  /*
  ** SLU, 12/05/91
  ** B730303
  */
  output_clip_rect(pDDC, pDDC->DCState.ClipComplexity,
                   &pDDC->DCState.ClipRect);
  return  TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = send_trailer
 *
 * DESCRIPTION   = Sends a command string to the plotter which
 *                 terminates a single page of output.
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

LOCAL BOOL  send_trailer(PDDC pDDC)
{

  /*
  ** obtain access to the physical device information
  */
  PPDEVICE     pPDevice = pDDC->pPDevice;
  SHORT        Plotter  = pPDevice->Plotter;
  PPROFILEINFO pSetup   = pPDevice->pSetup;
  BOOL         bResult;

  /*
  ** if we are constructing a string, terminate it.
  */
  check_string_construction(pDDC);
  /*
  ** if we are constructing a line, terminate it.
  */
  check_line_construction(pDDC);

  if (pPDevice->ulCurrentPrintMode == PCL ||
      pPDevice->ulCurrentPrintMode == HPRTL)
  {
    pPDevice->ulCurrentPrintMode = HPGL2;
    output_bytes(pDDC,"\033%0B");
    output_bytes(pDDC,"PG;");
  }


  output_bytes(pDDC, "SP");
  pDDC->pPDevice->bPenIsSelected = FALSE;

  /*
  ** allow the user to view, the output, or eject the page.
  */
  if ((pPDevice->usHPGLType & HPGL_AUTOVIEW) &&
       !pPDevice->pSetup->PaperFeed)
  {
    output_bytes(pDDC, "NR");
  }
  else
  {
    if (!(pPDevice->usHPGLType & HPGL_AUTOVIEW) &&
        !pPDevice->pSetup->PaperFeed)
    {
      POINTL ptlEnd;

      //ptlEnd.x = pPDevice->ClipRect.xLeft   + RESOLUTION;
      //ptlEnd.y = pPDevice->ClipRect.yBottom + RESOLUTION;
      ptlEnd.x = pPDevice->ClipRect.xLeft   + 1;
      ptlEnd.y = pPDevice->ClipRect.yBottom + 1;

      if ((pPDevice->Plotter == CLASS_HP7475A ||
           pPDevice->Plotter == CLASS_HP7550A) &&
           (MEDSZ_A|MEDSZ_A4) & (1 << pPDevice->pSetup->Size))
      {
        //ptlEnd.y = pPDevice->ClipRect.yTop - RESOLUTION;
        ptlEnd.y = pPDevice->ClipRect.yTop - 1;
      }
      output_bytes(pDDC, "PU");
      pPDevice->bPenIsUp = TRUE;
      output_point(pDDC, &ptlEnd);
      if (PlotterClass[Plotter].usHPGLCaps & HPGL2)
      {
        output_bytes(pDDC, "PG");
      }
    }
    else
    {
      output_bytes(pDDC, "PG");
    }
  }

  output_bytes(pDDC, ";");

  if (PlotterClass[Plotter].usHPGLCaps & HPGL2)
  {
    USHORT usCopies;
    /*
    ** Get the number of uncollated copies from the flags word.
    ** Values should be between 0-16.
    ** User interface value of 1 is stored as 0.
    */
    usCopies = (pPDevice->pSetup->usFlags & FL_UNCOLLATEDMASK);
    if (usCopies)
    {
      output_bytes(pDDC, "RP");     /* replot command */
      output_number(pDDC, (LONG)usCopies);
      output_bytes(pDDC, ";");
    }
  }

  /*
  ** send device mode termination string if necessary
  */
  if (PlotterClass[Plotter].pszDeviceModeTerm)
    output_bytes(pDDC, PlotterClass[Plotter].pszDeviceModeTerm);

  /*
  ** Make sure we do not think our palette is valid
  */
  pPDevice->usPensInPalette = 0;
  pPDevice->usLastPenDefined = 0;

  /*
  ** Flush everything
  */
  bResult = GplThreadFlushBuffer(pPDevice->hThread, FALSE);

  /*
  ** set the physical device information to a null state.
  */
  pPDevice->PhysPosition.x = pPDevice->PhysPosition.y = -256L;
  pPDevice->bLineInConstruction = pPDevice->bStringInConstruction = FALSE;
  pPDevice->bPenIsUp = TRUE;
  pPDevice->bPlotRelative = FALSE;
  pPDevice->bPenIsSelected = FALSE;
  pPDevice->CurrentPen = -1;
  pPDevice->CurrentSpeed = -1;
  pPDevice->CurrentForce = -1;
  pPDevice->CurrentAcceleration = -1;
  pPDevice->CurrentPenThickness = -1;
  pPDevice->CurrentTRMode = -1;        /* current transparency mode undefined*/
  pPDevice->CharCell.cx = pPDevice->CharCell.cy = -1L;
  pPDevice->CharAngle.x = pPDevice->CharShear.y = 1;
  pPDevice->CharAngle.y = pPDevice->CharShear.x = 0;

  pPDevice->bStandardCharSetActive = TRUE;
  if ((PlotterClass[Plotter].usHPGLCaps & HPGL_CHARSET7) || pSetup->bGECOption)
    pPDevice->AltCharSet = 7;
  else
    pPDevice->AltCharSet = 2;

  pPDevice->usCurrentFillPattern = PATSYM_SOLID;/* default fill type        */
  pPDevice->usCurrentFillAngle = 0;    /* default fill angle                */
  pPDevice->ptlRefPoint.x = 0;         /* Current device pattern X ref pt   */
  pPDevice->ptlRefPoint.y = 0;         /* Current device pattern Y ref pt   */
  pPDevice->usLineType = 0;
  pPDevice->iLineJoin = -1;            /* Device default LineJoin -1 in
                                          usLineJoin will force linejoin to
                                          be set the first time.            */
  pPDevice->iLineEnd = LINEEND_DEFAULT;
  pPDevice->usCurrentMCROP = 252;      /* Device default MC is 252 TSo      */

  return (bResult);
}

/*
** EXPORTED FUNCTIONS
*/
/***************************************************************************
 *
 * FUNCTION NAME = output_point
 *
 * DESCRIPTION   = output a standard coordinate pair ("x","y")
 *
 * INPUT         = pDDC    - pointer to device driver context
 *                 pPOINTS - pointr to Point
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID  output_point(PDDC pDDC,PPOINTL pPoints)
{
  output_number(pDDC, pPoints->x);
  output_comma(pDDC);
  output_number(pDDC, pPoints->y);

  return;
}

/***************************************************************************
 *
 * FUNCTION NAME = convert_point
 *
 * DESCRIPTION   = convert a point to a different coordinate system
 *
 * INPUT         = pDDC      - pointer to device driver context
 *                 pptl      - point to convert
 *                 fRelative - flag
 *
 * OUTPUT        = converted point
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID  convert_point(PDDC pDDC,PPOINTL pptl,BOOL fRelative)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  POINTL   ptlOld,ptlNew;
  BOOL     fRollFeed;

  fRollFeed = IsRollPaper(pPDevice->pSetup->Size);
  ptlOld    = *pptl;

  if (!fRelative)
  {
    if (pPDevice->fsTransform & DXF_CENTERED)
    {
      if (fRollFeed)
        ptlOld.x += pPDevice->lBandOffset;

      if (pPDevice->fsTransform & DXF_PORTRAIT)
      {
        ptlNew.x = ptlOld.y-pPDevice->lCapsHeight / 2;
        ptlNew.y = pPDevice->lCapsWidth/2-ptlOld.x;
      }
      else
      {
        ptlNew.x = ptlOld.x-pPDevice->lCapsWidth / 2;
        ptlNew.y = ptlOld.y-pPDevice->lCapsHeight / 2;
      }
    }
    else
    {
      if (pPDevice->fsTransform & DXF_PORTRAIT)
      {
        ptlNew.x = ptlOld.y;
        ptlNew.y = pPDevice->lCapsWidth-ptlOld.x;
      }
      else
      {
        ptlNew = ptlOld;
      }
    }
  }
  else
  {
    if (pPDevice->fsTransform & DXF_PORTRAIT)
    {
      ptlNew.x =  ptlOld.y;
      ptlNew.y = -ptlOld.x;
    }
    else
    {
      ptlNew = ptlOld;
    }
  }

  *pptl = ptlNew;
}

/***************************************************************************
 *
 * FUNCTION NAME = check_fill_type
 *
 * DESCRIPTION   = check the currently set fill type
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = FALSE if pattern SOLID or DEFAULT, else TRUE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  check_fill_type(PDDC pDDC)
{
  BOOL bResult = TRUE;


  switch (pDDC->DCState.abnd.usSymbol)
  {
    case  PATSYM_DEFAULT :
    case  PATSYM_SOLID :
      bResult = FALSE;
      break;
  }

  return (bResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = check_line_construction
 *
 * DESCRIPTION   = determine if the PS/DC pair currently has a line in
 *                 construction.
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  check_line_construction(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;


  if (pPDevice->bLineInConstruction || !pPDevice->bPenIsUp)
  {
    pPDevice->bPenIsUp = TRUE;
    pPDevice->bLineInConstruction = FALSE;
    pPDevice->bPlotRelative = FALSE;
    output_bytes(pDDC, "PUPA");
  }

  return (TRUE);
}

/***************************************************************************
 *
 * FUNCTION NAME = check_string_construction
 *
 * DESCRIPTION   = determine if we currently have a string label in
 *                 construction. If so, terminate it!!
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  check_string_construction(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;

  /*
  ** if we have a string label open
  */
  if (pPDevice->bStringInConstruction)
  {
    /*
    ** We must end the current string to send commands
    ** to the plotter
    */
    output_bytes(pDDC, "\003");
    pPDevice->bStringInConstruction = FALSE;
  }

  return (TRUE);
}
/***************************************************************************
 *
 * FUNCTION NAME = construct_fixed
 *
 * DESCRIPTION   = Take a fixed point number and output it as a
 *                 floating number with up to 4 digits following the
 *                 decimal point.  Trailing zeroes after the decimal
 *                 point are truncated.  A simple 0 is output if the
 *                 converted number is zero.
 *
 * INPUT         = pDDC   - pointer to device driver context
 *                 Number - number to output
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID  construct_fixed(PDDC pDDC,LONG Number)
{
  CHAR   achLocal[TEMP_BUF_SIZE];
  ULONG  ulFraction;
  USHORT usIndex;

  usIndex = 0;

  if (Number)
  {
    if (Number < 0)
    {
      Number = -Number;
      achLocal[usIndex++] = '-';
    }

    if (HIUSHORT(Number))
    {
      /*
      **  The integer part
      */
      usIndex += int_to_str((LONG)HIUSHORT(Number), &achLocal[usIndex], 1);
    }

    /*
    **      See if there is a fractional part to output.  The following
    **  statement converts the lower 16 bits of the input value to an
    **  integer in the range 0 to 9999.  If not zero,  output a decimal
    **  point and the number.
    */
    ulFraction = (ULONG)((LOUSHORT(Number) * 10000L) / 65536L);

    /*
    ** Get fractional part
    */
    //Number = (LONG)LOUSHORT(Number);

    if (ulFraction)
    {
      /*
      ** Take only the 4 most significant digets
      */
      while (ulFraction > 9999L)
      {
        ulFraction = round_divide(ulFraction, 10L);
      } /* endwhile */

      achLocal[usIndex++] = '.';
      usIndex += int_to_str(ulFraction, &achLocal[usIndex], 4);

      /*
      **   Delete any trailing zeroes
      */
      if (achLocal[usIndex-1] == '0')
      {
        achLocal[--usIndex] = '\0';

        if (achLocal[usIndex-1] == '0')
          achLocal[--usIndex] = '\0';
      }
    }
  }

  /*
  **   The following condition is not obvious.  It is to deal with the case
  **  where the input Number is very small, such that it is not zero,
  **  but the fractional conversion part evaluates to zero.
  */
  if (usIndex == 0)
  {
    usIndex = 1;
    achLocal[0] = '0';
  }

  achLocal[usIndex] = 0;
  output_bytes(pDDC, achLocal);

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = construct_float
 *
 * DESCRIPTION   = generate a formatted floating point number for
 *                 output to the plotter as part of command string The
 *                 number received to this function can have any number
 *                 of decimal places. The multiplier parameter will
 *                 determine the number of decimal places.
 *                    2000 with a multiplier of 1000, = 2.0
 *                    2000 with a multiplier of 10000, = .20
 *
 *
 * INPUT         = pDDC   - pointer to device driver context
 *                 lNumber - number to output
 *                 usMultiplier - Number of decimal places
 *                                ie. 100   = 2 decimal places
 *                                    1000  = 3 decimal places
 *                                    10000 = 4 decimal places
 *                                    100000= 5 decimal places
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  construct_float(PDDC pDDC, LONG lNumber, ULONG ulMultiplier)
{
  char  szNumText[16];
  PSZ   pszNumText = szNumText;
  LONG  lTemp;
  short sIndex;


  for (sIndex = 0; sIndex < 10; sIndex++)
    szNumText[sIndex] = '\0';

  if (lNumber == 0)
  {
    *pszNumText++ = '0';
  }
  else
  {
    if (lNumber < 0)
    {
      *pszNumText++ = '-';
      lNumber = -lNumber;
    }

    /*
    ** get int part
    */
    lTemp = lNumber/ulMultiplier;

    if (lTemp)
    {
      int_to_str(lTemp, pszNumText, 1);
      pszNumText += strlen (pszNumText);
    }

    /*
    ** get decimal part
    */
    lTemp = lNumber % ulMultiplier;

    if (lTemp)
    {
      SHORT sDigits = 0;
      while (ulMultiplier = ulMultiplier / 10)
      {
        sDigits++;
      } /* endwhile */

      /*
      ** convert decimal part
      */
      *pszNumText++ = '.';
      sIndex = int_to_str(lTemp, pszNumText, MAX(sDigits, 1) );

      /*
      ** delete trailing zeros
      ** leaving 1 if it exist.
      */
      while ( (sIndex > 1) && (pszNumText[--sIndex] == '0') )
      {
        pszNumText[sIndex] = '\0';
      }
    }
  }

  output_bytes(pDDC, szNumText);

  return  TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = construct_point
 *
 * DESCRIPTION   = output an (x,y) coordinate pair to the plotter,
 *                 applying any needed transformation.
 *
 * INPUT         = pDDC      - pointer to device driver context
 *                 pPoints   - point to output
 *                 bPelative - flag
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID  construct_point(PDDC pDDC,PPOINTL pPoints,BOOL bRelative)
{
  POINTL CnvPts;

  CnvPts = *pPoints;
  convert_point(pDDC, &CnvPts, bRelative);
  output_point(pDDC, &CnvPts);

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = draw_line
 *
 * DESCRIPTION   = draw a line
 *
 * INPUT         = pDDC  - pointer to device driver context
 *                 pFrom - pointer to line start point
 *                 pTo   - pointer to line end point
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  draw_line(PDDC pDDC,PPOINTL pFrom,PPOINTL pTo)
{
  set_line_type(pDDC, pDDC->DCState.lbnd.usType);
  move_pen(pDDC, pFrom, FALSE);
  plot_line(pDDC, pTo);

  return (1);
}

/***************************************************************************
 *
 * FUNCTION NAME = draw_point
 *
 * DESCRIPTION   = draw a single point.   Seems kinda pointless, though.
 *
 * INPUT         = pDDC   - pointer to device driver context
 *                 pPoint - pointer to point to draw
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  draw_point(PDDC pDDC,PPOINTL pPoint)
{
  move_pen(pDDC, pPoint, FALSE);
  plot_line(pDDC, pPoint);

  return (1);
}

/***************************************************************************
 *
 * FUNCTION NAME = end_doc
 *
 * DESCRIPTION   = The end of the current document has been reached,
 *                 Send the necessary sequence of commands to setup
 *                 for the next document.
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL end_doc(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  USHORT JobID = 0;
  BOOL  fOK = TRUE;

  #if 0
  if (pPDevice->bDocStarted)
  {
    if (pPDevice->bBanding)
    {
      fOK = GplJournalPlayInstance (pPDevice->hJournalGpl,
                                     PLAY_JOURNAL_BAND
                                     | PLAY_DESTRUCTIVE
                                     | (FALSE ? PLAY_TOP_TO_BOTTOM :
                                               PLAY_BOTTOM_TO_TOP));

      assert( fOK );
      fOK = GplJournalDeleteInstance (pPDevice->hJournalGpl);
      assert( fOK );
      // Driver must free memory!
      GplMemoryFree (pPDevice->hJournalGpl);
      pPDevice->hJournalGpl = NULL;
    }
    else
    {
      if (pPDevice->bRaster)
      {
         RECTL  rctlPage;

         rctlPage.xLeft   = 0;
         rctlPage.yTop    = pDDC->pDeviceSurface->SurfaceBmapInfo.ulHeight;
         rctlPage.xRight  = pDDC->pDeviceSurface->SurfaceBmapInfo.ulWidth;
         rctlPage.yBottom = 0;
         fOK = band (pDDC, &rctlPage);
       }
    }
    assert( fOK );
  }
  #endif
  if (pDDC->usDCType == OD_QUEUED)
  {
    if (pPDevice->hSpooler)
      GplThreadFlushBuffer(pPDevice->hThread, TRUE); /* flush with wait     */
    JobID = SplQmEndDoc(pPDevice->hSpooler);
  }

  pPDevice->bDocStarted = FALSE;

  return (JobID);
}

/***************************************************************************
 *
 * FUNCTION NAME = end_job
 *
 * DESCRIPTION   = The current plot job has ended.  close the relevant
 *                 path
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  end_job(PDDC pDDC)
{
  BOOL bResult = TRUE;
  PPDEVICE pPDevice = pDDC->pPDevice;


  if (pDDC->usDCType == OD_DIRECT)
  {
    bResult = close_file_or_device(pDDC, pPDevice);
  }
  else
  {
    if (pDDC->usDCType == OD_QUEUED)
    {
      if (pPDevice->hSpooler)
      {
        GplThreadEnd(pPDevice->hThread);   /* End t2-thread stuff      */
        bResult = SplQmClose(pPDevice->hSpooler);
        pPDevice->hSpooler = 0L;
      }
    }
  }

  pPDevice->bJobStarted = FALSE;

  return (bResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = end_page
 *
 * DESCRIPTION   = The current page of output is full, send the
 *                 appropriate command sequence to obtain and new
 *                 page, and initialize it for output.
 *
 * INPUT         = pDDC - pointer to device driver context
 *                 hDC  - device context handle
 *                 FunN - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  end_page(PDDC pDDC,HDC hDC,BOOL bEndDoc,ULONG FunN)

{
  PPDEVICE pPDevice = pDDC->pPDevice;
  BOOL fOK = TRUE;


  if (pPDevice->bPageStarted &&
      !bEndDoc)
  {
    /*
    ** if we are banding on a raster device
    */
    if (pPDevice->bBanding)
    {
       // play bands
       fOK = GplJournalPlayInstance (pPDevice->hJournalGpl,
                                     PLAY_JOURNAL_BAND
                                     | PLAY_DESTRUCTIVE
                                     | ((FALSE)?
                                             PLAY_TOP_TO_BOTTOM :
                                             PLAY_BOTTOM_TO_TOP));
       assert( fOK );

       // Set up for the next page!
       fOK = GplJournalCreateInstance (pPDevice->hJournalGpl, NULL, 0);
    }
    else
    {
      // called for single band pages
      if (pPDevice->bRaster)
      {
        RECTL  rctlPage;
        LONG   lrc;

        rctlPage.xLeft   = 0;
        rctlPage.yBottom = 0;
        rctlPage.yTop    = pDDC->pDeviceSurface->SurfaceBmapInfo.ulHeight;
        rctlPage.xRight  = pDDC->pDeviceSurface->SurfaceBmapInfo.ulWidth;
        fOK = band (pDDC, &rctlPage);

        /*
        ** Erase the shadow DC bitmap only if we are not banding.  If we are
        ** banding, it is assumed that the banding code will erase the band
        ** bitmap.  Only need to erase the hps of the surface between
        ** pages. ( page 1 thru (N-1) ).
        */
        lrc = GreErasePS (pDDC->hdc);
        assert (lrc);
      }
    }
    assert( fOK );
  }

  if (pDDC->usDCType == OD_DIRECT)
  {
    DBPRINTF(("end_page, OD_DIRECT \n\r"));

    if (pPDevice->DataType == PM_Q_STD && pPDevice->bColorSorting)
      stop_color_sorting(pDDC, hDC);

    if (pPDevice->bPageStarted && !pPDevice->bSomeRawData)
      send_trailer(pDDC);

    GplThreadFlushBuffer(pPDevice->hThread, FALSE);

    if (pPDevice->DataType == PM_Q_STD && pPDevice->bColorSorting && !bEndDoc)
    {
      /*
      ** if we are doing an EndDoc don't start color sorting B735545
      */
      start_color_sorting(pDDC, hDC);
    }
  }
  else
  {
    if (pDDC->usDCType == OD_QUEUED && pPDevice->DataType == PM_Q_RAW)
    {
      if (pPDevice->bPageStarted)
      {
        DBPRINTF(("end_page function, OD_QUEUED output thread, PM_Q_RAW\n\r"));
        send_trailer(pDDC);
      }
    }
  }

  pPDevice->lNumBandsCompleted = 0;
  pPDevice->bPageStarted = FALSE;

  return (1);
}

/***************************************************************************
 *
 * FUNCTION NAME = get_closest_point
 *
 * DESCRIPTION   = given three points; A, B, and C, determine if B is
 *                 closer to A than C
 *
 * INPUT         = pPtsA - pointer to point A
 *                 pPtsB - pointer to point B
 *                 pPtsC - pointer to point C
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE if B is closer. FALSE if C is closer
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  get_closest_point(PPOINTL pPtsA,PPOINTL pPtsB,PPOINTL pPtsC)
{
  BOOL bResult = FALSE;

  LONG ABx = ABS(pPtsA->x-pPtsB->x),ABy = ABS(pPtsA->y-pPtsB->y),ACx =
             ABS(pPtsA->x-pPtsC->x),ACy = ABS(pPtsA->y-pPtsC->y),DistAB =
             (ABx * ABx)+(ABy * ABy), DistAC = (ACx * ACx)+(ACy * ACy);

  if (DistAB <= DistAC)
    bResult = TRUE;

  return (bResult);
}

/*
** GLOBAL FUNCTIONS
*/
/***************************************************************************
 *
 * FUNCTION NAME = move_pen
 *
 * DESCRIPTION   = update the current physical position of the plotter
 *                 pen or if we are doing an area fill, merely update
 *                 the DDC's physical pen position information.
 *
 * INPUT         = pDDC - pointer to device driver context
 *                 pPts - pointer to point to move to
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  move_pen(PDDC pDDC, PPOINTL pPts, BOOL bForceMove)
{
  PPDEVICE pPDevice = pDDC->pPDevice;

#if      FILLOPTPATH
  if (pDDC->pOptBuffer && pDDC->usOptMode == FOM_RECORD)
  {
    pPDevice->PhysPosition = *pPts;
    /*
    ** pPDevice->bLineInConstruction = FALSE;
    ** pPDevice->bPenIsUp = TRUE;
    */

    return (1);
  }                                    /* endif                             */
#endif

    if (pPDevice->PhysPosition.x != pPts->x ||
        pPDevice->PhysPosition.y != pPts->y ||
        bForceMove)
    {
      POINTL DistPts;


      if (pPDevice->bStringInConstruction)
      {
        output_bytes(pDDC, "\x03");
        pPDevice->bStringInConstruction = FALSE;
      }                                /* endif                             */

      DistPts.x = pPts->x-pPDevice->PhysPosition.x;
      DistPts.y = pPts->y-pPDevice->PhysPosition.y;

      if ((ABS(DistPts.x) > pPDevice->CurrentPenThickness) ||
          (ABS(DistPts.y) > pPDevice->CurrentPenThickness) ||
           bForceMove)
      {
        pPDevice->bLineInConstruction = FALSE;
        output_bytes(pDDC, "PA");
        pPDevice->bPlotRelative = FALSE;

        if (!pPDevice->bPenIsUp)
        {
          output_bytes(pDDC, "PU");
          pPDevice->bPenIsUp = TRUE;
        }
        construct_point(pDDC, pPts, FALSE);
      }
      else
      {
        if (pPDevice->bLineInConstruction)
        {
          output_comma(pDDC);
        }
        else
        {
          output_bytes(pDDC, "PR");
          pPDevice->bPlotRelative = TRUE;

          if (pPDevice->bPenIsUp)
          {
            output_bytes(pDDC, "PD");
            pPDevice->bPenIsUp = FALSE;
          }
          pPDevice->bLineInConstruction = TRUE;
        }                              /* endif                             */
        construct_point(pDDC, &DistPts, TRUE);
      }                                /* endif                             */
      pPDevice->PhysPosition = *pPts;
    }
    else
    {
      /*
      **    The move_pen is to the current physical position - so make
      **    sure that plot_line doesn't handle it as a multi section
      **    polyline
      **
      **
      **  commented out by MarkV
      **  This line was causing larger plot files. Why not just continue
      **  the line is we moved to the current position?
      **
      **   pPDevice->bLineInConstruction = FALSE;
      */

    }

  return (1);
}

/***************************************************************************
 *
 * FUNCTION NAME = lift_pen
 *
 * DESCRIPTION   = Lift the pen if it is not already up
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  lift_pen(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;

  pPDevice->bPenIsUp = TRUE;
  output_bytes(pDDC, "PU");
  pPDevice->bLineInConstruction = FALSE;

  return (TRUE);
}

/***************************************************************************
 *
 * FUNCTION NAME = next_band
 *
 * DESCRIPTION   = This function enables a new band or calls end page
 *                 if on the last band.  Only roll feed plotters are
 *                 banded.
 *
 * INPUT         = pDDC      - pointer to device driver context
 *                 hDC       - device context handle
 *                 pBandRect - band of interest
 *                 FunN      - function number
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  next_band(PDDC pDDC,HDC hDC,PRECTL pBandRect,ULONG FunN)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  long lBandOffset = pPDevice->lBandOffset,lNumBands = pPDevice->lNumBands,
     lNumBandsCompleted = ++pPDevice->lNumBandsCompleted;

  check_line_construction(pDDC);
  set_empty_rect(pBandRect);

  if (lNumBandsCompleted <= pPDevice->lNumBands)
  {
    if (IsRollPaper(pPDevice->pSetup->Size))
    {
      if (pPDevice->fsTransform & DXF_PORTRAIT)
      {
        pBandRect->yTop = (lNumBands == lNumBandsCompleted)?0L:
                    pPDevice->lLastBandLength + (lNumBands-lNumBandsCompleted-1L)
                    * pPDevice->lLengthRes;
        pBandRect->xRight  = pPDevice->lCapsWidth;
        pBandRect->yBottom = pBandRect->yTop+(lNumBands == lNumBandsCompleted?
                             pPDevice->lLastBandLength:pPDevice->lLengthRes);
      }
      else
      {
        pBandRect->xLeft = (lNumBands == lNumBandsCompleted)?0L:
                    pPDevice->lLastBandLength+(lNumBands-lNumBandsCompleted-1L)
                    * pPDevice->lLengthRes;
        pBandRect->yBottom = pPDevice->lCapsHeight;
        pBandRect->xRight  = pBandRect->xLeft+(lNumBands == lNumBandsCompleted?
                             pPDevice->lLastBandLength : pPDevice->lLengthRes);
      }

      pPDevice->lBandOffset = pPDevice->lLengthRes/2-pPDevice->lLastBandLength;

      if (lNumBands != lNumBandsCompleted)
        pPDevice->lBandOffset -= (lNumBands-lNumBandsCompleted) *
                                pPDevice->lLengthRes;

      if (lNumBandsCompleted != 1)
      {
        output_bytes(pDDC, "FR");
        set_default_p1p2(pDDC);
      }
    }
    else
    {
      pBandRect->yTop    = pPDevice->lCapsHeight;
      pBandRect->xRight  = pPDevice->lCapsWidth;
    }
  }
  else
    end_page(pDDC, hDC, FALSE, FunN);

  return (1);
}

/***************************************************************************
 *
 * FUNCTION NAME = output_comma
 *
 * DESCRIPTION   = output a comma
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID  output_comma(PDDC pDDC)
{
  output_bytes(pDDC, ",");
}

/***************************************************************************
 *
 * FUNCTION NAME = output_number
 *
 * DESCRIPTION   = output a number (integer implied) to the plotter as
 *                 part of a command string.
 *
 * INPUT         = pDDC - pointer to device driver context
 *                 Num  - number to output
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

VOID  output_number(PDDC pDDC,LONG Num)
{
  CHAR IntBuf[16];

  int_to_str(Num, IntBuf, 1);
  output_bytes(pDDC, IntBuf);

  return;
}

/***************************************************************************
 *
 * FUNCTION NAME = plot_line
 *
 * DESCRIPTION   = This function plots a line from the current
 *                 physical position, or queues the line for later
 *                 output if an area fill is currently in effect.
 *
 * INPUT         = pDDC - pointer to device driver context
 *                 pPts - pointer to the end point of line
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  plot_line(PDDC pDDC,PPOINTL pPts)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  POINTL RelativePts;

#if      FILLOPTPATH

  if (pDDC->pOptBuffer && pDDC->usOptMode == FOM_RECORD)
  {
    queue_opt_buffer(pDDC, pPDevice->PhysPosition, *pPts);   /* b724595 */

    /*
    ** pPDevice->bLineInConstruction = TRUE;
    ** pPDevice->bPenIsUp = FALSE;
    */

  }
  else
#endif
  {
    if (pPDevice->bLineInConstruction)
      output_comma(pDDC);
    else
    {
      if (pPDevice->bPenIsUp)
      {
        output_bytes(pDDC, "PD");
        pPDevice->bPenIsUp = FALSE;
      }
      output_bytes(pDDC, "PR");
      pPDevice->bLineInConstruction = TRUE;
      pPDevice->bPlotRelative = TRUE;
    }
    RelativePts.x = pPts->x-pPDevice->PhysPosition.x;
    RelativePts.y = pPts->y-pPDevice->PhysPosition.y;
    construct_point(pDDC, &RelativePts, TRUE);
  }

  pPDevice->PhysPosition = *pPts;

  return (1);
}

/***************************************************************************
 *
 * FUNCTION NAME = set_default_p1p2
 *
 * DESCRIPTION   = Initialize the default plotting extents.
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  set_default_p1p2(PDDC pDDC)
{
  //SHORT Index = PlotterClass[pDDC->pPDevice->Plotter].P1P2Index;
  //PRECTS pCorner = &P1P2Table[Index][pDDC->pPDevice->pSetup->Size];
  SHORT Plotter = pDDC->pPDevice->Plotter;
  BOOL  bResult = TRUE;
  RECTL Rectl;

  Rectl = pDDC->pPDevice->DefaultP1P2;

  /*
  ** if HPGL2 device-
  */
  if (PlotterClass[Plotter].usHPGLCaps & HPGL2)
  {
    /*
    ** Set the Plot size to p1p2
    ** for accurate scaling
    */
    output_bytes(pDDC, "PS");
    output_number(pDDC, Rectl.xRight - Rectl.xLeft);
    output_comma(pDDC);
    output_number(pDDC, Rectl.yTop - Rectl.yBottom);
  }

  bResult = (set_p1p2(pDDC, &Rectl));

  return (bResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = set_default_scale
 *
 * DESCRIPTION   = set the default scale for the current hardcopy
 *                 device and media size.
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  set_default_scale(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  RECTL Scale;

  /*
  ** Calculate the current point factor and multiply it
  ** by the x and y scaling factors
  **
  ** 1016 Plotter units per inch
  ** mult by 1000 to get 3 decimal places
  */
  Scale.xRight  = pPDevice->lIWScale;
  Scale.yTop    = Scale.xRight;
  Scale.xLeft   = 0;
  Scale.yBottom = 0;

  if (pPDevice->usHPGLType & HPGL2)
  {
    set_scale(pDDC, &Scale, SCALE_POINTFACTOR);
  }
  /*
  ** On some HPGL1 devices Point-Factor scaling does not work
  ** so we will use regular Anisotropic scaling.
  **
  ** divide P1P2 by the point factor(with imposed decimal places) to
  ** calculate the correct ratio of user units.
  */
  else
  {
    LONG lCapsWidth = pPDevice->lCapsWidth;
    LONG lCapsHeight = pPDevice->lCapsHeight;

    /*
    **  Match scale to P1P2
    **  If the orientation is not the same as the P1P2 setup,
    **  swap the width and height to make the scale match the P1P2.
    */
    if ((pPDevice->fsTransform & DXF_PORTRAIT))
    {
      SWAPLONG(lCapsWidth, lCapsHeight);
    }
    /*
    **  Set Scale X coordinate
    */
    if (pPDevice->CurrentP1P2.xLeft < 0)
    {
      /*
      **  Fix numbers for centered Origin
      */
      Scale.xLeft  = -lCapsWidth / 2;
      Scale.xRight =  lCapsWidth / 2;
    }
    else
    {
      Scale.xLeft  = 0;
      Scale.xRight = lCapsWidth;
    }

    /*
    **  Set Scale Y coordinate
    */
    if (pPDevice->CurrentP1P2.yBottom < 0)
    {
      /*
      **  Fix numbers for centered Origin
      */
      Scale.yBottom = -lCapsHeight / 2;
      Scale.yTop    =  lCapsHeight / 2;
    }
    else
    {
      Scale.yBottom = 0;
      Scale.yTop    = lCapsHeight;
    }

    return (set_scale(pDDC, &Scale, SCALE_ANISOTROPIC));
  }
  return(TRUE);
}

/***************************************************************************
 *
 * FUNCTION NAME = set_line_type
 *
 * DESCRIPTION   = setup the active line type, ie, dot-dash, dash,
 *                 whatever.
 *
 * INPUT         = pDDC     - pointer to device driver context
 *                 LineType - line type to set
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/
/*
**    HPGL2 LINE DEFINES
**                     Kind        Value        Discription
**      Line Ends       1            1               (hpgl2 + OS/2 default)
**                                   2          Square
**                                   3          Trianglular
**                                   4          Round
**
**      LineJoins       2            1          Mitered (hpgl2 default)
**                                   2          Mitered/beveled (OS/2 Miter)
**                                   3          Triangular
**                                   4          Round
**                                   5          Beveled (OS/2 default)
**                                   6          NO join applied
**      Miter Limit     3            **         5= default MiterLimit
**
*****************************************************************************/

#define  HPGL2_LINEEND_BUTT     1      /*      (default)                    */
#define  HPGL2_LINEEND_SQUARE   2      /* Square                            */
#define  HPGL2_LINEEND_ROUND    4      /* Round                             */

#define  HPGL2_LINEJOIN_MITERED      1 /* Mitered (default)                 */
#define  HPGL2_LINEJOIN_MITER_BEVEL  2 /* Mitered/Beveled(Bev. > Mit.limit  */
#define  HPGL2_LINEJOIN_ROUND        4 /* Round                             */
#define  HPGL2_LINEJOIN_BEVELED      5 /* Beveled                           */
#define  HPGL2_LINEJOIN_NOJOIN       6 /* NO join applied                   */

BOOL  set_line_type(PDDC pDDC,USHORT usLineType)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  SHORT Plotter = pPDevice->Plotter;


  if (usLineType != pPDevice->usLineType)
  {
    switch (usLineType)
    {
    /*
    ** C Set/2 Conversion -  Jim Russell
    ** New linetype values were put in per MarkV
    ** Old values are commented out
    */
      case  LINETYPE_DOT :
//        output_bytes(pDDC, "LT2,1");
        output_bytes(pDDC, "LT2,.5");
        break;
      case  LINETYPE_SHORTDASH :
//        output_bytes(pDDC, "LT2,2");
        output_bytes(pDDC, "LT2,1");
        break;
      case  LINETYPE_DASHDOT :
//        output_bytes(pDDC, "LT5,4");
        output_bytes(pDDC, "LT5,2");
        break;
      case  LINETYPE_DOUBLEDOT :
//        output_bytes(pDDC, "LT2,.5");
        output_bytes(pDDC, "LT2,.5");
        break;
      case  LINETYPE_LONGDASH :
//        output_bytes(pDDC, "LT3,4");
        output_bytes(pDDC, "LT3,2");
        break;
      case  LINETYPE_DASHDOUBLEDOT :
//        output_bytes(pDDC, "LT6,4");
        output_bytes(pDDC, "LT6,2");
        break;
      case  LINETYPE_ALTERNATE :
//        output_bytes(pDDC, "LT2,.25");
        output_bytes(pDDC, "LT2,.25");
        break;
      default  :
        output_bytes(pDDC, "LT");
        break;
    }
    pPDevice->usLineType = usLineType;
    pPDevice->bLineInConstruction = FALSE;
  }

  /*
  ** if we are on an HPGL2 device, check the lineend and linejoin
  ** conditions.
  */
  if (PlotterClass[Plotter].usHPGLCaps & HPGL2)
  {
    /*
    ** if we are doing wide lines from
    ** strokepath set the lineend and line join.
    */
    if (pDDC->DCState.bWideLines)
    {
      USHORT usLineJoin = 0;
      USHORT usLineEnd = 0;


      if (pDDC->DCState.lbnd.usJoin != pPDevice->iLineJoin)
      {
        switch (pDDC->DCState.lbnd.usJoin)
        {
          case  LINEJOIN_ROUND :
            usLineJoin = HPGL2_LINEJOIN_ROUND;
            break;
          case  LINEJOIN_MITRE :
            usLineJoin = HPGL2_LINEJOIN_MITER_BEVEL;
            break;
          case  LINEJOIN_BEVEL :
          default  :
            usLineJoin = HPGL2_LINEJOIN_BEVELED;
            break;
        }                              /* endswitch                         */
        pPDevice->iLineJoin = pDDC->DCState.lbnd.usJoin;
      }

      if (pDDC->DCState.lbnd.usEnd != pPDevice->iLineEnd)
      {
        switch (pDDC->DCState.lbnd.usEnd)
        {
          case  LINEEND_SQUARE :
            usLineEnd = HPGL2_LINEEND_SQUARE;
            break;
          case  LINEEND_ROUND :
            usLineEnd = HPGL2_LINEEND_ROUND;
            break;
          case  LINEEND_FLAT :
          default  :
            usLineEnd = HPGL2_LINEEND_BUTT;
            break;
        }                              /* endswitch                         */
        pPDevice->iLineEnd = pDDC->DCState.lbnd.usEnd;
      }

      /*
      ** Set the line attributes if they have changed
      */
      if (usLineJoin)
      {
        output_bytes(pDDC, "LA2,");
        output_number(pDDC, (LONG)usLineJoin);

        if (usLineEnd)
        {
          output_bytes(pDDC, ",1,");
          output_number(pDDC, (LONG)usLineEnd);
        }
        pPDevice->bLineInConstruction = FALSE;
      }
      else
      {
        if (usLineEnd)
        {
          output_bytes(pDDC, "LA1,");
          output_number(pDDC, (LONG)usLineEnd);
          pPDevice->bLineInConstruction = FALSE;
        }
      }
    }
    /*
    ** else set line end and line join to the
    ** defaults if necessary
    */
    else
    {
      if (pPDevice->iLineJoin != LINEJOIN_DEFAULT &&
          pPDevice->iLineJoin != LINEJOIN_BEVEL)
      {
        output_bytes(pDDC, "LA2,");
        output_number(pDDC, (LONG)HPGL2_LINEJOIN_BEVELED);

        pPDevice->iLineJoin = LINEJOIN_DEFAULT;
        pPDevice->bLineInConstruction = FALSE;
      }

      if (pPDevice->iLineEnd != LINEEND_DEFAULT &&
          pPDevice->iLineEnd != LINEEND_FLAT)
      {
        output_bytes(pDDC, "LA1,");
        output_number(pDDC, (LONG)HPGL2_LINEEND_BUTT);

        pPDevice->iLineEnd = LINEEND_DEFAULT;
        pPDevice->bLineInConstruction = FALSE;
      }
    }
  }

  return (TRUE);
}

/***************************************************************************
 *
 * FUNCTION NAME = set_p1p2
 *
 * DESCRIPTION   = set the current plotting extents
 *
 * INPUT         = pDDC    - pointer to device driver context
 *                 pCorner - bounding rectangle
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  set_p1p2(PDDC pDDC,PRECTL pCorner)
{
  PRECTL pP1P2 = (PRECTL)&pDDC->pPDevice->CurrentP1P2;

  /*
  ** Special case for Different paper orientation
  */
  if (pDDC->pPDevice->fsTransform & DXF_ROTATE90)
    set_rotation(pDDC,90L,FALSE);

  /*
  ** Set P1P2
  */
  output_bytes(pDDC, "IP");
  output_number(pDDC, pCorner->xLeft);
  output_comma(pDDC);
  output_number(pDDC, pCorner->yBottom);
  output_comma(pDDC);
  output_number(pDDC, pCorner->xRight);
  output_comma(pDDC);
  output_number(pDDC, pCorner->yTop);

  /*
  ** Update currentP1P2 accounting
  */
  *pP1P2 = *pCorner;

  return  TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = set_scale
 *
 * DESCRIPTION   = set the current values for plotter applied scaling.
 *
 * INPUT         = pDDC   - pointer to device driver context
 *                 pScale - scale
 *                 usType - Scale type
 *                          0 = (default) Anisotropic
 *                          1 = Isotropic
 *                          2 = Point-Factor
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  set_scale(PDDC pDDC,PRECTL pScale,USHORT usType)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  PRECTL pCurrentScale = (PRECTL)&pDDC->pPDevice->CurrentScale;

  /*
  ** set scale
  */
  output_bytes(pDDC, "SC");
  output_number(pDDC, pScale->xLeft);
  output_comma(pDDC);
  if (usType == SCALE_POINTFACTOR)
  {
    construct_float(pDDC, pScale->xRight, pPDevice->lIWScaleMultiplier);
  }
  else
  {
    output_number(pDDC, pScale->xRight);
  }
  output_comma(pDDC);
  output_number(pDDC, pScale->yBottom);
  output_comma(pDDC);
  if (usType == SCALE_POINTFACTOR)
  {
    construct_float(pDDC, pScale->yTop, pPDevice->lIWScaleMultiplier);
  }
  else
  {
    output_number(pDDC, pScale->yTop);
  }

  if (usType)
  {
    output_comma(pDDC);
    output_number(pDDC, (LONG)usType);
  }

  /*
  ** Update current scale accounting
  */
  *pCurrentScale = *pScale;

  pDDC->pPDevice->bLineInConstruction = FALSE;

  return  TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = set_rotation
 *
 * DESCRIPTION   = Rotates the plotter coordinate system 90, 180, and 270
 *
 * INPUT         = pDDC   - pointer to device driver context
 *                 lAngle - rotation angle 0, 90, 180  & 270
 *                 fChngeIPIW - BOOL update the IP( p1,p2) and IW(inputwindow)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE
 * RETURN-ERROR  = FALSE
 *
 **************************************************************************/

BOOL  set_rotation(PDDC pDDC,LONG lAngle, BOOL fChangeIPIW)
{
  PPDEVICE pPDevice = pDDC->pPDevice;

  /*
  ** set Rotation
  */
  if (lAngle != pPDevice->lCurrentRotate)
  {
    output_bytes(pDDC, "RO");
    output_number(pDDC, lAngle);
    pPDevice->lCurrentRotate = lAngle;
    if (fChangeIPIW)
    {
      output_bytes(pDDC, "IPIW");
    }
  }
  return  TRUE;
}

/***************************************************************************
 *
 * FUNCTION NAME = start_doc
 *
 * DESCRIPTION   = setup the plotter for a new document.  This
 *                 function is called by start page, and is the second
 *                 highest level initialization call.  It initializes
 *                 the beginning of a document, which is different
 *                 from the beginning of a page, since multiple pages
 *                 may exist in a document.
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  start_doc(PDDC pDDC)
{
  BOOL bResult = TRUE;
  PPDEVICE pPDevice = pDDC->pPDevice;

  pPDevice->bDocStarted = TRUE;


  if (!pPDevice->bJobStarted)
    bResult = start_job(pDDC);

  if (bResult && pDDC->usDCType == OD_QUEUED)
  {

    /*
    ** Caused ED.exe and picview not to print. MarkV
    ** if (!pPDevice->pszDocumentName)
    ** GplErrSetWarning(PMERR_STARTDOC_NOT_ISSUED);
    */
    bResult = SplQmStartDoc(pPDevice->hSpooler,
                            pPDevice->pszDocumentName ?
                                      pPDevice->pszDocumentName : (PSZ)"");
  }

  // if we need to band (that implies journaling)
  if (pPDevice->bBanding)
  {
     ULONG ulSize;
     // We may have already allocated an hjournal if the app
     // improperly called DEVESC_STARTDOC multiple times
     if (!pPDevice->hJournalGpl)
     {
        // The driver is responsible for allocating the memory!!
        // ... but first query the size.
        ulSize = GplJournalCreateInstance (NULL, NULL, 0);
        ASSERT (ulSize);
        if (ulSize)
        {
          // allocate the memory
          pPDevice->hJournalGpl = GplMemoryAlloc (pPDevice->hmcbHeap, ulSize);
          ASSERT (pPDevice->hJournalGpl);
          memset (pPDevice->hJournalGpl, 0, ulSize);
        }
     } /* end if */

     GplJournalCreateInstance (pPDevice->hJournalGpl,
                               &pPDevice->IJournal,
                               0);
  } /* end if */
  else
  {
    if (pPDevice->bRaster)
    {
       /*
       ** Erase the shadow DC bitmap only if we are not banding.  If we are
       ** banding, it is assumed that the banding code will erase the band
       ** bitmap.  Only need to erase the hps of the surface between
       ** pages. ( page 1 thru (N-1) ).
       */
       GreErasePS (pDDC->hdc);
     }
  }

  return (bResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = start_job
 *
 * DESCRIPTION   = send the necessary command sequence to start a plot
 *                 job
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  start_job(PDDC pDDC)
{
  BOOL bResult = TRUE;
  PPDEVICE pPDevice = pDDC->pPDevice;

  pPDevice->bJobStarted = TRUE;

  if (pDDC->usDCType == OD_DIRECT)
  {
    bResult = open_file_or_device(pDDC);
    pPDevice->bRealPlotter = TRUE;
  }
  else
  {
    if (pDDC->usDCType == OD_QUEUED)
      if ((pPDevice->hSpooler = open_spooler_queue(pDDC)) == 0L)
        bResult = FALSE;

    pPDevice->bRealPlotter = FALSE;
  }

  return (bResult);
}

/***************************************************************************
 *
 * FUNCTION NAME = start_page
 *
 * DESCRIPTION   = send the necessary commands to set up the plotter
 *                 for a new page of output.
 *
 * INPUT         = pDDC - pointer to device driver context
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 * RETURN-ERROR  = NONE
 *
 **************************************************************************/

BOOL  start_page(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;

  /*
  ** moved to escquery.c-escape and dosio.c-output_bytes
  **pPDevice->bSomeRawData = FALSE;
  */

  if (!pPDevice->bDocStarted)
    start_doc(pDDC);

  if (pDDC->usDCType == OD_DIRECT)
  {
    if (pPDevice->pSetup->PaperFeed == 0 &&
        (pPDevice->CurrentPage || !pPDevice->pSetup->bPreloaded))
    {
      /*
      ** Wait for all the data for the previous page to be output
      */
      GplThreadFlushBuffer(pPDevice->hThread, TRUE);
      prompt_for_page(pDDC);
    }
  }

  pPDevice->bPageStarted = TRUE;
  send_header(pDDC);
  ++pPDevice->CurrentPage;

  return (1);
}

