program HPGLVIEW;
{-----------------------------------------------------------------------------}
 (*                          An HPGL viewer

    The viewer takes a file of HPGL commands and displays the plot commands
    on the screen.

    It always represents the page (A3 or A4) lying sideways on the
    screen, to preserve the maximum resolution, and ignores the aspect ratio
    of the screen for the same reason.

    Its designed to show you what the plot looks like on the page not be
    an absolute mimic of a plot.  If you need absolute precision, plot it.

-------------------------------------------------------------------------------
            HPGLVIEW - a on-screen Previewer for HPGL files
    
                Copyright (C) 1991 Giovanni S. Moretti

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

-----------------------------------------------------------------------------
    Giovanni Moretti                    | EMAIL:  G.Moretti@massey.ac.nz
    Computer Centre,  Massey University |  
    Palmerston North, New Zealand       |
-----------------------------------------------------------------------------

   Please send me a copy of any major hacks/improvements so I can coordinate
   any new releases.

   HP-GL is (probably) a trademark of Hewlett-Packard Company.

*)
{-----------------------------------------------------------------------------}
{ RCS Control Information
  $Author: Giovanni_Moretti $
  $Date: 91/04/18 15:57:59 $
  $Revision: 1.1 $
  $Log:	hpglview.pas $
Revision 1.1  91/04/18  15:57:59  ROOT_DOS
Initial revision

} 
uses graph, dos, crt, smalfont, { Like FONTS.PAS with only SMALLFONT left}
     drivers;

const A3_char_width  = 0.285; {cm}
      A3_char_height = 0.375;

      A4_char_width  = 0.187;
      A4_char_height = 0.269;

      unknown_max    = 100;

var pen_down     : boolean;
    cmd          : string;          {Current HPGL command being done}
    x, y, x1,y1  : real;            {and the numeric arguments to this cmd}
    lbl          : string;          {or a character string arg for LB cmd }

    filename     : string;
    inf          : text;            {Input file}

    screen_max_X : integer;         {No of dots across screen -1}
    screen_max_Y : integer;         {and number down            }

    Graphics_Driver  : integer;     { for BGI - may be set manually with /G }
    Graphics_mode    : integer;     { for BGI - may be set manually with /G }
{-----------------------------------------------------------------------------}
    use_plotter_units : boolean;    {If TRUE => NO Scaling, FALSE => Scale}

    x_max        : real;            {Maximum and Minimum values as defined}
    x_min        : real;            {by the SC (Scale) command            }
    y_max        : real;
    y_min        : real;

    x_p1, y_p1   : integer;
    x_p2, y_p2   : integer;
{-----------------------------------------------------------------------------}
    Paper_size     : integer;         {Either a 3 or 4 (A3 or A4) }
    X_plot_area_mm : integer;
    Y_plot_area_mm : integer;
    args           : boolean;
    hard_clip_X    : integer;
    hard_clip_Y    : integer;

    colour         : integer;         {Current colour from video palette}
{-----------------------------------------------------------------------------}
    char_height    : real;         {Currently set Character Height and Width}
    char_width     : real;         {Always defined - used by SI and DI cmds }

    text_direction : word;         {Used by DI cmd }
{-----------------------------------------------------------------------------}
    symbol_mode    : boolean;      {Whether a symbol is drawn on a PA or PR }
    symbol_char    : char;         {command - used by the SM instruction    }
{-----------------------------------------------------------------------------}
    {Remember unimplemented commands in this array for later }

    unknown_cmds   : array [1..unknown_max] of string;
    unknown_count  : integer;

    cnt          : integer;
    pause        : char;

    ch           : char;
    debug        : boolean;
    digits       : set of char;
    i            : integer;      {General Purpose Integer}
    finished     : boolean;

    initialise_cmd_count : integer;  {Used to pause on 2nd, 3rd ... IN cmd}
    IP_cmd_count         : integer;  {Used to change colours on 2nd .. IP cmd}
    auto_detect_graphics : boolean;  {altered be /G command line option }
{-----------------------------------------------------------------------------}

procedure SET_DEFAULT_TEXT_DIRECTION;
begin
  text_Direction:= HorizDir;
  SetTextJustify(LeftText,  BottomText);
end;

{-----------------------------------------------------------------------------}
{ Set up the appropriate scaling factors to draw text of the size defined by
  HEIGHT and WIDTH.

  HEIGHT and WIDTH are remembered in CHAR_HEIGHT and CHAR_WIDTH so
  this procedure  can be reused if TEXT_DIRECTION is redefined.

  This procedure is used whenever it is necessary to change either
  text size or direction.
}

procedure SET_TEXT_SIZE (width, height : real); {Arguments are in Centimetres}

var x_top, x_bottom, y_top, y_bottom : integer;
    width_in_mm, height_in_mm        : real;
    scale_x, scale_y                 : real;
    letter_height, letter_width      : integer;

begin
      {Scale ratios by 100 so as not to lose too much precision on ROUND()}
      x_top:= 100; x_bottom:= 100; y_top:= 100; y_bottom:= 100;

      {This Section Gives "Approximately" the correct size lettering }
      SetTextStyle(SmallFont, HorizDir, 2);
      letter_height:= TextHeight('M');     {Remember Size of Standard Characters}
      letter_width := TextWidth('M');

      width_in_mm := letter_width / screen_max_X * X_plot_area_mm;
      scale_x:= (width*10) / width_in_mm;     { *10 as SI args are in cm}
      if scale_x > 10 then scale_x:= 10;
      if scale_X > 1 then x_top   := round(scale_x   *100)
      else                x_bottom:= round(1/scale_X *100);

      height_in_mm:= Letter_Height / screen_max_Y * Y_plot_area_mm;
      scale_y:= (height*10) / height_in_mm;   { *10 as SI args are in cm}
      if scale_y > 10 then scale_y:= 10;
      if scale_y > 1 then Y_top   := round(scale_Y   *100)
      else                Y_bottom:= round(1/scale_Y *100);

      SetUserCharSize(x_top, x_bottom, y_top, y_bottom);
      SetTextStyle(smallFont, text_direction, UserCharSize);
end;

{-----------------------------------------------------------------------------}

procedure SET_DEFAULT_TEXT_SIZE;
begin
  if paper_size = 4 then      { A4 Paper }
    begin
      char_height   := A4_char_height;
      char_width    := A4_char_width;
    end
  else                        { A3 Paper }
    begin
      char_width    := A3_char_width;
      char_height   := A3_char_height;
    end;
  set_text_size(char_width, char_height);
end;
{-----------------------------------------------------------------------------}

procedure SET_P1_P2 (x, y, x1, y1 : integer);
begin
  x_p1:= x;
  y_p1:= y;
  x_p2:= x1;
  y_p2:= y1;
end;
{-----------------------------------------------------------------------------}

procedure SET_DEFAULT_P1_P2;
begin
  if paper_size = 4 then     { A4 Paper }
    begin
      x_p2          := 10430;
      x_p1          :=   430;
      y_p2          :=  7400;
      y_p1          :=   200;
    end
  else                        { A3 Paper }
    begin
      x_p2          := 15580;
      x_p1          :=   380;
      y_p2          := 10430;
      y_p1          :=   430;
    end;
end;
{-----------------------------------------------------------------------------}

procedure SET_PAPER_SIZE ( default :  integer);
begin
  if default <> 0 then paper_size:= default
  else
    repeat
      write('A4 or A3 paper (3/4) : ');
      readln(paper_size);
    until paper_size in [3,4];

  if paper_size = 4 then
    begin                     { A4 Paper }
      X_plot_area_mm:=   270;
      Y_plot_area_mm:=   190;
      hard_clip_X   := 10870;
      hard_clip_Y   := 7600;
    end
  else                        { A3 Paper }
    begin
      X_plot_area_mm:=   399;
      Y_plot_area_mm:=   271;
      hard_clip_X   := 15970;
      hard_clip_Y   := 10870;
    end;
end;

{-----------------------------------------------------------------------------}
{Search the list of already Unknown Commands to see if the current one has
 already been added to the list
}

function SEEN_BEFORE (cmd : string) : boolean;
var i : integer;
    found : boolean;
begin
  i:= 0;
  found:= false;
  while (i < unknown_count) and not found do
    begin
      i:= i+1;
      if unknown_cmds[i] = cmd then found:= true;
    end;
  seen_before:= found;
end;
{-----------------------------------------------------------------------------}

{ READARG - a version of READ-A-NUMBER that will handle numbers that start
  with a decimal point (no leading digit)

  On exit skip past any trailing terminators/separators
}

procedure READARG (var num: real);
label 99;
var seen_point : boolean;
    divisor    : integer;
    is_negative: boolean;
begin
  while not eof (inf) and not (ch in digits+['-']) do
     read(inf, ch);  {Just in Case}

  if eof(inf) then goto 99;

  num:= 0;
  seen_point := false;
  divisor    := 1;
  is_negative:= false;

  if ch = '-' then begin is_negative:= true; read(inf, ch); end;

  while ch in digits do
    begin
      if ch = '.' then seen_point:= true
      else
        begin
          if seen_point then divisor:= divisor * 10;
          num:= num*10 + (ord(ch)-ord('0'));
        end;
      read(inf, ch);
    end;

  if seen_point then num:= num / divisor;

  if is_negative then num:= -num;

(*  if ch in [',',';'] then read(inf, ch);    {Skip past terminator/separator}*)
  99:
end;
{-----------------------------------------------------------------------------}
{ GET THE NEXT HPGL COMMAND  into "cmd" }

{  On EXIT:     "cmd"    - contains HPGL command }

procedure GET_CMD;
label 99;
begin

{ Skip past any non-alphabetic characters }

  while not eof(inf) and not (ch in ['A'..'Z','a'..'z']) do
    read(inf, ch);  {Skip junk}

  if eof(inf) then begin cmd:= 'ZZ'; goto 99; end;

   cmd := '';
   cmd:= concat(cmd, upcase(ch));
   read(inf, ch);
   if ch <> ';' then
     begin
       cmd:= concat(cmd, upcase(ch));
       read(inf, ch);
     end;
  99:
end;
{-----------------------------------------------------------------------------}
procedure READ_OR_RESET_ARGS (num_of_args : integer);
begin
  if ch = ';' then args:= false
  else
    begin
      readarg(x);
      if num_of_args >= 2 then readarg(y);
      if num_of_args >= 3 then readarg(x1);
      if num_of_args  = 4 then readarg(y1);
      args:= true;
    end
end;

{-----------------------------------------------------------------------------}

{ SCALE THE PARAMETERS X & Y INTO SCREEN UNITS }

{If scaling is active the incoming values should be between:

   X_min .. X_max  which is scaled to fit between x_p1 .. x_p2 and ditto for Y

 If not, then use plotter units and scale these to fit onto the screen
}

procedure SCALE (var x, y : integer);
begin
  if use_plotter_units then
    begin
      x:= round( x/(hard_clip_X) * screen_max_X);
      y:= screen_max_y - round( y/(hard_clip_y) * screen_max_Y);
    end
  else {Scaling is Active - Fit plot Using Coords P1 and P2 - defined in IP cmd}
    begin
      x:= round((((x-x_min)/(x_max-x_min)) * (x_p2-X_p1) + x_p1)/Hard_clip_X * Screen_max_X);
      y:= round((((y-y_min)/(y_max-y_min)) * (y_p2-y_p1) + y_p1)/Hard_clip_Y * Screen_max_Y);
      y:= screen_max_y - y;
    end;
end;

{-----------------------------------------------------------------------------}
            {PU - PEN UP - Set Current co-ord if any arguments}

procedure do_PU_cmd;

var x_int, y_int: integer;
begin
  read_or_reset_args(2);
  if args then
    begin
      x_int:= round(x);
      y_int:= round(y);
      scale(x_int, y_int);
      moveTo(x_int, y_int);
    end;
  pen_down:= false;
end; 
{----------------------------------------------------------------------------}

           {PD - PEN DOWN - Move to and Draw if a co-ord present }

procedure do_PD_cmd;

var    x_int, y_int: integer;
begin
  read_or_reset_args(2);
  if args then
    begin
      x_int:= round(x);
      y_int:= round(y);
      scale(x_int, y_int);
      lineto(x_int,y_int);
    end;
  pen_down:= true;
end; 


{-----------------------------------------------------------------------------}

        { PA - POINT ABSOLUTE - Move to x,y, Drawing if PEN DOWN}

procedure do_PA_cmd;

var    x_int, y_int: integer;
      save_x, save_y : integer;
begin
  read_or_reset_args(2);
  if args then
    begin
      x_int:= round(x);
      y_int:= round(y);
      scale(x_int, y_int);
      if pen_down then lineto(x_int,y_int)
      else moveto(x_int,y_int);
       if symbol_mode then {If symbol-mode then PLOT symbol Char at Destination}
        begin
          save_x:= getX; save_y:= getY;
          outtext(symbol_char);
          moveto(save_x, save_y);  {Ensure current point doesn't change}
        end;
    end;
end;


{-----------------------------------------------------------------------------}

         { PR - POINT RELATIVE - Moveto or Draw relative to Last position}

procedure do_PR_cmd;

var    x_int, y_int: integer;
      save_x, save_y : integer;
begin
  read_or_reset_args(2);
  if args then
    begin
      x_int:= round(x);
      y_int:= round(y);
      scale(x_int, y_int);
      x:= getX;
      y:= GetY;
      if pen_down then lineto(GetX+x_int, GetY+y_int)
      else moveto(GetX+x_int, GetY+y_int);
       if symbol_mode then  {Plot symbol character at endpoint}
        begin
          save_x:= getX; save_y:= getY;
          outtext(symbol_char);
          moveto(save_x, save_y);
        end;
    end;
end; 

{-----------------------------------------------------------------------------}

           { LB - LABEL - Output text in current SIze and DIrection}

procedure do_LB_cmd;
begin
  lbl:= '';
  while ch <> #03 do
    begin
      lbl:= concat(lbl, ch);
      read(inf, ch);
    end;
  read(inf, ch); {skip past ";"}
  outtext(lbl);
end; 

{-----------------------------------------------------------------------------}

               { CI - DRAW CIRCLE - centred at current point }

{ This should really take into account the current scaling so you can draw 
  elipses (I think). At the moment it always draws perfect circles, using the
  X size as its Radius
}

procedure do_CI_cmd;

begin
  read_or_reset_args(1);   { read Radius }
  x:=  x/(hard_clip_X) * screen_max_X;
  circle(getX, getY, round(x));
end; 

{-----------------------------------------------------------------------------}
       { IP - Set the Initial Points for use by the SCale command }

{ To display Concatenated Plots in a different colour, it swaps colours, if 
  available on each IP cmd.  This is useful for viewing different layers of 
  a printed circuit layout 
}

procedure do_IP_cmd;
begin
  read_or_reset_args(4);
  if not args then set_default_P1_P2
  else
   begin
     set_P1_P2(round(x), round(y), round(x1), round(y1));
     colour:= colour - 1;
     if colour = 0 then colour:= GetMaxColor;
     setcolor(colour);
  end;
end; 

{-----------------------------------------------------------------------------}

                      { SC - Define Coordinate Scale }

{ If there are arguments, scale all following coordinates so that the 
  ranges defined by the SCALE command lies within the PLOT AREA defined 
  by the IP command coordinates.

  If no arguments the revert to using absolute plotter coordinates.
 }

procedure do_SC_cmd;
begin
  if ch = ';' then args:= false
  else
   begin   {Don't use READ_AND_RESET as args in wrong order (x,x1,y,y1) }
     readarg(x); readarg(x1); readarg(y); readarg(y1);
   end;
  if args = false then use_plotter_units:= true
  else
    begin          {Scale Command - from now on coords will arrive    }
      x_min:= x;   {                scaled between Xmin-Xmax, Ymin-max}
      y_min:= y;
      x_max:= x1;
      y_max:= y1;
      use_plotter_units:= false;
    end;
end; 

{-----------------------------------------------------------------------------}
                { IN - INITIALISE - Beep and wait for a keypress}

procedure do_IN_cmd;
begin
  set_default_P1_P2;
  pen_down:= false;
  if initialise_cmd_count > 0 then
    begin
      write(#7);
      pause:= readkey;
    end;
  initialise_cmd_count:= initialise_cmd_count+1;
end; 

{-----------------------------------------------------------------------------}

            { SP - SET PEN - Usually used to change PEN Colour}

procedure do_SP_cmd;
begin
  read_or_reset_args(1);
  if args then  {Display in a different colour if possible}
  if trunc(x) in [1..GetMaxColor] then
    setcolor(GetMaxColor - round(x) + 1)
  else
    begin
      colour:= colour-1;
      if colour = 0 then colour:= GetMaxColor;
      SetColor(Colour);
    end;
end; 

{-----------------------------------------------------------------------------}
    { SI - Define TEXT SIZE in Centimetres, or reset it to the Default size }

procedure do_SI_cmd;
begin
  read_or_reset_args(2);
  if args = false then set_default_text_size
  else
    begin
      set_text_size(x,y);
      {Now to remember for later - for use by DI instruction}
      char_width := x;
      char_height:= y;
    end;
end;

{-----------------------------------------------------------------------------}
            { SR - Set Text Size - Relative to Scaling Points }

{ This bit has only been loosely tested (ie once) }

procedure do_SR_cmd;

var  actual_width, actual_height : real; {for SR cmd}
begin
  read_or_reset_args(2);
  if args = false then set_default_text_size
  else
    begin
      actual_width := x/100 * (x_p2 - x_p1) * 0.0025; {cm}
      actual_height:= y/100 * (y_p2 - y_p1) * 0.0025; {cm}
      set_text_size(actual_width, actual_height);
      {Now to remember for later - for use by DI instruction}
      char_width := actual_width;
      char_height:= actual_height;
    end;
end; 

{-----------------------------------------------------------------------------}
       { SM - SYMBOL MODE - Plot a symbol after each plot/move}

procedure do_SM_cmd;
begin
  if ch = ';' then symbol_mode:= false
  else
    begin
      symbol_mode:= true;
      symbol_char:= ch;
    end;
  read(inf, ch);
end; 

{-----------------------------------------------------------------------------}
        { DI/DR - Set Orientation to display following Text }

{ Ideally we should be able to Rotate the direction of the text
  (ie horizontal but upside down, but Turbo's toolbox doesn't handle this
  Instead we'll use the SetTextJustify to do it in 90 degree increments
}

procedure do_DI_or_DR_cmd;
begin
  read_or_reset_args(2);
  if not args then  set_Default_Text_direction {set to Defaults}
  else
    begin
      if abs(x) >= abs(y) then {RUN > RISE}
        begin
	  text_Direction:= HorizDir;
	  if x > 0 then  SetTextJustify(LeftText,  BottomText) {  0 degrees}
	  else           SetTextJustify(RightText, TopText);   {180 degrees}
	end
      else
	begin
	  text_direction:= VertDir;
	  if y >= 0 then  SetTextJustify(RightText,  BottomText) { 90 degrees}
	  else            SetTextJustify(LeftText,   TopText);   {270 degrees}
        end;

      {Now call SET_TEXT_SIZE to alter TextDirection, using prev WIDTH&HEIGHT}
      set_text_size(char_width, char_height); {Either Default or Set by SI instr}
    end;
end;

{-----------------------------------------------------------------------------}
           {DF - Default - Reset various parameters to known state }

procedure do_DF_cmd;
begin
  set_default_text_direction;
  symbol_mode:= false;
  use_plotter_units:= true;  {Turn off scaling}
end;
{-----------------------------------------------------------------------------}

             { DECODE and EXECUTE the HPGL command in "cmd" }


procedure DECODE_and_EXECUTE_HPGL_COMMAND;
begin

  if      cmd = 'PU' then do_PU_cmd
  else if cmd = 'PD' then do_PD_cmd
  else if cmd = 'PA' then do_PA_cmd
  else if cmd = 'PR' then do_PR_cmd
  else if cmd = 'LB' then do_LB_cmd
  else if cmd = 'CI' then do_CI_cmd
  else if cmd = 'IP' then do_IP_cmd
  else if cmd = 'SC' then do_SC_cmd
  else if cmd = 'IN' then do_IN_cmd
  else if cmd = 'SP' then do_SP_cmd
  else if cmd = 'SI' then do_SI_cmd
  else if cmd = 'SR' then do_SR_cmd
  else if cmd = 'SM' then do_SM_cmd
  else if cmd = 'DF' then do_DF_cmd
  else if cmd = 'DI' then do_DI_or_DR_cmd
  else if cmd = 'DR' then do_DI_or_DR_cmd
  else if cmd = 'VS' then read_or_reset_args(1) {VELOCITY SET (of Pen) - Ignore}
  else if cmd = 'ZZ' then {End of file Sentinel - ignore this cmd}
  else  {UNIMPLEMENTED COMMAND}
    if unknown_count = unknown_max then finished:= true {abort}
    else
      begin  {Add Unknown Command to list of unknowns}
        unknown_count:= unknown_count+1;

        while (ch <> ';') and (not eof(inf)) do
          begin
            if (length(cmd) < 78) then cmd:= concat(cmd, ch);
            read(inf, ch);
          end;
         unknown_cmds[unknown_count]:= cmd;
      end;
end;
{-----------------------------------------------------------------------------}

procedure VIEW_UNKNOWN_COMMANDS;
var i : integer;
    ch : char;
begin
  clrscr;
  if unknown_count = 0 then Writeln('No unimplemented HPGL commands encountered')
  else
    begin
      writeln('                      UNIMPLEMENTED HPGL COMMANDS');
      writeln;
      for i:= 1 to unknown_count do
        begin
          writeln(unknown_cmds[i]);
          if (unknown_count mod 20 ) = 0 then
            begin
              write('Press any key : '); ch:= readkey;
            end;
        end;
      writeln;
    end;
    ch:= readkey;
end;
{-----------------------------------------------------------------------------}

procedure Graphics_Error(error: string);
begin
  Writeln(error, ': ', GraphErrorMsg(GraphResult));
  Halt(1);
end;


procedure REGISTER_GRAPHICS_BITS;
begin
  { Register all the drivers }
  if RegisterBGIdriver(@CGADriverProc)    < 0 then  graphics_error('CGA');
  if RegisterBGIdriver(@EGAVGADriverProc) < 0 then  graphics_error('EGA/VGA');
  if RegisterBGIdriver(@HercDriverProc)   < 0 then  graphics_error('Herc');
  if RegisterBGIdriver(@ATTDriverProc)    < 0 then  graphics_error('AT&T');
  if RegisterBGIdriver(@PC3270DriverProc) < 0 then  graphics_error('PC 3270');


  { Register SMALL Font - only one used }
  if RegisterBGIfont(@SmallFontProc) < 0 then graphics_error('Small Font');

  if auto_detect_graphics then
    Graphics_Driver := Detect;                { autodetect the hardware }

  InitGraph(Graphics_Driver, Graphics_Mode, '');  { activate graphics }
  if GraphResult <> grOk then                     { any errors? }
  begin
    Writeln('Graphics init error: ', GraphErrorMsg(Graphics_Driver));
    Halt(1);
  end;
end;

procedure OPTIONS_ERROR (msg : string);
begin
  writeln(msg);
  writeln('Type ');
  writeln('      HPGLVIEW ? ');
  writeln('for a list of valid options');
  writeln;
  halt(1);
end;
{-----------------------------------------------------------------------------}
{SET THE GRAPHICS DRIVER and MODE accordin to the Supplied arguments }

{ Graphics Driver and Mode are set up via a command line option of the form:

  /G graphics-driver graphics-mode

  Check that the mode is valid for the indicated driver, ABORT if it isn't.
}

procedure MANUAL_GRAPHICS_SETUP (driver_no, mode_no : integer);
var status : integer;
begin
  if paramcount < mode_no then options_error ('Must define Graphics Driver & Mode')
  else
    begin
      val(paramstr(driver_no), graphics_driver, status);
      if status <> 0 then options_error('Graphics driver must be integer');

      if not (graphics_driver in [1..10]) then
         options_error(concat('Invalid Graphics Driver : ', paramstr(driver_no)));

      val(paramstr(mode_no), graphics_mode, status);
      if status <> 0 then
         options_error(concat('Graphics mode must be integer : ',paramstr(mode_no)));

      {Check the Specified Mode is valid for Defined Driver}
      case graphics_driver of
       {CGA}        1 :if not (graphics_mode in [0..4]) then options_error('Graphics mode for CGA must be 0..4');
       {MCGA}       2 :if not (graphics_mode in [0..5]) then options_error('Graphics mode for MCGA must be 0..5');
       {EGA }       3 :if not (graphics_mode in [0..1]) then options_error('Graphics mode for EGA must be 0..1');
       {EGA64}      4 :if not (graphics_mode in [0..1]) then options_error('Graphics mode for EGA64 must be 0..1');
       {EGAMono}    5 :if not (graphics_mode in [3   ]) then options_error('Graphics mode for EGAMono must be  3');
       {IBM8514}    6 :if not (graphics_mode in [0   ]) then options_error('Graphics mode for IBM8514 must be  0');
       {HercMono}   7 :if not (graphics_mode in [0   ]) then options_error('Graphics mode for HercMono must be  0');
       {ATT400}     8 :if not (graphics_mode in [0..5]) then options_error('Graphics mode for ATT400 must be 0..5');
       {VGA}        9 :if not (graphics_mode in [0..2]) then options_error('Graphics mode for VGA must be 0..2');
       {PC3270}    10 :if not (graphics_mode in [0   ]) then options_error('Graphics mode for PC3270 must be 0');
      end; {case}
    end;
end;

procedure display_licence;
begin
clrscr;
writeln('             HPGLVIEW - a on-screen Previewer for HPGL files');
writeln('                Copyright (C) 1991 Giovanni S. Moretti');
writeln('');
writeln('    This program is free software; you can redistribute it and/or modify');
writeln('    it under the terms of the GNU General Public License as published by');
writeln('    the Free Software Foundation; either version 1, or (at your option)');
writeln('    any later version.');
writeln('');
writeln('    This program is distributed in the hope that it will be useful,');
writeln('    but WITHOUT ANY WARRANTY; without even the implied warranty of');
writeln('    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the');
writeln('    GNU General Public License for more details.');
writeln('');
writeln('    You should have received a copy of the GNU General Public License');
writeln('    along with this program; if not, write to the Free Software');
writeln('    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.');
writeln('-----------------------------------------------------------------------------');
writeln('    Giovanni Moretti                    | EMAIL:  G.Moretti@massey.ac.nz');
writeln('    Computer Centre,  Massey University |  ');
writeln('    Palmerston North, New Zealand       |');
writeln('-----------------------------------------------------------------------------');
writeln('Please send me copies of any major improvements so I can coordinate new');
writeln('versions.');
writeln;
write('Press any key for help screen : '); ch:= readkey;
end;
 {-----------------------------------------------------------------------------}
procedure QUICK_HELP;
begin
display_licence;
clrscr;
writeln('   *********  HPGLVIEW v1.0 - A Screen Previewer for HPGL files *********');
writeln('');
writeln('This program will display the contents of an HPGL file, normally intended for');
writeln('a plotter, on the PC''s screen.  The screen is treated as an A3 or A4 page and');
writeln('aspect ratio effects are ignored to maximise the available resolution.');
writeln('');
writeln('   HPGLVIEW  <filename> [/A3 | /A4 ] [/G graphics-driver graphics-mode ] [/D]');
writeln('');
writeln('  Options : /A3 /A4 - Paper size, /G - Graphics Mode, /D Show unknown HPGL cmds');
writeln('  Defaults: A4 page, Autodetect graphics driver, Ignore unknown cmds');
writeln;
writeln('GRAPHICS DRIVERS AND (VALID MODES): Try mode 0 if unsure.');
writeln('CGA    : 1(0-4)  CGA     : 2(0-5)  EGA: 3(0-1)     EGA64: 4(0-1)  EGAmono: 5(3)');
writeln('IBM8514: 6(0)    HercMono: 7(0)    ATT400: 8(0-5)  VGA: 9(0-2)    PC3270: 10(0)');
writeln('');
writeln('Recognised HPGL Commands');
writeln('  PA, PU, PD, PR, LB, CI, VS, SP, IP, SC, SM, SI, DI, IN, DR, SR, DF');
writeln('');
writeln('Plotter Limits: A4 - Plot area 270x190mm, Hard limits X/Y = 10870x 7600');
writeln('                A3             399x271mm                    15970x10870');
writeln;
writeln('written by Giovanni Moretti                       Email: G.Moretti@massey.ac.nz');
writeln;
halt(1);
end;

{----------------------------------------------------------------------------}
            { Decode in execute the command line options (if any ) }

procedure SET_OPTIONS;
var next : integer;
begin
  debug:= false;
  paper_size:= 4;
  auto_detect_graphics:= true;

  next:= 2;
  while next <= paramcount do
    begin
      if (paramstr(next) = '/h') or (paramstr(next) = '/H') then quick_help
      else
      if (paramstr(next) = '/a3')  or (paramstr(next) = '/A3') then
         paper_size:= 3
      else
      if (paramstr(next) = '/a4')  or (paramstr(next) = '/A4') then
         paper_size:= 4
      else
      if (paramstr(next) = '/d')  or (paramstr(next) = '/D') then debug:= true
      else
      if (paramstr(next) = '/g')  or (paramstr(next) = '/G') then
        begin
          auto_detect_graphics:= false;
          manual_graphics_setup(next + 1, next+2);
          next:= next+2;
        end
      else options_error(concat('Unknown Option : ',paramstr(next)));
      next:= next+1;
    end;
  set_paper_size(paper_size);
end; {set_options}
{----------------------------------------------------------------------------}
                                { MAINLINE }
begin
  digits:= ['0'..'9','.'];

  unknown_count    := 0;
  for i:= 1 to unknown_max do unknown_cmds[i]:= '';

  ch:= ' ';
  debug:= false;
  if paramcount = 0 then quick_help;

  filename:= paramstr(1);
  if filename[1] in [ '/', '?']  then quick_help;

  assign(inf, filename);
  {$I-} reset(inf); {$I+}
  if ioresult <> 0 then begin writeln('HPGLVIEW: ', filename, ' not found'); halt; end;

  {GOT A VALID FILENAME}

  set_paper_size(4);

  if paramcount > 1 then set_options;

  cnt:= 0;
  clrscr;
{---------------------------------------------------------------------------}
              { Set up graphics and display Boarder }

  register_graphics_bits;   {Load appropriate graphics driver and Font }

             { Draw the Border Representing the Printed Page }
  screen_max_X:= getMaxX;
  screen_max_Y:= getMaxY;
  moveto(0,0);
  lineto(screen_max_x,0);
  lineto(screen_max_x, screen_max_y);
  lineto(0, screen_max_y);
  lineto(0,0);

                     { Display File title at top of page }
  SetColor(GetMaxColor);
  colour:= GetmaxColor;
  SetTextStyle(smallfont,horizdir,6);
  SetTextJustify(CenterText,TopText);
  OutTextXY(screen_max_x div 2, 0, concat(' *** ', filename, ' *** '));
{----------------------------------------------------------------------------}

 { Set up HPGL Defaults related to Paper Size }

  text_direction      := horizDir;
  use_plotter_units   := true;      {Can be changed with SCALE command }
  symbol_mode         := false;
  initialise_cmd_count:= 0;
  IP_cmd_count        := 0;

  set_default_P1_P2;        {Define initial Scaling Points for this paper size}
  set_default_text_direction;
  set_default_text_size;    {As defined in HPGL manual}

 {------------------------------  Main Loop  --------------------------------}

  finished:= false;
  while not eof(inf) and not finished do
    begin
      if keypressed then   {If ESC pressed then abort immediately }
        begin
          ch:= readkey;
          if ch = #$1B {ESC} then finished:= true;
        end;
      if not finished and not eof(inf) then
        begin
          get_cmd;
          decode_and_execute_HPGL_command;
       end;
    end;
  if not finished then begin write(chr(7)); pause:= readkey; end;
  closegraph;
  if debug then view_unknown_commands;
end.
